4.52. kink/io/SCANNER

Companion mod of scanners.

4.52.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.with_just_or(
    {(:Line)
      stdout.print_line(Line.repr)
    }
    { 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.

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

Scanner.scan_rune reads a rune from the underlying source.

Precondition:

• $config, is specified, must be a fun which takes a conf val.

The conf val provides the following methods:

• C.on_present($present_cont) : uses $present_cont as the present cont. If the method is not called, {(:Rune) [Rune] } is used as the present cont.

• C.on_absent($absent_cont) : uses $absent_cont as the absent cont. If the method is not called, { [] } is used as the absent cont.

• C.on_error($error_cont) : uses $error_cont as the error cont. If the method is not called, the default error cont raises an exception on an IO error.

Result:

• If the scanner scans a rune, scan_rune tail-calls the present cont with the rune.

• If there is no more rune, scan_rune tail-calls the absent cont with no args.

• If an IO error occurs, scan_rune tail-calls the error cont with the error message.

If the present cont and/or the absent cont are not specified, scan_rune returns a maybe vec of a rune.

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

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

The present cont and the absent cont can be specified by C.on_present and C.on_absent.

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

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

Example:

: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_rune{(:C)
  C.on_error{(:M)
    stderr.print_line(M)
  }
}
# => java.io.IOException: Stream Closed

CONTROL.try(
  { S.scan_rune }
  { raise('not here') }
  {(:Exc)
    stderr.print_line(Exc.message)
  }
)
# => Stream_input.read_bin: java.io.IOException: Stream Closed

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

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

Precondition:

• $config, if given, must be a fun which takes a conf val

The conf val provides the following methods:

• C.on_present($present_cont) : uses $present_cont as the present cont. If not called, {(:L) [L] } is used as the default present cont.

• C.on_absent($absent_cont) : uses $absent_cont as the absent cont. If not called, { [] } is used as the default absent cont.

• C.on_error($error_cont) : uses $error_cont as the error cont. If not called, the default error cont raises an exception on an IO error.

Result:

• If scan_line succeeds to fetch a str, it tail-calls the present cont with the str.

• If a str is not available, scan_line tail-calls the absent cont with no args.

• If an IO error occurs, scan_line tail-calls the error cont with an error message.

If the present cont and/or the absent cont are not specified, scan_line reurns a maybe vec of a str.

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

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

The present cont and the absent cont can be specified by C.on_present and C.on_absent.

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

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

When an error occurs, Scanner.scan_line tail-calls the error cont with an error message, or raises an exception if the error cont is not given.

Example:

: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_line{(:C)
  C.on_error{(:Msg) stderr.print_line(Msg) }
}
# => java.io.IOException: Stream Closed

CONTROL.try(
  { S.scan_line }
  { raise('not here') }
  {(:Exc)
    stderr.print_line(Exc.message)
  }
)
# => Stream_input.read_bin: java.io.IOException: Stream Closed

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

Scanner.scan_all reads the remaining runes as a str.

Precondition:

• $config, if specified, must be a fun which takes a conf val

The conf val provides the following methods

• C.on_success($success_cont) : uses $success_cont as the success cont. If not called, VAL.identity is used as the default success cont.

• C.on_error($error_cont) : uses $error_cont as the error cont. If not called, the default error cont raises an exception on an IO error.

Result:

• If scan_all succeeds to scan a str, it tail-calls the success cont with the str.

• If an IO error occurs, scan_all tail-calls the error cont with an error message.

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)   # => ""

When an error occurs, Scanner.scan_all tail-calls the error cont with an error message, or raises an exception if it is not given.

: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{(:M)
    stderr.print_line(M)
  }
}
# => java.io.IOException: Stream Closed

CONTROL.try(
  { S.scan_all }
  { raise('not here') }
  {(:Exc)
    stderr.print_line(Exc.message)
  }
)
# => Stream_input.read_all: java.io.IOException: Stream Closed

4.52.2. SCANNER.is?(Val)

SCANNER.is? returns whether the Val is a scanner.