Skip to main content

Module parse

Module parse 

Source
Expand description

Precedence-climbing parser, lowering straight to the tatara-lisp quoted form.

The load-bearing design decision, made here and once: the parser’s output IS a tatara_lisp::Sexp. There is no private blue AST that later gets converted. That is Tenet 1 — blue source parses to tatara-lisp — and building it any other way would make homoiconicity a conversion step rather than an identity, which is the difference between blue’s macro story working and merely being claimed.

The consequence to keep in view: every surface construct must have a well-defined s-expression it means. Where the mapping is not obvious it is written down in the test module, because the tests are the specification of the surface until the mechanized spec exists.

§Spans are built, not bolted on

Every production returns a Spanned — tatara-lisp’s parallel tree where each node carries the byte range that produced it — and the spanless parse_program is a projection of it via Spanned::to_sexp.

That direction is deliberate and it is the second time this file has learned the lesson: the spanless tree is derivable from the spanned one and the reverse is not. When the parser produced Sexp and callers wanted positions, the only answers available were invent one (a squiggle under unrelated code) or use nothing — and the type checker took the second, so every type error in the editor was reported at line 0, column 0 regardless of where it was. A span discarded at the parser cannot be recovered by anything downstream, exactly as [crate::lex] says of trivia.

Two honest limits, both stated where they are caused rather than inferred:

  • A node blue synthesizes (the define a def lowers to, the not an unless lowers to, the equal? a case arm compares with) has no source text of its own. It is given the span of the token that caused it — the def, the unless, the when — which is where a reader looks when told something about it.
  • An interpolation’s inner expression is parsed from a sub-source (the text between #{ and }), so its own offsets are relative to that substring and would point into the wrong buffer. Those nodes are stamped with the whole string literal’s span instead. Sub-span accuracy inside an interpolation needs the lexer to record each part’s offset in the outer source, which it does not.

Structs§

Comment
A comment, and where it sat.
Infix
What an infix operator binds like, and what it lowers to.
ParseError

Constants§

BLOCK_KEYWORDS
The words that are reserved but are not heads of a SURFACE_KEYWORDS form — block delimiters and the three literals.
INFIX
The complete infix table. Precedence follows Ruby’s where Ruby has an opinion; |> (below) sits under everything so a |> f |> g chains without parentheses.
LOWERED_ASSERT
Every surface keyword that BEGINS an expression.
LOWERED_CONCAT
The callee string interpolation lowers to.
LOWERED_MAP
The callee a {...} map literal lowers to.
MAX_EXPR_DEPTH
Maximum expression nesting before the parser refuses.
SURFACE_KEYWORDS

Functions§

comments
Every comment in src, with its byte span and whether it sits alone on its line.
is_reserved_word
A surface keyword may not be rebound. if = 1 is a mistake, not a binding, and letting it through would shadow the form for the rest of the file.
parse_expr
Parse a single blue expression. Convenience for tests and the REPL.
parse_program
Parse a blue program into a sequence of tatara-lisp forms.
parse_program_in
Parse a program written in a Yakugo surface.
parse_program_spanned
Parse, keeping each top-level form’s source span.
parse_program_spanned_with_depth
parse_program_spanned with the nesting bound supplied by the caller.
parse_program_tree
Parse a blue program keeping every node’s source span.
parse_program_tree_in
parse_program_in keeping every node’s source span.
parse_program_tree_with_depth
parse_program_tree with the nesting bound supplied by the caller.
parse_program_with_depth
parse_program with the nesting bound supplied by the caller.