// https://github.com/alexkappa/mustache/blob/master/parse.go
///|
priv struct Parser {
readToken : () -> Token
mut buf : Array[Token]
}
///|
fn Parser::new(lex : Lexer) -> Parser {
{ readToken: () => lex.token(), buf: [] }
}
///| read next token
fn Parser::read(self : Parser) -> Token {
if self.buf.length() > 0 {
let r = self.buf[0]
self.buf = self.buf[1:].to_array()
return r
}
return (self.readToken)()
}
///| read N next token
fn Parser::readn(self : Parser, n : Int) -> Array[Token] {
let tokens = []
for _ in 0.. Array[Token] raise ReachedEOF {
let tokens = []
for {
let token = self.read()
tokens.push(token)
match token.type_ {
TokenType::EOF => raise ReachedEOF
tokenType if tokenType == t => return tokens
_ => ()
}
}
}
///|
fn Parser::readv(self : Parser, t : Token) -> Array[Token] raise ReachedEOF {
let tokens = []
for {
let read = self.readt(t.type_)
tokens.append(read)
if read.length() > 0 &&
read.last().map_or(false, last => last.type_ == t.type_) {
break
}
}
tokens
}
///|
fn Parser::peek(self : Parser) -> Token {
if self.buf.length() > 0 {
self.buf[0]
} else {
let t = (self.readToken)()
self.buf.push(t)
t
}
}
///|
fn Parser::peekn(self : Parser, n : Int) -> Array[Token] raise ReachedEOF {
if self.buf.length() > n {
self.buf[:n].to_array()
} else {
for i in self.buf.length().. Array[Token] raise {
for i in 0.. raise ReachedEOF
tType if tType == t => return self.buf[:i].to_array()
_ => ()
}
}
for {
let token = (self.readToken)()
self.buf.push(token)
match token.type_ {
TokenType::EOF => raise ReachedEOF
tType if tType == t => return self.buf
_ => ()
}
}
fail("EOF not found")
}
///|
fn Parser::errorf(token : Token, message : String) -> ParseError {
ParseError::FormatError(
"\{token.line}:\{token.col} syntax error. (\{token.type_}: \{token.value}) \{message}",
)
}
///|
fn Parser::parse(self : Parser) -> Array[Node] raise ParseError {
let nodes : Array[Node] = []
for {
let token = self.read()
match token.type_ {
EOF => break
Error => raise Parser::errorf(token, token.value)
Text => nodes.push(TextNode(token.value))
LeftDelim => nodes.push(self.parse_tag())
RawStart => nodes.push(self.parse_raw_tag())
SetDelim => nodes.push(DelimNode)
_ => ()
}
}
nodes
}
///|
test {
let testCases = [
"hello\n{{world}}", "foo {{{bar}}}\nbaz {{! this is ignored }}", "\nfoo {{bar}} baz {{=| |=}}\r\n |foo| |={{ }}=| {{bar}}",
]
for testCase in testCases {
let lexer = Lexer::new("test", testCase)
let parser = Parser::new(lexer)
let nodes = parser.parse()
println(nodes)
}
}
///|
test {
let lexer = Lexer::new("test", "Begin.\n{{! Comment Block! }}\nEnd.")
let parser = Parser::new(lexer)
let nodes = parser.parse()
println(nodes)
}
///|
test {
let lexer = Lexer::new("test", "hello {{^world}}world{{/world}}")
let parser = Parser::new(lexer)
let nodes = parser.parse()
println(nodes)
}
test {
let lexer = Lexer::new("test", "hello {{#world}}world{{/world}}")
let parser = Parser::new(lexer)
let nodes = parser.parse()
println(nodes)
}
test {
let lexer = Lexer::new("test", "hello {{world}}world")
let parser = Parser::new(lexer)
let nodes = parser.parse()
println(nodes)
}
///|
fn Parser::parse_tag(self : Parser) -> Node raise ParseError {
let token = self.read()
match token.type_ {
Identifier => self.parse_var(token, true)
RawStart => self.parse_raw_tag()
RawAlt => self.parse_raw_tag()
Comment => self.parse_comment()
SectionInverse => self.parse_section(true)
SectionStart => self.parse_section(false)
Partial => self.parse_partial()
_ => raise ParseError::FormatError("unreachable code")
}
}
///|
fn Parser::parse_raw_tag(self : Parser) -> Node raise ParseError {
let token = self.read()
if token.type_ != Identifier {
raise Parser::errorf(token, "unexpected token")
}
let next = self.read()
if next.type_ != RawEnd {
raise Parser::errorf(next, "unexpected token")
}
let next = self.read()
if next.type_ != RightDelim {
raise Parser::errorf(next, "unexpected token")
}
VarNode({ name: token.value, escape: false })
}
///|
fn Parser::parse_var(
self : Parser,
ident : Token,
escape : Bool
) -> Node raise ParseError {
let next = self.read()
if next.type_ != RightDelim {
raise Parser::errorf(next, "unexpected token")
}
VarNode({ name: ident.value, escape })
}
///|
fn Parser::parse_comment(self : Parser) -> Node raise ParseError {
let mut comment = ""
for {
let token = self.read()
match token.type_ {
EOF => raise Parser::errorf(token, "unexpected token")
Error => raise Parser::errorf(token, token.value)
RightDelim => return CommentNode(comment)
_ => comment = comment + token.value
}
}
}
///|
fn Parser::parse_section(
self : Parser,
inverse : Bool
) -> Node raise ParseError {
let token = self.read()
if token.type_ != Identifier {
raise Parser::errorf(token, "unexpected token")
}
let next = self.read()
if next.type_ != RightDelim {
raise Parser::errorf(token, "unexpected token")
}
let tokens : Array[Token] = []
let mut stack = 1
for {
let read = (try? self.readv(token))
.map_err(ParseError::ReadError(_))
.unwrap_or_error()
tokens.append(read)
if read.length() > 1 {
// tt := read[len(read)-2]
let tt = read[read.length() - 2]
match tt.type_ {
SectionStart | SectionInverse => stack = stack + 1
SectionEnd => stack = stack - 1
_ => ()
}
}
if stack == 0 {
break
}
}
// StartSection, Ientifier, RightDelim
let nodes = self.sub_parser(tokens[:tokens.length() - 3].to_array()).parse()
SectionNode({ name: token.value, inverted: inverse, elems: nodes })
}
///|
fn Parser::sub_parser(_ : Parser, tokens : Array[Token]) -> Parser {
let eofToken = { type_: TokenType::EOF, value: "", line: 0, col: 0 }
{ readToken: () => eofToken, buf: tokens.copy() }
}
///|
fn Parser::parse_partial(self : Parser) -> Node raise ParseError {
let token = self.read()
if token.type_ != Identifier {
raise Parser::errorf(token, "unexpected token")
}
let next = self.read()
if next.type_ != RightDelim {
raise Parser::errorf(next, "unexpected token")
}
PartialNode(token.value)
}