///|
priv struct Scanner {
input : String
mut pos : Int32
mut line : Int32
mut column : Int32
}
///|
fn Scanner::new(input : String) -> Scanner {
{ input, pos: 0, line: 1, column: 1, }
}
///|
fn Scanner::position(self : Scanner) -> Position {
{ line: self.line, column: self.column, }
}
///|
fn Scanner::offset(self : Scanner) -> Int32 {
self.pos
}
///|
fn Scanner::peek(self : Scanner) -> Char? {
self.input.get_char(self.pos)
}
///|
fn Scanner::peek_next(self : Scanner) -> Char? {
match self.peek() {
None => None
Some(c) => self.input.get_char(self.pos + c.utf16_len())
}
}
///|
fn Scanner::advance(self : Scanner) -> Char? {
match self.peek() {
None => None
Some(c) => {
let step = c.utf16_len()
self.pos = self.pos + step
if c is '\r' {
if self.peek() is Some('\n') {
self.pos = self.pos + '\n'.utf16_len()
}
self.line = self.line + 1
self.column = 1
Some('\n')
} else if c is '\n' {
self.line = self.line + 1
self.column = 1
Some('\n')
} else {
self.column = self.column + 1
Some(c)
}
}
}
}
///|
fn Scanner::eat(self : Scanner, ch : Char) -> Bool {
match self.peek() {
Some(c) if c == ch => {
ignore(self.advance())
true
}
_ => false
}
}
///|
fn Scanner::expect(self : Scanner, ch : Char) -> Unit raise ParseError {
if !self.eat(ch) {
match self.peek() {
None => raise ParseError::UnexpectedEof(pos=self.position())
Some(found) =>
raise ParseError::UnexpectedChar(pos=self.position(), found~)
}
}
}
///|
fn Scanner::skip_ws(self : Scanner) -> Unit {
while self.peek() is Some(c) && c.is_whitespace() {
ignore(self.advance())
}
}
///|
fn Scanner::skip_comment(self : Scanner) -> Unit {
if self.peek() is Some('#') {
ignore(self.advance())
while self.peek() is Some(c) && !(c is '\n' || c is '\r') {
ignore(self.advance())
}
}
}
///|
fn Scanner::skip_ws_and_comments(self : Scanner) -> Unit {
let mut progressed = true
while progressed {
progressed = false
let before = self.pos
self.skip_ws()
let skipped_ws = self.pos != before
if skipped_ws || self.column == 1 {
let before_comment = self.pos
self.skip_comment()
if self.pos != before_comment {
progressed = true
}
}
if self.pos != before {
progressed = true
}
}
}