Readers and Writers

Readers

  • Any stream of input data is called a Reader in Slag.
  • This philosophy pervades the language - for example, lists, ranges, strings, and files may all have create_reader() called on them to produce a reader of the appropriate type.
  • An object that can create a Reader object is called a Readable object and should incorporate the Readable<<DataType>> aspect.
  • Given an object that is a Reader<<DataType>>, Slag defines "read" as "to extract one unit of the given DataType".  If you have a Reader<<Char>>, you know that calling read() on it will return a single Char value.
  • The forEach loop automatically works with any Reader or Readable object, setting the loop counter to be each value read (after potentially calling create_reader()).  See advanced forEach loops for an example of creating a custom reader.

 

Readable<<DataType>>
underlying aspect : ReadableType
  • DESCRIPTION
    • Base aspect for any object that can create a reader of type 'DataType'. To make a Readable class, incorporate this aspect and implement create_reader().
    • forEach loops automatically work with readable and reader types.
  • METHODS
    • create_reader().Reader<<DataType>> : abstract
      • Creates and returns a reader capable of reading the data from this object.
    • to_List().DataType[]
      • Converts this Readable data into a list. Creates a reader and fowards the call to it.

 

Reader<<DataType>>
underlying aspect : ReaderType, ListAdaptable<<DataType>>
  • DESCRIPTION
    • Base aspect for all readers. A reader is something that can return a series of values of a given datatype. To make a class a Reader, incorporate this aspect and define has_another(), peek(), and read().
    • forEach loops automatically work with readable and reader types.
    • See also: Readable<<DataType>>, StandardReader<<DataType>>.
  • METHODS
    • has_another().Logical : abstract
      • Returns "true" if this reader has another value that can be previewed with peek() or read with read().
      • Some "generator" readers, such as random number generators, *always* have another value.
    • peek().DataType : abstract
      • Returns the next value that will be returned from read(). If there is no next value then a NoNextValueError is thrown.
    • read().DataType : abstract
      • Returns the next value from this reader. If (not has_another) then a NoNextValueError is thrown.
    • is_exhausted().Logical
      • Returns "true" if this reader does not have another value or "false" if it does.
    • available().Int32
      • Returns the minimum number of additional values that can be immediately read from this reader.
      • If the reader is exhausted or if it must block to peek or read next then the result will be 0. It is important to note that '0' does not necessarily mean the reader is exhausted.
      • Many readers will report that they have 1 available even if there are thousands of values waiting to be read. The 'available' property should be used as an aide for buffer sizes and the like but should not be taken as an absolute size.
    • remaining().Int32
      • This optional property returns how many values remain to be read before the reader is exhausted. Not all readers can implement this property; those that don't will throw an UnsupportedMethodError when it's accessed.
    • position().Int32
      • This optional property-read returns the zero-based read position of this reader. When a reader is first created position() returns 0. After a single read() it returns 1, and so on.
      • Readers that don't implement this property will throw an UnsupportedMethodError when it's accessed.
    • position(Int32 new_position)
      • This optional property-write sets the zero-based read position of this reader. When a reader is first created position() returns 0. After a single read() it returns 1, and so on. Readers that don't implement this property will throw an UnsupportedMethodError when it's accessed.
    • rewind()
      • This optional method resets this reader so that the next read() will return the first value of the series. Readers that don't implement this property will throw an UnsupportedMethodError when it's accessed.
    • skip([Int32 skip_count])
      • Discards exactly the next 'skip_count' number of values rather than reading them. If there are not enough values remaining then a NoNextValueError will be thrown from read().
      • This program fragment:
          local var a = reader.read
          reader.read
          reader.read
          local var b = reader.read
        
        

        is equivalent to this:

          local var a = reader.read
          reader.skip(2)
          local var b = reader.read
        
    • consume(DataType look_for).Logical
      • Attempts to match and discard the next value from this reader, returning true, or else leaves the next value still pending and returns false.
      • Returns:
          "true" if old.peek == look_for
          "false" if old.peek != look_for
        
        

        Invariant:

          if (old.peek == look_for) old.skip
        
    • to_List().DataType[]
      • Creates and returns a list of all the remaining values of this reader.
    • read_list(Int32 size).DataType[]
      • Reads the next 'size' number of values and returns them in a list. Requires that there are at least 'size' number of values remaining to be read.
    • close()
      • Closes this reader as a source of input. Used for file readers, but for most others this command does nothing.
    • to_String().String
      • Returns a string representation of this reader's values.

 

StandardReader<<DataType>>
underlying aspect : Reader<<DataType>>
  • DESCRIPTION
    • Framework for a reader that has a peekable "next" element that is set with prep_next. To use, incorporate this aspect and define the prep_next method.
  • PROPERTIES
    • next : DataType
      • Set in prep_next(). If has_next is null, the value of 'next' is undefined.
    • has_next : Logical
      • Set to the value returned from prep_next().
  • METHODS
    • has_another().Logical
      • Returns "true" if this reader has another value that can be previewed with peek() or read with read().
    • peek().DataType
      • Returns the next value that will be returned from read(). If there is no next value then a NoNextValueError is thrown.
    • read().DataType
      • Returns the next value from this reader and prepares the value that will come after.
    • prep_next().Logical : abstract
      • Sets up the next value to be returned. This will be the value returned from peek() and read by read(). Returns true on success, false if there's nothing more to read.

 

