5.63. kink/io/SCANNER

Companion mod of scanners.

5.63.1. type scanner

A scanner is an input port for strs from an underlying source of runes such as a file.

A scanner provides sequential forward access to the underlying source of runes.

Example:

:FILE.require_from('kink/io/')
:CHARSET.require_from('kink/charset/')
:CONTROL.require_from('kink/')

CONTROL.with_finally{(:finally)
  :S = FILE.open_to_scan('/etc/passwd' CHARSET.utf8)
  # result of FILE.open_to_scan also supports .close method of input type
  finally{ S.close }

  S.scan_line{
    C.on_success{(:Line)
      stdout.print_line(Line.repr)
    }
    C.on_eof{ stdout.print_line('no line found') }
  }
}
# => "root:x:0:0:root:/root:/bin/bash\n"

Operations on a scanner may cause IO errors.

Commonly used scanners:

• `stdin` is a scanner+input connected to the standard input.

• kink/io/FILE.open_to_scan returns a scanner+input for a file.

• kink/io/STR_SCANNER.new returns an in-memory scanner for a str.

5.63.1.1. Scanner.scan_rune(...[$config={}])

`scan_rune` reads a rune from the underlying source.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_eof($eof): default = a fun which raises an exception

• C.on_error($error): default = a fun which raises an exception

If a rune is scanned, `scan_rune` tail-calls $success with the rune.

If there is no more rune, `scan_rune` tail-calls $eof with no arg.

If an IO error occurs, `scan_rune` tail-calls $error with an `exception`.

Preconditions

• $success must be a fun which takes an int `num` of the rune.

• $eof must be a fun which takes no arg.

• $error must be a fun which takes an `exception`.

Example

:STR_SCANNER.require_from('kink/io/')

:S <- STR_SCANNER.new('foo')
:loop <- {(:Vec)
  S.scan_rune{(:C)
    C.on_success{(:Rune) loop(Vec + [Rune]) }
    C.on_eof{ Vec }
  }
}
:Result <- loop([])
stdout.print_line(Result.repr) # => [102 111 111]

5.63.1.2. Scanner.scan_line(...[$config={}])

`scan_line` reads a line, which ends with "\n" or the end of the stream.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_eof($eof): default = a fun which raises an exception

• C.on_error($error): default = a fun which raises an exception

If a line is fetched, `scan_line` tail-calls $success with a str of the line.

If no str is available in the source, `scan_line` tail-calls $eof with no arg.

If an IO error occurs, `scan_line` tail-calls $error with an `exception`.

Preconditions

• $success must be a fun which takes a str of the line.

• $eof must be a fun which takes no arg.

• $error must be a fun which takes an `exception`.

Example

:STR_SCANNER.require_from('kink/io/')

:S <- STR_SCANNER.new("foo\nbar")
:loop <- {(:Vec)
  S.scan_line{(:C)
    C.on_success{(:Line) loop(Vec + [Line]) }
    C.on_eof{ Vec }
  }
}
:Result <- loop([])
stdout.print_line(Result.repr)  # => ["foo\n" "bar"]

5.63.1.3. Scanner.scan_all(...[$config={}])

`scan_all` reads the remaining runes as a `str`.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = a fun which raises an exception

If no IO error occurs, `scan_all` tail-calls $success with a `str` read from the source.

If an IO error occurs, `scan_all` tail-calls $error with an `exception`.

Preconditions

• $success must be a fun which takes a `str`

• $error must be a fun which takes an `exception`

Example

:STR_SCANNER.require_from('kink/io/')

:S <- STR_SCANNER.new("foo\nbar")
stdout.print_line(S.scan_all.repr)   # => "foo\nbar"
stdout.print_line(S.scan_all.repr)   # => ""
stdout.print_line(S.scan_all.repr)   # => ""

Example: IO error

:FILE.require_from('kink/io/')
:CHARSET.require_from('kink/charset/')
:CONTROL.require_from('kink/')

:S <- FILE.open_to_scan('/etc/passwd' CHARSET.utf8)
S.close
S.scan_all{(:C)
  C.on_error{(:Exc)
    stderr.print_line(Exc.message)
  }
}
# => already closed

S.scan_all
# Output:
#   -- main exception
#   [..root..]
#   {..call by host..}
#   ,,,
#   {builtin:kink-mods/kink/EXCEPTION.kn L117 C13 current_traces} { TRACE.-->current_traces }
#   already closed

5.63.2. SCANNER.is?(Val)

`is?` returns whether `Val` is a `scanner`.