flow "TokenParsing" v1.0 Convert raw input text into a validated structural representation suitable for downstream evaluation."
flow "TokenParsing" v1.0
meta:
domain: "language-structure"
intent: "Convert raw input text into a validated structural representation suitable for downstream evaluation."
zero_shot: true
notes:
- "Tokenisation is boundary carving at the character layer."
- "Parsing is hierarchy formation under grammar constraints."
- "Output is a ParseResult with AST + diagnostics."
# -------------------------
# IO
# -------------------------
io:
in:
source_text: str
grammar: GrammarSpec
mode: enum("strict","recover") = "strict"
out:
result: ParseResult
# -------------------------
# TYPES
# -------------------------
types:
GrammarSpec:
description: "Defines token classes, precedence, associativity, and production rules."
fields:
token_rules: list[TokenRule]
productions: list[ProductionRule]
start_symbol: str
precedence: list[PrecedenceRule]?
whitespace: enum("ignore","significant") = "ignore"
indentation: enum("ignore","significant") = "ignore"
TokenRule:
fields:
name: str
pattern: regex
priority: int = 0
emit: bool = true
ProductionRule:
fields:
lhs: str
rhs: list[str] # symbols: token names or non-terminals
action: str? # semantic constructor label (internal)
PrecedenceRule:
fields:
operator: str
precedence: int
associativity: enum("left","right","nonassoc")
Token:
fields:
kind: str # token class name
lexeme: str # raw substring
span: Span
value: any? # optional decoded value (number/string)
Span:
fields:
start: int
end: int
line: int?
col: int?
ASTNode:
fields:
tag: str # node type
children: list[ASTNode]
token: Token? # optional anchor token
span: Span
attrs: map[str,any]? # optional node metadata
Diagnostic:
fields:
level: enum("INFO","WARN","ERROR","FATAL")
code: str
message: str
span: Span?
ParseResult:
fields:
ok: bool
tokens: list[Token]
ast: ASTNode?
diagnostics: list[Diagnostic]
remainder: str? # for recovery or partial parse
# -------------------------
# INTERNAL STATE
# -------------------------
state:
cursor:
idx: int = 0
line: int = 1
col: int = 1
buffer:
text: str = io.in.source_text
stream:
tokens: list[Token] = []
parse:
ast: ASTNode? = null
stack: list[any] = [] # parser stack / frames
diag:
diagnostics: list[Diagnostic] = []
config:
grammar: GrammarSpec = io.in.grammar
mode: enum("strict","recover") = io.in.mode
# -------------------------
# INVARIANTS
# -------------------------
invariants:
# Cursor and spans are coherent
- name: "CursorWithinBounds"
rule: "0 <= cursor.idx <= len(buffer.text)"
- name: "TokenSpanMonotonic"
rule: "forall i in [0..len(stream.tokens)-2]: stream.tokens[i].span.end <= stream.tokens[i+1].span.start"
- name: "TokenSpanValid"
rule: "forall t in stream.tokens: 0 <= t.span.start < t.span.end <= len(buffer.text)"
- name: "GrammarStartSymbolExists"
rule: "exists p in config.grammar.productions where p.lhs == config.grammar.start_symbol"
# If ok, AST must exist and cover at least one token
- name: "OkImpliesAST"
rule: "result.ok == true => parse.ast != null"
- name: "ASTSpanWithinText"
rule: "parse.ast != null => 0 <= parse.ast.span.start <= parse.ast.span.end <= len(buffer.text)"
# -------------------------
# TRANSITIONS
# -------------------------
transitions:
- name: "Init"
do:
- set cursor.idx = 0
- set cursor.line = 1
- set cursor.col = 1
- set stream.tokens = []
- set parse.ast = null
- set diag.diagnostics = []
# ---------- TOKENISATION ----------
- name: "Tokenise"
do:
- while cursor.idx < len(buffer.text):
do:
- call "ConsumeWhitespaceOrIndent"()
- if cursor.idx >= len(buffer.text): break
- call "MatchNextToken"()
- name: "ConsumeWhitespaceOrIndent"
when: "config.grammar.whitespace == 'ignore' or config.grammar.indentation == 'significant'"
do:
- # This is deliberately abstract:
- # - If whitespace ignored: skip spaces/tabs/newlines, update line/col
- # - If indentation significant: emit INDENT/DEDENT/NEWLINE tokens as per indentation stack
- call "AdvanceCursorOverLayout"()
- name: "MatchNextToken"
do:
- let start = cursor.idx
- let best = call "BestTokenMatch"(buffer.text, cursor.idx, config.grammar.token_rules)
- if best.found == false:
do:
- call "EmitDiag"("ERROR","LEX_UNEXPECTED_CHAR","Unexpected character", Span(start,start+1,cursor.line,cursor.col))
- if config.mode == "strict": goto "Fail"
- else:
do:
- call "AdvanceCursor"(1)
- continue
- else:
do:
- if best.rule.emit == true:
do:
- append stream.tokens:
Token(
kind=best.rule.name,
lexeme=best.lexeme,
span=Span(start, start+best.len, best.line?, best.col?),
value=best.value?
)
- call "AdvanceCursor"(best.len)
# ---------- PARSING ----------
- name: "Parse"
do:
- call "ValidateGrammar"(config.grammar)
- if exists d in diag.diagnostics where d.level in ["ERROR","FATAL"]:
do:
- if config.mode == "strict": goto "Fail"
- set parse.ast = call "BuildAST"(stream.tokens, config.grammar)
- if parse.ast == null:
do:
- call "EmitDiag"("ERROR","PARSE_FAILED","Could not form a valid parse tree", null)
- if config.mode == "strict": goto "Fail"
- goto "AssembleResult"
# ---------- RESULT ----------
- name: "AssembleResult"
do:
- set io.out.result =
ParseResult(
ok = (parse.ast != null) and not (exists d in diag.diagnostics where d.level in ["ERROR","FATAL"]),
tokens = stream.tokens,
ast = parse.ast,
diagnostics = diag.diagnostics,
remainder = null
)
- goto "End"
- name: "Fail"
do:
- set io.out.result =
ParseResult(
ok = false,
tokens = stream.tokens,
ast = null,
diagnostics = diag.diagnostics,
remainder = substring(buffer.text, cursor.idx, len(buffer.text))
)
- goto "End"
- name: "End"
do:
- stop
# -------------------------
# FUNCTIONS (INTERNAL)
# -------------------------
functions:
- name: "BestTokenMatch"
signature: "BestTokenMatch(text:str, idx:int, rules:list[TokenRule]) -> Match"
behaviour:
- "Try all rules; choose the longest match, then highest priority."
- "Return found=false if none match at idx."
- "Optionally decode value (e.g., INT literal)."
- name: "ValidateGrammar"
signature: "ValidateGrammar(G:GrammarSpec) -> void"
behaviour:
- "Ensure start_symbol has productions."
- "Ensure all referenced symbols are defined as token_rules or appear as lhs in productions."
- "Emit diagnostics for missing/undefined symbols."
- name: "BuildAST"
signature: "BuildAST(tokens:list[Token], G:GrammarSpec) -> ASTNode?"
behaviour:
- "Apply a deterministic strategy (e.g., Pratt parser for expressions + production-driven descent for statements)."
- "Respect precedence/associativity if provided."
- "If indentation significant, treat INDENT/DEDENT as structural delimiters."
- "Return null on unrecoverable structure failure."
- name: "AdvanceCursor"
signature: "AdvanceCursor(n:int) -> void"
behaviour:
- "Move idx by n; update line/col if crossing newlines."
- name: "AdvanceCursorOverLayout"
signature: "AdvanceCursorOverLayout() -> void"
behaviour:
- "Skip or emit layout tokens depending on grammar.whitespace and grammar.indentation."
- name: "EmitDiag"
signature: "EmitDiag(level:str, code:str, msg:str, span:Span?) -> void"
behaviour:
- "Append Diagnostic(level, code, msg, span) to diag.diagnostics."
# -------------------------
# MONITORS
# -------------------------
monitors:
- name: "ParseCoherenceMonitor"
watch:
- "CursorWithinBounds"
- "TokenSpanValid"
- "TokenSpanMonotonic"
- "GrammarStartSymbolExists"
- "OkImpliesAST"
- "ASTSpanWithinText"
on_violation:
- call "EmitDiag"("FATAL","COHERENCE_BREAK","Token parsing coherence invariant violated", null)
- if config.mode == "strict": goto "Fail"
# -------------------------
# OUTPUT CONTRACT
# -------------------------
contract:
guarantee:
- "If ok=true: tokens and ast define a single coherent structural interpretation under the provided GrammarSpec."
- "If ok=false: diagnostics explain the earliest detected boundary failure; remainder indicates unparsed region."
Download Code
Copy to Clipboard
Minimal example grammar snippet (expression + assignment)
# OPTIONAL: Minimal example grammar snippet (expression + assignment)
# (This is *illustrative*; your real Flow Algebra grammar will add blocks, indentation, and keywords.)
GrammarSpec:
start_symbol: "Stmt"
whitespace: "ignore"
indentation: "ignore"
token_rules:
- {name: "IDENT", pattern: "[A-Za-z_][A-Za-z0-9_]*", priority: 1, emit: true}
- {name: "INT", pattern: "[0-9]+", priority: 1, emit: true}
- {name: "EQ", pattern: "=", priority: 2, emit: true}
- {name: "PLUS", pattern: "\\+", priority: 2, emit: true}
- {name: "LP", pattern: "\\(", priority: 2, emit: true}
- {name: "RP", pattern: "\\)", priority: 2, emit: true}
precedence:
- {operator: "PLUS", precedence: 10, associativity: "left"}
productions:
- {lhs: "Stmt", rhs: ["IDENT","EQ","Expr"], action: "Assign"}
- {lhs: "Expr", rhs: ["Expr","PLUS","Term"], action: "Add"}
- {lhs: "Expr", rhs: ["Term"], action: "ExprTerm"}
- {lhs: "Term", rhs: ["IDENT"], action: "Var"}
- {lhs: "Term", rhs: ["INT"], action: "Int"}
- {lhs: "Term", rhs: ["LP","Expr","RP"], action: "Group"}
Download Code
Copy to Clipboard
Tokenise + parse Flow Algebra source into a deterministic AST using layout (indentation) as structure."
zero_shot: true
flow "FlowAlgebraTokenParsing" v1.0
meta:
domain: "flow-algebra"
intent: "Tokenise + parse Flow Algebra source into a deterministic AST using layout (indentation) as structure."
zero_shot: true
notes:
- "This grammar is designed for LLM-executable parsing: explicit sections + indentation boundaries."
- "Tokenizer emits INDENT/DEDENT/NEWLINE to make hierarchy explicit."
- "Parser builds a Sectioned-AST: Flow -> Sections -> Entries -> Values/Blocks."
# -------------------------
# IO
# -------------------------
io:
in:
source_text: str
mode: enum("strict","recover") = "strict"
indent_width: int = 2 # recommended 2 for YAML-style
allow_tabs: bool = false
out:
result: ParseResult
# -------------------------
# TYPES
# -------------------------
types:
Token:
fields:
kind: str
lexeme: str
span: Span
value: any?
Span:
fields:
start: int
end: int
line: int
col: int
Diagnostic:
fields:
level: enum("INFO","WARN","ERROR","FATAL")
code: str
message: str
span: Span?
ASTNode:
fields:
tag: str
attrs: map[str,any]?
children: list[ASTNode]
span: Span
ParseResult:
fields:
ok: bool
tokens: list[Token]
ast: ASTNode?
diagnostics: list[Diagnostic]
remainder: str?
# Internal helper record for indentation/layout
LayoutState:
fields:
indent_stack: list[int] # stack of indentation columns (spaces)
pending_dedents: int # count of DEDENT tokens to emit
at_line_start: bool
current_indent: int
# -------------------------
# INTERNAL STATE
# -------------------------
state:
buffer:
text: str = io.in.source_text
n: int = len(io.in.source_text)
cursor:
idx: int = 0
line: int = 1
col: int = 1
layout:
indent_stack: list[int] = [0]
pending_dedents: int = 0
at_line_start: bool = true
current_indent: int = 0
stream:
tokens: list[Token] = []
parse:
pos: int = 0 # token index cursor
ast: ASTNode? = null
diag:
diagnostics: list[Diagnostic] = []
config:
mode: enum("strict","recover") = io.in.mode
indent_width: int = io.in.indent_width
allow_tabs: bool = io.in.allow_tabs
# -------------------------
# INVARIANTS
# -------------------------
invariants:
- name: "CursorWithinBounds"
rule: "0 <= cursor.idx <= buffer.n"
- name: "IndentStackNonEmpty"
rule: "len(layout.indent_stack) >= 1"
- name: "IndentStackBaseZero"
rule: "layout.indent_stack[0] == 0"
- name: "TokensMonotonicSpans"
rule: "forall i in [0..len(stream.tokens)-2]: stream.tokens[i].span.end <= stream.tokens[i+1].span.start"
- name: "OkImpliesAST"
rule: "io.out.result.ok == true => parse.ast != null"
# -------------------------
# TOKEN KINDS (REFERENCE)
# -------------------------
# KEYWORDS: FLOW, META, IO, TYPES, STATE, INVARIANTS, TRANSITIONS, FUNCTIONS, MONITORS, CONTRACT, PARAMS
# SYMBOLS: COLON(:), DASH(-), QUOTE("), LBRACE({), RBRACE(}), LBRACKET([), RBRACKET(]), COMMA(,), DOT(.)
# OPS: ARROW(-> or →), EQ(=), LT(<), GT(>), LE(<=), GE(>=), NE(!=), AND, OR, NOT
# LITERALS: IDENT, STRING, NUMBER, BOOL, NULL
# LAYOUT: NEWLINE, INDENT, DEDENT, EOF
# COMMENTS: COMMENT (typically not emitted)
# -------------------------
# TRANSITIONS
# -------------------------
transitions:
- name: "Init"
do:
- set cursor.idx = 0
- set cursor.line = 1
- set cursor.col = 1
- set layout.indent_stack = [0]
- set layout.pending_dedents = 0
- set layout.at_line_start = true
- set layout.current_indent = 0
- set stream.tokens = []
- set parse.pos = 0
- set parse.ast = null
- set diag.diagnostics = []
- name: "Tokenise"
do:
- while cursor.idx < buffer.n:
do:
- if layout.pending_dedents > 0:
do:
- call "EmitToken"("DEDENT","", call "SpanHere"(0))
- set layout.pending_dedents = layout.pending_dedents - 1
- continue
- if layout.at_line_start == true:
do:
- call "HandleLineIndent"()
- set layout.at_line_start = false
- # after indent handling, fall through to normal token scanning
- let ch = call "PeekChar"(0)
- if ch == "\r":
do: - call "Advance"(1) # ignore CR
- else if ch == "\n":
do:
- call "EmitToken"("NEWLINE","\n", call "SpanHere"(1))
- call "Advance"(1)
- set layout.at_line_start = true
- else if call "IsSpace"(ch):
do:
- call "Advance"(1) # mid-line spaces ignored (YAML style)
- else if ch == "#" :
do:
- call "ConsumeComment"()
- # comment not emitted
- else:
do:
- call "ScanNextToken"()
- # End of file: close indentation blocks
- while len(layout.indent_stack) > 1:
do:
- pop layout.indent_stack
- call "EmitToken"("DEDENT","", call "SpanHere"(0))
- call "EmitToken"("EOF","", call "SpanHere"(0))
- name: "Parse"
do:
- set parse.ast = call "ParseFlowDocument"()
- if parse.ast == null:
do:
- call "EmitDiag"("ERROR","PARSE_FAILED","Could not parse Flow Algebra document", call "SpanAtToken"(parse.pos))
- goto "Fail"
- goto "AssembleResult"
- name: "AssembleResult"
do:
- set io.out.result =
ParseResult(
ok = not (exists d in diag.diagnostics where d.level in ["ERROR","FATAL"]),
tokens = stream.tokens,
ast = parse.ast,
diagnostics = diag.diagnostics,
remainder = null
)
- goto "End"
- name: "Fail"
do:
- set io.out.result =
ParseResult(
ok = false,
tokens = stream.tokens,
ast = null,
diagnostics = diag.diagnostics,
remainder = substring(buffer.text, cursor.idx, buffer.n)
)
- goto "End"
- name: "End"
do: - stop
# -------------------------
# LEXER FUNCTIONS
# -------------------------
functions:
- name: "PeekChar"
signature: "PeekChar(k:int) -> str"
behaviour:
- "Return buffer.text[cursor.idx+k] if in range else '\\0'."
- name: "Advance"
signature: "Advance(n:int) -> void"
behaviour:
- "Advance cursor.idx by n; update line/col on newlines."
- name: "SpanHere"
signature: "SpanHere(n:int) -> Span"
behaviour:
- "Span from current cursor position to cursor+n, with current line/col."
- name: "EmitToken"
signature: "EmitToken(kind:str, lexeme:str, span:Span, value:any?=null) -> void"
behaviour:
- "Append Token(kind,lexeme,span,value) to stream.tokens."
- name: "EmitDiag"
signature: "EmitDiag(level:str, code:str, msg:str, span:Span?) -> void"
behaviour:
- "Append Diagnostic(level,code,msg,span) to diag.diagnostics."
- "If level in ERROR/FATAL and config.mode=='strict', parsing will Fail."
- name: "IsSpace"
signature: "IsSpace(ch:str) -> bool"
behaviour:
- "True iff ch in [' ', '\\t']."
- name: "ConsumeComment"
signature: "ConsumeComment() -> void"
behaviour:
- "Consume from '#' to end-of-line (excluding newline)."
- name: "HandleLineIndent"
signature: "HandleLineIndent() -> void"
behaviour:
- "Measure leading spaces (or tabs if allowed) from line start."
- "If blank line or comment-only line: do not change indent."
- "If indentation increased: emit INDENT and push new indent."
- "If decreased: emit DEDENTs until matching indent exists; else error."
- "If indentation not aligned to indent_width (optional): warn or error."
- "If tabs present and allow_tabs=false: error."
- name: "ScanNextToken"
signature: "ScanNextToken() -> void"
behaviour:
- "Scan in order: Strings, numbers, operators/arrows, punctuation, identifiers/keywords."
- "Prefer longest match for multi-char ops (<=, >=, !=, ->)."
# -------------------------
# Scanner details (normative rules)
# -------------------------
- name: "ScanString"
signature: "ScanString() -> bool"
behaviour:
- "If next char is '\"' or '\\'', consume quoted string with escapes."
- "Emit STRING token with decoded value."
- "Return true if matched else false."
- name: "ScanNumber"
signature: "ScanNumber() -> bool"
behaviour:
- "Match int or float (e.g., 12, 12.3)."
- "Emit NUMBER with numeric value."
- "Return true if matched else false."
- name: "ScanOperatorOrPunct"
signature: "ScanOperatorOrPunct() -> bool"
behaviour:
- "Match in precedence order:"
- "ARROW: '->' or '→'"
- "LE:'<=' GE:'>=' NE:'!=' EQ:'=' LT:'<' GT:'>'"
- "COLON:':' COMMA:',' DOT:'.' DASH:'-'"
- "LBRACE:'{' RBRACE:'}' LBRACKET:'[' RBRACKET:']' LPAREN:'(' RPAREN:')'"
- "Emit corresponding token."
- "Return true if matched else false."
- name: "ScanIdentOrKeyword"
signature: "ScanIdentOrKeyword() -> bool"
behaviour:
- "Match [A-Za-z_][A-Za-z0-9_]*"
- "If lexeme in section_keywords: emit SECTION token (e.g., 'meta','io','types','state'...)."
- "If lexeme in literals: emit BOOL/NULL."
- "Else emit IDENT."
- "Return true if matched else false."
# -------------------------
# PARSER (SECTIONED YAML-LIKE)
# -------------------------
functions:
- name: "SpanAtToken"
signature: "SpanAtToken(pos:int) -> Span?"
behaviour:
- "Return stream.tokens[pos].span if pos in range else null."
- name: "Tok"
signature: "Tok(k:int=0) -> Token"
behaviour:
- "Return stream.tokens[parse.pos+k]."
- name: "Accept"
signature: "Accept(kind:str) -> bool"
behaviour:
- "If Tok().kind == kind: parse.pos++ and return true else false."
- name: "Expect"
signature: "Expect(kind:str, code:str, msg:str) -> Token?"
behaviour:
- "If Tok().kind == kind: consume and return token."
- "Else EmitDiag(ERROR, code, msg, Tok().span) and return null."
- name: "SkipNewlines"
signature: "SkipNewlines() -> void"
behaviour:
- "Consume consecutive NEWLINE tokens."
- name: "ParseFlowDocument"
signature: "ParseFlowDocument() -> ASTNode?"
behaviour:
- "Document := SkipNewlines FlowDecl SkipNewlines Section* EOF"
- "Return AST(tag='Document', children=[FlowDecl, ...Sections...])."
- name: "ParseFlowDecl"
signature: "ParseFlowDecl() -> ASTNode?"
behaviour:
- "FlowDecl := 'flow' STRING (IDENT)? NEWLINE"
- "e.g. flow \"TokenParsing\" v1.0"
- "Build AST(tag='FlowDecl', attrs:{name,version?})."
- name: "ParseSection"
signature: "ParseSection() -> ASTNode?"
behaviour:
- "Section := SectionKey ':' NEWLINE INDENT Entry* DEDENT"
- "SectionKey is IDENT or SECTION keyword (meta, io, types, state, invariants, transitions, functions, monitors, contract, params)."
- "Entry := KeyValue | ListItem | NestedMap"
- name: "ParseEntry"
signature: "ParseEntry() -> ASTNode?"
behaviour:
- "Entry patterns:"
- "1) KeyValue: IDENT ':' Value NEWLINE"
- "2) NestedMap: IDENT ':' NEWLINE INDENT Entry* DEDENT"
- "3) ListItem: '-' (Value | InlineMap) NEWLINE OR '-' NEWLINE INDENT Entry* DEDENT"
- "Return AST nodes: Pair, Map, List, Item."
- name: "ParseValue"
signature: "ParseValue() -> ASTNode?"
behaviour:
- "Value := STRING | NUMBER | BOOL | NULL | IDENT | InlineList | InlineMap"
- "InlineList := '[' (Value (',' Value)*)? ']'"
- "InlineMap := '{' (IDENT ':' Value (',' IDENT ':' Value)*)? '}'"
- "Return AST(tag='Value', attrs:{kind,value})."
# -------------------------
# GRAMMAR RULES (EXECUTABLE NOTES)
# -------------------------
grammar:
section_keywords:
- "meta"
- "io"
- "types"
- "state"
- "params"
- "invariants"
- "transitions"
- "functions"
- "monitors"
- "contract"
literals:
bool_true: ["true","True"]
bool_false: ["false","False"]
nulls: ["null","Null","none","None"]
# High-level structural constraints specific to Flow Algebra
constraints:
- "Exactly one FlowDecl must appear before any Sections."
- "Sections must be top-level (indent 0)."
- "Within a Section, indentation must be consistent and closed before next Section."
- "Duplicate keys within the same map level should WARN (or ERROR in strict)."
# -------------------------
# MONITORS (COHERENCE + RECOVERY)
# -------------------------
monitors:
- name: "LayoutCoherence"
watch:
- "IndentStackNonEmpty"
- "IndentStackBaseZero"
on_violation:
- call "EmitDiag"("FATAL","LAYOUT_STACK_CORRUPT","Indent stack corrupted during tokenisation", call "SpanHere"(0))
- if config.mode == "strict": goto "Fail"
- name: "ParseProgress"
watch:
- "parse.pos"
rule:
- "Parser must consume tokens monotonically; if stuck, emit PARSE_STALL and fail/recover."
on_violation:
- call "EmitDiag"("ERROR","PARSE_STALL","Parser made no progress (likely unexpected token)", call "SpanAtToken"(parse.pos))
- if config.mode == "strict": goto "Fail"
# -------------------------
# OUTPUT CONTRACT
# -------------------------
contract:
guarantee:
- "ok=true => a single deterministic Sectioned-AST exists for the Flow Algebra document."
- "Tokenizer always emits EOF and closes all open indentation with DEDENT tokens."
- "Any indentation decrease must match a previous indent level exactly, else LAYOUT_MISMATCH."
diagnostics:
- "LEX_UNEXPECTED_CHAR: unknown character"
- "LAYOUT_TABS_FORBIDDEN: tabs detected but not allowed"
- "LAYOUT_WIDTH_MISMATCH: indent not multiple of indent_width"
- "LAYOUT_MISMATCH: dedent to non-existent indent level"
- "PARSE_EXPECTED: expected token kind missing"
- "PARSE_FAILED: unable to build AST"
Download Code
Copy to Clipboard
Canonical AST shape (so your engine can store and diff parse trees consistently)
# (OPTIONAL) Canonical AST shape (so your engine can store and diff parse trees consistently)
ast_schema:
Document:
children: [FlowDecl, Section*]
FlowDecl:
attrs: {name:str, version:str?}
Section:
attrs: {key:str}
children: [Entry*]
Entry:
variants:
- Pair: {key:str, value:Value}
- Map: {key:str, children:[Entry*]}
- List: {key:str, items:[Item*]} # when written as key: [..] or key:\n - ...
- Item: {value:Value? , children:[Entry*]?}
Value:
variants:
- Scalar: {kind: enum("string","number","bool","null","ident"), value:any}
- InlineList: {items:[Value*]}
- InlineMap: {pairs:[(str,Value)*]}
Download Code
Copy to Clipboard