TextReader
aspect : Reader<<Char>>
  • DESCRIPTION
    • A convenience aspect for incorporating Reader<<Char>>.
    • Example:
        class CustomTextReader : TextReader
          ...
        endClass
      

 

Writers

  • Any object that may have data written to it is called a Writer.  A Writer that can write Int32 values is a Writer<<Int32>>.
  • An object that can create a Writer object is called a Writable object and should incorporate the Writable<<DataType>> aspect.
  • To write() means to output a single unit of the writer's fundamental data type.  To print() means to convert various types of information into text form.
  • A TextWriter is a Writer<<Char>> with numerous print() methods for printing different data types.
  • The most often used writers in Slag are the StringBuilder and the FileWriter, both of which extend TextWriter (see the sections on Strings and Files).

 

Writable<<DataType>>
underlying aspect
  • DESCRIPTION
    • A type that's writable is capable of creating a writer that knows how to write data to it.
  • METHODS
    • create_writer().Writer<<DataType>> : abstract
      • Returns a writer capable of writing data to this object.

 

Writer<<DataType>>
underlying aspect
  • DESCRIPTION
    • The base aspect for all writers.
  • METHODS
    • write(DataType value) : abstract
      • Outputs 'value' in an implementation-specific way.
      • Requires:
          holds_another == true
        
    • write(Reader<<DataType>> src)
      • Writes each of the values in 'src' to this writer.
    • capacity().Int32
      • Returns the minimum number of additional values that can written with this writer, or (-1) for "unlimited".
    • holds_another().Logical
      • Returns true if at least one more value can be written with write().
    • position().Int32
      • This optional property-read returns the zero-based write position. This will be "0" before anything is written, "1" after a single write(), and so on.
      • Not all writers implement this property; those that don't will throw an UnsupportedMethodError when it's accessed.
    • position(Int32 new_pos)
      • This optional property-write sets the zero-based write position. It is up to the implementation to define how repositioning works with gaps and what happens to data at a later spot after a reposition to an earlier spot.
      • Not all writers implement this property; those that don't will throw an UnsupportedMethodError when it's accessed.
    • skip(Int32 skip_count)
      • This optional method repositions the writer to be 'skip_count' values further along.
      • If a writer does not implement the 'position' property then an UnsupportedMethodError will be generated when skip() is called.
    • flush()
      • Orders the writer to write out any buffered data in preparations for closing the writer.
      • Invariant: close() should always call flush() first.
    • close()
      • Closes the writer; no further information may be written to this writer. Should always be called after data is finished being written.
      • Implementations should call flush() as the first step of close().
      • Multiple calls to a writer's close() are permitted although only the first call actually causes an action.
    • clean_up()
      • Closes the writer. Base aspect Writer does not incorporate the RequiresCleanup interface, but types for writers that do this method will be called when there are no more references to the object.

 

TextWriter
class : Writer<<Char>>
  • DESCRIPTION
    • Defines a Writer<<Char>> that can print a variety of common Slag types in text form.
    • In Slag I/O terminology, "writing" data means to write successive units of that data type to the output, while "printing" means to translate into text form.
  • METHODS
    • print(Char ch)
      • Prints the given Char. The symbol corresponding to the Unicode value of 'n' will be displayed.
    • print(String st)
      • Prints the given String.
    • print(Object obj)
      • Prints the to_String representation of the given object, or else the world "null" if the 'obj' reference is null.
    • print(Int64 n)
      • Prints the given Int64 in text form.
    • print(Byte n)
      • Prints the given Byte in text form.
    • print(Real64 n)
      • Prints the given Real64 in text form.
    • print(Real32 n)
      • Prints the given Real32 in text form.
    • print(Logical n)
      • Prints the given Logical in text form as "true", "false", or "void".
    • println(String st)
      • Prints the given String value and advances the cursor to the next line.
    • println(Object obj)
      • Prints the given object's to_String value and advances the cursor to the next line.
    • println(Int64 n)
      • Prints the given Int64 value and advances the cursor to the next line.
    • println(Byte n)
      • Prints the given Byte value and advances the cursor to the next line.
    • println(Char n)
      • Prints the given Char value and advances the cursor to the next line.
    • println(Real64 n)
      • Prints the given Real64 value and advances the cursor to the next line.
    • println(Logical n)
      • Prints the given Logical value and advances the cursor to the next line.
    • println()
      • Advances the cursor to the next line.
    • print(Reader<<Char>> reader)
      • Prints out every character that can be obtained from the reader.
    • println(Reader<<Char>> reader)
      • Prints out every character that can be obtained from the reader and advances the cursor to the next line.