4.58. kink/io/SCANNER¶
Companion mod of scanners.
4.58.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.58.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: java.io.IOException: Stream Closed
4.58.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: java.io.IOException: Stream Closed
4.58.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)
}
}
# => io error: Stream Closed
CONTROL.try(
{ S.scan_all }
{ raise('not here') }
{(:Exc)
stderr.print_line(Exc.message)
}
)
# => java exception: java.io.IOException: Stream Closed
4.58.2. SCANNER.is?(Val)¶
SCANNER.is? returns whether the Val is a scanner.