trivia_builder.pony

use "collections"

use ast = "../ast"

class TriviaBuilder
  let _context: Context

  let trivia: NamedRule = NamedRule("trivia")
  let comment: NamedRule = NamedRule("a comment" where memoize' = true)
  let comment_line: NamedRule = NamedRule("a line comment")
  let comment_nested: NamedRule = NamedRule("a nested comment")
  let ws: NamedRule = NamedRule("whitespace")
  let eol: NamedRule = NamedRule("a line end")
  let dol: NamedRule = NamedRule("a double end of line")
  let eof: NamedRule = NamedRule("end of file")

  new create(context: Context) =>
    _context = context
    _build_trivia()
    _build_comment()
    _build_comment_line()
    _build_comment_nested()
    _build_ws()
    _build_eol()
    _build_dol()
    _build_eof()

  fun ref _build_trivia() =>
    trivia.set_body(Plus(Disj([ comment; ws; eol ])))

  fun ref _build_comment() =>
    comment.set_body(Disj([ comment_line; comment_nested ]))

  fun ref _build_comment_line() =>
    // '//' (!EOL .)* EOL
    comment_line.set_body(
      Conj(
        [ Literal("//")
          Star(Conj([ Neg(eol); Single() ]))
          Look(Disj([ eol; eof ])) ]),
      {(d, r, c, b) =>
        let str = recover val String .> concat(r.start.values(r.next)) end
        ast.NodeWith[ast.Trivia](
          _Build.info(d, r), c, ast.Trivia(ast.LineCommentTrivia, str))
      })

  fun ref _build_comment_nested() =>
    // '/*' (!'*/' .)* '*/'
    comment_nested.set_body(
      Conj(
        [ Literal("/*")
          Star(Conj([ Neg(Literal("*/")); Single() ]))
          Literal("*/") ]),
      {(d, r, c, b) =>
        let str = recover val String .> concat(r.start.values(r.next)) end
        ast.NodeWith[ast.Trivia](
          _Build.info(d, r), c, ast.Trivia(ast.NestedCommentTrivia, str))
      })

  fun ref _build_ws() =>
    ws.set_body(
      Plus(Single(" \t")),
      {(d, r, c, b) =>
        let str = recover val String .> concat(r.start.values(r.next)) end
        ast.NodeWith[ast.Trivia](
          _Build.info(d, r), c, ast.Trivia(ast.WhiteSpaceTrivia, str))
      })

  fun ref _build_eol() =>
    eol.set_body(
      Disj(
        [ Literal("\r\n")
          Literal("\n")
          Literal("\r") ]),
        {(d, r, c, b) =>
          let str = recover val String .> concat(r.start.values(r.next)) end
          ast.NodeWith[ast.Trivia](
            _Build.info(d, r), c, ast.Trivia(ast.EndOfLineTrivia, str))
        })

  fun ref _build_dol() =>
    dol.set_body(Star(Conj([ eol; Ques(ws) ]), 2))

  fun ref _build_eof() =>
    eof.set_body(
      Neg(Single),
      {(d, r, c, b) =>
        ast.NodeWith[ast.Trivia](
          _Build.info(d, r), c, ast.Trivia(ast.EndOfFileTrivia, ""))
      })