git.delta.rocks / jrsonnet / refs/commits / c678cf8e698f

difftreelog

refactor introduce ungrammar

Yaroslav Bolyukin2022-06-17parent: #5382799.patch.diff
in: master

23 files changed

added.cargo/configdiffbeforeafterboth
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --manifest-path ./xtask/Cargo.toml --"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [workspace]
 package.version = "0.5.0-pre95"
 package.repository = "https://github.com/CertainLach/jrsonnet"
-members = ["crates/*", "bindings/jsonnet", "cmds/*", "tests"]
+members = ["crates/*", "bindings/jsonnet", "cmds/*", "tests", "xtask"]
 default-members = ["cmds/jrsonnet"]
 resolver = "2"
 
modifiedcrates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/Cargo.toml
+++ b/crates/jrsonnet-rowan-parser/Cargo.toml
@@ -18,3 +18,4 @@
 backtrace = "0.3.63"
 indoc = "1.0.3"
 insta = "1.10.0"
+anyhow = "1.0.57"
addedcrates/jrsonnet-rowan-parser/jsonnet.ungramdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/jsonnet.ungram
@@ -0,0 +1,334 @@
+SourceFile = Expr
+
+ExprBinary =
+    lhs:Expr
+    BinaryOperator
+    rhs:Expr
+ExprUnary =
+    UnaryOperator
+    rhs:Expr
+ExprSlice =
+    Expr
+    '['
+    SliceDesc
+    ']'
+ExprIndex =
+    Expr
+    '.'
+    index:Name
+ExprIndexExpr =
+    base:Expr
+    '['
+    index:Expr
+    ']'
+ExprApply =
+    Expr
+    '('
+    ArgsDesc
+    ')'
+    'tailstrict'?
+ExprObjExtend =
+    Expr
+    '{'
+    ObjBody
+    '}'
+ExprParened =
+    '('
+    Expr
+    ')'
+
+ExprLiteral =
+    Literal
+ExprIntrinsicThisFile =
+    '$intrinsicThisFile'
+ExprIntrinsicId =
+    '$intrinsicId'
+ExprIntrinsic =
+    '$intrinsic'
+    '('
+    name:Name
+    ')'
+ExprString =
+    String
+ExprNumber =
+    Number
+ExprArray =
+    '['
+    (Expr (',' Expr)* ','?)?
+    ']'
+ExprObject =
+    '{'
+    ObjBody
+    '}'
+ExprArrayComp =
+    '['
+    Expr
+    ','?
+    ForSpec
+    CompSpec*
+    ']'
+ExprImport =
+    'importstr' String
+|   'importbin' String
+|   'import' String
+
+ExprVar =
+    name:Name
+ExprLocal =
+    'local'
+    (Bind (',' Bind)* ','?)
+    ';'
+ExprIfThenElse =
+    'if'
+    cond:Expr
+    'then'
+    then:Expr
+    ('else' else_:Expr)?
+ExprFunction =
+    'function'
+    '('
+    ParamsDesc
+    ')'
+    Expr
+ExprAssert =
+    Assertion
+    ';'
+    Expr
+ExprError =
+    'error'
+    Expr
+
+Expr =
+    ExprBinary
+|   ExprUnary
+|   ExprSlice
+|   ExprIndex
+|   ExprIndexExpr
+|   ExprApply
+|   ExprObjExtend
+|   ExprParened
+|   ExprIntrinsicThisFile
+|   ExprIntrinsicId
+|   ExprIntrinsic
+|   ExprString
+|   ExprNumber
+|   ExprArray
+|   ExprObject
+|   ExprArrayComp
+|   ExprImport
+|   ExprVar
+|   ExprLocal
+|   ExprIfThenElse
+|   ExprFunction
+|   ExprAssert
+|   ExprError
+
+BinaryOperator =
+    '||' | '&&'
+|   '|' | '^' | '&'
+|   '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in'
+|   '<<' | '>>'
+|   '+' | '-'
+|   '*' | '/' | '%'
+
+UnaryOperator =
+    '-' | '!' | '~'
+
+SliceDesc =
+    from:Expr?
+    ':'
+    (
+        end:Expr?
+        (
+            ':'
+            step:Expr?
+        )?
+    )?
+
+Name =
+    'ident'
+
+ArgsDesc =
+    (Arg (',' Arg)* ','?)?
+Arg =
+    (name:Name '=')? Expr
+
+ObjBodyComp =
+    pre:ObjLocalPostComma*
+    '['
+    key:Expr
+    ']'
+    '+'?
+    ':'
+    value:Expr
+    post:ObjLocalPreComma*
+    ForSpec
+    CompSpec*
+ObjBodyMemberList =
+    (Member (',' Member) ','?)?
+ObjBody =
+    ObjBodyComp
+|   ObjBodyMemberList
+
+ObjLocalPostComma =
+    ObjLocal
+    ','
+ObjLocalPreComma =
+    ','
+    ObjLocal
+
+MemberBindStmt = ObjLocal
+MemberAssertStmt = Assertion
+MemberField = Field
+Member =
+    MemberBindStmt
+|   MemberAssertStmt
+|   MemberField
+
+ObjLocal =
+    'local'
+    Bind
+
+FieldNormal =
+    FieldName
+    '+'?
+    Visibility
+    Expr
+FieldMethod =
+    FieldName
+    '('
+    ParamsDesc
+    ')'
+    Visibility
+    Expr
+Field =
+    FieldNormal
+|   FieldMethod
+
+FieldNameFixed =
+    id:Name
+|   String
+FieldNameDynamic =
+    '['
+    Expr
+    ']'
+FieldName =
+    FieldNameFixed
+|   FieldNameDynamic
+
+Visibility =
+    ':::'
+|   '::'
+|   ':'
+
+Literal =
+    'null'
+|   'true'
+|   'false'
+|   'self'
+|   '$'
+|   'super'
+
+String =
+    'string_double'
+|   'string_single'
+|   'string_double_verbatim'
+|   'string_single_verbatim'
+|   'string_block'
+
+Number =
+    'number'
+
+ForSpec =
+    'for'
+    bind:Name
+    'in'
+    Expr
+IfSpec =
+    'if'
+    Expr
+CompSpec =
+    ForSpec
+|   IfSpec
+
+BindDestruct =
+    into:Destruct
+    '='
+    value:Expr
+BindFunction =
+    name:Name
+    '('
+    params:ParamsDesc
+    ')'
+    '='
+    value:Expr
+Bind =
+    BindDestruct
+|   BindFunction
+
+ParamsDesc =
+    (Param (',' Param)* ','?)?
+Param =
+    Destruct
+    (
+        '='
+        Expr
+    )?
+
+Assertion =
+    'assert'
+    condition:Expr
+    (
+        ':'
+        Expr
+    )?
+
+DestructFull =
+    into:Name
+DestructSkip =
+    '?'
+DestructArray =
+    '['
+    start:(
+        Destruct
+        (',' Destruct)*
+        ','?
+    )?
+    DestructRest?
+    ','?
+    end:(
+        Destruct
+        (',' Destruct)*
+        ','?
+    )
+    ']'
+DestructObject =
+    '{'
+    (
+        DestructObjectField
+        (',' DestructObjectField)*
+        ','?
+    )?
+    DestructRest?
+    ','?
+    '}'
+Destruct =
+    DestructFull
+    DestructSkip
+    DestructArray
+    DestructObject
+
+DestructRest =
+    '...'
+    into:Name?
+
+DestructObjectField =
+    field:Name
+    (
+        ':'
+        Destruct
+    )?
+    (
+        '='
+        Expr
+    )?
addedcrates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -0,0 +1 @@
+pub trait AstToken {}
modifiedcrates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/event.rs
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -3,8 +3,9 @@
 use rowan::{GreenNode, GreenNodeBuilder, Language};
 
 use crate::{
-	lex::{Lang, Lexeme, SyntaxKind},
+	lex::Lexeme,
 	parser::{Parse, SyntaxError},
+	JsonnetLanguage, SyntaxKind,
 };
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -69,7 +70,7 @@
 					}
 
 					for kind in kinds.into_iter().rev() {
-						self.builder.start_node(Lang::kind_to_raw(kind));
+						self.builder.start_node(JsonnetLanguage::kind_to_raw(kind));
 					}
 				}
 				Event::Token => self.token(),
