git @ Cat's Eye Technologies Tamsin / master lib / tamsin_parser.tamsin
master

Tree @master (Download .tar.gz)

tamsin_parser.tamsin @masterraw · history · blame

# Parse Tamsin source to Tamsin AST, written in Tamsin.
# Distributed under a BSD-style license; see LICENSE.

# REQUIRES lib/tamsin_scanner.tamsin
# REQUIRES lib/list.tamsin

# Note that this may contain support for some features which are not in
# the current released or pre-released version.

tamsin_parser {
  parse    = grammar using tamsin_scanner:scanner.
  grammar  = {"@" & pragma & "."} &
             LM  nil &
             LP  nil &
             {
                 production  P & "." & LP  list(P, LP)
                 | module  M & LM  list(M, LM)
             } &
             list:reverse(LP, nil)  LP &
             MM  module(main, LP) &
             list:reverse(LM, nil)  LM &
             ($:equal(LP, nil) | LM  list(module(main, LP), LM)) &
             return program(LM).
  module   = word  N &
             LP  nil &
             "{" &
             {production  P & "." & LP  list(P, LP)} &
             "}" &
             list:reverse(LP, nil)  LP &
             return module(N, LP).
  production = word  N &
             F  nil &
             [formals  F] &
             "=" &
             expr0  E &
             return production(N, list(prodbranch(F, nil, E), nil)).
  formals  = L  nil &
             "(" &
             term  T & L  list(T, L) &
             {"," & term  T & L  list(T, L)} &
             ")" &
             list:reverse(L, nil)  L &
             return L
             | "[" & expr0 & "]".
  expr0    = expr1  L & {("|" | "||") & expr1  R & L  or(L, R)} & L.
  expr1    = expr2  L & {("&" | "&&") & expr2  R & L  and(L, R)} & L.
  expr2    = expr3  L & ["using" & prodref  P & L  using(L, P)
                         | "@" & texpr  T & L  on(L, T)] & L.
  expr3    = expr4  L & [("→" | "->") & variable  V & L  send(L, V)] & L.
  expr4    = expr5  L & ("/" & texpr  T &
                           ("/" & term  T2 & return fold(L, T, T2)
                            | return fold(L, T, nil))
                          | return L).
  expr5    = "(" & expr0  E & ")" & E
           | "[" & expr0  E & "]" &
             return or(E, call(prodref('$', return), list(atom(nil), nil)))
           | "{" & expr0  E & "}" & return while(E)
           | "!" & expr5  E & return not(E)
           | "set" & variable  V & "=" & texpr  T & return set(V, T)
           | "return" & texpr  T & return call(prodref('$', return), list(T, nil))
           | "fail" & texpr  T & return call(prodref('$', fail), list(T, nil))
           | "print" & texpr  T & return call(prodref('$', print), list(T, nil))
           | "any" & return call(prodref('$', any), nil)
           | "eof" & return call(prodref('$', 'eof'), nil)
           | terminal
           | variable  V & 
             (("←" | "<-") & texpr  T & return set(V, T)
             | return call(prodref('$', return), list(V, nil)))
           | sq_string  T &
             $:unquote(T, '\'', '\'')  T &
             return call(prodref('$', return), list(atom(T), nil))
           | pq_string  T &
             $:unquote(T, '“', '”')  T &
             expect_chars(T)  E &
             return and(E, call(prodref('$', return), list(atom(T), nil)))
           | prodref  P &
             L  nil &
             ["(" &
              texpr  T & L  list(T, L) &
              {"," & texpr  T & L  list(T, L)} &
              ")"] &
             list:reverse(L, nil)  L &
             return call(P, L).

  texpr    = term  T & {"+" & term  S & T  concat(T, S)} & T.
  term     = term0.
  term0    = variable
           | "[" & L  atom(nil) &
                  [term  T & L  constructor(list, list(T, list(L, nil))) &
                  {"," & term  T & L  constructor(list, list(T, list(L, nil)))}] &
              Tail  atom(nil) &
                  ["|" & term  Tail] &
              "]" &
              reverse_c(L, Tail)  L &
              return L
           | atom  A & L  nil & ["(" &
                                    term0  T & L  list(T, L) &
                                      {"," & term0  T & L  list(T, L)} &
                                    ")"] &
                                    list:reverse(L, nil)  L &
                                    ($:equal(L, nil) & return atom(A)
                                     | return constructor(A, L)).
  atom     = word
           | sq_string  T &
             $:unquote(T, '\'', '\'').

  terminal = terminal0  T & return call(prodref('$', expect), list(T, nil)).
  terminal0 = dq_string  T & $:unquote(T, '"', '"')  T & return atom(T)
           | ("«" | "<<") & texpr  T & ("»" | ">>") & return T.

  prodref  = modref  M & ":" & word  P & return prodref(M, P)
           | ":" & word  P & return prodref('', P)
           | word  P & return prodref('', P).
  modref   = "$" | word.
  pragma   = "alias" & word & word & "=" & prodref
           | "unalias" & word.

  word = $:alnum.
  variable = $:upper  V & return variable(V).
  sq_string = $:startswith('\'').
  dq_string = $:startswith('"').
  pq_string = $:startswith('“').
  
  ## utility functions on the AST ##

  # Given the name of a module and a program AST, return the named
  # module AST found within that program, or fail.

  find_module(N, program(Ms)) = find_module(N, Ms).
  find_module(N1, list(module(N2, Ps), T)) =
      $:equal(N1, N2) & return module(N2, Ps) | find_module(N1, T).
  find_module(N, list(H, T)) = find_module(N, T).
  find_module(N, nil) = fail 'no ' + N + ' module'.

  # Given the name of a production and a module AST, return the named
  # production AST found within that module, or fail.

  find_production(N, module(MN, Ps)) = find_production(N, Ps).
  find_production(N1, list(production(N2, Bs), T)) =
      $:equal(N1, N2) & return production(N2, Bs) | find_production(N1, T).
  find_production(N, list(H, T)) = find_production(N, T).
  find_production(N, nil) = fail 'no ' + N + ' production'.

  # Given the name of a module and the name of a production,
  # return the production AST for module:production in the program, or fail.

  find_production_global(MN, PN, P) =
      find_module(MN, P)  M & find_production(PN, M).

  reverse_c(constructor(list, list(Fst, list(Snd, nil))), Acc) =
      Acc  constructor(list, list(Fst, list(Acc, nil))) &
      reverse_c(Snd, Acc).
  reverse_c(Other, Acc) = Acc.

  # Given a single-character string, return call(prodref('$', 'expect'), S)
  # Given a string, return and(call(prodref('$', 'expect'), head(S)),
  #                            expect_chars(tail(S))).

  expect_chars(S) = (expect_chars_r using $:utf8) @ S.
  expect_chars_r = any  C &
    E  call(prodref('$', 'expect'), list(atom(C), nil)) &
    ((eof & return E) | (expect_chars_r  R & return and(E, R))).
}