moonrockz/gleam-parser/syntax does not have a README file
pub(open) trait Handler {
fn on_enter(Self, EnterEvent) -> Control = _
fn on_leave(Self, LeaveEvent) -> Unit = _
}pub(open) trait Visitor {
fn visit_definition(Self, NodeRef) -> Control = _
fn visit_statement(Self, NodeRef) -> Control = _
fn visit_expression(Self, NodeRef) -> Control = _
fn visit_pattern(Self, NodeRef) -> Control = _
fn visit_type(Self, NodeRef) -> Control = _
fn visit_constant(Self, NodeRef) -> Control = _
fn visit_guard(Self, NodeRef) -> Control = _
fn visit_clause(Self, NodeRef) -> Control = _
fn visit_other(Self, NodeRef) -> Control = _
fn leave(Self, NodeRef) -> Unit = _
}fn EnterEvent::new(category~ : String, kind~ : String, field~ : String?, start~ : Int, path~ : NodePath) -> EnterEventtest {
let path = @syntax.NodePath::root().child("definitions", 0)
let e = @syntax.EnterEvent::new(
category="definition",
kind="function",
field=Some("definitions"),
start=0,
path~,
)
inspect(e.depth, content="1")
inspect(e.path, content="definitions[0]")
}pub struct EventReader {
// private fields
}test {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn f(x) {\n x\n}\n"),
)
let reader = @syntax.EventReader::new(root)
let log = []
let mut body = ""
while reader.next() is Some(event) {
match event {
Enter(e) => {
log.push("+" + e.kind)
if e.category == "expression" {
body = "\{e.field.unwrap()} at depth \{e.depth}: \{e.path}"
}
// Skip the arguments: their `Leave` comes next.
if e.category == "arg" {
reader.skip_children()
}
}
Leave(e) => log.push("-" + e.node.kind())
}
}
inspect(
log.join(" "),
content="+module +function +arg -arg +expression +var -var -expression -function -module",
)
inspect(
body,
content="expression at depth 3: definitions[0].body[0].expression[0]",
)
}impl EventSource for EventReadertest {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let reader = @syntax.EventReader::new(root)
let args = []
for event in reader.iter() {
if event is Enter(e) {
if e.category == "expression" {
reader.skip_children()
}
if e.category == "arg" {
args.push(e.path.to_string())
}
}
}
debug_inspect(
args,
content="[\"definitions[0].arguments[0]\", \"definitions[0].arguments[1]\"]",
)
}test {
let root = @syntax.NodeRef::of_result(@parser.parse_text("const x = 1\n"))
let e = @syntax.LeaveEvent::new(node=root, path=@syntax.NodePath::root())
inspect(e.depth, content="0")
inspect(e.node.kind(), content="module")
}test {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let path = @syntax.NodePath::root()
.child("definitions", 0)
.child("body", 0)
.child("expression", 0)
.child("right", 0)
inspect(path, content="definitions[0].body[0].expression[0].right[0]")
inspect(path.depth(), content="4")
debug_inspect(path.last(), content="Some({ field: \"right\", index: 0 })")
debug_inspect(path.resolve(root).map(n => n.kind()), content="Some(\"var\")")
let up = path.parent().unwrap()
inspect(up, content="definitions[0].body[0].expression[0]")
inspect(up.resolve(root).unwrap().kind(), content="bin_op")
let missing = @syntax.NodePath::root().child("definitions", 5)
inspect(missing.resolve(root) is None, content="true")
}test {
let to_body = @syntax.NodePath::root()
.child("definitions", 0)
.child("body", 0)
let below = @syntax.NodePath::root().child("expression", 0).child("right", 0)
inspect(
to_body.append(below),
content="definitions[0].body[0].expression[0].right[0]",
)
inspect(to_body.append(@syntax.NodePath::root()) == to_body, content="true")
}test {
let path = @syntax.NodePath::from_steps([
{ field: "definitions", index: 0, },
{ field: "body", index: 1, },
])
inspect(path, content="definitions[0].body[1]")
debug_inspect(
path.steps().map(s => s.field),
content="[\"definitions\", \"body\"]",
)
inspect(@syntax.NodePath::from_steps(path.steps()) == path, content="true")
}pub(all) enum NodeRef {
Module(Module, ModuleCst)
Definition(TargetedDefinition)
Statement(Statement)
Expr(Expr)
Pattern(Pattern)
TypeAst(TypeAst)
Constant(Constant)
ClauseGuard(ClauseGuard)
BitArraySize(BitArraySize)
Clause(Clause)
Arg(Arg)
RecordConstructor(RecordConstructor)
RecordConstructorArg(RecordConstructorArg)
UseAssignment(UseAssignment)
TailPattern(TailPattern)
ExprArg(CallArg[Expr])
PatternArg(CallArg[Pattern])
ConstantArg(CallArg[Constant])
ExprUpdateArg(RecordUpdateArg[Expr])
ConstantUpdateArg(RecordUpdateArg[Constant])
ExprRecord(RecordBeingUpdated[Expr])
ConstantRecord(RecordBeingUpdated[Constant])
ExprSegment(BitArraySegment[Expr])
PatternSegment(BitArraySegment[Pattern])
ConstantSegment(BitArraySegment[Constant])
ExprOption(BitArrayOption[Expr])
PatternOption(BitArrayOption[Pattern])
ConstantOption(BitArrayOption[Constant])
} derive(Eq, Debug)test {
let r = @parser.parse_text("pub fn main() {\n 1\n}\n")
let root = @syntax.NodeRef::of_result(r)
let def = root.children()[0]
inspect("\{def.category()}/\{def.kind()}", content="definition/function")
match def {
Definition(t) =>
match t.definition {
Function(f) =>
debug_inspect(f.name.map(n => n.name), content="Some(\"main\")")
_ => fail("not a function")
}
_ => fail("not a definition")
}
}test {
let call = @syntax.NodeRef::Expr(@parser.parse_expression_text("f(x, y)"))
debug_inspect(
call.children().map(c => c.kind()),
content="[\"var\", \"call_arg\", \"call_arg\"]",
)
}test {
let sum = @syntax.NodeRef::Expr(@parser.parse_expression_text("a + b"))
let labels = sum
.children_with_fields()
.map(x => "\{x.0.field}[\{x.0.index}]:\{x.1.kind()}")
inspect(labels.join(" "), content="left[0]:var right[0]:var")
}test {
let r = @parser.parse_text("pub fn f(a, b) {\n a\n}\n")
let f = @syntax.NodeRef::of_result(r).field("definitions")[0]
inspect(f.field("arguments").length(), content="2")
inspect(f.field("no_such_field").length(), content="0")
}test {
let sum = @syntax.NodeRef::Expr(@parser.parse_expression_text("a + b"))
let right = sum.field("right")[0]
debug_inspect(sum.field_of(right), content="Some(\"right\")")
debug_inspect(right.field_of(sum), content="None")
}test {
let r = @parser.parse_text("pub fn f(a) {\n a\n}\n")
let f = @syntax.NodeRef::of_result(r).field("definitions")[0]
debug_inspect(
f.fields(),
content="[\"arguments\", \"body\", \"return_annotation\"]",
)
inspect(f.field("return_annotation").length(), content="0")
}test {
let e = @syntax.NodeRef::Expr(@parser.parse_expression_text("[1, 2]"))
let p = @syntax.NodeRef::Pattern(@parser.parse_pattern_text("[a, b]"))
inspect("\{e.category()}/\{e.kind()}", content="expression/list")
inspect("\{p.category()}/\{p.kind()}", content="pattern/list")
}test {
let root = @syntax.NodeRef::of_result(@parser.parse_text("const x = 1\n"))
inspect(root.kind(), content="module")
inspect(root.children().length(), content="1")
}test {
let r = @parser.parse_text("pub fn main() {\n 1\n}\n")
let def = @syntax.NodeRef::of_result(r).children()[0]
debug_inspect(def.span(), content="{ start: 0, end: 21 }")
}pub struct Tree {
// private fields
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let body = tree.root().field("definitions")[0].field("body")[0]
inspect(tree.parent(body).unwrap().kind(), content="function")
inspect(tree.parent(tree.root()) is None, content="true")
debug_inspect(
tree.ancestors(body).map(n => n.kind()),
content="[\"function\", \"module\"]",
)
debug_inspect(
tree.path(body).map(n => n.kind()),
content="[\"module\", \"function\", \"expression\"]",
)
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
// Byte 25 is the `b` in `a + b`.
let b = tree.node_at(25).unwrap()
inspect("\{b.category()}/\{b.kind()}", content="expression/var")
inspect(
tree.node_path(b).unwrap(),
content="definitions[0].body[0].expression[0].right[0]",
)
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let b = tree.root().field("definitions")[0].field("arguments")[1]
debug_inspect(
tree.step(b),
content="Some({ field: \"arguments\", index: 1 })",
)
debug_inspect(tree.field_of(b), content="Some(\"arguments\")")
debug_inspect(tree.field_of(tree.root()), content="None")
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn f() {\n 1\n}\n\nconst x = 2\n"),
)
let f = tree.root().field("definitions")[0]
debug_inspect(
tree.tokens_in(f.span()).map(t => t.lexeme),
content="[\"pub\", \"fn\", \"f\", \"(\", \")\", \"{\", \"1\", \"}\"]",
)
}pub struct TreeCursor {
// private fields
}test {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let cursor = @syntax.TreeCursor::new(root)
inspect(cursor.goto_first_child(), content="true")
inspect(cursor.node().kind(), content="function")
inspect(cursor.goto_next_sibling(), content="false")
// Down to the arguments of `add`.
ignore(cursor.goto_first_child())
inspect(cursor.goto_next_sibling(), content="true")
debug_inspect(cursor.field_name(), content="Some(\"arguments\")")
inspect(cursor.depth(), content="2")
inspect(cursor.path(), content="definitions[0].arguments[1]")
// A copy moves on its own.
let other = cursor.copy()
ignore(other.goto_parent())
inspect(cursor.node().kind(), content="arg")
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let p = 23 // the `+` in `a + b`
let contains = (s : @span.SrcSpan) => s.start <= p && p < s.end
let cursor = @syntax.TreeCursor::new(tree.root())
while cursor.goto_first_child_for(p) {
if !contains(cursor.node().span()) {
ignore(cursor.goto_parent())
break
}
}
inspect(cursor.node().kind(), content="bin_op")
inspect(tree.node_at(p) == Some(cursor.node()), content="true")
// A plain loop goes one node too far, to `b` after the `+`.
let naive = @syntax.TreeCursor::new(tree.root())
while naive.goto_first_child_for(p) {
}
inspect(naive.path(), content="definitions[0].body[0].expression[0].right[0]")
}test {
let tree = @syntax.Tree::new(
@parser.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let cursor = @syntax.TreeCursor::new(tree.root())
inspect(cursor.goto_node_at(23), content="true")
inspect(cursor.node().kind(), content="bin_op")
inspect(cursor.path(), content="definitions[0].body[0].expression[0]")
// `pub` is outside the current node: the cursor stays.
inspect(cursor.goto_node_at(0), content="false")
inspect(cursor.node().kind(), content="bin_op")
}struct Names {
functions : Array[String]
}
impl @gleam.Visitor for Names with fn visit_definition(self, n) {
match n {
Definition({ definition: Function(f), .. }) =>
self.functions.push(f.name.map(x => x.name).unwrap_or("?"))
_ => ()
}
Continue
}
test {
let root = @gleam.NodeRef::of_result(
@gleam.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let names : Names = { functions: [], }
@gleam.accept(root, names)
debug_inspect(names.functions, content="[\"add\"]")
}test {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn f() {\n [[1]]\n}\n"),
)
// Count the expressions and find the deepest nesting.
let (count, _, deepest) = @syntax.fold(
root,
(0, 0, 0),
(acc, n) => {
let (count, depth, deepest) = acc
let count = count + (if n.category() == "expression" { 1 } else { 0 })
let depth = depth + 1
let deepest = if depth > deepest { depth } else { deepest }
((count, depth, deepest), Continue)
},
leave=(acc, _) => (acc.0, acc.1 - 1, acc.2),
)
inspect(count, content="3")
inspect(deepest, content="6")
}test {
let rows = @syntax.kind_table()
let row = rows.filter(r => r.0 == "expression" && r.1 == "call")[0]
debug_inspect(row.2, content="[\"fun\", \"arguments\"]")
}struct Paths {
found : Array[String]
}
impl @gleam.Handler for Paths with fn on_enter(self, e) {
if e.category == "arg" {
self.found.push(e.path.to_string())
}
Continue
}
test {
let root = @gleam.NodeRef::of_result(
@gleam.parse_text("pub fn add(a, b) {\n a + b\n}\n"),
)
let paths : Paths = { found: [], }
@gleam.push_events(@gleam.EventReader::new(root), paths)
inspect(paths.found[1], content="definitions[0].arguments[1]")
}test {
let r = @parser.parse_text("pub fn f() {\n [[1]]\n}\n")
let root = @syntax.NodeRef::of_result(r)
let nodes = @syntax.reduce(root, (_, kids) => {
let mut n = 1
for k in kids {
n += k
}
n
})
inspect(nodes, content="6")
}test {
let sum = @syntax.NodeRef::Expr(@parser.parse_expression_text("a + b"))
let shown = @syntax.reduce_with_fields(sum, (n, kids) => {
let parts = kids.to_owned().map(k => "\{k.0.field}=\{k.1}")
if parts.length() == 0 {
n.kind()
} else {
n.kind() + "{" + parts.join(" ") + "}"
}
})
inspect(shown, content="bin_op{left=var right=var}")
}test {
let root = @syntax.NodeRef::of_result(
@parser.parse_text("pub fn f(x) {\n x\n}\n"),
)
// Log enter and leave; skip the arguments.
let log = []
@syntax.walk(
root,
n => {
log.push("+" + n.kind())
if n.category() == "arg" {
SkipChildren
} else {
Continue
}
},
leave=n => log.push("-" + n.kind()),
)
inspect(
log.join(" "),
content="+module +function +arg -arg +expression +var -var -expression -function -module",
)
// Stop at the first `var`: the walk ends there, with no more `leave`.
let seen = []
@syntax.walk(
root,
n => {
seen.push("+" + n.kind())
if n.kind() == "var" {
Stop
} else {
Continue
}
},
leave=n => seen.push("-" + n.kind()),
)
inspect(
seen.join(" "),
content="+module +function +arg -arg +expression +var",
)
}Install
Download zipParser and parsing utilities for the Gleam programming language in MoonBit
Dependencies