#Scans through a string, moving the "cursor" when a match occurs.
scanner = object.new
#Initialize with string to match against
scanner.init = { str |
my.str = str
my.pos = 0
}
scanner.prototype {
#Scan using either a string or a regex
my.scan = { matcher |
when { matcher.string? } { scan_string matcher }
{ matcher.regex? } { scan_regex matcher }
{ true } { throw "Expected string or regex" }
}
#Attempt to match string against current position. Advances position
#when there is a match.
my.scan_string = { matcher |
patch = my.str[my.pos, my.pos + matcher.length - 1]
true? patch == matcher
{
my.pos = my.pos + matcher.length
patch
}
}
#Attempt to match regex against current position. Advances position
#when there is a match.
my.scan_regex = { matcher |
current = my.pos
result = my.str.match(matcher, current)
true? result && { current == result.start_pos }
{
my.pos = result.end_pos + 1
result
}
}
my.rest = {
my.str[my.pos, -1]
}
my.end? = { my.pos >= str.length }
my.end = { my.pos = str.length }
}
export scanner, :scanner