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
Why a source did not parse, and where.

Constants§

BLOCK_KEYWORDS
The words that are reserved but are not heads of a SURFACE_KEYWORDS form — block delimiters and the three literals.
BUILTIN_QUALIFIER
The qualifier that names a builtin: blue::count is the interpreter’s count, whatever a program defines. No package may be named it.
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
The callee assert e lowers to.
LOWERED_ATTEMPTED
LOWERED_CONCAT
The callee string interpolation lowers to.
LOWERED_ENSURE
LOWERED_ERROR_MATCH
LOWERED_FAILED
LOWERED_FIELD
LOWERED_HEADS
LOWERED_KWARGS
LOWERED_MAP
The callee a {...} map literal lowers to.
LOWERED_MERGE
LOWERED_OPTIONAL_ARG
LOWERED_OPTIONAL_ARITY
LOWERED_RERAISE
LOWERED_SPLICE
MASTER_VERBS
Every surface keyword that BEGINS an expression.
MAX_EXPR_DEPTH
Maximum expression nesting before the parser refuses.
OPERATOR_KEYWORDS
OPTIONAL_MARKER
QUALIFIER
The character a qualified name’s AST symbol joins its two halves with: retsu::first is the symbol retsu/first. The surface never writes it (/ is division, and no identifier can contain it), which is what makes a symbol with text on both sides of one / unambiguously qualified.
RESCUED
SURFACE_KEYWORDS
SYMBOL_SPELLINGS

Functions§

comments
Every comment in src, with its byte span and whether it sits alone on its line.
is_callable_name
Can name be written as the head of a call — name(…)?
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.
keyword_doc
One line for a reader on each reserved word: what it writes, and the shape.
label_name
Whether name: lexes as exactly one label spelling name: the parser’s and the formatter’s shared test for a key that can be punned ({name:}). It asks the lexer, so a name ending in ? or ! puns too and no second list of “plain names” can drift from what a label is.
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_documented
Parse keeping the documentation nodes @doc and @moduledoc lower to (crate::doc). Every other parse_program* door strips them.
parse_program_documented_with_depth
parse_program_documented with the nesting bound supplied by the caller.
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.
parses_on_this_thread
How many programs this thread has parsed: an independent count for a caller that promises to parse a text once, read off the parser itself rather than off the caller’s own bookkeeping.
qualified
retsu/first → Some(("retsu", "first")): the package and the name of a qualified symbol. None for every other symbol, / (division) included.
qualify
The AST symbol for pkg::name.
spelling_notes