///|
fn names(xs : ArrayView[@ast.Node[String]]) -> Array[NodeRef] {
xs.map(x => Name(x))
}
///|
fn expressions(xs : ArrayView[@ast.Node[@ast.Expression]]) -> Array[NodeRef] {
xs.map(x => Expression(x))
}
///|
fn patterns(xs : ArrayView[@ast.Node[@ast.Pattern]]) -> Array[NodeRef] {
xs.map(x => Pattern(x))
}
///|
fn types(xs : ArrayView[@ast.Node[@ast.TypeAnnotation]]) -> Array[NodeRef] {
xs.map(x => TypeAnnotation(x))
}
///|
fn record_fields(xs : ArrayView[@ast.Node[@ast.RecordField]]) -> Array[NodeRef] {
xs.map(x => RecordField(x))
}
///|
fn setters(xs : ArrayView[@ast.Node[@ast.RecordSetter]]) -> Array[NodeRef] {
xs.map(x => RecordSetter(x))
}
///|
fn[T] optional(x : T?, f : (T) -> NodeRef) -> Array[NodeRef] {
match x {
Some(v) => [f(v)]
None => []
}
}
///|
fn function_entries(f : @ast.Function) -> Array[(String, Array[NodeRef])] {
[
("documentation", optional(f.documentation, d => Documentation(d))),
("signature", optional(f.signature, s => Signature(s))),
("declaration", [Implementation(f.declaration)]),
]
}
///|
/// The doc attributes of the declaration at `range`.
fn declaration_attributes(
groups : ArrayView[@parser.AttributeGroup],
range : @ast.Range,
) -> ArrayView[@parser.DocAttribute] {
for g in groups {
if g.target is Declaration(range=r, ..) && r == range {
return g.attributes
}
}
[]
}
///|
/// The node's fields in elm-syntax JSON order, each with its child nodes.
fn NodeRef::entries(self : NodeRef) -> Array[(String, Array[NodeRef])] {
match self {
File(file, groups) => {
let module_attributes = []
for g in groups {
if g.target is Module {
module_attributes.append(g.attributes.map(a => Attribute(a)))
}
}
[
("moduleDefinition", [Module(file.module_definition)]),
("imports", file.imports.map(i => Import(i))),
(
"declarations",
file.declarations.map(d => {
Declaration(d, declaration_attributes(groups, d.range))
}),
),
("comments", file.comments.map(c => Comment(c))),
("attributes", module_attributes),
]
}
Module(m) =>
match m.value {
NormalModule(d) | PortModule(d) =>
[
("moduleName", [ModuleName(d.module_name)]),
("exposingList", [Exposing(d.exposing_list)]),
]
EffectModule(d) =>
[
("moduleName", [ModuleName(d.module_name)]),
("exposingList", [Exposing(d.exposing_list)]),
("command", optional(d.command, n => Name(n))),
("subscription", optional(d.subscription, n => Name(n))),
]
}
Exposing(x) =>
match x.value {
All(_) => []
Explicit(items) => [("explicit", items.map(i => Expose(i)))]
}
Import(i) =>
[
("moduleName", [ModuleName(i.value.module_name)]),
("moduleAlias", optional(i.value.module_alias, n => ModuleName(n))),
("exposingList", optional(i.value.exposing_list, e => Exposing(e))),
]
Declaration(d, attributes) => {
let attribute_nodes = attributes.map(a => Attribute(a))
match d.value {
FunctionDeclaration(f) =>
[..function_entries(f), ("attributes", attribute_nodes)]
AliasDeclaration(a) =>
[
("documentation", optional(a.documentation, x => Documentation(x))),
("name", [Name(a.name)]),
("generics", names(a.generics)),
("typeAnnotation", [TypeAnnotation(a.type_annotation)]),
("attributes", attribute_nodes),
]
CustomTypeDeclaration(t) =>
[
("documentation", optional(t.documentation, x => Documentation(x))),
("name", [Name(t.name)]),
("generics", names(t.generics)),
("constructors", t.constructors.map(c => Constructor(c))),
("attributes", attribute_nodes),
]
PortDeclaration(s) =>
[
("name", [Name(s.name)]),
("typeAnnotation", [TypeAnnotation(s.type_annotation)]),
("attributes", attribute_nodes),
]
InfixDeclaration(i) =>
[("operator", [Name(i.operator)]), ("function", [Name(i.function)])]
Destructuring(p, e) =>
[("pattern", [Pattern(p)]), ("expression", [Expression(e)])]
}
}
LetDeclaration(d) =>
match d.value {
LetFunction(f) => function_entries(f)
LetDestructuring(p, e) =>
[("pattern", [Pattern(p)]), ("expression", [Expression(e)])]
}
Signature(s) =>
[
("name", [Name(s.value.name)]),
("typeAnnotation", [TypeAnnotation(s.value.type_annotation)]),
]
Implementation(i) =>
[
("name", [Name(i.value.name)]),
("arguments", patterns(i.value.arguments)),
("expression", [Expression(i.value.expression)]),
]
Constructor(c) =>
[("name", [Name(c.value.name)]), ("arguments", types(c.value.arguments))]
Expression(x) => expression_entries(x.value)
Case(c) =>
[
("pattern", [Pattern(c.pattern)]),
("expression", [Expression(c.expression)]),
]
RecordSetter(s) =>
[
("field", [Name(s.value.field)]),
("expression", [Expression(s.value.expression)]),
]
Pattern(x) => pattern_entries(x.value)
TypeAnnotation(x) => type_entries(x.value)
RecordField(f) =>
[
("name", [Name(f.value.name)]),
("typeAnnotation", [TypeAnnotation(f.value.type_annotation)]),
]
Attribute(Attribute(name~, arguments~, ..)) =>
[("name", [Name(name)]), ("arguments", expressions(arguments))]
Attribute(Docs(names=ns, ..)) => [("names", names(ns))]
ModuleName(_) | Expose(_) | Documentation(_) | Name(_) | Comment(_) => []
}
}
///|
fn expression_entries(e : @ast.Expression) -> Array[(String, Array[NodeRef])] {
match e {
Application(xs) => [("application", expressions(xs))]
OperatorApplication(_, _, l, r) =>
[("left", [Expression(l)]), ("right", [Expression(r)])]
IfBlock(c, t, e) =>
[
("clause", [Expression(c)]),
("then", [Expression(t)]),
("else", [Expression(e)]),
]
Negation(x) => [("negation", [Expression(x)])]
TupledExpression(xs) => [("tupled", expressions(xs))]
ListExpr(xs) => [("list", expressions(xs))]
ParenthesizedExpression(x) => [("parenthesized", [Expression(x)])]
LetExpression(b) =>
[
("declarations", b.declarations.map(d => LetDeclaration(d))),
("expression", [Expression(b.expression)]),
]
CaseExpression(b) =>
[
("cases", b.cases.map(c => Case(c))),
("expression", [Expression(b.expression)]),
]
LambdaExpression(l) =>
[
("patterns", patterns(l.args)),
("expression", [Expression(l.expression)]),
]
RecordAccess(x, name) =>
[("expression", [Expression(x)]), ("name", [Name(name)])]
RecordExpr(xs) => [("record", setters(xs))]
RecordUpdateExpression(name, xs) =>
[("name", [Name(name)]), ("updates", setters(xs))]
UnitExpr
| FunctionOrValue(_, _)
| PrefixOperator(_)
| Operator(_)
| Hex(_)
| Integer(_)
| Floatable(_)
| Literal(_)
| CharLiteral(_)
| RecordAccessFunction(_)
| GLSLExpression(_) => []
}
}
///|
fn pattern_entries(p : @ast.Pattern) -> Array[(String, Array[NodeRef])] {
match p {
TuplePattern(xs) | ListPattern(xs) => [("value", patterns(xs))]
RecordPattern(xs) => [("value", names(xs))]
UnConsPattern(l, r) => [("left", [Pattern(l)]), ("right", [Pattern(r)])]
NamedPattern(_, xs) => [("patterns", patterns(xs))]
AsPattern(x, name) => [("name", [Name(name)]), ("pattern", [Pattern(x)])]
ParenthesizedPattern(x) => [("value", [Pattern(x)])]
AllPattern
| UnitPattern
| CharPattern(_)
| StringPattern(_)
| HexPattern(_)
| IntPattern(_)
| FloatPattern(_)
| VarPattern(_) => []
}
}
///|
fn type_entries(t : @ast.TypeAnnotation) -> Array[(String, Array[NodeRef])] {
match t {
Typed(_, args) => [("args", types(args))]
Tupled(xs) => [("values", types(xs))]
FunctionTypeAnnotation(l, r) =>
[("left", [TypeAnnotation(l)]), ("right", [TypeAnnotation(r)])]
Record(fields) => [("value", record_fields(fields))]
GenericRecord(name, fields) =>
[("name", [Name(name)]), ("values", record_fields(fields.value))]
GenericType(_) | Unit => []
}
}
///|
/// The names of this node's fields, in elm-syntax JSON order.
pub fn NodeRef::fields(self : NodeRef) -> Array[String] {
self.entries().map(e => e.0)
}
///|
/// The nodes in field `name` (a new array; empty for an absent or unknown
/// field).
pub fn NodeRef::field(self : NodeRef, name : String) -> Array[NodeRef] {
for e in self.entries() {
if e.0 == name {
return e.1
}
}
[]
}
///|
fn start_before(a : NodeRef, b : NodeRef) -> Int {
let x = a.range().start
let y = b.range().start
if x.row != y.row {
x.row.compare(y.row)
} else {
x.column.compare(y.column)
}
}
///|
/// All child nodes in source order (a new array), each with its field name
/// and its index in that field. Children that start at the same place keep
/// their field order.
fn NodeRef::children_with_fields(
self : NodeRef,
) -> Array[(String, Int, NodeRef)] {
let all : Array[(Int, String, Int, NodeRef)] = []
for e in self.entries() {
for i, c in e.1 {
all.push((all.length(), e.0, i, c))
}
}
// Array::sort_by is not stable, so the original position breaks ties.
all.sort_by((a, b) => {
let c = start_before(a.3, b.3)
if c != 0 {
c
} else {
a.0.compare(b.0)
}
})
all.map(x => (x.1, x.2, x.3))
}
///|
/// All child nodes in source order (a new array).
pub fn NodeRef::children(self : NodeRef) -> Array[NodeRef] {
self.children_with_fields().map(x => x.2)
}
///|
/// The field of this node that holds `child`, if any.
pub fn NodeRef::field_of(self : NodeRef, child : NodeRef) -> String? {
for e in self.entries() {
if e.1.any(c => same(c, child)) {
return Some(e.0)
}
}
None
}
///|
/// Every node type: (category, kind, fields), one row per kind. AGENTS.md
/// lists the same table.
pub fn kind_table() -> Array[(String, String, Array[String])] {
let function_fields = ["documentation", "signature", "declaration"]
let rows : Array[(String, String, Array[String])] = [
(
"file",
"file",
["moduleDefinition", "imports", "declarations", "comments", "attributes"],
),
("module", "normal", ["moduleName", "exposingList"]),
("module", "port", ["moduleName", "exposingList"]),
(
"module",
"effect",
["moduleName", "exposingList", "command", "subscription"],
),
("module_name", "module_name", []),
("exposing", "all", []),
("exposing", "explicit", ["explicit"]),
("expose", "infix", []),
("expose", "function", []),
("expose", "typeOrAlias", []),
("expose", "typeexpose", []),
("import", "import", ["moduleName", "moduleAlias", "exposingList"]),
("declaration", "function", [..function_fields, "attributes"]),
(
"declaration",
"typeAlias",
["documentation", "name", "generics", "typeAnnotation", "attributes"],
),
(
"declaration",
"typedecl",
["documentation", "name", "generics", "constructors", "attributes"],
),
("declaration", "port", ["name", "typeAnnotation", "attributes"]),
("declaration", "infix", ["operator", "function"]),
("declaration", "destructuring", ["pattern", "expression"]),
("documentation", "documentation", []),
("signature", "signature", ["name", "typeAnnotation"]),
("implementation", "implementation", ["name", "arguments", "expression"]),
("constructor", "constructor", ["name", "arguments"]),
("let_declaration", "function", function_fields),
("let_declaration", "destructuring", ["pattern", "expression"]),
("case_branch", "case_branch", ["pattern", "expression"]),
("record_setter", "record_setter", ["field", "expression"]),
("record_field", "record_field", ["name", "typeAnnotation"]),
("name", "name", []),
("comment", "comment", []),
("attribute", "attribute", ["name", "arguments"]),
("attribute", "docs", ["names"]),
("expression", "unit", []),
("expression", "application", ["application"]),
("expression", "operatorapplication", ["left", "right"]),
("expression", "functionOrValue", []),
("expression", "ifBlock", ["clause", "then", "else"]),
("expression", "prefixoperator", []),
("expression", "operator", []),
("expression", "hex", []),
("expression", "integer", []),
("expression", "float", []),
("expression", "negation", ["negation"]),
("expression", "literal", []),
("expression", "charLiteral", []),
("expression", "tupled", ["tupled"]),
("expression", "list", ["list"]),
("expression", "parenthesized", ["parenthesized"]),
("expression", "let", ["declarations", "expression"]),
("expression", "case", ["cases", "expression"]),
("expression", "lambda", ["patterns", "expression"]),
("expression", "recordAccess", ["expression", "name"]),
("expression", "recordAccessFunction", []),
("expression", "record", ["record"]),
("expression", "recordUpdate", ["name", "updates"]),
("expression", "glsl", []),
("pattern", "all", []),
("pattern", "unit", []),
("pattern", "char", []),
("pattern", "string", []),
("pattern", "hex", []),
("pattern", "int", []),
("pattern", "float", []),
("pattern", "tuple", ["value"]),
("pattern", "record", ["value"]),
("pattern", "uncons", ["left", "right"]),
("pattern", "list", ["value"]),
("pattern", "var", []),
("pattern", "named", ["patterns"]),
("pattern", "as", ["name", "pattern"]),
("pattern", "parentisized", ["value"]),
("type", "generic", []),
("type", "typed", ["args"]),
("type", "unit", []),
("type", "tupled", ["values"]),
("type", "function", ["left", "right"]),
("type", "record", ["value"]),
("type", "genericRecord", ["name", "values"]),
]
rows
}