@@ -92,7 +93,7 @@
 	fn token(&mut self) {
 		let lexeme = self.lexemes[self.offset];
 		self.builder
-			.token(Lang::kind_to_raw(lexeme.kind), lexeme.text);
+			.token(JsonnetLanguage::kind_to_raw(lexeme.kind), lexeme.text);
 		self.offset += 1;
 	}
 	fn skip_whitespace(&mut self) {
addedcrates/jrsonnet-rowan-parser/src/generated/mod.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/mod.rs
@@ -0,0 +1,3 @@
+// mod tokens;
+// mod nodes;
+pub mod syntax_kinds;
addedcrates/jrsonnet-rowan-parser/src/generated/nodes.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
@@ -0,0 +1,2072 @@
+//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.
+
+#![allow(non_snake_case)]
+use crate::{
+	ast::{self, support, AstChildren, AstNode},
+	SyntaxKind::{self, *},
+	SyntaxNode, SyntaxToken, T,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SourceFile {
+	pub(crate) syntax: SyntaxNode,
+}
+impl SourceFile {
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprBinary {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprBinary {
+	pub fn lhs(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn binary_operator(&self) -> Option<BinaryOperator> { support::child(&self.syntax) }
+	pub fn rhs(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BinaryOperator {
+	pub(crate) syntax: SyntaxNode,
+}
+impl BinaryOperator {
+	pub fn or_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![||]) }
+	pub fn and_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&&]) }
+	pub fn bit_or_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![|]) }
+	pub fn bit_xor_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![^]) }
+	pub fn bit_and_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) }
+	pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![==]) }
+	pub fn ne_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!=]) }
+	pub fn lt_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
+	pub fn gt_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
+	pub fn le_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<=]) }
+	pub fn ge_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>=]) }
+	pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
+	pub fn lhs_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<<]) }
+	pub fn rhs_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>>]) }
+	pub fn plus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![+]) }
+	pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
+	pub fn mul_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![*]) }
+	pub fn div_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![/]) }
+	pub fn modulo_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![%]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprUnary {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprUnary {
+	pub fn unary_operator(&self) -> Option<UnaryOperator> { support::child(&self.syntax) }
+	pub fn rhs(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UnaryOperator {
+	pub(crate) syntax: SyntaxNode,
+}
+impl UnaryOperator {
+	pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
+	pub fn not_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) }
+	pub fn bit_not_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![~]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprSlice {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprSlice {
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn slice_desc(&self) -> Option<SliceDesc> { support::child(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SliceDesc {
+	pub(crate) syntax: SyntaxNode,
+}
+impl SliceDesc {
+	pub fn from(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+	pub fn end(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn step(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIndex {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIndex {
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn dot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![.]) }
+	pub fn index(&self) -> Option<Name> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Name {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Name {
+	pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIndexExpr {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIndexExpr {
+	pub fn base(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn index(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprApply {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprApply {
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn args_desc(&self) -> Option<ArgsDesc> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+	pub fn tailstrict_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![tailstrict])
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ArgsDesc {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ArgsDesc {
+	pub fn args(&self) -> AstChildren<Arg> { support::children(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprObjExtend {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprObjExtend {
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn l_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
+	pub fn obj_body(&self) -> Option<ObjBody> { support::child(&self.syntax) }
+	pub fn r_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprParened {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprParened {
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprLiteral {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprLiteral {
+	pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Literal {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Literal {
+	pub fn null_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![null]) }
+	pub fn true_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![true]) }
+	pub fn false_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![false]) }
+	pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
+	pub fn dollar_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['$']) }
+	pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIntrinsicThisFile {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIntrinsicThisFile {
+	pub fn intrinsic_this_file_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!["$intrinsicThisFile"])
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIntrinsicId {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIntrinsicId {
+	pub fn intrinsic_id_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!["$intrinsicId"])
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIntrinsic {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIntrinsic {
+	pub fn intrinsic_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!["$intrinsic"])
+	}
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn name(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprString {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprString {
+	pub fn string(&self) -> Option<String> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct String {
+	pub(crate) syntax: SyntaxNode,
+}
+impl String {
+	pub fn string_double_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![string_double])
+	}
+	pub fn string_single_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![string_single])
+	}
+	pub fn string_double_verbatim_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![string_double_verbatim])
+	}
+	pub fn string_single_verbatim_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![string_single_verbatim])
+	}
+	pub fn string_block_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![string_block])
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprNumber {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprNumber {
+	pub fn number(&self) -> Option<Number> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Number {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Number {
+	pub fn number_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![number]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprArray {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprArray {
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn exprs(&self) -> AstChildren<Expr> { support::children(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprObject {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprObject {
+	pub fn l_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
+	pub fn obj_body(&self) -> Option<ObjBody> { support::child(&self.syntax) }
+	pub fn r_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprArrayComp {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprArrayComp {
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+	pub fn for_spec(&self) -> Option<ForSpec> { support::child(&self.syntax) }
+	pub fn comp_specs(&self) -> AstChildren<CompSpec> { support::children(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ForSpec {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ForSpec {
+	pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
+	pub fn bind(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprImport {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprImport {
+	pub fn importstr_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![importstr])
+	}
+	pub fn string(&self) -> Option<String> { support::child(&self.syntax) }
+	pub fn importbin_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![importbin])
+	}
+	pub fn import_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![import]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprVar {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprVar {
+	pub fn name(&self) -> Option<Name> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprLocal {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprLocal {
+	pub fn local_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![local]) }
+	pub fn binds(&self) -> AstChildren<Bind> { support::children(&self.syntax) }
+	pub fn semi_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprIfThenElse {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprIfThenElse {
+	pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
+	pub fn cond(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn then_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![then]) }
+	pub fn then(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
+	pub fn else_(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprFunction {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprFunction {
+	pub fn function_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![function])
+	}
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn params_desc(&self) -> Option<ParamsDesc> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ParamsDesc {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ParamsDesc {
+	pub fn params(&self) -> AstChildren<Param> { support::children(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprAssert {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprAssert {
+	pub fn assertion(&self) -> Option<Assertion> { support::child(&self.syntax) }
+	pub fn semi_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Assertion {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Assertion {
+	pub fn assert_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![assert]) }
+	pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ExprError {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ExprError {
+	pub fn error_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![error]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Arg {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Arg {
+	pub fn name(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn assign_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjBodyComp {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ObjBodyComp {
+	pub fn pre(&self) -> AstChildren<ObjLocalPostComma> { support::children(&self.syntax) }
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn key(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+	pub fn plus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![+]) }
+	pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+	pub fn value(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn post(&self) -> AstChildren<ObjLocalPreComma> { support::children(&self.syntax) }
+	pub fn for_spec(&self) -> Option<ForSpec> { support::child(&self.syntax) }
+	pub fn comp_specs(&self) -> AstChildren<CompSpec> { support::children(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjLocalPostComma {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ObjLocalPostComma {
+	pub fn obj_local(&self) -> Option<ObjLocal> { support::child(&self.syntax) }
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjLocalPreComma {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ObjLocalPreComma {
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+	pub fn obj_local(&self) -> Option<ObjLocal> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjBodyMemberList {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ObjBodyMemberList {
+	pub fn member(&self) -> Option<Member> { support::child(&self.syntax) }
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ObjLocal {
+	pub(crate) syntax: SyntaxNode,
+}
+impl ObjLocal {
+	pub fn local_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![local]) }
+	pub fn bind(&self) -> Option<Bind> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberBindStmt {
+	pub(crate) syntax: SyntaxNode,
+}
+impl MemberBindStmt {
+	pub fn obj_local(&self) -> Option<ObjLocal> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberAssertStmt {
+	pub(crate) syntax: SyntaxNode,
+}
+impl MemberAssertStmt {
+	pub fn assertion(&self) -> Option<Assertion> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberField {
+	pub(crate) syntax: SyntaxNode,
+}
+impl MemberField {
+	pub fn field(&self) -> Option<Field> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldNormal {
+	pub(crate) syntax: SyntaxNode,
+}
+impl FieldNormal {
+	pub fn field_name(&self) -> Option<FieldName> { support::child(&self.syntax) }
+	pub fn plus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![+]) }
+	pub fn visibility(&self) -> Option<Visibility> { support::child(&self.syntax) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Visibility {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Visibility {
+	pub fn coloncoloncolon_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![:::])
+	}
+	pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
+	pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldMethod {
+	pub(crate) syntax: SyntaxNode,
+}
+impl FieldMethod {
+	pub fn field_name(&self) -> Option<FieldName> { support::child(&self.syntax) }
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn params_desc(&self) -> Option<ParamsDesc> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+	pub fn visibility(&self) -> Option<Visibility> { support::child(&self.syntax) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldNameFixed {
+	pub(crate) syntax: SyntaxNode,
+}
+impl FieldNameFixed {
+	pub fn id(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn string(&self) -> Option<String> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FieldNameDynamic {
+	pub(crate) syntax: SyntaxNode,
+}
+impl FieldNameDynamic {
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct IfSpec {
+	pub(crate) syntax: SyntaxNode,
+}
+impl IfSpec {
+	pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BindDestruct {
+	pub(crate) syntax: SyntaxNode,
+}
+impl BindDestruct {
+	pub fn into(&self) -> Option<Destruct> { support::child(&self.syntax) }
+	pub fn assign_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+	pub fn value(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Destruct {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Destruct {
+	pub fn destruct_full(&self) -> Option<DestructFull> { support::child(&self.syntax) }
+	pub fn destruct_skip(&self) -> Option<DestructSkip> { support::child(&self.syntax) }
+	pub fn destruct_array(&self) -> Option<DestructArray> { support::child(&self.syntax) }
+	pub fn destruct_object(&self) -> Option<DestructObject> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BindFunction {
+	pub(crate) syntax: SyntaxNode,
+}
+impl BindFunction {
+	pub fn name(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+	pub fn params(&self) -> Option<ParamsDesc> { support::child(&self.syntax) }
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+	pub fn assign_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+	pub fn value(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Param {
+	pub(crate) syntax: SyntaxNode,
+}
+impl Param {
+	pub fn destruct(&self) -> Option<Destruct> { support::child(&self.syntax) }
+	pub fn assign_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructFull {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructFull {
+	pub fn into(&self) -> Option<Name> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructSkip {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructSkip {
+	pub fn question_mark_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![?]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructArray {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructArray {
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
+	pub fn start(&self) -> AstChildren<Destruct> { support::children(&self.syntax) }
+	pub fn destruct_rest(&self) -> Option<DestructRest> { support::child(&self.syntax) }
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+	pub fn end(&self) -> AstChildren<Destruct> { support::children(&self.syntax) }
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructRest {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructRest {
+	pub fn dotdotdot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![...]) }
+	pub fn into(&self) -> Option<Name> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructObject {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructObject {
+	pub fn l_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
+	pub fn destruct_object_fields(&self) -> AstChildren<DestructObjectField> {
+		support::children(&self.syntax)
+	}
+	pub fn destruct_rest(&self) -> Option<DestructRest> { support::child(&self.syntax) }
+	pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+	pub fn r_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DestructObjectField {
+	pub(crate) syntax: SyntaxNode,
+}
+impl DestructObjectField {
+	pub fn field(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+	pub fn destruct(&self) -> Option<Destruct> { support::child(&self.syntax) }
+	pub fn assign_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Expr {
+	ExprBinary(ExprBinary),
+	ExprUnary(ExprUnary),
+	ExprSlice(ExprSlice),
+	ExprIndex(ExprIndex),
+	ExprIndexExpr(ExprIndexExpr),
+	ExprApply(ExprApply),
+	ExprObjExtend(ExprObjExtend),
+	ExprParened(ExprParened),
+	ExprIntrinsicThisFile(ExprIntrinsicThisFile),
+	ExprIntrinsicId(ExprIntrinsicId),
+	ExprIntrinsic(ExprIntrinsic),
+	ExprString(ExprString),
+	ExprNumber(ExprNumber),
+	ExprArray(ExprArray),
+	ExprObject(ExprObject),
+	ExprArrayComp(ExprArrayComp),
+	ExprImport(ExprImport),
+	ExprVar(ExprVar),
+	ExprLocal(ExprLocal),
+	ExprIfThenElse(ExprIfThenElse),
+	ExprFunction(ExprFunction),
+	ExprAssert(ExprAssert),
+	ExprError(ExprError),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ObjBody {
+	ObjBodyComp(ObjBodyComp),
+	ObjBodyMemberList(ObjBodyMemberList),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum CompSpec {
+	ForSpec(ForSpec),
+	IfSpec(IfSpec),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Bind {
+	BindDestruct(BindDestruct),
+	BindFunction(BindFunction),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Member {
+	MemberBindStmt(MemberBindStmt),
+	MemberAssertStmt(MemberAssertStmt),
+	MemberField(MemberField),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Field {
+	FieldNormal(FieldNormal),
+	FieldMethod(FieldMethod),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum FieldName {
+	FieldNameFixed(FieldNameFixed),
+	FieldNameDynamic(FieldNameDynamic),
+}
+impl AstNode for SourceFile {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprBinary {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_BINARY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for BinaryOperator {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == BINARY_OPERATOR }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprUnary {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_UNARY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for UnaryOperator {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == UNARY_OPERATOR }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprSlice {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_SLICE }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for SliceDesc {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_DESC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIndex {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INDEX }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Name {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == NAME }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIndexExpr {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INDEX_EXPR }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprApply {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_APPLY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ArgsDesc {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == ARGS_DESC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprObjExtend {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_OBJ_EXTEND }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprParened {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_PARENED }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprLiteral {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_LITERAL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Literal {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIntrinsicThisFile {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC_THIS_FILE }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIntrinsicId {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC_ID }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIntrinsic {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprString {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STRING }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for String {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == STRING }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprNumber {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_NUMBER }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Number {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == NUMBER }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprArray {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ARRAY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprObject {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_OBJECT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprArrayComp {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ARRAY_COMP }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ForSpec {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_SPEC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprImport {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_IMPORT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprVar {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_VAR }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprLocal {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_LOCAL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprIfThenElse {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_IF_THEN_ELSE }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprFunction {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_FUNCTION }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ParamsDesc {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == PARAMS_DESC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprAssert {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ASSERT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Assertion {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == ASSERTION }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ExprError {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ERROR }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Arg {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == ARG }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ObjBodyComp {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_BODY_COMP }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ObjLocalPostComma {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL_POST_COMMA }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ObjLocalPreComma {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL_PRE_COMMA }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ObjBodyMemberList {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_BODY_MEMBER_LIST }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for ObjLocal {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for MemberBindStmt {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_BIND_STMT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for MemberAssertStmt {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_ASSERT_STMT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for MemberField {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_FIELD }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for FieldNormal {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NORMAL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Visibility {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for FieldMethod {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_METHOD }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for FieldNameFixed {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NAME_FIXED }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for FieldNameDynamic {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NAME_DYNAMIC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for IfSpec {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == IF_SPEC }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for BindDestruct {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == BIND_DESTRUCT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Destruct {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for BindFunction {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == BIND_FUNCTION }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for Param {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructFull {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_FULL }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructSkip {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_SKIP }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructArray {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_ARRAY }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructRest {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_REST }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructObject {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_OBJECT }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl AstNode for DestructObjectField {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_OBJECT_FIELD }
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl From<ExprBinary> for Expr {
+	fn from(node: ExprBinary) -> Expr { Expr::ExprBinary(node) }
+}
+impl From<ExprUnary> for Expr {
+	fn from(node: ExprUnary) -> Expr { Expr::ExprUnary(node) }
+}
+impl From<ExprSlice> for Expr {
+	fn from(node: ExprSlice) -> Expr { Expr::ExprSlice(node) }
+}
+impl From<ExprIndex> for Expr {
+	fn from(node: ExprIndex) -> Expr { Expr::ExprIndex(node) }
+}
+impl From<ExprIndexExpr> for Expr {
+	fn from(node: ExprIndexExpr) -> Expr { Expr::ExprIndexExpr(node) }
+}
+impl From<ExprApply> for Expr {
+	fn from(node: ExprApply) -> Expr { Expr::ExprApply(node) }
+}
+impl From<ExprObjExtend> for Expr {
+	fn from(node: ExprObjExtend) -> Expr { Expr::ExprObjExtend(node) }
+}
+impl From<ExprParened> for Expr {
+	fn from(node: ExprParened) -> Expr { Expr::ExprParened(node) }
+}
+impl From<ExprIntrinsicThisFile> for Expr {
+	fn from(node: ExprIntrinsicThisFile) -> Expr { Expr::ExprIntrinsicThisFile(node) }
+}
+impl From<ExprIntrinsicId> for Expr {
+	fn from(node: ExprIntrinsicId) -> Expr { Expr::ExprIntrinsicId(node) }
+}
+impl From<ExprIntrinsic> for Expr {
+	fn from(node: ExprIntrinsic) -> Expr { Expr::ExprIntrinsic(node) }
+}
+impl From<ExprString> for Expr {
+	fn from(node: ExprString) -> Expr { Expr::ExprString(node) }
+}
+impl From<ExprNumber> for Expr {
+	fn from(node: ExprNumber) -> Expr { Expr::ExprNumber(node) }
+}
+impl From<ExprArray> for Expr {
+	fn from(node: ExprArray) -> Expr { Expr::ExprArray(node) }
+}
+impl From<ExprObject> for Expr {
+	fn from(node: ExprObject) -> Expr { Expr::ExprObject(node) }
+}
+impl From<ExprArrayComp> for Expr {
+	fn from(node: ExprArrayComp) -> Expr { Expr::ExprArrayComp(node) }
+}
+impl From<ExprImport> for Expr {
+	fn from(node: ExprImport) -> Expr { Expr::ExprImport(node) }
+}
+impl From<ExprVar> for Expr {
+	fn from(node: ExprVar) -> Expr { Expr::ExprVar(node) }
+}
+impl From<ExprLocal> for Expr {
+	fn from(node: ExprLocal) -> Expr { Expr::ExprLocal(node) }
+}
+impl From<ExprIfThenElse> for Expr {
+	fn from(node: ExprIfThenElse) -> Expr { Expr::ExprIfThenElse(node) }
+}
+impl From<ExprFunction> for Expr {
+	fn from(node: ExprFunction) -> Expr { Expr::ExprFunction(node) }
+}
+impl From<ExprAssert> for Expr {
+	fn from(node: ExprAssert) -> Expr { Expr::ExprAssert(node) }
+}
+impl From<ExprError> for Expr {
+	fn from(node: ExprError) -> Expr { Expr::ExprError(node) }
+}
+impl AstNode for Expr {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			EXPR_BINARY
+			| EXPR_UNARY
+			| EXPR_SLICE
+			| EXPR_INDEX
+			| EXPR_INDEX_EXPR
+			| EXPR_APPLY
+			| EXPR_OBJ_EXTEND
+			| EXPR_PARENED
+			| EXPR_INTRINSIC_THIS_FILE
+			| EXPR_INTRINSIC_ID
+			| EXPR_INTRINSIC
+			| EXPR_STRING
+			| EXPR_NUMBER
+			| EXPR_ARRAY
+			| EXPR_OBJECT
+			| EXPR_ARRAY_COMP
+			| EXPR_IMPORT
+			| EXPR_VAR
+			| EXPR_LOCAL
+			| EXPR_IF_THEN_ELSE
+			| EXPR_FUNCTION
+			| EXPR_ASSERT
+			| EXPR_ERROR => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			EXPR_BINARY => Expr::ExprBinary(ExprBinary { syntax }),
+			EXPR_UNARY => Expr::ExprUnary(ExprUnary { syntax }),
+			EXPR_SLICE => Expr::ExprSlice(ExprSlice { syntax }),
+			EXPR_INDEX => Expr::ExprIndex(ExprIndex { syntax }),
+			EXPR_INDEX_EXPR => Expr::ExprIndexExpr(ExprIndexExpr { syntax }),
+			EXPR_APPLY => Expr::ExprApply(ExprApply { syntax }),
+			EXPR_OBJ_EXTEND => Expr::ExprObjExtend(ExprObjExtend { syntax }),
+			EXPR_PARENED => Expr::ExprParened(ExprParened { syntax }),
+			EXPR_INTRINSIC_THIS_FILE => {
+				Expr::ExprIntrinsicThisFile(ExprIntrinsicThisFile { syntax })
+			}
+			EXPR_INTRINSIC_ID => Expr::ExprIntrinsicId(ExprIntrinsicId { syntax }),
+			EXPR_INTRINSIC => Expr::ExprIntrinsic(ExprIntrinsic { syntax }),
+			EXPR_STRING => Expr::ExprString(ExprString { syntax }),
+			EXPR_NUMBER => Expr::ExprNumber(ExprNumber { syntax }),
+			EXPR_ARRAY => Expr::ExprArray(ExprArray { syntax }),
+			EXPR_OBJECT => Expr::ExprObject(ExprObject { syntax }),
+			EXPR_ARRAY_COMP => Expr::ExprArrayComp(ExprArrayComp { syntax }),
+			EXPR_IMPORT => Expr::ExprImport(ExprImport { syntax }),
+			EXPR_VAR => Expr::ExprVar(ExprVar { syntax }),
+			EXPR_LOCAL => Expr::ExprLocal(ExprLocal { syntax }),
+			EXPR_IF_THEN_ELSE => Expr::ExprIfThenElse(ExprIfThenElse { syntax }),
+			EXPR_FUNCTION => Expr::ExprFunction(ExprFunction { syntax }),
+			EXPR_ASSERT => Expr::ExprAssert(ExprAssert { syntax }),
+			EXPR_ERROR => Expr::ExprError(ExprError { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			Expr::ExprBinary(it) => &it.syntax,
+			Expr::ExprUnary(it) => &it.syntax,
+			Expr::ExprSlice(it) => &it.syntax,
+			Expr::ExprIndex(it) => &it.syntax,
+			Expr::ExprIndexExpr(it) => &it.syntax,
+			Expr::ExprApply(it) => &it.syntax,
+			Expr::ExprObjExtend(it) => &it.syntax,
+			Expr::ExprParened(it) => &it.syntax,
+			Expr::ExprIntrinsicThisFile(it) => &it.syntax,
+			Expr::ExprIntrinsicId(it) => &it.syntax,
+			Expr::ExprIntrinsic(it) => &it.syntax,
+			Expr::ExprString(it) => &it.syntax,
+			Expr::ExprNumber(it) => &it.syntax,
+			Expr::ExprArray(it) => &it.syntax,
+			Expr::ExprObject(it) => &it.syntax,
+			Expr::ExprArrayComp(it) => &it.syntax,
+			Expr::ExprImport(it) => &it.syntax,
+			Expr::ExprVar(it) => &it.syntax,
+			Expr::ExprLocal(it) => &it.syntax,
+			Expr::ExprIfThenElse(it) => &it.syntax,
+			Expr::ExprFunction(it) => &it.syntax,
+			Expr::ExprAssert(it) => &it.syntax,
+			Expr::ExprError(it) => &it.syntax,
+		}
+	}
+}
+impl From<ObjBodyComp> for ObjBody {
+	fn from(node: ObjBodyComp) -> ObjBody { ObjBody::ObjBodyComp(node) }
+}
+impl From<ObjBodyMemberList> for ObjBody {
+	fn from(node: ObjBodyMemberList) -> ObjBody { ObjBody::ObjBodyMemberList(node) }
+}
+impl AstNode for ObjBody {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			OBJ_BODY_COMP | OBJ_BODY_MEMBER_LIST => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			OBJ_BODY_COMP => ObjBody::ObjBodyComp(ObjBodyComp { syntax }),
+			OBJ_BODY_MEMBER_LIST => ObjBody::ObjBodyMemberList(ObjBodyMemberList { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			ObjBody::ObjBodyComp(it) => &it.syntax,
+			ObjBody::ObjBodyMemberList(it) => &it.syntax,
+		}
+	}
+}
+impl From<ForSpec> for CompSpec {
+	fn from(node: ForSpec) -> CompSpec { CompSpec::ForSpec(node) }
+}
+impl From<IfSpec> for CompSpec {
+	fn from(node: IfSpec) -> CompSpec { CompSpec::IfSpec(node) }
+}
+impl AstNode for CompSpec {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			FOR_SPEC | IF_SPEC => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			FOR_SPEC => CompSpec::ForSpec(ForSpec { syntax }),
+			IF_SPEC => CompSpec::IfSpec(IfSpec { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			CompSpec::ForSpec(it) => &it.syntax,
+			CompSpec::IfSpec(it) => &it.syntax,
+		}
+	}
+}
+impl From<BindDestruct> for Bind {
+	fn from(node: BindDestruct) -> Bind { Bind::BindDestruct(node) }
+}
+impl From<BindFunction> for Bind {
+	fn from(node: BindFunction) -> Bind { Bind::BindFunction(node) }
+}
+impl AstNode for Bind {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			BIND_DESTRUCT | BIND_FUNCTION => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			BIND_DESTRUCT => Bind::BindDestruct(BindDestruct { syntax }),
+			BIND_FUNCTION => Bind::BindFunction(BindFunction { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			Bind::BindDestruct(it) => &it.syntax,
+			Bind::BindFunction(it) => &it.syntax,
+		}
+	}
+}
+impl From<MemberBindStmt> for Member {
+	fn from(node: MemberBindStmt) -> Member { Member::MemberBindStmt(node) }
+}
+impl From<MemberAssertStmt> for Member {
+	fn from(node: MemberAssertStmt) -> Member { Member::MemberAssertStmt(node) }
+}
+impl From<MemberField> for Member {
+	fn from(node: MemberField) -> Member { Member::MemberField(node) }
+}
+impl AstNode for Member {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			MEMBER_BIND_STMT | MEMBER_ASSERT_STMT | MEMBER_FIELD => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			MEMBER_BIND_STMT => Member::MemberBindStmt(MemberBindStmt { syntax }),
+			MEMBER_ASSERT_STMT => Member::MemberAssertStmt(MemberAssertStmt { syntax }),
+			MEMBER_FIELD => Member::MemberField(MemberField { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			Member::MemberBindStmt(it) => &it.syntax,
+			Member::MemberAssertStmt(it) => &it.syntax,
+			Member::MemberField(it) => &it.syntax,
+		}
+	}
+}
+impl From<FieldNormal> for Field {
+	fn from(node: FieldNormal) -> Field { Field::FieldNormal(node) }
+}
+impl From<FieldMethod> for Field {
+	fn from(node: FieldMethod) -> Field { Field::FieldMethod(node) }
+}
+impl AstNode for Field {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			FIELD_NORMAL | FIELD_METHOD => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			FIELD_NORMAL => Field::FieldNormal(FieldNormal { syntax }),
+			FIELD_METHOD => Field::FieldMethod(FieldMethod { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			Field::FieldNormal(it) => &it.syntax,
+			Field::FieldMethod(it) => &it.syntax,
+		}
+	}
+}
+impl From<FieldNameFixed> for FieldName {
+	fn from(node: FieldNameFixed) -> FieldName { FieldName::FieldNameFixed(node) }
+}
+impl From<FieldNameDynamic> for FieldName {
+	fn from(node: FieldNameDynamic) -> FieldName { FieldName::FieldNameDynamic(node) }
+}
+impl AstNode for FieldName {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			FIELD_NAME_FIXED | FIELD_NAME_DYNAMIC => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			FIELD_NAME_FIXED => FieldName::FieldNameFixed(FieldNameFixed { syntax }),
+			FIELD_NAME_DYNAMIC => FieldName::FieldNameDynamic(FieldNameDynamic { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			FieldName::FieldNameFixed(it) => &it.syntax,
+			FieldName::FieldNameDynamic(it) => &it.syntax,
+		}
+	}
+}
+impl std::fmt::Display for Expr {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjBody {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for CompSpec {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Bind {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Member {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Field {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for FieldName {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for SourceFile {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprBinary {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for BinaryOperator {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprUnary {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for UnaryOperator {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprSlice {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for SliceDesc {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIndex {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Name {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIndexExpr {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprApply {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ArgsDesc {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprObjExtend {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprParened {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprLiteral {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Literal {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIntrinsicThisFile {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIntrinsicId {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIntrinsic {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprString {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for String {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprNumber {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Number {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprArray {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprObject {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprArrayComp {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ForSpec {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprImport {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprVar {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprLocal {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprIfThenElse {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprFunction {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ParamsDesc {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprAssert {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Assertion {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ExprError {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Arg {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjBodyComp {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjLocalPostComma {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjLocalPreComma {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjBodyMemberList {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for ObjLocal {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for MemberBindStmt {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for MemberAssertStmt {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for MemberField {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for FieldNormal {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Visibility {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for FieldMethod {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for FieldNameFixed {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for FieldNameDynamic {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for IfSpec {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for BindDestruct {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Destruct {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for BindFunction {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for Param {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructFull {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructSkip {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructArray {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructRest {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructObject {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for DestructObjectField {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
addedcrates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs
@@ -0,0 +1,307 @@
+//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.
+
+#![allow(bad_style, missing_docs, unreachable_pub)]
+use logos::Logos;
+#[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`."]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Logos)]
+#[repr(u16)]
+pub enum SyntaxKind {
+	#[doc(hidden)]
+	TOMBSTONE,
+	#[doc(hidden)]
+	EOF,
+	#[token("||")]
+	OR,
+	#[token("&&")]
+	AND,
+	#[token("|")]
+	BIT_OR,
+	#[token("^")]
+	BIT_XOR,
+	#[token("&")]
+	BIT_AND,
+	#[token("==")]
+	EQ,
+	#[token("!=")]
+	NE,
+	#[token("<")]
+	LT,
+	#[token(">")]
+	GT,
+	#[token("<=")]
+	LE,
+	#[token(">=")]
+	GE,
+	#[token("<<")]
+	LHS,
+	#[token(">>")]
+	RHS,
+	#[token("+")]
+	PLUS,
+	#[token("-")]
+	MINUS,
+	#[token("*")]
+	MUL,
+	#[token("/")]
+	DIV,
+	#[token("%")]
+	MODULO,
+	#[token("!")]
+	NOT,
+	#[token("~")]
+	BIT_NOT,
+	#[token("[")]
+	L_BRACK,
+	#[token("]")]
+	R_BRACK,
+	#[token("(")]
+	L_PAREN,
+	#[token(")")]
+	R_PAREN,
+	#[token("{")]
+	L_BRACE,
+	#[token("}")]
+	R_BRACE,
+	#[token(":")]
+	COLON,
+	#[token("::")]
+	COLONCOLON,
+	#[token(":::")]
+	COLONCOLONCOLON,
+	#[token(";")]
+	SEMI,
+	#[token(".")]
+	DOT,
+	#[token("...")]
+	DOTDOTDOT,
+	#[token(",")]
+	COMMA,
+	#[token("$")]
+	DOLLAR,
+	#[token("=")]
+	ASSIGN,
+	#[token("?")]
+	QUESTION_MARK,
+	#[token("$intrinsicThisFile")]
+	INTRINSIC_THIS_FILE,
+	#[token("$intrinsicId")]
+	INTRINSIC_ID,
+	#[token("$intrinsic")]
+	INTRINSIC,
+	#[token("tailstrict")]
+	TAILSTRICT_KW,
+	#[token("importstr")]
+	IMPORTSTR_KW,
+	#[token("importbin")]
+	IMPORTBIN_KW,
+	#[token("import")]
+	IMPORT_KW,
+	#[token("local")]
+	LOCAL_KW,
+	#[token("if")]
+	IF_KW,
+	#[token("then")]
+	THEN_KW,
+	#[token("else")]
+	ELSE_KW,
+	#[token("function")]
+	FUNCTION_KW,
+	#[token("error")]
+	ERROR_KW,
+	#[token("in")]
+	IN_KW,
+	#[token("null")]
+	NULL_KW,
+	#[token("true")]
+	TRUE_KW,
+	#[token("false")]
+	FALSE_KW,
+	#[token("self")]
+	SELF_KW,
+	#[token("super")]
+	SUPER_KW,
+	#[token("for")]
+	FOR_KW,
+	#[token("assert")]
+	ASSERT_KW,
+	#[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?")]
+	NUMBER,
+	#[regex("\"(?s:[^\"\\\\]|\\\\.)*\"")]
+	STRING_DOUBLE,
+	#[regex("'(?s:[^'\\\\]|\\\\.)*'")]
+	STRING_SINGLE,
+	#[regex("@\"(?:[^\"]|\"\")*\"")]
+	STRING_DOUBLE_VERBATIM,
+	#[regex("@'(?:[^']|'')*'")]
+	STRING_SINGLE_VERBATIM,
+	#[regex("\\|\\|\\|")]
+	STRING_BLOCK,
+	#[regex("[_a-zA-Z][_a-zA-Z0-9]*")]
+	IDENT,
+	#[regex("[ \\t\\n\\r]+")]
+	WHITESPACE,
+	#[regex("//[^\\r\\n]*(\\r\\n|\\n)?")]
+	SINGLE_LINE_SLASH_COMMENT,
+	#[regex("#[^\\r\\n]*(\\r\\n|\\n)?")]
+	SINGLE_LINE_HASH_COMMENT,
+	#[regex("/\\*([^*]|\\*[^/])*\\*/")]
+	MULTI_LINE_COMMENT,
+	#[error]
+	ERROR,
+	SOURCE_FILE,
+	EXPR_BINARY,
+	BINARY_OPERATOR,
+	EXPR_UNARY,
+	UNARY_OPERATOR,
+	EXPR_SLICE,
+	SLICE_DESC,
+	EXPR_INDEX,
+	NAME,
+	EXPR_INDEX_EXPR,
+	EXPR_APPLY,
+	ARGS_DESC,
+	EXPR_OBJ_EXTEND,
+	EXPR_PARENED,
+	EXPR_LITERAL,
+	LITERAL,
+	EXPR_INTRINSIC_THIS_FILE,
+	EXPR_INTRINSIC_ID,
+	EXPR_INTRINSIC,
+	EXPR_STRING,
+	STRING,
+	EXPR_NUMBER,
+	EXPR_ARRAY,
+	EXPR_OBJECT,
+	EXPR_ARRAY_COMP,
+	FOR_SPEC,
+	EXPR_IMPORT,
+	EXPR_VAR,
+	EXPR_LOCAL,
+	EXPR_IF_THEN_ELSE,
+	EXPR_FUNCTION,
+	PARAMS_DESC,
+	EXPR_ASSERT,
+	ASSERTION,
+	EXPR_ERROR,
+	ARG,
+	OBJ_BODY_COMP,
+	OBJ_LOCAL_POST_COMMA,
+	OBJ_LOCAL_PRE_COMMA,
+	OBJ_BODY_MEMBER_LIST,
+	OBJ_LOCAL,
+	MEMBER_BIND_STMT,
+	MEMBER_ASSERT_STMT,
+	MEMBER_FIELD,
+	FIELD_NORMAL,
+	VISIBILITY,
+	FIELD_METHOD,
+	FIELD_NAME_FIXED,
+	FIELD_NAME_DYNAMIC,
+	IF_SPEC,
+	BIND_DESTRUCT,
+	DESTRUCT,
+	BIND_FUNCTION,
+	PARAM,
+	DESTRUCT_FULL,
+	DESTRUCT_SKIP,
+	DESTRUCT_ARRAY,
+	DESTRUCT_REST,
+	DESTRUCT_OBJECT,
+	DESTRUCT_OBJECT_FIELD,
+	EXPR,
+	OBJ_BODY,
+	COMP_SPEC,
+	BIND,
+	MEMBER,
+	FIELD,
+	FIELD_NAME,
+	#[doc(hidden)]
+	__LAST,
+}
+use self::SyntaxKind::*;
+impl SyntaxKind {
+	pub fn is_keyword(self) -> bool {
+		match self {
+			TAILSTRICT_KW | IMPORTSTR_KW | IMPORTBIN_KW | IMPORT_KW | LOCAL_KW | IF_KW
+			| THEN_KW | ELSE_KW | FUNCTION_KW | ERROR_KW | IN_KW | NULL_KW | TRUE_KW | FALSE_KW
+			| SELF_KW | SUPER_KW | FOR_KW | ASSERT_KW => true,
+			_ => false,
+		}
+	}
+	pub fn is_punct(self) -> bool {
+		match self {
+			OR | AND | BIT_OR | BIT_XOR | BIT_AND | EQ | NE | LT | GT | LE | GE | LHS | RHS
+			| PLUS | MINUS | MUL | DIV | MODULO | NOT | BIT_NOT | L_BRACK | R_BRACK | L_PAREN
+			| R_PAREN | L_BRACE | R_BRACE | COLON | COLONCOLON | COLONCOLONCOLON | SEMI | DOT
+			| DOTDOTDOT | COMMA | DOLLAR | ASSIGN | QUESTION_MARK | INTRINSIC_THIS_FILE
+			| INTRINSIC_ID | INTRINSIC => true,
+			_ => false,
+		}
+	}
+	pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+		let kw = match ident {
+			"tailstrict" => TAILSTRICT_KW,
+			"importstr" => IMPORTSTR_KW,
+			"importbin" => IMPORTBIN_KW,
+			"import" => IMPORT_KW,
+			"local" => LOCAL_KW,
+			"if" => IF_KW,
+			"then" => THEN_KW,
+			"else" => ELSE_KW,
+			"function" => FUNCTION_KW,
+			"error" => ERROR_KW,
+			"in" => IN_KW,
+			"null" => NULL_KW,
+			"true" => TRUE_KW,
+			"false" => FALSE_KW,
+			"self" => SELF_KW,
+			"super" => SUPER_KW,
+			"for" => FOR_KW,
+			"assert" => ASSERT_KW,
+			_ => return None,
+		};
+		Some(kw)
+	}
+	pub fn from_char(c: char) -> Option<SyntaxKind> {
+		let tok = match c {
+			'|' => BIT_OR,
+			'^' => BIT_XOR,
+			'&' => BIT_AND,
+			'<' => LT,
+			'>' => GT,
+			'+' => PLUS,
+			'-' => MINUS,
+			'*' => MUL,
+			'/' => DIV,
+			'%' => MODULO,
+			'!' => NOT,
+			'~' => BIT_NOT,
+			'[' => L_BRACK,
+			']' => R_BRACK,
+			'(' => L_PAREN,
+			')' => R_PAREN,
+			'{' => L_BRACE,
+			'}' => R_BRACE,
+			':' => COLON,
+			';' => SEMI,
+			'.' => DOT,
+			',' => COMMA,
+			'$' => DOLLAR,
+			'=' => ASSIGN,
+			'?' => QUESTION_MARK,
+			_ => return None,
+		};
+		Some(tok)
+	}
+	pub fn from_raw(r: u16) -> Self {
+		assert!(r < Self::__LAST as u16);
+		unsafe { std::mem::transmute(r) }
+	}
+	pub fn into_raw(self) -> u16 {
+		self as u16
+	}
+}
+#[macro_export]
+macro_rules ! T { [||] => { $ crate :: SyntaxKind :: OR } ; [&&] => { $ crate :: SyntaxKind :: AND } ; [|] => { $ crate :: SyntaxKind :: BIT_OR } ; [^] => { $ crate :: SyntaxKind :: BIT_XOR } ; [&] => { $ crate :: SyntaxKind :: BIT_AND } ; [==] => { $ crate :: SyntaxKind :: EQ } ; [!=] => { $ crate :: SyntaxKind :: NE } ; [<] => { $ crate :: SyntaxKind :: LT } ; [>] => { $ crate :: SyntaxKind :: GT } ; [<=] => { $ crate :: SyntaxKind :: LE } ; [>=] => { $ crate :: SyntaxKind :: GE } ; [<<] => { $ crate :: SyntaxKind :: LHS } ; [>>] => { $ crate :: SyntaxKind :: RHS } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [*] => { $ crate :: SyntaxKind :: MUL } ; [/] => { $ crate :: SyntaxKind :: DIV } ; [%] => { $ crate :: SyntaxKind :: MODULO } ; [!] => { $ crate :: SyntaxKind :: NOT } ; [~] => { $ crate :: SyntaxKind :: BIT_NOT } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_BRACE } ; ['}'] => { $ crate :: SyntaxKind :: R_BRACE } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLONCOLON } ; [:::] => { $ crate :: SyntaxKind :: COLONCOLONCOLON } ; [;] => { $ crate :: SyntaxKind :: SEMI } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [...] => { $ crate :: SyntaxKind :: DOTDOTDOT } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['$'] => { $ crate :: SyntaxKind :: DOLLAR } ; [=] => { $ crate :: SyntaxKind :: ASSIGN } ; [?] => { $ crate :: SyntaxKind :: QUESTION_MARK } ; ["$intrinsicThisFile"] => { $ crate :: SyntaxKind :: INTRINSIC_THIS_FILE } ; ["$intrinsicId"] => { $ crate :: SyntaxKind :: INTRINSIC_ID } ; ["$intrinsic"] => { $ crate :: SyntaxKind :: INTRINSIC } ; [tailstrict] => { $ crate :: SyntaxKind :: TAILSTRICT_KW } ; [importstr] => { $ crate :: SyntaxKind :: IMPORTSTR_KW } ; [importbin] => { $ crate :: SyntaxKind :: IMPORTBIN_KW } ; [import] => { $ crate :: SyntaxKind :: IMPORT_KW } ; [local] => { $ crate :: SyntaxKind :: LOCAL_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [then] => { $ crate :: SyntaxKind :: THEN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [function] => { $ crate :: SyntaxKind :: FUNCTION_KW } ; [error] => { $ crate :: SyntaxKind :: ERROR_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [null] => { $ crate :: SyntaxKind :: NULL_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [assert] => { $ crate :: SyntaxKind :: ASSERT_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+pub use T;
addedcrates/jrsonnet-rowan-parser/src/generated/tokens.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/generated/tokens.rs
@@ -0,0 +1,155 @@
+//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.
+
+use crate::{
+	ast::AstToken,
+	SyntaxKind::{self, *},
+	SyntaxToken,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Whitespace {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Whitespace {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for Whitespace {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == WHITESPACE }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Comment {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Comment {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for Comment {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == COMMENT }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct String {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for String {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for String {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == STRING }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct StringVerbantim {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for StringVerbantim {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for StringVerbantim {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == STRING_VERBANTIM }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct StringBlock {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for StringBlock {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for StringBlock {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == STRING_BLOCK }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Number {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Number {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for Number {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == NUMBER }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Ident {
+	pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Ident {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(&self.syntax, f)
+	}
+}
+impl AstToken for Ident {
+	fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT }
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		if Self::can_cast(syntax.kind()) {
+			Some(Self { syntax })
+		} else {
+			None
+		}
+	}
+	fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
addedcrates/jrsonnet-rowan-parser/src/language.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/language.rs
@@ -0,0 +1,24 @@
+use rowan::Language;
+
+use crate::SyntaxKind;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum JsonnetLanguage {}
+impl Language for JsonnetLanguage {
+	type Kind = SyntaxKind;
+
+	fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
+		SyntaxKind::from_raw(raw.0)
+	}
+
+	fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
+		rowan::SyntaxKind(kind.into_raw())
+	}
+}
+
+pub type SyntaxNode = rowan::SyntaxNode<JsonnetLanguage>;
+pub type SyntaxToken = rowan::SyntaxToken<JsonnetLanguage>;
+pub type SyntaxElement = rowan::SyntaxElement<JsonnetLanguage>;
+pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<JsonnetLanguage>;
+pub type SyntaxElementChildren = rowan::SyntaxElementChildren<JsonnetLanguage>;
+pub type PreorderWithTokens = rowan::api::PreorderWithTokens<JsonnetLanguage>;
modifiedcrates/jrsonnet-rowan-parser/src/lex.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lex.rs
+++ b/crates/jrsonnet-rowan-parser/src/lex.rs
@@ -1,249 +1,41 @@
-use crate::string_block::lex_str_block_test;
 use core::ops::Range;
-use logos::Logos;
-use rowan::{Checkpoint, TextRange, TextSize};
-use std::{convert::TryFrom, iter::Peekable};
-
-#[derive(Logos, Debug, PartialEq, Hash, Eq, PartialOrd, Ord, Clone, Copy)]
-#[repr(u16)]
-pub enum SyntaxKind {
-	#[token("assert")]
-	KeywordAssert = 0,
-
-	#[token("else")]
-	KeywordElse,
-
-	#[token("error")]
-	KeywordError,
-
-	#[token("false")]
-	KeywordFalse,
-
-	#[token("for")]
-	KeywordFor,
-
-	#[token("function")]
-	KeywordFunction,
-
-	#[token("if")]
-	KeywordIf,
-
-	#[token("import")]
-	KeywordImport,
-
-	#[token("importstr")]
-	KeywordImportStr,
-
-	#[token("local")]
-	KeywordLocal,
-
-	#[token("null")]
-	KeywordNull,
-
-	#[token("tailstrict")]
-	KeywordTailStrict,
-
-	#[token("then")]
-	KeywordThen,
-
-	#[token("self")]
-	KeywordSelf,
-
-	#[token("super")]
-	KeywordSuper,
-
-	#[token("true")]
-	KeywordTrue,
-
-	#[regex(r"[_a-zA-Z][_a-zA-Z0-9]*")]
-	Ident,
-
-	#[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?")]
-	Number,
-
-	#[regex(r"(?:0|[1-9][0-9]*)\.[^0-9]")]
-	ErrorNumJunkAfterDecimalPoint,
-
-	#[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][^+\-0-9]")]
-	ErrorNumJunkAfterExponent,
-
-	#[regex(r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][+-][^0-9]")]
-	ErrorNumJunkAfterExponentSign,
-
-	#[token("{")]
-	SymbolLeftBrace,
-
-	#[token("}")]
-	SymbolRightBrace,
-
-	#[token("[")]
-	SymbolLeftBracket,
-
-	#[token("]")]
-	SymbolRightBracket,
-
-	#[token(",")]
-	SymbolComma,
-
-	#[token(".")]
-	SymbolDot,
-
-	#[token("(")]
-	LParen,
-
-	#[token(")")]
-	RParen,
-
-	#[token(";")]
-	SymbolSemi,
-	#[token(":")]
-	SymbolColon,
-
-	#[token("$")]
-	SymbolDollar,
-
-	#[token("*")]
-	OpMul,
-	#[token("/")]
-	OpDiv,
-	#[token("%")]
-	OpMod,
-	#[token("+")]
-	OpPlus,
-	#[token("-")]
-	OpMinus,
-	#[token("<<")]
-	OpShiftLeft,
-	#[token(">>")]
-	OpShiftRight,
-	#[token("<")]
-	OpLessThan,
-	#[token(">")]
-	OpGreaterThan,
-	#[token("<=")]
-	OpLessThanOrEqual,
-	#[token(">=")]
-	OpGreaterThanOrEqual,
-	#[token("==")]
-	OpEqual,
-	#[token("!=")]
-	OpNotEqual,
-	#[token("&")]
-	OpBitAnd,
-	#[token("^")]
-	OpBitXor,
-	#[token("|")]
-	OpBitOr,
-	#[token("&&")]
-	OpAnd,
-	#[token("||")]
-	OpOr,
-	#[token("in")]
-	OpIn,
-	#[token("!")]
-	OpNot,
-	#[token("~")]
-	OpBitNegate,
-	#[token("=")]
-	SymbolAssign,
+use std::convert::TryFrom;
 
-	#[regex("\"(?s:[^\"\\\\]|\\\\.)*\"")]
-	StringDoubleQuoted,
+use logos::Logos;
+use rowan::{TextRange, TextSize};
 
-	#[regex("'(?s:[^'\\\\]|\\\\.)*'")]
-	StringSingleQuoted,
-
-	#[regex("@\"(?:[^\"]|\"\")*\"")]
-	StringDoubleVerbatim,
-
-	#[regex("@'(?:[^']|'')*'")]
-	StringSingleVerbatim,
-
-	#[regex(r"\|\|\|", lex_str_block_test)]
-	StringBlock, //(StringBlockToken),
-
-	#[regex("\"(?s:[^\"\\\\]|\\\\.)*")]
-	ErrorStringDoubleQuotedUnterminated,
-
-	#[regex("'(?s:[^'\\\\]|\\\\.)*")]
-	ErrorStringSingleQuotedUnterminated,
-
-	#[regex("@\"(?:[^\"]|\"\")*")]
-	ErrorStringDoubleVerbatimUnterminated,
-
-	#[regex("@'(?:[^']|'')*")]
-	ErrorStringSingleVerbatimUnterminated,
-
-	#[regex("@[^\"'\\s]\\S+")]
-	ErrorStringMissingQuotes,
-
-	#[token("/*/")]
-	ErrorCommentTooShort,
-
-	#[regex(r"/\*([^*]|\*[^/])+")]
-	ErrorCommentUnterminated,
-
-	#[regex(r"[ \t\n\r]+")]
-	Whitespace,
-
-	#[regex(r"//[^\r\n]*(\r\n|\n)?")]
-	SingelLineSlashComment,
-
-	#[regex(r"#[^\r\n]*(\r\n|\n)?")]
-	SingleLineHashComment,
-
-	#[regex(r"/\*([^*]|\*[^/])*\*/")]
-	MultiLineComment,
-
-	#[error]
-	Error,
-
-	ErrorPositionalAfterNamed,
-
-	Literal,
-	Expr,
-	Array,
-	ArrayElem,
-	Object,
-	Field,
-
-	CompspecFor,
-	CompspecIf,
-
-	Slice,
-	FieldAccess,
-	ObjectApply,
-	FunctionCall,
-	FunctionDef,
-	BodyDef,
-
-	BinOp,
-	UnaryOp,
-	Local,
-	ExprError,
-	ExprAssert,
-	ExprImport,
-
-	DefParam,
-	DefParams,
-
-	DefArgs,
-	DefNamedArg,
-	DefPositionalArg,
-
-	Parened,
+use crate::SyntaxKind;
 
-	Root,
-}
-
 impl SyntaxKind {
 	pub fn is_trivia(self) -> bool {
 		matches!(
 			self,
-			Self::Whitespace
-				| Self::MultiLineComment
-				| Self::SingelLineSlashComment
-				| Self::SingleLineHashComment
+			Self::WHITESPACE
+				| Self::MULTI_LINE_COMMENT
+				| Self::SINGLE_LINE_HASH_COMMENT
+				| Self::SINGLE_LINE_SLASH_COMMENT
+		)
+	}
+	pub fn is_string(self) -> bool {
+		matches!(
+			self,
+			Self::STRING_SINGLE
+				| Self::STRING_DOUBLE
+				| Self::STRING_SINGLE_VERBATIM
+				| Self::STRING_DOUBLE_VERBATIM
+				| Self::STRING_BLOCK
+		)
+	}
+	pub fn is_number(self) -> bool {
+		matches!(self, Self::NUMBER)
+	}
+	pub fn is_literal(self) -> bool {
+		matches!(
+			self,
+			Self::NULL_KW
+				| Self::TRUE_KW | Self::FALSE_KW
+				| Self::SELF_KW | Self::DOLLAR
+				| Self::SUPER_KW
 		)
 	}
 }
@@ -291,25 +83,4 @@
 
 pub fn lex(input: &str) -> Vec<Lexeme<'_>> {
 	Lexer::new(input).collect()
-}
-
-impl From<SyntaxKind> for rowan::SyntaxKind {
-	fn from(kind: SyntaxKind) -> Self {
-		Self(kind as u16)
-	}
-}
-
-use SyntaxKind::*;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Lang {}
-impl rowan::Language for Lang {
-	type Kind = SyntaxKind;
-	fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
-		assert!(raw.0 <= Root as u16);
-		unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
-	}
-	fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
-		kind.into()
-	}
 }
modifiedcrates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lib.rs
+++ b/crates/jrsonnet-rowan-parser/src/lib.rs
@@ -1,139 +1,20 @@
 #![deny(unused_must_use)]
 
+mod ast;
 mod binary;
 mod event;
+mod generated;
+mod language;
 mod lex;
 mod marker;
 mod parser;
 mod string_block;
+mod tests;
 mod token_set;
 mod unary;
-
-#[cfg(test)]
-mod tests {
-	use miette::{Diagnostic, GraphicalReportHandler, LabeledSpan};
-	use thiserror::Error;
 
-	use crate::parser::parse;
-
-	#[derive(Debug, Error)]
-	#[error("syntax error")]
-	struct MyDiagnostic {
-		code: String,
-		spans: Vec<LabeledSpan>,
-	}
-	impl Diagnostic for MyDiagnostic {
-		fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
-			None
-		}
-
-		fn severity(&self) -> Option<miette::Severity> {
-			None
-		}
-
-		fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
-			None
-		}
-
-		fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
-			None
-		}
-
-		fn source_code(&self) -> Option<&dyn miette::SourceCode> {
-			Some(&self.code)
-		}
-
-		fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
-			Some(Box::new(self.spans.clone().into_iter()))
-		}
-
-		fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
-			None
-		}
-	}
-
-	fn process(text: &str) -> String {
-		use std::fmt::Write;
-		let mut out = String::new();
-		let node = parse(text);
-		write!(out, "{:#?}", node.syntax()).unwrap();
-		if !node.errors.is_empty() && !text.is_empty() {
-			writeln!(out, "===").unwrap();
-			for err in &node.errors {
-				writeln!(out, "{:?}", err).unwrap();
-			}
-			let diag = MyDiagnostic {
-				code: text.to_string(),
-				spans: node.errors.into_iter().map(|e| e.into()).collect(),
-			};
-
-			let handler = GraphicalReportHandler::new();
-
-			write!(out, "===").unwrap();
-			handler.render_report(&mut out, &diag).unwrap();
-		}
-		out
-	}
-	macro_rules! mk_test {
-		($($name:ident => $test:expr)+) => {$(
-			#[test]
-			fn $name() {
-				let src = indoc::indoc!($test);
-				let result = process(&src);
-				insta::assert_snapshot!(stringify!($name), result, src);
-
-			}
-		)+};
-	}
-	mk_test!(
-		empty => r#" "#
-		function => r#"
-			function(a, b = 1) a + b
-		"#
-		function_error_no_value => r#"
-			function(a, b = ) a + b
-		"#
-		function_error_rparen => r#"
-			function(a, b
-		"#
-		function_error_body => r#"
-			function(a, b)
-		"#
-		local_novalue => r#"
-			local a =
-		"#
-		local_no_value_recovery => r#"
-			local a =
-			local b = 3;
-			1
-		"#
-
-		array_comp => r#"
-			[a for a in [1, 2, 3]]
-		"#
-		array_comp_incompatible_with_multiple_elems => r#"
-			[a for a in [1, 2, 3], b]
-		"#
-
-		no_rhs => r#"
-			a +
-		"#
-		no_lhs => r#"
-			+ 2
-		"#
-		no_operator => "
-			2 2
-		"
-
-		named_before_positional => "
-			a(1, 2, b=4, 3, 5, k = 12, 6)
-		"
-
-		wrong_field_end => "
-			{
-				a: 1;
-				b: 2;
-			}
-		"
-	);
-}
+pub use generated::syntax_kinds::SyntaxKind;
+pub use language::{
+	JsonnetLanguage, PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode,
+	SyntaxNodeChildren, SyntaxToken,
+};
modifiedcrates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/marker.rs
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -1,7 +1,7 @@
 use drop_bomb::DropBomb;
 use rowan::TextRange;
 
-use crate::{event::Event, lex::SyntaxKind, parser::Parser};
+use crate::{event::Event, parser::Parser, SyntaxKind};
 
 pub struct Ranger {
 	pub pos: usize,
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
before · crates/jrsonnet-rowan-parser/src/parser.rs
1use std::cell::Cell;2use std::fmt::Display;3use std::rc::Rc;45use miette::Diagnostic;6use miette::LabeledSpan;7use miette::SourceOffset;8use miette::SourceSpan;9use rowan::GreenNode;1011use rowan::TextRange;12use rowan::TextSize;13use thiserror::Error;1415use crate::binary::BinaryOperator;16use crate::event::Event;17use crate::event::Sink;18use crate::lex::lex;19use crate::lex::Lang;20use crate::lex::Lexeme;21use crate::lex::SyntaxKind;22use crate::lex::SyntaxKind::*;23use crate::marker::AsRange;24use crate::marker::CompletedMarker;25use crate::marker::FinishedRanger;26use crate::marker::Marker;27use crate::marker::Ranger;28use crate::token_set::TokenSet;29use crate::unary::UnaryOperator;3031pub struct Parse {32	pub green_node: GreenNode,33	pub errors: Vec<SyntaxError>,34}3536#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]37pub enum ExpectedSyntax {38	Named(&'static str),39	Unnamed(SyntaxKind),40}41impl Display for ExpectedSyntax {42	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {43		match self {44			ExpectedSyntax::Named(n) => write!(f, "{}", n),45			ExpectedSyntax::Unnamed(u) => write!(f, "{:?}", u),46		}47	}48}4950pub struct Parser<'i> {51	lexemes: &'i [Lexeme<'i>],52	pub offset: usize,53	pub events: Vec<Event>,54	pub entered: u32,55	pub hints: Vec<(u32, TextRange, String)>,56	pub last_error_token: usize,57	expected_syntax: Option<ExpectedSyntax>,58	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,59}6061const DEFAULT_RECOVERY_SET: TokenSet = TokenSet::new(&[62	SymbolSemi,63	RParen,64	SymbolRightBracket,65	SymbolRightBrace,66	KeywordLocal,67]);6869#[derive(Clone, Debug, PartialEq, Eq)]70pub enum SyntaxError {71	Unexpected {72		expected: ExpectedSyntax,73		found: SyntaxKind,74		range: TextRange,75	},76	Missing {77		expected: ExpectedSyntax,78		offset: TextSize,79	},80	Custom {81		error: String,82		range: TextRange,83	},84	Hint {85		error: String,86		range: TextRange,87	},88}8990impl Into<LabeledSpan> for SyntaxError {91	fn into(self) -> LabeledSpan {92		match self {93			SyntaxError::Unexpected {94				expected,95				found,96				range,97			} => LabeledSpan::new_with_span(98				Some(format!("expected {}, found {:?}", expected, found)),99				SourceSpan::new(100					SourceOffset::from(usize::from(range.start())),101					SourceOffset::from(usize::from(range.end() - range.start())),102				),103			),104			SyntaxError::Missing { expected, offset } => LabeledSpan::new_with_span(105				Some(format!("missing {}", expected)),106				SourceSpan::new(107					SourceOffset::from(usize::from(offset)),108					SourceOffset::from(0),109				),110			),111			SyntaxError::Custom { error, range } | SyntaxError::Hint { error, range } => {112				LabeledSpan::new_with_span(113					Some(format!("{}", error)),114					SourceSpan::new(115						SourceOffset::from(usize::from(range.start())),116						SourceOffset::from(usize::from(range.end() - range.start())),117					),118				)119			}120		}121	}122}123124impl<'i> Parser<'i> {125	fn new(lexemes: &'i [Lexeme<'i>]) -> Self {126		Self {127			lexemes,128			offset: 0,129			events: vec![],130			entered: 0,131			last_error_token: 0,132			hints: vec![],133			expected_syntax: None,134			expected_syntax_tracking_state: Rc::new(Cell::new(135				ExpectedSyntaxTrackingState::Unnamed,136			)),137		}138	}139	pub fn clear_outdated_hints(&mut self) {140		let amount = self141			.hints142			.iter()143			.rev()144			.take_while(|h| h.0 > self.entered)145			.count();146		self.hints.truncate(self.hints.len() - amount)147	}148	fn clear_expected_syntaxes(&mut self) {149		self.expected_syntax = None;150		self.expected_syntax_tracking_state151			.set(ExpectedSyntaxTrackingState::Unnamed);152	}153	pub fn start(&mut self) -> Marker {154		let start_event_idx = self.events.len();155		self.events.push(Event::Placeholder);156		self.entered += 1;157		Marker::new(start_event_idx, self.offset)158	}159	pub fn start_ranger(&mut self) -> Ranger {160		let pos = self.offset;161		Ranger { pos }162	}163	fn parse(mut self) -> Vec<Event> {164		let m = self.start();165		expr(&mut self);166		if !self.at_end() {167			let ranger = self.start_ranger();168169			while self.peek().is_some() {170				self.bump()171			}172			let end = ranger.finish(&self);173			self.custom_error(end, "unexpected input after expression");174		}175		m.complete(&mut self, Root);176177		self.events178	}179180	pub(crate) fn expect(&mut self, kind: SyntaxKind) {181		self.expect_with_recovery_set(kind, TokenSet::default())182	}183184	pub(crate) fn expect_with_recovery_set(&mut self, kind: SyntaxKind, recovery_set: TokenSet) {185		if self.at(kind) {186			self.bump();187		} else {188			self.error_with_recovery_set(recovery_set);189		}190	}191192	pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {193		if self.at(kind) {194			self.bump();195		} else {196			self.error_with_no_skip();197		}198	}199	pub(crate) fn last_token_range(&self) -> Option<TextRange> {200		self.lexemes.last().map(|Lexeme { range, .. }| *range)201	}202	fn current_token(&self) -> Lexeme<'i> {203		self.lexemes[self.offset]204	}205	fn previous_token(&mut self) -> Option<Lexeme<'i>> {206		if self.offset == 0 {207			return None;208		}209		let mut previous_token_idx = self.offset - 1;210		while self211			.lexemes212			.get(previous_token_idx)213			.map_or(false, |l| l.kind.is_trivia())214			&& previous_token_idx != 0215		{216			previous_token_idx -= 1;217		}218219		Some(self.lexemes[previous_token_idx])220	}221	pub fn start_of_token(&self, mut idx: usize) -> TextSize {222		while self.lexemes[idx].kind.is_trivia() {223			idx += 1;224		}225		self.lexemes[idx].range.start()226	}227	pub fn end_of_token(&self, mut idx: usize) -> TextSize {228		while self.lexemes[idx].kind.is_trivia() {229			idx -= 1;230		}231		self.lexemes[idx].range.end()232	}233	pub(crate) fn custom_error(&mut self, marker: impl AsRange, error: impl AsRef<str>) {234		self.last_error_token = marker.end_token();235		self.events.push(Event::Error(SyntaxError::Custom {236			error: error.as_ref().to_string(),237			range: marker.as_range(self),238		}));239	}240	pub(crate) fn error_with_recovery_set(241		&mut self,242		recovery_set: TokenSet,243	) -> Option<CompletedMarker> {244		self.error_with_recovery_set_no_default(recovery_set.union(DEFAULT_RECOVERY_SET))245	}246	pub fn error_with_no_skip(&mut self) -> Option<CompletedMarker> {247		self.error_with_recovery_set_no_default(TokenSet::ALL)248	}249250	pub fn error_with_recovery_set_no_default(251		&mut self,252		recovery_set: TokenSet,253	) -> Option<CompletedMarker> {254		let expected_syntax = self.expected_syntax.take().unwrap();255		self.expected_syntax_tracking_state256			.set(ExpectedSyntaxTrackingState::Unnamed);257258		if self.at_end() || self.at_set(recovery_set) {259			let range = self260				.previous_token()261				.map(|t| t.range)262				.unwrap_or(TextRange::at(TextSize::from(0), TextSize::from(0)));263264			self.events.push(Event::Error(SyntaxError::Missing {265				expected: expected_syntax,266				offset: range.end(),267			}));268			return None;269		}270271		let current_token = self.current_token();272273		self.events.push(Event::Error(SyntaxError::Unexpected {274			expected: expected_syntax.clone(),275			found: current_token.kind,276			range: current_token.range,277		}));278		self.clear_expected_syntaxes();279		self.last_error_token = self.offset;280281		let m = self.start();282		self.bump();283		Some(m.complete(self, SyntaxKind::Error))284	}285286	fn bump(&mut self) {287		self.skip_trivia();288		assert_ne!(self.offset, self.lexemes.len(), "already at end");289		self.events.push(Event::Token);290		self.offset += 1;291		self.clear_expected_syntaxes();292	}293	fn peek(&mut self) -> Option<SyntaxKind> {294		self.skip_trivia();295		self.peek_raw()296	}297	pub fn peek_token(&mut self) -> Option<&Lexeme<'i>> {298		self.skip_trivia();299		self.peek_token_raw()300	}301	fn skip_trivia(&mut self) {302		while self.peek_raw().map(|c| c.is_trivia()).unwrap_or(false) {303			self.offset += 1;304		}305	}306	fn peek_raw(&mut self) -> Option<SyntaxKind> {307		self.lexemes.get(self.offset).map(|l| l.kind)308	}309	fn peek_token_raw(&mut self) -> Option<&Lexeme<'i>> {310		self.lexemes.get(self.offset)311	}312	#[must_use]313	pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {314		self.expected_syntax_tracking_state315			.set(ExpectedSyntaxTrackingState::Named);316		self.expected_syntax = Some(ExpectedSyntax::Named(name));317318		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))319	}320	pub fn at(&mut self, kind: SyntaxKind) -> bool {321		if let ExpectedSyntaxTrackingState::Unnamed = self.expected_syntax_tracking_state.get() {322			self.expected_syntax = Some(ExpectedSyntax::Unnamed(kind));323		}324		self.peek() == Some(kind)325	}326	pub fn at_set(&mut self, set: TokenSet) -> bool {327		self.peek().map_or(false, |k| set.contains(k))328	}329	pub fn at_end(&mut self) -> bool {330		self.peek().is_none()331	}332}333pub(crate) struct ExpectedSyntaxGuard {334	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,335}336337impl ExpectedSyntaxGuard {338	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>) -> Self {339		Self {340			expected_syntax_tracking_state,341		}342	}343}344345impl Drop for ExpectedSyntaxGuard {346	fn drop(&mut self) {347		self.expected_syntax_tracking_state348			.set(ExpectedSyntaxTrackingState::Unnamed);349	}350}351352#[derive(Debug, Clone, Copy)]353enum ExpectedSyntaxTrackingState {354	Named,355	Unnamed,356}357macro_rules! at_match {358	($p:ident {359		$($r:ident => $e:expr,)*360		_ => $else:expr $(,)?361	}) => {{362		$(363			if $p.at($r) {$e} else364		)* {365			$else366		}367	}}368}369370fn expr(p: &mut Parser) {371	expr_binding_power(p, 0);372}373fn expr_binding_power(p: &mut Parser, minimum_binding_power: u8) -> Option<CompletedMarker> {374	let mut lhs = lhs(p)?;375376	loop {377		let op = at_match!(p {378			OpMul => BinaryOperator::Mul,379			OpDiv => BinaryOperator::Div,380			OpMod => BinaryOperator::Mod,381			OpPlus => BinaryOperator::Plus,382			OpMinus => BinaryOperator::Minus,383			OpShiftLeft => BinaryOperator::ShiftLeft,384			OpShiftRight => BinaryOperator::ShiftRight,385			OpLessThan => BinaryOperator::LessThan,386			OpGreaterThan => BinaryOperator::GreaterThan,387			OpLessThanOrEqual => BinaryOperator::LessThanOrEqual,388			OpGreaterThanOrEqual => BinaryOperator::GreaterThanOrEqual,389			OpEqual => BinaryOperator::Equal,390			OpNotEqual => BinaryOperator::NotEqual,391			OpBitAnd => BinaryOperator::BitAnd,392			OpBitXor => BinaryOperator::BitXor,393			OpBitOr => BinaryOperator::BitOr,394			OpAnd => BinaryOperator::And,395			OpOr => BinaryOperator::Or,396			OpIn => BinaryOperator::In,397			SymbolLeftBrace => BinaryOperator::ObjectApply,398			_ => break,399		});400		let (left_binding_power, right_binding_power) = op.binding_power();401		if left_binding_power < minimum_binding_power {402			break;403		}404405		// Object apply is not a real operator, we dont have something to bump406		if op != BinaryOperator::ObjectApply {407			p.bump();408		}409410		let m = lhs.precede(p);411		let parsed_rhs = expr_binding_power(p, right_binding_power).is_some();412		lhs = m.complete(413			p,414			if op == BinaryOperator::ObjectApply {415				ObjectApply416			} else {417				BinOp418			},419		);420421		if !parsed_rhs {422			break;423		}424	}425	Some(lhs)426}427fn compspec(p: &mut Parser) {428	assert!(p.at(KeywordFor) || p.at(KeywordIf));429	if p.at(KeywordFor) {430		let m = p.start();431		p.bump();432		p.expect(Ident);433		p.expect(OpIn);434		expr(p);435		m.complete(p, CompspecFor);436	} else if p.at(KeywordIf) {437		let m = p.start();438		p.bump();439		expr(p);440		m.complete(p, CompspecIf);441	} else {442		unreachable!()443	}444}445fn comma(p: &mut Parser) -> bool {446	if p.at(SymbolComma) {447		p.bump();448		true449	} else {450		false451	}452}453fn comma_with_alternatives(p: &mut Parser, set: TokenSet) -> bool {454	if p.at(SymbolComma) {455		p.bump();456		true457	} else if p.at_set(set) {458		p.expect_with_no_skip(SymbolComma);459		p.bump();460		true461	} else {462		false463	}464}465fn field_name(p: &mut Parser) {466	let _e = p.expected_syntax_name("field name");467	if p.at(SymbolLeftBracket) {468		p.bump();469		expr(p);470		p.expect(SymbolRightBracket);471	} else if p.at(Ident) {472		p.bump()473	} else {474		p.error_with_recovery_set(TokenSet::new(&[SymbolSemi]));475	}476}477fn object(p: &mut Parser) -> CompletedMarker {478	assert!(p.at(SymbolLeftBrace));479	let m = p.start();480	p.bump();481482	loop {483		if p.at(SymbolRightBrace) {484			p.bump();485			break;486		}487		let m = p.start();488		field_name(p);489		p.expect(SymbolColon);490		expr(p);491		while p.at(KeywordFor) || p.at(KeywordIf) {492			compspec(p)493		}494		m.complete(p, Field);495		if comma_with_alternatives(p, TokenSet::new(&[SymbolAssign])) {496			continue;497		}498		p.expect(SymbolRightBrace);499		break;500	}501502	m.complete(p, Object)503}504505fn params(p: &mut Parser) -> CompletedMarker {506	assert!(p.at(LParen));507	let m = p.start();508	p.bump();509510	loop {511		if p.at(RParen) {512			p.bump();513			break;514		}515		let m = p.start();516		p.expect(Ident);517		if p.at(SymbolAssign) {518			p.bump();519			expr(p);520		}521		m.complete(p, DefParam);522		if comma(p) {523			continue;524		}525		p.expect(RParen);526		break;527	}528529	m.complete(p, DefParams)530}531fn args(p: &mut Parser) {532	assert!(p.at(LParen));533	p.bump();534535	let mut error_positional_start = None::<Marker>;536	let mut started_named = Cell::new(false);537	let mut on_positional = |p: &mut Parser, m: Marker| {538		let c = m.complete(p, DefPositionalArg);539		if started_named.get() && error_positional_start.is_none() {540			error_positional_start = Some(c.precede(p));541		}542	};543	loop {544		if p.at(RParen) {545			break;546		}547548		let m = p.start();549		if p.at(Ident) {550			p.bump();551			if p.at(SymbolAssign) {552				p.bump();553				expr(p);554				m.complete(p, DefNamedArg);555				started_named.set(true);556			} else {557				on_positional(p, m);558			}559		} else {560			expr(p);561			on_positional(p, m);562		}563		if comma(p) {564			continue;565		}566		break;567	}568	if let Some(error_positional_start) = error_positional_start {569		let c = error_positional_start.complete(p, ErrorPositionalAfterNamed);570		p.custom_error(c, "positional arguments can't be placed after named")571	}572	p.expect(RParen);573}574575fn array(p: &mut Parser) -> CompletedMarker {576	assert!(p.at(SymbolLeftBracket));577	// Start the list node578	let m = p.start();579	p.bump(); // '['580581	// This vec will have at most one element in case of correct input582	let mut compspecs = Vec::with_capacity(1);583	let mut elems = 0;584585	loop {586		if p.at(SymbolRightBracket) {587			p.bump();588			break;589		}590		elems += 1;591		let m = p.start();592		{593			let m = p.start();594			expr(p);595			m.complete(p, BodyDef);596		}597		let c = p.start_ranger();598		let mut had_spec = false;599		while p.at(KeywordFor) || p.at(KeywordIf) {600			had_spec = true;601			compspec(p)602		}603		if had_spec {604			compspecs.push(c.finish(p));605		}606		m.complete(p, ArrayElem);607		if comma(p) {608			continue;609		}610		p.expect(SymbolRightBracket);611		break;612	}613614	if elems > 1 && !compspecs.is_empty() {615		for spec in compspecs {616			p.custom_error(617				spec,618				"compspec may only be used if there is only one array element",619			)620		}621	}622623	m.complete(p, Array)624}625626fn lhs(p: &mut Parser) -> Option<CompletedMarker> {627	let mut lhs = lhs_basic(p)?;628629	loop {630		if p.at(SymbolDot) {631			let m = lhs.precede(p);632			p.bump();633			p.expect(Ident);634			lhs = m.complete(p, FieldAccess);635		} else if p.at(SymbolLeftBracket) {636			let m = lhs.precede(p);637			p.bump();638			// Start639			if !p.at(SymbolColon) {640				expr(p);641			}642			if p.at(SymbolColon) {643				p.bump();644				// End645				if !p.at(SymbolRightBracket) && !p.at(SymbolColon) {646					expr(p);647				}648				if p.at(SymbolColon) {649					p.bump();650					// Step651					if !p.at(SymbolRightBracket) {652						expr(p);653					}654				}655			}656			p.expect(SymbolRightBracket);657			lhs = m.complete(p, Slice);658		} else if p.at(LParen) {659			let m = lhs.precede(p);660			args(p);661			lhs = m.complete(p, FunctionCall);662		} else {663			break;664		}665	}666667	Some(lhs)668}669670fn lhs_basic(p: &mut Parser) -> Option<CompletedMarker> {671	let _e = p.expected_syntax_name("value");672	Some(673		if p.at(Number)674			|| p.at(StringSingleQuoted)675			|| p.at(StringDoubleQuoted)676			|| p.at(StringSingleVerbatim)677			|| p.at(StringDoubleVerbatim)678			|| p.at(StringBlock)679			|| p.at(KeywordNull)680			|| p.at(SymbolDollar)681			|| p.at(KeywordSuper)682			|| p.at(KeywordSelf)683		{684			let m = p.start();685			p.bump();686			m.complete(p, Literal)687		} else if p.at(Ident) {688			let m = p.start();689			p.bump();690			m.complete(p, Ident)691		} else if p.at(SymbolLeftBracket) {692			array(p)693		} else if p.at(SymbolLeftBrace) {694			object(p)695		} else if p.at(KeywordLocal) {696			let m = p.start();697			p.bump();698			let mut sus_local = None;699			loop {700				p.expect_with_recovery_set(701					Ident,702					TokenSet::new(&[SymbolAssign, SymbolSemi, KeywordLocal]),703				);704				if p.at(LParen) {705					params(p);706				}707708				let sus_local_candidate = p.start_ranger();709				p.expect_with_recovery_set(710					SymbolAssign,711					TokenSet::new(&[SymbolSemi, KeywordLocal]),712				);713714				sus_local = p.at(KeywordLocal).then(|| sus_local_candidate.finish(p));715				expr(p);716717				if !comma(p) {718					break;719				}720			}721			p.expect(SymbolSemi);722			if let Some(sus_local) = sus_local {723				if sus_local.had_error_since(p) {724					p.custom_error(sus_local, "unusal local placement, missing ';' ?")725				}726			}727			{728				let m = p.start();729				expr(p);730				m.complete(p, BodyDef);731			}732			m.complete(p, Local)733		} else if p.at(KeywordFunction) {734			let m = p.start();735			p.bump();736			args(p);737			{738				let m = p.start();739				expr(p);740				m.complete(p, BodyDef);741			}742			m.complete(p, FunctionDef)743		} else if p.at(KeywordError) {744			let m = p.start();745			p.bump();746			expr(p);747			m.complete(p, ExprError)748		} else if p.at(KeywordAssert) {749			let m = p.start();750			p.bump();751			expr(p);752			if p.at(SymbolColon) {753				p.bump();754				expr(p);755			}756			m.complete(p, ExprAssert)757		} else if p.at(KeywordImport) || p.at(KeywordImportStr) {758			let m = p.start();759			p.bump();760			expr(p);761			m.complete(p, ExprImport)762		} else if p.at(OpMinus) || p.at(OpNot) || p.at(OpBitNegate) {763			let op = match p.peek().unwrap() {764				OpMinus => UnaryOperator::Minus,765				OpNot => UnaryOperator::Not,766				OpBitNegate => UnaryOperator::BitNegate,767				_ => unreachable!(),768			};769			let ((), right_binding_power) = op.binding_power();770771			let m = p.start();772			p.bump();773			expr_binding_power(p, right_binding_power);774			m.complete(p, UnaryOp)775		} else if p.at(LParen) {776			let m = p.start();777			p.bump();778			expr(p);779			assert!(p.at(RParen));780			p.bump();781			m.complete(p, Parened)782		} else {783			p.error_with_no_skip();784			return None;785		},786	)787}788789type SyntaxNode = rowan::SyntaxNode<Lang>;790#[allow(unused)]791type SyntaxToken = rowan::SyntaxToken<Lang>;792#[allow(unused)]793type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;794795impl Parse {796	pub fn syntax(&self) -> SyntaxNode {797		SyntaxNode::new_root(self.green_node.clone())798	}799}800801pub fn parse(input: &str) -> Parse {802	let lexemes = lex(input);803	let parser = Parser::new(&lexemes);804	let events = parser.parse();805	dbg!(&events);806	let sink = Sink::new(events, &lexemes);807808	sink.finish()809}
after · crates/jrsonnet-rowan-parser/src/parser.rs
1use std::{cell::Cell, fmt::Display, rc::Rc};23use miette::{LabeledSpan, SourceOffset, SourceSpan};4use rowan::{GreenNode, TextRange, TextSize};56use crate::{7	binary::BinaryOperator,8	event::{Event, Sink},9	lex::{lex, Lexeme},10	marker::{AsRange, CompletedMarker, Marker, Ranger},11	token_set::SyntaxKindSet,12	unary::UnaryOperator,13	SyntaxKind,14	SyntaxKind::*,15	SyntaxNode, T, TS,16};1718pub struct Parse {19	pub green_node: GreenNode,20	pub errors: Vec<SyntaxError>,21}2223#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]24pub enum ExpectedSyntax {25	Named(&'static str),26	Unnamed(SyntaxKind),27}28impl Display for ExpectedSyntax {29	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {30		match self {31			ExpectedSyntax::Named(n) => write!(f, "{}", n),32			ExpectedSyntax::Unnamed(u) => write!(f, "{:?}", u),33		}34	}35}3637pub struct Parser<'i> {38	lexemes: &'i [Lexeme<'i>],39	pub offset: usize,40	pub events: Vec<Event>,41	pub entered: u32,42	pub hints: Vec<(u32, TextRange, String)>,43	pub last_error_token: usize,44	expected_syntax: Option<ExpectedSyntax>,45	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,46}4748const DEFAULT_RECOVERY_SET: SyntaxKindSet = TS![; ')' ']' '}' local];4950#[derive(Clone, Debug, PartialEq, Eq)]51pub enum SyntaxError {52	Unexpected {53		expected: ExpectedSyntax,54		found: SyntaxKind,55		range: TextRange,56	},57	Missing {58		expected: ExpectedSyntax,59		offset: TextSize,60	},61	Custom {62		error: String,63		range: TextRange,64	},65	Hint {66		error: String,67		range: TextRange,68	},69}7071impl Into<LabeledSpan> for SyntaxError {72	fn into(self) -> LabeledSpan {73		match self {74			SyntaxError::Unexpected {75				expected,76				found,77				range,78			} => LabeledSpan::new_with_span(79				Some(format!("expected {}, found {:?}", expected, found)),80				SourceSpan::new(81					SourceOffset::from(usize::from(range.start())),82					SourceOffset::from(usize::from(range.end() - range.start())),83				),84			),85			SyntaxError::Missing { expected, offset } => LabeledSpan::new_with_span(86				Some(format!("missing {}", expected)),87				SourceSpan::new(88					SourceOffset::from(usize::from(offset)),89					SourceOffset::from(0),90				),91			),92			SyntaxError::Custom { error, range } | SyntaxError::Hint { error, range } => {93				LabeledSpan::new_with_span(94					Some(format!("{}", error)),95					SourceSpan::new(96						SourceOffset::from(usize::from(range.start())),97						SourceOffset::from(usize::from(range.end() - range.start())),98					),99				)100			}101		}102	}103}104105impl<'i> Parser<'i> {106	fn new(lexemes: &'i [Lexeme<'i>]) -> Self {107		Self {108			lexemes,109			offset: 0,110			events: vec![],111			entered: 0,112			last_error_token: 0,113			hints: vec![],114			expected_syntax: None,115			expected_syntax_tracking_state: Rc::new(Cell::new(116				ExpectedSyntaxTrackingState::Unnamed,117			)),118		}119	}120	pub fn clear_outdated_hints(&mut self) {121		let amount = self122			.hints123			.iter()124			.rev()125			.take_while(|h| h.0 > self.entered)126			.count();127		self.hints.truncate(self.hints.len() - amount)128	}129	fn clear_expected_syntaxes(&mut self) {130		self.expected_syntax = None;131		self.expected_syntax_tracking_state132			.set(ExpectedSyntaxTrackingState::Unnamed);133	}134	pub fn start(&mut self) -> Marker {135		let start_event_idx = self.events.len();136		self.events.push(Event::Placeholder);137		self.entered += 1;138		Marker::new(start_event_idx, self.offset)139	}140	pub fn start_ranger(&mut self) -> Ranger {141		let pos = self.offset;142		Ranger { pos }143	}144	fn parse(mut self) -> Vec<Event> {145		let m = self.start();146		expr(&mut self);147		if !self.at_end() {148			let ranger = self.start_ranger();149150			while self.peek().is_some() {151				self.bump()152			}153			let end = ranger.finish(&self);154			self.custom_error(end, "unexpected input after expression");155		}156		m.complete(&mut self, SOURCE_FILE);157158		self.events159	}160161	pub(crate) fn expect(&mut self, kind: SyntaxKind) {162		self.expect_with_recovery_set(kind, TS![])163	}164165	pub(crate) fn expect_with_recovery_set(166		&mut self,167		kind: SyntaxKind,168		recovery_set: SyntaxKindSet,169	) {170		if self.at(kind) {171			self.bump();172		} else {173			self.error_with_recovery_set(recovery_set);174		}175	}176177	pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {178		if self.at(kind) {179			self.bump();180		} else {181			self.error_with_no_skip();182		}183	}184	pub(crate) fn last_token_range(&self) -> Option<TextRange> {185		self.lexemes.last().map(|Lexeme { range, .. }| *range)186	}187	fn current_token(&self) -> Lexeme<'i> {188		self.lexemes[self.offset]189	}190	fn previous_token(&mut self) -> Option<Lexeme<'i>> {191		if self.offset == 0 {192			return None;193		}194		let mut previous_token_idx = self.offset - 1;195		while self196			.lexemes197			.get(previous_token_idx)198			.map_or(false, |l| l.kind.is_trivia())199			&& previous_token_idx != 0200		{201			previous_token_idx -= 1;202		}203204		Some(self.lexemes[previous_token_idx])205	}206	pub fn start_of_token(&self, mut idx: usize) -> TextSize {207		while self.lexemes[idx].kind.is_trivia() {208			idx += 1;209		}210		self.lexemes[idx].range.start()211	}212	pub fn end_of_token(&self, mut idx: usize) -> TextSize {213		while self.lexemes[idx].kind.is_trivia() {214			idx -= 1;215		}216		self.lexemes[idx].range.end()217	}218	pub(crate) fn custom_error(&mut self, marker: impl AsRange, error: impl AsRef<str>) {219		self.last_error_token = marker.end_token();220		self.events.push(Event::Error(SyntaxError::Custom {221			error: error.as_ref().to_string(),222			range: marker.as_range(self),223		}));224	}225	pub(crate) fn error_with_recovery_set(226		&mut self,227		recovery_set: SyntaxKindSet,228	) -> Option<CompletedMarker> {229		self.error_with_recovery_set_no_default(recovery_set.union(DEFAULT_RECOVERY_SET))230	}231	pub fn error_with_no_skip(&mut self) -> Option<CompletedMarker> {232		self.error_with_recovery_set_no_default(SyntaxKindSet::ALL)233	}234235	pub fn error_with_recovery_set_no_default(236		&mut self,237		recovery_set: SyntaxKindSet,238	) -> Option<CompletedMarker> {239		let expected_syntax = self.expected_syntax.take().unwrap();240		self.expected_syntax_tracking_state241			.set(ExpectedSyntaxTrackingState::Unnamed);242243		if self.at_end() || self.at_set(recovery_set) {244			let range = self245				.previous_token()246				.map(|t| t.range)247				.unwrap_or(TextRange::at(TextSize::from(0), TextSize::from(0)));248249			self.events.push(Event::Error(SyntaxError::Missing {250				expected: expected_syntax,251				offset: range.end(),252			}));253			return None;254		}255256		let current_token = self.current_token();257258		self.events.push(Event::Error(SyntaxError::Unexpected {259			expected: expected_syntax.clone(),260			found: current_token.kind,261			range: current_token.range,262		}));263		self.clear_expected_syntaxes();264		self.last_error_token = self.offset;265266		let m = self.start();267		self.bump();268		Some(m.complete(self, SyntaxKind::ERROR))269	}270271	fn bump(&mut self) {272		self.skip_trivia();273		assert_ne!(self.offset, self.lexemes.len(), "already at end");274		self.events.push(Event::Token);275		self.offset += 1;276		self.clear_expected_syntaxes();277	}278	fn peek(&mut self) -> Option<SyntaxKind> {279		self.skip_trivia();280		self.peek_raw()281	}282	pub fn peek_token(&mut self) -> Option<&Lexeme<'i>> {283		self.skip_trivia();284		self.peek_token_raw()285	}286	fn skip_trivia(&mut self) {287		while self.peek_raw().map(|c| c.is_trivia()).unwrap_or(false) {288			self.offset += 1;289		}290	}291	fn peek_raw(&mut self) -> Option<SyntaxKind> {292		self.lexemes.get(self.offset).map(|l| l.kind)293	}294	fn peek_token_raw(&mut self) -> Option<&Lexeme<'i>> {295		self.lexemes.get(self.offset)296	}297	#[must_use]298	pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {299		self.expected_syntax_tracking_state300			.set(ExpectedSyntaxTrackingState::Named);301		self.expected_syntax = Some(ExpectedSyntax::Named(name));302303		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))304	}305	pub fn at(&mut self, kind: SyntaxKind) -> bool {306		if let ExpectedSyntaxTrackingState::Unnamed = self.expected_syntax_tracking_state.get() {307			self.expected_syntax = Some(ExpectedSyntax::Unnamed(kind));308		}309		self.peek() == Some(kind)310	}311	pub fn at_set(&mut self, set: SyntaxKindSet) -> bool {312		self.peek().map_or(false, |k| set.contains(k))313	}314	pub fn at_end(&mut self) -> bool {315		self.peek().is_none()316	}317}318pub(crate) struct ExpectedSyntaxGuard {319	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,320}321322impl ExpectedSyntaxGuard {323	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>) -> Self {324		Self {325			expected_syntax_tracking_state,326		}327	}328}329330impl Drop for ExpectedSyntaxGuard {331	fn drop(&mut self) {332		self.expected_syntax_tracking_state333			.set(ExpectedSyntaxTrackingState::Unnamed);334	}335}336337#[derive(Debug, Clone, Copy)]338enum ExpectedSyntaxTrackingState {339	Named,340	Unnamed,341}342macro_rules! at_match {343	($p:ident {344		$($r:expr => $e:expr,)*345		_ => $else:expr $(,)?346	}) => {{347		$(348			if $p.at($r) {$e} else349		)* {350			$else351		}352	}}353}354355fn expr(p: &mut Parser) {356	expr_binding_power(p, 0);357}358fn expr_binding_power(p: &mut Parser, minimum_binding_power: u8) -> Option<CompletedMarker> {359	let mut lhs = lhs(p)?;360361	loop {362		let op = at_match!(p {363			T![*] => BinaryOperator::Mul,364			T![/] => BinaryOperator::Div,365			T![%] => BinaryOperator::Mod,366			T![+] => BinaryOperator::Plus,367			T![-] => BinaryOperator::Minus,368			T![<<] => BinaryOperator::ShiftLeft,369			T![>>] => BinaryOperator::ShiftRight,370			T![<] => BinaryOperator::LessThan,371			T![>] => BinaryOperator::GreaterThan,372			T![<=] => BinaryOperator::LessThanOrEqual,373			T![>=] => BinaryOperator::GreaterThanOrEqual,374			T![==] => BinaryOperator::Equal,375			T![!=] => BinaryOperator::NotEqual,376			T![&] => BinaryOperator::BitAnd,377			T![^] => BinaryOperator::BitXor,378			T![|] => BinaryOperator::BitOr,379			T![&&] => BinaryOperator::And,380			T![||] => BinaryOperator::Or,381			T![in] => BinaryOperator::In,382			T!['{'] => BinaryOperator::ObjectApply,383			_ => break,384		});385		let (left_binding_power, right_binding_power) = op.binding_power();386		if left_binding_power < minimum_binding_power {387			break;388		}389390		// Object apply is not a real operator, we dont have something to bump391		if op != BinaryOperator::ObjectApply {392			p.bump();393		}394395		let m = lhs.precede(p);396		let parsed_rhs = expr_binding_power(p, right_binding_power).is_some();397		lhs = m.complete(398			p,399			if op == BinaryOperator::ObjectApply {400				EXPR_OBJ_EXTEND401			} else {402				EXPR_BINARY403			},404		);405406		if !parsed_rhs {407			break;408		}409	}410	Some(lhs)411}412fn compspec(p: &mut Parser) {413	assert!(p.at(T![for]) || p.at(T![if]));414	if p.at(T![for]) {415		let m = p.start();416		p.bump();417		p.expect(IDENT);418		p.expect(T![in]);419		expr(p);420		m.complete(p, FOR_SPEC);421	} else if p.at(T![in]) {422		let m = p.start();423		p.bump();424		expr(p);425		m.complete(p, IF_SPEC);426	} else {427		unreachable!()428	}429}430fn comma(p: &mut Parser) -> bool {431	if p.at(T![,]) {432		p.bump();433		true434	} else {435		false436	}437}438fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {439	if p.at(T![,]) {440		p.bump();441		true442	} else if p.at_set(set) {443		p.expect_with_no_skip(T![,]);444		p.bump();445		true446	} else {447		false448	}449}450fn field_name(p: &mut Parser) {451	let _e = p.expected_syntax_name("field name");452	if p.at(T!['[']) {453		p.bump();454		expr(p);455		p.expect(T![']']);456	} else if p.at(IDENT) {457		p.bump()458	} else {459		p.error_with_recovery_set(TS![;]);460	}461}462fn object(p: &mut Parser) -> CompletedMarker {463	assert!(p.at(T!['{']));464	let m = p.start();465	p.bump();466467	loop {468		if p.at(T!['}']) {469			p.bump();470			break;471		}472		let m = p.start();473		field_name(p);474		p.expect(T![,]);475		expr(p);476		while p.at(T![for]) || p.at(T![if]) {477			compspec(p)478		}479		m.complete(p, MEMBER);480		if comma_with_alternatives(p, SyntaxKindSet::new(&[T![=]])) {481			continue;482		}483		p.expect(R_BRACE);484		break;485	}486487	m.complete(p, OBJ_BODY)488}489490fn params(p: &mut Parser) -> CompletedMarker {491	assert!(p.at(T!['(']));492	let m = p.start();493	p.bump();494495	loop {496		if p.at(T![')']) {497			p.bump();498			break;499		}500		let m = p.start();501		p.expect(IDENT);502		if p.at(T![=]) {503			p.bump();504			expr(p);505		}506		m.complete(p, PARAM);507		if comma(p) {508			continue;509		}510		p.expect(T![')']);511		break;512	}513514	m.complete(p, PARAMS_DESC)515}516fn args(p: &mut Parser) {517	assert!(p.at(T!['(']));518	p.bump();519520	let mut error_positional_start = None::<Marker>;521	let mut started_named = Cell::new(false);522	let mut on_positional = |p: &mut Parser, m: Marker| {523		let c = m.complete(p, ARG);524		if started_named.get() && error_positional_start.is_none() {525			error_positional_start = Some(c.precede(p));526		}527	};528	loop {529		if p.at(T![')']) {530			break;531		}532533		let m = p.start();534		if p.at(IDENT) {535			p.bump();536			if p.at(T![=]) {537				p.bump();538				expr(p);539				m.complete(p, ARG);540				started_named.set(true);541			} else {542				on_positional(p, m);543			}544		} else {545			expr(p);546			on_positional(p, m);547		}548		if comma(p) {549			continue;550		}551		break;552	}553	if let Some(error_positional_start) = error_positional_start {554		let c = error_positional_start.complete(p, ERROR);555		p.custom_error(c, "positional arguments can't be placed after named")556	}557	p.expect(T![')']);558}559560fn array(p: &mut Parser) -> CompletedMarker {561	assert!(p.at(T!['[']));562	// Start the list node563	let m = p.start();564	p.bump(); // '['565566	// This vec will have at most one element in case of correct input567	let mut compspecs = Vec::with_capacity(1);568	let mut elems = 0;569570	loop {571		if p.at(T![']']) {572			p.bump();573			break;574		}575		elems += 1;576		expr(p);577		let c = p.start_ranger();578		let mut had_spec = false;579		while p.at(T![for]) || p.at(T![if]) {580			had_spec = true;581			compspec(p)582		}583		if had_spec {584			compspecs.push(c.finish(p));585		}586		if comma(p) {587			continue;588		}589		p.expect(T![']']);590		break;591	}592593	if elems > 1 && !compspecs.is_empty() {594		for spec in compspecs {595			p.custom_error(596				spec,597				"compspec may only be used if there is only one array element",598			)599		}600601		m.complete(p, EXPR_ARRAY)602	} else if !compspecs.is_empty() {603		m.complete(p, EXPR_ARRAY_COMP)604	} else {605		m.complete(p, EXPR_ARRAY)606	}607}608609fn lhs(p: &mut Parser) -> Option<CompletedMarker> {610	let mut lhs = lhs_basic(p)?;611612	loop {613		if p.at(T![.]) {614			let m = lhs.precede(p);615			p.bump();616			p.expect(IDENT);617			lhs = m.complete(p, EXPR_INDEX);618		} else if p.at(T!['[']) {619			let m = lhs.precede(p);620			p.bump();621			// Start622			if !p.at(T![:]) {623				expr(p);624			}625			if p.at(T![:]) {626				p.bump();627				// End628				if !p.at(T![']']) && !p.at(T![:]) {629					expr(p);630				}631				if p.at(T![:]) {632					p.bump();633					// Step634					if !p.at(T![']']) {635						expr(p);636					}637				}638			}639			p.expect(T![']']);640			lhs = m.complete(p, EXPR_SLICE);641		} else if p.at(T!['(']) {642			let m = lhs.precede(p);643			args(p);644			lhs = m.complete(p, EXPR_APPLY);645		} else {646			break;647		}648	}649650	Some(lhs)651}652653fn lhs_basic(p: &mut Parser) -> Option<CompletedMarker> {654	let _e = p.expected_syntax_name("value");655	Some(if p.peek().map(|l| l.is_literal()).unwrap_or(false) {656		let m = p.start();657		p.bump();658		m.complete(p, EXPR_LITERAL)659	} else if p.peek().map(|l| l.is_string()).unwrap_or(false) {660		let m = p.start();661		p.bump();662		m.complete(p, EXPR_STRING)663	} else if p.peek().map(|l| l.is_number()).unwrap_or(false) {664		let m = p.start();665		p.bump();666		m.complete(p, EXPR_NUMBER)667	} else if p.at(IDENT) {668		let m = p.start();669		p.bump();670		m.complete(p, EXPR_VAR)671	} else if p.at(T!['[']) {672		array(p)673	} else if p.at(T!['{']) {674		object(p)675	} else if p.at(T![local]) {676		let m = p.start();677		p.bump();678		let mut sus_local = None;679		loop {680			p.expect_with_recovery_set(IDENT, TS![= ; local]);681			if p.at(T!['(']) {682				params(p);683			}684685			let sus_local_candidate = p.start_ranger();686			p.expect_with_recovery_set(T![=], TS![; local]);687688			sus_local = p.at(T![local]).then(|| sus_local_candidate.finish(p));689			expr(p);690691			if !comma(p) {692				break;693			}694		}695		p.expect(T![;]);696		if let Some(sus_local) = sus_local {697			if sus_local.had_error_since(p) {698				p.custom_error(sus_local, "unusal local placement, missing ';' ?")699			}700		}701		expr(p);702		m.complete(p, T![local])703	} else if p.at(T![function]) {704		let m = p.start();705		p.bump();706		args(p);707		expr(p);708		m.complete(p, EXPR_FUNCTION)709	} else if p.at(T![error]) {710		let m = p.start();711		p.bump();712		expr(p);713		m.complete(p, EXPR_ERROR)714	} else if p.at(T![assert]) {715		let m = p.start();716		p.bump();717		expr(p);718		if p.at(T![:]) {719			p.bump();720			expr(p);721		}722		m.complete(p, EXPR_ASSERT)723	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {724		let m = p.start();725		p.bump();726		expr(p);727		m.complete(p, EXPR_IMPORT)728	} else if p.at(T![-]) || p.at(T![!]) || p.at(T![~]) {729		let op = match p.peek().unwrap() {730			T![-] => UnaryOperator::Minus,731			T![!] => UnaryOperator::Not,732			T![~] => UnaryOperator::BitNegate,733			_ => unreachable!(),734		};735		let ((), right_binding_power) = op.binding_power();736737		let m = p.start();738		p.bump();739		expr_binding_power(p, right_binding_power);740		m.complete(p, EXPR_UNARY)741	} else if p.at(T!['(']) {742		let m = p.start();743		p.bump();744		expr(p);745		assert!(p.at(T![')']));746		p.bump();747		m.complete(p, EXPR_PARENED)748	} else {749		p.error_with_no_skip();750		return None;751	})752}753754impl Parse {755	pub fn syntax(&self) -> SyntaxNode {756		SyntaxNode::new_root(self.green_node.clone())757	}758}759760pub fn parse(input: &str) -> Parse {761	let lexemes = lex(input);762	let parser = Parser::new(&lexemes);763	let events = parser.parse();764	dbg!(&events);765	let sink = Sink::new(events, &lexemes);766767	sink.finish()768}
modifiedcrates/jrsonnet-rowan-parser/src/string_block.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/string_block.rs
+++ b/crates/jrsonnet-rowan-parser/src/string_block.rs
@@ -11,7 +11,7 @@
 
 use StringBlockToken::*;
 
-use crate::lex::SyntaxKind;
+use crate::SyntaxKind;
 
 pub fn lex_str_block_test<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>) {
 	lex_str_block(lex);
addedcrates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -0,0 +1,127 @@
+#![cfg(test)]
+
+use miette::{Diagnostic, GraphicalReportHandler, LabeledSpan};
+use thiserror::Error;
+
+use crate::parser::parse;
+
+#[derive(Debug, Error)]
+#[error("syntax error")]
+struct MyDiagnostic {
+	code: String,
+	spans: Vec<LabeledSpan>,
+}
+impl Diagnostic for MyDiagnostic {
+	fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+		None
+	}
+
+	fn severity(&self) -> Option<miette::Severity> {
+		None
+	}
+
+	fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+		None
+	}
+
+	fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
+		None
+	}
+
+	fn source_code(&self) -> Option<&dyn miette::SourceCode> {
+		Some(&self.code)
+	}
+
+	fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
+		Some(Box::new(self.spans.clone().into_iter()))
+	}
+
+	fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
+		None
+	}
+}
+
+fn process(text: &str) -> String {
+	use std::fmt::Write;
+	let mut out = String::new();
+	let node = parse(text);
+	write!(out, "{:#?}", node.syntax()).unwrap();
+	if !node.errors.is_empty() && !text.is_empty() {
+		writeln!(out, "===").unwrap();
+		for err in &node.errors {
+			writeln!(out, "{:?}", err).unwrap();
+		}
+		let diag = MyDiagnostic {
+			code: text.to_string(),
+			spans: node.errors.into_iter().map(|e| e.into()).collect(),
+		};
+
+		let handler = GraphicalReportHandler::new();
+
+		write!(out, "===").unwrap();
+		handler.render_report(&mut out, &diag).unwrap();
+	}
+	out
+}
+macro_rules! mk_test {
+		($($name:ident => $test:expr)+) => {$(
+			#[test]
+			fn $name() {
+				let src = indoc::indoc!($test);
+				let result = process(&src);
+				insta::assert_snapshot!(stringify!($name), result, src);
+
+			}
+		)+};
+	}
+mk_test!(
+	empty => r#" "#
+	function => r#"
+			function(a, b = 1) a + b
+		"#
+	function_error_no_value => r#"
+			function(a, b = ) a + b
+		"#
+	function_error_rparen => r#"
+			function(a, b
+		"#
+	function_error_body => r#"
+			function(a, b)
+		"#
+	local_novalue => r#"
+			local a =
+		"#
+	local_no_value_recovery => r#"
+			local a =
+			local b = 3;
+			1
+		"#
+
+	array_comp => r#"
+			[a for a in [1, 2, 3]]
+		"#
+	array_comp_incompatible_with_multiple_elems => r#"
+			[a for a in [1, 2, 3], b]
+		"#
+
+	no_rhs => r#"
+			a +
+		"#
+	no_lhs => r#"
+			+ 2
+		"#
+	no_operator => "
+			2 2
+		"
+
+	named_before_positional => "
+			a(1, 2, b=4, 3, 5, k = 12, 6)
+		"
+
+	wrong_field_end => "
+			{
+				a: 1;
+				b: 2;
+			}
+		"
+);
modifiedcrates/jrsonnet-rowan-parser/src/token_set.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/token_set.rs
+++ b/crates/jrsonnet-rowan-parser/src/token_set.rs
@@ -1,24 +1,24 @@
-use crate::lex::SyntaxKind;
+use crate::SyntaxKind;
 
 #[derive(Clone, Copy, Default)]
-pub struct TokenSet(u64);
+pub struct SyntaxKindSet(u64);
 
-impl TokenSet {
+impl SyntaxKindSet {
 	pub const EMPTY: Self = Self(0);
 	pub const ALL: Self = Self(u64::MAX);
 
-	pub const fn new(kinds: &[SyntaxKind]) -> TokenSet {
+	pub const fn new(kinds: &[SyntaxKind]) -> SyntaxKindSet {
 		let mut res = 0u64;
 		let mut i = 0;
 		while i < kinds.len() {
 			res |= mask(kinds[i]);
 			i += 1
 		}
-		TokenSet(res)
+		SyntaxKindSet(res)
 	}
 
-	pub const fn union(self, other: TokenSet) -> TokenSet {
-		TokenSet(self.0 | other.0)
+	pub const fn union(self, other: SyntaxKindSet) -> SyntaxKindSet {
+		SyntaxKindSet(self.0 | other.0)
 	}
 
 	pub const fn contains(&self, kind: SyntaxKind) -> bool {
@@ -29,3 +29,14 @@
 const fn mask(kind: SyntaxKind) -> u64 {
 	1u64 << (kind as usize)
 }
+
+#[macro_export]
+macro_rules! TS {
+	($($tt:tt)*) => {
+		SyntaxKindSet::new(&[
+			$(
+				T![$tt]
+			),*
+		])
+	};
+}
addedxtask/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.57"
+itertools = "0.10.3"
+proc-macro2 = "1.0.39"
+quote = "1.0.18"
+ungrammar = "1.16.1"
+xshell = "0.2.2"
addedxtask/src/main.rsdiffbeforeafterboth
--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,7 @@
+use anyhow::Result;
+
+mod sourcegen;
+
+fn main() -> Result<()> {
+	sourcegen::generate_ungrammar()
+}
addedxtask/src/sourcegen/ast.rsdiffbeforeafterboth
--- /dev/null
+++ b/xtask/src/sourcegen/ast.rs
@@ -0,0 +1,93 @@
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+
+use super::{escape_token_macro, KindsSrc};
+
+impl AstNodeSrc {
+	pub fn remove_field(&mut self, to_remove: Vec<usize>) {
+		to_remove.into_iter().rev().for_each(|idx| {
+			self.fields.remove(idx);
+		});
+	}
+}
+
+#[allow(dead_code)]
+#[derive(Default, Debug)]
+pub struct AstSrc {
+	pub tokens: Vec<String>,
+	pub nodes: Vec<AstNodeSrc>,
+	pub enums: Vec<AstEnumSrc>,
+}
+#[derive(Debug)]
+pub struct AstNodeSrc {
+	pub doc: Vec<String>,
+	pub name: String,
+	pub traits: Vec<String>,
+	pub fields: Vec<Field>,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Field {
+	Token(String),
+	Node {
+		name: String,
+		ty: String,
+		cardinality: Cardinality,
+	},
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Cardinality {
+	Optional,
+	Many,
+}
+
+#[derive(Debug, Clone)]
+pub struct AstEnumSrc {
+	pub doc: Vec<String>,
+	pub name: String,
+	pub traits: Vec<String>,
+	pub variants: Vec<String>,
+}
+
+impl Field {
+	pub fn is_many(&self) -> bool {
+		matches!(
+			self,
+			Field::Node {
+				cardinality: Cardinality::Many,
+				..
+			}
+		)
+	}
+	pub fn token_kind(&self) -> Option<TokenStream> {
+		match self {
+			Field::Token(token) => {
+				let token: TokenStream = escape_token_macro(token);
+				Some(quote! { T![#token] })
+			}
+			_ => None,
+		}
+	}
+
+	pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {
+		match self {
+			Field::Token(name) => {
+				if let Some(punct_name) = kinds.get_punct_name(name) {
+					format_ident!("{}_token", punct_name.to_lowercase())
+				} else {
+					format_ident!("{}_token", name.to_lowercase())
+				}
+			}
+			Field::Node { name, .. } => {
+				format_ident!("{}", name)
+			}
+		}
+	}
+	pub fn ty(&self) -> proc_macro2::Ident {
+		match self {
+			Field::Token(_) => format_ident!("SyntaxToken"),
+			Field::Node { ty, .. } => format_ident!("{}", ty),
+		}
+	}
+}
addedxtask/src/sourcegen/mod.rsdiffbeforeafterboth
--- /dev/null
+++ b/xtask/src/sourcegen/mod.rs
@@ -0,0 +1,922 @@
+use std::{
+	collections::{BTreeSet, HashSet},
+	path::PathBuf,
+};
+
+use anyhow::Result;
+use ast::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field};
+use itertools::Itertools;
+use proc_macro2::{Punct, Spacing, TokenStream};
+use quote::{format_ident, quote};
+use ungrammar::{Grammar, Rule};
+use util::{
+	ensure_file_contents, pluralize, reformat, to_lower_snake_case, to_pascal_case,
+	to_upper_snake_case,
+};
+
+mod ast;
+mod util;
+
+pub fn generate_ungrammar() -> Result<()> {
+	let grammar: Grammar = include_str!(concat!(
+		env!("CARGO_MANIFEST_DIR"),
+		"/../crates/jrsonnet-rowan-parser/jsonnet.ungram"
+	))
+	.parse()?;
+
+	let mut kinds: KindsSrc = KindsSrc {
+		punct: puncts![
+			"||" => "OR";
+			"&&" => "AND";
+			"|" => "BIT_OR";
+			"^" => "BIT_XOR";
+			"&" => "BIT_AND";
+			"==" => "EQ";
+			"!=" => "NE";
+			"<" => "LT";
+			">" => "GT";
+			"<=" => "LE";
+			">=" => "GE";
+			"<<" => "LHS";
+			">>" => "RHS";
+			"+" => "PLUS";
+			"-" => "MINUS";
+			"*" => "MUL";
+			"/" => "DIV";
+			"%" => "MODULO";
+			"!" => "NOT";
+			"~" => "BIT_NOT";
+			"[" => "L_BRACK";
+			"]" => "R_BRACK";
+			"(" => "L_PAREN";
+			")" => "R_PAREN";
+			"{" => "L_BRACE";
+			"}" => "R_BRACE";
+			":" => "COLON";
+			"::" => "COLONCOLON";
+			":::" => "COLONCOLONCOLON";
+			";" => "SEMI";
+			"." => "DOT";
+			"..." => "DOTDOTDOT";
+			"," => "COMMA";
+			"$" => "DOLLAR";
+			"=" => "ASSIGN";
+			"?" => "QUESTION_MARK";
+			"$intrinsicThisFile" => "INTRINSIC_THIS_FILE";
+			"$intrinsicId" => "INTRINSIC_ID";
+			"$intrinsic" => "INTRINSIC";
+		],
+		keywords: vec![],
+		literals: literals![
+			"NUMBER" => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?";
+			"STRING_DOUBLE" => "\"(?s:[^\"\\\\]|\\\\.)*\"";
+			"STRING_SINGLE" => "'(?s:[^'\\\\]|\\\\.)*'";
+			"STRING_DOUBLE_VERBATIM" => "@\"(?:[^\"]|\"\")*\"";
+			"STRING_SINGLE_VERBATIM" => "@'(?:[^']|'')*'";
+			"STRING_BLOCK" => r"\|\|\|";
+
+			"IDENT" => r"[_a-zA-Z][_a-zA-Z0-9]*";
+			"WHITESPACE" => r"[ \t\n\r]+";
+			"SINGLE_LINE_SLASH_COMMENT" => r"//[^\r\n]*(\r\n|\n)?";
+			"SINGLE_LINE_HASH_COMMENT" => r"#[^\r\n]*(\r\n|\n)?";
+			"MULTI_LINE_COMMENT" => r"/\*([^*]|\*[^/])*\*/";
+		],
+		nodes: vec![],
+	};
+
+	let ast = lower(&kinds, &grammar);
+
+	for node in &ast.nodes {
+		let name = to_upper_snake_case(&node.name);
+		if !kinds.is_literal(&name) {
+			kinds.nodes.push(name);
+		}
+	}
+	for enum_ in &ast.enums {
+		let name = to_upper_snake_case(&enum_.name);
+		if !kinds.is_literal(&name) {
+			kinds.nodes.push(name);
+		}
+	}
+	for token in grammar.tokens() {
+		let token = &grammar[token];
+		let token = &token.name.clone();
+		let name = to_upper_snake_case(token);
+		if !kinds.is_punct(token) && !kinds.is_literal(&name) {
+			kinds.keywords.push(token.to_owned());
+		}
+	}
+
+	let syntax_kinds = generate_syntax_kinds(&kinds)?;
+
+	let tokens = generate_tokens(&ast)?;
+
+	let nodes = generate_nodes(&kinds, &ast)?;
+	ensure_file_contents(
+		&PathBuf::from(concat!(
+			env!("CARGO_MANIFEST_DIR"),
+			"/../crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs",
+		)),
+		&syntax_kinds,
+	)?;
+	ensure_file_contents(
+		&PathBuf::from(concat!(
+			env!("CARGO_MANIFEST_DIR"),
+			"/../crates/jrsonnet-rowan-parser/src/generated/tokens.rs",
+		)),
+		&tokens,
+	)?;
+	ensure_file_contents(
+		&PathBuf::from(concat!(
+			env!("CARGO_MANIFEST_DIR"),
+			"/../crates/jrsonnet-rowan-parser/src/generated/nodes.rs",
+		)),
+		&nodes,
+	)?;
+	Ok(())
+}
+
+fn generate_tokens(grammar: &AstSrc) -> Result<String> {
+	let tokens = grammar.tokens.iter().map(|token| {
+		let name = format_ident!("{}", token);
+		let kind = format_ident!("{}", to_upper_snake_case(token));
+		quote! {
+			#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+			pub struct #name {
+				pub(crate) syntax: SyntaxToken,
+			}
+			impl std::fmt::Display for #name {
+				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+					std::fmt::Display::fmt(&self.syntax, f)
+				}
+			}
+			impl AstToken for #name {
+				fn can_cast(kind: SyntaxKind) -> bool { kind == #kind }
+				fn cast(syntax: SyntaxToken) -> Option<Self> {
+					if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+				}
+				fn syntax(&self) -> &SyntaxToken { &self.syntax }
+			}
+		}
+	});
+
+	Ok(reformat(
+		&quote! {
+			use crate::{SyntaxKind::{self, *}, SyntaxToken, ast::AstToken};
+			#(#tokens)*
+		}
+		.to_string(),
+	)?
+	.replace("#[derive", "\n#[derive"))
+}
+
+fn generate_syntax_kinds(grammar: &KindsSrc) -> Result<String> {
+	let (single_byte_tokens_values, single_byte_tokens): (Vec<_>, Vec<_>) = grammar
+		.punct
+		.iter()
+		.filter(|(token, _name)| token.len() == 1)
+		.map(|(token, name)| (token.chars().next().unwrap(), format_ident!("{}", name)))
+		.unzip();
+
+	let punctuation_values = grammar
+		.punct
+		.iter()
+		.map(|(token, _name)| escape_token_macro(token));
+	let punctuation = grammar
+		.punct
+		.iter()
+		.map(|(_token, name)| format_ident!("{}", name))
+		.collect::<Vec<_>>();
+	let punctuation_enum = grammar
+		.punct
+		.iter()
+		.map(|(token, name)| {
+			let id = format_ident!("{}", name);
+			quote! {
+				#[token(#token)]
+				#id
+			}
+		})
+		.collect::<Vec<_>>();
+
+	let x = |name: &str| format_ident!("{}_KW", to_upper_snake_case(name));
+	let full_keywords_values = &grammar.keywords;
+	let full_keywords = full_keywords_values.iter().map(|s| x(s.as_str()));
+
+	let all_keywords_values = grammar.keywords.to_vec();
+	let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
+	let all_keywords = all_keywords_values
+		.iter()
+		.map(|s| x(&**s))
+		.collect::<Vec<_>>();
+	let all_keywords_enum = all_keywords_values
+		.iter()
+		.map(|s| {
+			let id = x(&**s);
+			quote! {
+				#[token(#s)]
+				#id
+			}
+		})
+		.collect::<Vec<_>>();
+
+	let tokens_enum = grammar
+		.literals
+		.iter()
+		.map(|l| {
+			let regex = &l.regex;
+			let id = format_ident!("{}", l.name);
+			let lexer = l
+				.lexer
+				.as_ref()
+				.map(|l| {
+					let id: TokenStream = l.parse().expect("path");
+					quote! {
+						, #id
+					}
+				})
+				.unwrap_or_else(|| quote! {});
+			quote! {
+				#[regex(#regex #lexer)]
+				#id
+			}
+		})
+		.collect::<Vec<_>>();
+
+	let nodes = grammar
+		.nodes
+		.iter()
+		.map(|name| format_ident!("{}", name))
+		.collect::<Vec<_>>();
+
+	let ast = quote! {
+		#![allow(bad_style, missing_docs, unreachable_pub)]
+		use logos::Logos;
+
+		/// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.
+		#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Logos)]
+		#[repr(u16)]
+		pub enum SyntaxKind {
+			#[doc(hidden)]
+			TOMBSTONE,
+			#[doc(hidden)]
+			EOF,
+			#(#punctuation_enum,)*
+			#(#all_keywords_enum,)*
+			#(#tokens_enum,)*
+			#[error]
+			ERROR,
+			#(#nodes,)*
+			#[doc(hidden)]
+			__LAST,
+		}
+		use self::SyntaxKind::*;
+
+		impl SyntaxKind {
+			pub fn is_keyword(self) -> bool {
+				match self {
+					#(#all_keywords)|* => true,
+					_ => false,
+				}
+			}
+
+			pub fn is_punct(self) -> bool {
+				match self {
+					#(#punctuation)|* => true,
+					_ => false,
+				}
+			}
+
+			pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+				let kw = match ident {
+					#(#full_keywords_values => #full_keywords,)*
+					_ => return None,
+				};
+				Some(kw)
+			}
+
+			pub fn from_char(c: char) -> Option<SyntaxKind> {
+				let tok = match c {
+					#(#single_byte_tokens_values => #single_byte_tokens,)*
+					_ => return None,
+				};
+				Some(tok)
+			}
+
+			pub fn from_raw(r: u16) -> Self {
+				assert!(r < Self::__LAST as u16);
+				unsafe { std::mem::transmute(r) }
+			}
+			pub fn into_raw(self) -> u16 {
+				self as u16
+			}
+		}
+
+		#[macro_export]
+		macro_rules! T {
+			#([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*
+			#([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*
+			[lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
+			[ident] => { $crate::SyntaxKind::IDENT };
+			[shebang] => { $crate::SyntaxKind::SHEBANG };
+		}
+		pub use T;
+	};
+
+	reformat(&ast.to_string())
+}
+
+pub struct KindsSrc {
+	pub punct: Vec<(String, String)>,
+	pub keywords: Vec<String>,
+	pub literals: Vec<LiteralKind>,
+	pub nodes: Vec<String>,
+}
+
+pub struct LiteralKind {
+	name: String,
+	regex: String,
+	lexer: Option<String>,
+}
+
+#[macro_export]
+macro_rules! literals {
+	($($name:expr => $regex:expr $(, $lexer:expr)?);* $(;)?) => {
+		vec![
+			$(LiteralKind {
+				name: $name.to_owned(),
+				regex: $regex.to_owned(),
+				lexer: None $(.or_else(|| Some($lexer.to_string())))?,
+			}),*
+		]
+	};
+}
+
+#[macro_export]
+macro_rules! puncts {
+	($($tok:expr => $name:expr);* $(;)?) => {
+		vec![
+			$(($tok.to_owned(), $name.to_owned())),*
+		]
+	};
+}
+use crate::{literals, puncts};
+
+impl KindsSrc {
+	pub fn is_punct(&self, tok: &str) -> bool {
+		self.punct.iter().any(|(t, _)| *t == tok)
+	}
+	pub fn is_literal(&self, tok: &str) -> bool {
+		self.literals.iter().any(|l| l.name == tok)
+	}
+
+	fn get_punct_name(&self, tok: &str) -> Option<&str> {
+		self.punct
+			.iter()
+			.find(|(t, _)| *t == tok)
+			.map(|(_, n)| n.as_str())
+	}
+}
+
+fn generate_nodes(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {
+	let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+		.nodes
+		.iter()
+		.map(|node| {
+			let name = format_ident!("{}", node.name);
+			let kind = format_ident!("{}", to_upper_snake_case(&node.name));
+			let traits = node.traits.iter().map(|trait_name| {
+				let trait_name = format_ident!("{}", trait_name);
+				quote!(impl ast::#trait_name for #name {})
+			});
+
+			let methods = node.fields.iter().map(|field| {
+				let method_name = field.method_name(kinds);
+				let ty = field.ty();
+
+				if field.is_many() {
+					quote! {
+						pub fn #method_name(&self) -> AstChildren<#ty> {
+							support::children(&self.syntax)
+						}
+					}
+				} else if let Some(token_kind) = field.token_kind() {
+					quote! {
+						pub fn #method_name(&self) -> Option<#ty> {
+							support::token(&self.syntax, #token_kind)
+						}
+					}
+				} else {
+					quote! {
+						pub fn #method_name(&self) -> Option<#ty> {
+							support::child(&self.syntax)
+						}
+					}
+				}
+			});
+			(
+				quote! {
+					#[pretty_doc_comment_placeholder_workaround]
+					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+					pub struct #name {
+						pub(crate) syntax: SyntaxNode,
+					}
+
+					#(#traits)*
+
+					impl #name {
+						#(#methods)*
+					}
+				},
+				quote! {
+					impl AstNode for #name {
+						fn can_cast(kind: SyntaxKind) -> bool {
+							kind == #kind
+						}
+						fn cast(syntax: SyntaxNode) -> Option<Self> {
+							if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+						}
+						fn syntax(&self) -> &SyntaxNode { &self.syntax }
+					}
+				},
+			)
+		})
+		.unzip();
+
+	let (enum_defs, enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+		.enums
+		.iter()
+		.map(|en| {
+			let variants: Vec<_> = en
+				.variants
+				.iter()
+				.map(|var| format_ident!("{}", var))
+				.collect();
+			let name = format_ident!("{}", en.name);
+			let kinds: Vec<_> = variants
+				.iter()
+				.map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
+				.collect();
+			let traits = en.traits.iter().map(|trait_name| {
+				let trait_name = format_ident!("{}", trait_name);
+				quote!(impl ast::#trait_name for #name {})
+			});
+
+			let ast_node = quote! {
+				impl AstNode for #name {
+					fn can_cast(kind: SyntaxKind) -> bool {
+						match kind {
+							#(#kinds)|* => true,
+							_ => false,
+						}
+					}
+					fn cast(syntax: SyntaxNode) -> Option<Self> {
+						let res = match syntax.kind() {
+							#(
+							#kinds => #name::#variants(#variants { syntax }),
+							)*
+							_ => return None,
+						};
+						Some(res)
+					}
+					fn syntax(&self) -> &SyntaxNode {
+						match self {
+							#(
+							#name::#variants(it) => &it.syntax,
+							)*
+						}
+					}
+				}
+			};
+
+			(
+				quote! {
+					#[pretty_doc_comment_placeholder_workaround]
+					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+					pub enum #name {
+						#(#variants(#variants),)*
+					}
+
+					#(#traits)*
+				},
+				quote! {
+					#(
+						impl From<#variants> for #name {
+							fn from(node: #variants) -> #name {
+								#name::#variants(node)
+							}
+						}
+					)*
+					#ast_node
+				},
+			)
+		})
+		.unzip();
+
+	let (any_node_defs, any_node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+		.nodes
+		.iter()
+		.flat_map(|node| node.traits.iter().map(move |t| (t, node)))
+		.into_group_map()
+		.into_iter()
+		.sorted_by_key(|(k, _)| *k)
+		.map(|(trait_name, nodes)| {
+			let name = format_ident!("Any{}", trait_name);
+			let trait_name = format_ident!("{}", trait_name);
+			let kinds: Vec<_> = nodes
+				.iter()
+				.map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string())))
+				.collect();
+
+			(
+				quote! {
+					#[pretty_doc_comment_placeholder_workaround]
+					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+					pub struct #name {
+						pub(crate) syntax: SyntaxNode,
+					}
+					impl ast::#trait_name for #name {}
+				},
+				quote! {
+					impl #name {
+						#[inline]
+						pub fn new<T: ast::#trait_name>(node: T) -> #name {
+							#name {
+								syntax: node.syntax().clone()
+							}
+						}
+					}
+					impl AstNode for #name {
+						fn can_cast(kind: SyntaxKind) -> bool {
+							match kind {
+								#(#kinds)|* => true,
+								_ => false,
+							}
+						}
+						fn cast(syntax: SyntaxNode) -> Option<Self> {
+							Self::can_cast(syntax.kind()).then(|| #name { syntax })
+						}
+						fn syntax(&self) -> &SyntaxNode {
+							&self.syntax
+						}
+					}
+				},
+			)
+		})
+		.unzip();
+
+	let enum_names = grammar.enums.iter().map(|it| &it.name);
+	let node_names = grammar.nodes.iter().map(|it| &it.name);
+
+	let display_impls = enum_names
+		.chain(node_names.clone())
+		.map(|it| format_ident!("{}", it))
+		.map(|name| {
+			quote! {
+				impl std::fmt::Display for #name {
+					fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+						std::fmt::Display::fmt(self.syntax(), f)
+					}
+				}
+			}
+		});
+
+	let defined_nodes: HashSet<_> = node_names.collect();
+
+	for node in kinds
+		.nodes
+		.iter()
+		.map(|kind| to_pascal_case(kind))
+		.filter(|name| !defined_nodes.iter().any(|&it| it == name))
+	{
+		drop(node)
+		// FIXME: restore this
+		// eprintln!("Warning: node {} not defined in ast source", node);
+	}
+
+	let ast = quote! {
+		#![allow(non_snake_case)]
+		use crate::{
+			SyntaxNode, SyntaxToken, SyntaxKind::{self, *},
+			ast::{self, AstNode, AstChildren, support},
+			T,
+		};
+
+		#(#node_defs)*
+		#(#enum_defs)*
+		#(#any_node_defs)*
+		#(#node_boilerplate_impls)*
+		#(#enum_boilerplate_impls)*
+		#(#any_node_boilerplate_impls)*
+		#(#display_impls)*
+	};
+
+	let ast = ast.to_string().replace("T ! [", "T![");
+
+	let mut res = String::with_capacity(ast.len() * 2);
+
+	let mut docs = grammar
+		.nodes
+		.iter()
+		.map(|it| &it.doc)
+		.chain(grammar.enums.iter().map(|it| &it.doc));
+
+	for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") {
+		res.push_str(chunk);
+		if let Some(doc) = docs.next() {
+			write_doc_comment(doc, &mut res);
+		}
+	}
+
+	let res = reformat(&res)?;
+	Ok(res.replace("#[derive", "\n#[derive"))
+}
+
+fn write_doc_comment(contents: &[String], dest: &mut String) {
+	use std::fmt::Write;
+	for line in contents {
+		writeln!(dest, "///{}", line).unwrap();
+	}
+}
+
+fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {
+	let tokens = "Whitespace Comment String StringVerbantim StringBlock Number Ident"
+		.split_ascii_whitespace()
+		.map(|it| it.to_string())
+		.collect::<Vec<_>>();
+
+	let mut res = AstSrc {
+		tokens,
+		..Default::default()
+	};
+
+	let nodes = grammar.iter().collect::<Vec<_>>();
+
+	for &node in &nodes {
+		let name = grammar[node].name.clone();
+		let rule = &grammar[node].rule;
+		match lower_enum(grammar, rule) {
+			Some(variants) => {
+				let enum_src = AstEnumSrc {
+					doc: Vec::new(),
+					name,
+					traits: Vec::new(),
+					variants,
+				};
+				res.enums.push(enum_src);
+			}
+			None => {
+				let mut fields = Vec::new();
+				lower_rule(&mut fields, grammar, None, rule);
+				res.nodes.push(AstNodeSrc {
+					doc: Vec::new(),
+					name,
+					traits: Vec::new(),
+					fields,
+				});
+			}
+		}
+	}
+
+	deduplicate_fields(&mut res);
+	extract_enums(&mut res);
+	extract_struct_traits(kinds, &mut res);
+	extract_enum_traits(&mut res);
+	res
+}
+
+fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {
+	let alternatives = match rule {
+		Rule::Alt(it) => it,
+		_ => return None,
+	};
+	let mut variants = Vec::new();
+	for alternative in alternatives {
+		match alternative {
+			Rule::Node(it) => variants.push(grammar[*it].name.clone()),
+			Rule::Token(it) if grammar[*it].name == ";" => (),
+			_ => return None,
+		}
+	}
+	Some(variants)
+}
+
+fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) {
+	if lower_comma_list(acc, grammar, label, rule) {
+		return;
+	}
+
+	match rule {
+		Rule::Node(node) => {
+			let ty = grammar[*node].name.clone();
+			let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty));
+			let field = Field::Node {
+				name,
+				ty,
+				cardinality: Cardinality::Optional,
+			};
+			acc.push(field);
+		}
+		Rule::Token(token) => {
+			assert!(label.is_none(), "uexpected label: {:?}", label);
+			let name = grammar[*token].name.clone();
+			let field = Field::Token(name);
+			acc.push(field);
+		}
+		Rule::Rep(inner) => {
+			if let Rule::Node(node) = &**inner {
+				let ty = grammar[*node].name.clone();
+				let name = label
+					.cloned()
+					.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
+				let field = Field::Node {
+					name,
+					ty,
+					cardinality: Cardinality::Many,
+				};
+				acc.push(field);
+				return;
+			}
+			todo!("unsupported repitition: {:?}", rule)
+		}
+		Rule::Labeled { label: l, rule } => {
+			assert!(label.is_none());
+			lower_rule(acc, grammar, Some(l), rule);
+		}
+		Rule::Seq(rules) | Rule::Alt(rules) => {
+			for rule in rules {
+				lower_rule(acc, grammar, label, rule)
+			}
+		}
+		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule),
+	}
+}
+
+// (T (',' T)* ','?)
+fn lower_comma_list(
+	acc: &mut Vec<Field>,
+	grammar: &Grammar,
+	label: Option<&String>,
+	rule: &Rule,
+) -> bool {
+	let rule = match rule {
+		Rule::Seq(it) => it,
+		_ => return false,
+	};
+	let (node, repeat, trailing_comma) = match rule.as_slice() {
+		[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {
+			(node, repeat, trailing_comma)
+		}
+		_ => return false,
+	};
+	let repeat = match &**repeat {
+		Rule::Seq(it) => it,
+		_ => return false,
+	};
+	match repeat.as_slice() {
+		[comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),
+		_ => return false,
+	}
+	let ty = grammar[*node].name.clone();
+	let name = label
+		.cloned()
+		.unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
+	let field = Field::Node {
+		name,
+		ty,
+		cardinality: Cardinality::Many,
+	};
+	acc.push(field);
+	true
+}
+
+fn deduplicate_fields(ast: &mut AstSrc) {
+	for node in &mut ast.nodes {
+		let mut i = 0;
+		'outer: while i < node.fields.len() {
+			for j in 0..i {
+				let f1 = &node.fields[i];
+				let f2 = &node.fields[j];
+				if f1 == f2 {
+					node.fields.remove(i);
+					continue 'outer;
+				}
+			}
+			i += 1;
+		}
+	}
+}
+
+fn extract_enums(ast: &mut AstSrc) {
+	for node in &mut ast.nodes {
+		for enm in &ast.enums {
+			let mut to_remove = Vec::new();
+			for (i, field) in node.fields.iter().enumerate() {
+				let ty = field.ty().to_string();
+				if enm.variants.iter().any(|it| it == &ty) {
+					to_remove.push(i);
+				}
+			}
+			if to_remove.len() == enm.variants.len() {
+				node.remove_field(to_remove);
+				let ty = enm.name.clone();
+				let name = to_lower_snake_case(&ty);
+				node.fields.push(Field::Node {
+					name,
+					ty,
+					cardinality: Cardinality::Optional,
+				});
+			}
+		}
+	}
+}
+
+fn extract_struct_traits(kinds: &KindsSrc, ast: &mut AstSrc) {
+	// TODO: add common accessor traits here.
+	let traits: &[(&str, &[&str])] = &[];
+
+	for node in &mut ast.nodes {
+		for (name, methods) in traits {
+			extract_struct_trait(kinds, node, name, methods);
+		}
+	}
+}
+
+fn extract_struct_trait(
+	kinds: &KindsSrc,
+	node: &mut AstNodeSrc,
+	trait_name: &str,
+	methods: &[&str],
+) {
+	let mut to_remove = Vec::new();
+	for (i, field) in node.fields.iter().enumerate() {
+		let method_name = field.method_name(kinds).to_string();
+		if methods.iter().any(|&it| it == method_name) {
+			to_remove.push(i);
+		}
+	}
+	if to_remove.len() == methods.len() {
+		node.traits.push(trait_name.to_string());
+		node.remove_field(to_remove);
+	}
+}
+
+fn extract_enum_traits(ast: &mut AstSrc) {
+	let enums = ast.enums.clone();
+	for enm in &mut ast.enums {
+		if enm.name == "Stmt" {
+			continue;
+		}
+		let nodes = &ast.nodes;
+
+		let mut variant_traits = enm.variants.iter().map(|var| {
+			nodes
+				.iter()
+				.find_map(|node| {
+					if &node.name != var {
+						return None;
+					}
+					Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())
+				})
+				.unwrap_or_else(|| {
+					enums
+						.iter()
+						.find_map(|node| {
+							if &node.name != var {
+								return None;
+							}
+							Some(node.traits.iter().cloned().collect::<BTreeSet<_>>())
+						})
+						.unwrap_or_else(|| {
+							panic!("{}", {
+								&format!(
+									"Could not find a struct `{}` for enum `{}::{}`",
+									var, enm.name, var
+								)
+							})
+						})
+				})
+		});
+
+		let mut enum_traits = match variant_traits.next() {
+			Some(it) => it,
+			None => continue,
+		};
+		for traits in variant_traits {
+			enum_traits = enum_traits.intersection(&traits).cloned().collect();
+		}
+		enm.traits = enum_traits.into_iter().collect();
+	}
+}
+
+pub fn escape_token_macro(token: &str) -> TokenStream {
+	if "{}[]()$".contains(token) {
+		let c = token.chars().next().unwrap();
+		quote! { #c }
+	} else if token.contains('$') {
+		quote! { #token }
+	} else {
+		let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint));
+		quote! { #(#cs)* }
+	}
+}
addedxtask/src/sourcegen/util.rsdiffbeforeafterboth
--- /dev/null
+++ b/xtask/src/sourcegen/util.rs
@@ -0,0 +1,92 @@
+use std::{fs, path::Path};
+
+use anyhow::{bail, Result};
+use xshell::{cmd, Shell};
+
+/// Checks that the `file` has the specified `contents`. If that is not the
+/// case, updates the file and then fails the test.
+pub fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> {
+	if let Ok(old_contents) = fs::read_to_string(file) {
+		if normalize_newlines(&old_contents) == normalize_newlines(contents) {
+			// File is already up to date.
+			return Ok(());
+		}
+	}
+
+	eprintln!(" {} was not up-to-date, updating\n", file.display());
+	if std::env::var("CI").is_ok() {
+		eprintln!("NOTE: run `cargo test` locally and commit the updated files\n");
+	}
+	if let Some(parent) = file.parent() {
+		let _ = fs::create_dir_all(parent);
+	}
+	fs::write(file, contents).unwrap();
+	bail!("some file was not up to date and has been updated, simply re-run the tests");
+}
+
+// Eww, someone configured git to use crlf?
+fn normalize_newlines(s: &str) -> String {
+	s.replace("\r\n", "\n")
+}
+
+pub(crate) fn pluralize(s: &str) -> String {
+	format!("{}s", s)
+}
+
+pub fn to_upper_snake_case(s: &str) -> String {
+	let mut buf = String::with_capacity(s.len());
+	let mut prev = false;
+	for c in s.chars() {
+		if c.is_ascii_uppercase() && prev {
+			buf.push('_')
+		}
+		prev = true;
+
+		buf.push(c.to_ascii_uppercase());
+	}
+	buf
+}
+pub fn to_lower_snake_case(s: &str) -> String {
+	let mut buf = String::with_capacity(s.len());
+	let mut prev = false;
+	for c in s.chars() {
+		if c.is_ascii_uppercase() && prev {
+			buf.push('_')
+		}
+		prev = true;
+
+		buf.push(c.to_ascii_lowercase());
+	}
+	buf
+}
+
+pub fn to_pascal_case(s: &str) -> String {
+	let mut buf = String::with_capacity(s.len());
+	let mut prev_is_underscore = true;
+	for c in s.chars() {
+		if c == '_' {
+			prev_is_underscore = true;
+		} else if prev_is_underscore {
+			buf.push(c.to_ascii_uppercase());
+			prev_is_underscore = false;
+		} else {
+			buf.push(c.to_ascii_lowercase());
+		}
+	}
+	buf
+}
+
+pub fn reformat(text: &str) -> Result<String> {
+	// let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
+	// rustfmt()?;
+	let sh = Shell::new()?;
+	let stdout = cmd!(sh, "rustfmt --config fn_single_line=true")
+		.stdin(text)
+		.read()?;
+	Ok(format!(
+		"{}\n\n{}\n",
+		"//! This is a generated file, please do not edit manually. Changes can be
+//! made in codegeneration that lives in `xtask` top-level dir.",
+		stdout
+	))
+}