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

difftreelog

refactor parser and grammar compatibility

Yaroslav Bolyukin2023-09-04parent: #c678cf8.patch.diff
in: master

46 files changed

addedcmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "jrsonnet-fmt"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+dprint-core = "0.58.2"
+jrsonnet-rowan-parser = { path = "../../crates/jrsonnet-rowan-parser" }
addedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/main.rs
@@ -0,0 +1,513 @@
+use std::any::type_name;
+
+use dprint_core::formatting::{PrintItems, PrintOptions, Signal};
+use jrsonnet_rowan_parser::{
+	nodes::{
+		ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
+		DestructRest, Expr, Field, FieldName, ForSpec, IfSpec, ImportKind, LhsExpr, Literal,
+		Member, Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, String,
+		UnaryOperator,
+	},
+	AstToken, SyntaxToken,
+};
+
+pub trait Printable {
+	fn print(&self) -> PrintItems;
+}
+
+macro_rules! pi {
+	(@i; $($t:tt)*) => {{
+		#[allow(unused_mut)]
+		let mut o = PrintItems::new();
+		pi!(@s; o: $($t)*);
+		o
+	}};
+	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{
+		$o.push_str($e);
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: nl $($t:tt)*) => {{
+		$o.push_signal(Signal::NewLine);
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: >i $($t:tt)*) => {{
+		$o.push_signal(Signal::StartIndent);
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: <i $($t:tt)*) => {{
+		$o.push_signal(Signal::FinishIndent);
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{
+		$o.extend($expr.print());
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{
+		if $e {
+			pi!(@s; $o: $($then)*);
+		}
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{
+		if $e {
+			pi!(@s; $o: $($then)*);
+		} else {
+			pi!(@s; $o: $($else)*);
+		}
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $i:ident:) => {}
+}
+macro_rules! p {
+	(new: $($t:tt)*) => {
+		pi!(@i; $($t)*)
+	};
+	($o:ident: $($t:tt)*) => {
+		pi!(@s; $o: $($t)*)
+	};
+}
+
+impl<P> Printable for Option<P>
+where
+	P: Printable,
+{
+	fn print(&self) -> PrintItems {
+		if let Some(v) = self {
+			v.print()
+		} else {
+			p!(new: str(
+				&format!(
+					"/*missing {}*/",
+					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")
+				),
+			))
+		}
+	}
+}
+
+impl Printable for SyntaxToken {
+	fn print(&self) -> PrintItems {
+		p!(new: str(&self.to_string()))
+	}
+}
+
+impl Printable for String {
+	fn print(&self) -> PrintItems {
+		p!(new: str(&format!("{}", self)))
+	}
+}
+impl Printable for Number {
+	fn print(&self) -> PrintItems {
+		p!(new: str(&format!("{}", self)))
+	}
+}
+
+impl Printable for Name {
+	fn print(&self) -> PrintItems {
+		p!(new: {self.ident_lit()})
+	}
+}
+
+impl Printable for DestructRest {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new: str("..."));
+		if let Some(name) = self.into() {
+			p!(pi: {name});
+		}
+		pi
+	}
+}
+
+impl Printable for Destruct {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new:);
+		match self {
+			Destruct::DestructFull(f) => {
+				p!(pi: {f.name()})
+			}
+			Destruct::DestructSkip(_) => p!(pi: str("?")),
+			Destruct::DestructArray(a) => {
+				p!(pi: str("[") >i nl);
+				for el in a.destruct_array_parts() {
+					match el {
+						DestructArrayPart::DestructArrayElement(e) => {
+							p!(pi: {e.destruct()} str(",") nl)
+						}
+						DestructArrayPart::DestructRest(d) => {
+							p!(pi: {d} str(",") nl)
+						}
+					}
+				}
+				p!(pi: <i str("]"));
+			}
+			Destruct::DestructObject(o) => {
+				p!(pi: str("{") >i nl);
+				for item in o.destruct_object_fields() {
+					p!(pi: {item.field()});
+					if let Some(des) = item.destruct() {
+						p!(pi: str(": ") {des})
+					}
+					if let Some(def) = item.expr() {
+						p!(pi: str(" = ") {def});
+					}
+					p!(pi: str(",") nl);
+				}
+				if let Some(rest) = o.destruct_rest() {
+					p!(pi: {rest} nl)
+				}
+				p!(pi: <i str("}"));
+			}
+		}
+		pi
+	}
+}
+
+impl Printable for FieldName {
+	fn print(&self) -> PrintItems {
+		match self {
+			FieldName::FieldNameFixed(f) => {
+				if let Some(id) = f.id() {
+					p!(new: {id})
+				} else if let Some(str) = f.string() {
+					p!(new: {str})
+				} else {
+					p!(new: str("/*missing FieldName*/"))
+				}
+			}
+			FieldName::FieldNameDynamic(d) => {
+				p!(new: str("[") {d.expr()} str("]"))
+			}
+		}
+	}
+}
+impl Printable for Field {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new:);
+		match self {
+			Field::FieldNormal(n) => {
+				p!(pi: {n.field_name()});
+				if n.plus_token().is_some() {
+					p!(pi: str("+"));
+				}
+				p!(pi: str(": ") {n.expr()});
+			}
+			Field::FieldMethod(m) => {
+				p!(pi: {m.field_name()} {m.params_desc()} str(": ") {m.expr()});
+			}
+		}
+		pi
+	}
+}
+
+impl Printable for ObjLocal {
+	fn print(&self) -> PrintItems {
+		p!(new: str("local ") {self.bind()})
+	}
+}
+
+impl Printable for Assertion {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new: str("assert ") {self.condition()});
+		if self.colon_token().is_some() || self.message().is_some() {
+			p!(pi: str(": ") {self.message()})
+		}
+		pi
+	}
+}
+
+impl Printable for ParamsDesc {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new: str("(") >i nl);
+		for param in self.params() {
+			p!(pi: {param.destruct()});
+			if param.assign_token().is_some() || param.expr().is_some() {
+				p!(pi: str(" = ") {param.expr()})
+			}
+			p!(pi: str(",") nl)
+		}
+		p!(pi: <i str(")"));
+		pi
+	}
+}
+impl Printable for ArgsDesc {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new: str("(") >i nl);
+		for arg in self.args() {
+			if arg.name().is_some() || arg.assign_token().is_some() {
+				p!(pi: {arg.name()} str(" = "));
+			}
+			p!(pi: {arg.expr()} str(",") nl)
+		}
+		p!(pi: <i str(")"));
+		pi
+	}
+}
+impl Printable for SliceDesc {
+	fn print(&self) -> PrintItems {
+		let mut pi = p!(new: str("["));
+		if self.from().is_some() {
+			p!(pi: {self.from()});
+		}
+		p!(pi: str(":"));
+		if self.end().is_some() {
+			p!(pi: {self.end().map(|e|e.expr())})
+		}
+		// Keep only one : in case if we don't need step
+		if self.step().is_some() {
+			p!(pi: str(":") {self.step().map(|e|e.expr())});
+		}
+		p!(pi: str("]"));
+		pi
+	}
+}
+
+impl Printable for ObjBody {
+	fn print(&self) -> PrintItems {
+		match self {
+			ObjBody::ObjBodyComp(_) => todo!(),
+			ObjBody::ObjBodyMemberList(l) => {
+				let mut pi = p!(new:);
+				for mem in l.members() {
+					match mem {
+						Member::MemberBindStmt(b) => {
+							p!(pi: {b.obj_local()})
+						}
+						Member::MemberAssertStmt(ass) => {
+							p!(pi: {ass.assertion()})
+						}
+						Member::MemberField(f) => {
+							p!(pi: {f.field()})
+						}
+					}
+					p!(pi: str(",") nl)
+				}
+				pi
+			}
+		}
+	}
+}
+impl Printable for UnaryOperator {
+	fn print(&self) -> PrintItems {
+		p!(new: str(self.text()))
+	}
+}
+impl Printable for BinaryOperator {
+	fn print(&self) -> PrintItems {
+		p!(new: str(self.text()))
+	}
+}
+impl Printable for Bind {
+	fn print(&self) -> PrintItems {
+		match self {
+			Bind::BindDestruct(d) => {
+				p!(new: {d.into()} str(" = ") {d.value()})
+			}
+			Bind::BindFunction(f) => {
+				p!(new: str("function") {f.params()} str(" = ") {f.value()})
+			}
+		}
+	}
+}
+impl Printable for Literal {
+	fn print(&self) -> PrintItems {
+		p!(new: str(&self.syntax().to_string()))
+	}
+}
+impl Printable for ImportKind {
+	fn print(&self) -> PrintItems {
+		p!(new: str(&self.syntax().to_string()))
+	}
+}
+impl Printable for LhsExpr {
+	fn print(&self) -> PrintItems {
+		p!(new: {self.expr()})
+	}
+}
+impl Printable for ForSpec {
+	fn print(&self) -> PrintItems {
+		p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})
+	}
+}
+impl Printable for IfSpec {
+	fn print(&self) -> PrintItems {
+		p!(new: str("if ") {self.expr()})
+	}
+}
+impl Printable for CompSpec {
+	fn print(&self) -> PrintItems {
+		match self {
+			CompSpec::ForSpec(f) => f.print(),
+			CompSpec::IfSpec(i) => i.print(),
+		}
+	}
+}
+impl Printable for Expr {
+	fn print(&self) -> PrintItems {
+		match self {
+			Expr::ExprBinary(b) => {
+				p!(new: {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()})
+			}
+			Expr::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),
+			Expr::ExprSlice(s) => {
+				p!(new: {s.expr()} {s.slice_desc()})
+			}
+			Expr::ExprIndex(i) => {
+				p!(new: {i.expr()} str(".") {i.index()})
+			}
+			Expr::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),
+			Expr::ExprApply(a) => {
+				let mut pi = p!(new: {a.expr()} {a.args_desc()});
+				if a.tailstrict_kw_token().is_some() {
+					p!(pi: str(" tailstrict"));
+				}
+				pi
+			}
+			Expr::ExprObjExtend(ex) => {
+				p!(new: {ex.lhs_expr()} str(" ") {ex.expr()})
+			}
+			Expr::ExprParened(p) => {
+				p!(new: str("(") {p.expr()} str(")"))
+			}
+			Expr::ExprIntrinsicThisFile(_) => p!(new: str("$intrinsicThisFile")),
+			Expr::ExprIntrinsicId(_) => p!(new: str("$intrinsicId")),
+			Expr::ExprIntrinsic(i) => p!(new: str("$intrinsic(") {i.name()} str(")")),
+			Expr::ExprString(s) => p!(new: {s.string()}),
+			Expr::ExprNumber(n) => p!(new: {n.number()}),
+			Expr::ExprArray(a) => {
+				let mut pi = p!(new: str("[") >i nl);
+				for el in a.exprs() {
+					p!(pi: {el} str(",") nl);
+				}
+				p!(pi: <i str("]"));
+				pi
+			}
+			Expr::ExprObject(o) => {
+				p!(new: str("{") >i nl {o.obj_body()} <i str("}"))
+			}
+			Expr::ExprArrayComp(arr) => {
+				let mut pi = p!(new: str("[") {arr.expr()});
+				for spec in arr.comp_specs() {
+					p!(pi: str(" ") {spec});
+				}
+				p!(pi: str("]"));
+				pi
+			}
+			Expr::ExprImport(v) => {
+				p!(new: {v.import_kind()} str(" ") {v.string()})
+			}
+			Expr::ExprVar(n) => p!(new: {n.name()}),
+			Expr::ExprLocal(l) => {
+				let mut pi = p!(new: str("local") >i nl);
+				for bind in l.binds() {
+					p!(pi: {bind} str(",") nl);
+				}
+				p!(pi: <i str(";") nl {l.expr()});
+				pi
+			}
+			Expr::ExprIfThenElse(ite) => {
+				let mut pi =
+					p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});
+				if ite.else_kw_token().is_some() || ite.else_().is_some() {
+					p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})
+				}
+				pi
+			}
+			Expr::ExprFunction(f) => p!(new: str("function") {f.params_desc()} str(" ") {f.expr()}),
+			Expr::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),
+			Expr::ExprError(e) => p!(new: str("error ") {e.expr()}),
+			Expr::ExprLiteral(l) => {
+				p!(new: {l.literal()})
+			}
+		}
+	}
+}
+
+impl Printable for SourceFile {
+	fn print(&self) -> PrintItems {
+		assert!(self.expr().is_some());
+		self.expr().print()
+	}
+}
+
+fn main() {
+	let (parsed, _errors) = jrsonnet_rowan_parser::parse(
+		r#"
+
+
+		# Edit me!
+		local b = import "b.libsonnet";  # comment
+		local a = import "a.libsonnet";
+
+			 local f(x,y)=x+y;
+
+		local {a: [b, ..., c], d, ...e} = null;
+
+		local ass = assert false : false; false;
+
+		local fn = function(a, b, c = 3) 4;
+
+		local comp = [a for b in c if d == e];
+		local ocomp = {[k]: 1 for k in v};
+
+		local ? = skip;
+
+		local intr = $intrinsic(test);
+		local intrId = $intrinsicId;
+		local intrThisFile = $intrinsicThisFile;
+
+		local ie = a[expr];
+
+		local unary = !a;
+
+		local Template = {z: "foo"};
+
+		{
+						local
+
+					h = 3,
+					assert self.a == 1
+
+					: "error",
+		"f": ((((((3)))))) ,
+		"g g":
+		f(4,2),
+		arr: [[
+		  1, 2,
+		  ],
+		  3,
+		  {
+			  b: {
+				  c: {
+					  k: [16]
+				  }
+			  }
+		  }
+		  ],
+		  m: a[1::],
+		  m: b[::],
+		  k: if a         == b    then
+
+
+		  2
+
+		  else Template {}
+		} + Template
+
+
+"#,
+	);
+
+	// dbg!(errors);
+	dbg!(&parsed);
+
+	let o = dprint_core::formatting::format(
+		|| parsed.print(),
+		PrintOptions {
+			indent_width: 2,
+			max_width: 100,
+			use_tabs: false,
+			new_line_text: "\n",
+		},
+	);
+	println!("{}", o);
+}
modifiedcrates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/Cargo.toml
+++ b/crates/jrsonnet-rowan-parser/Cargo.toml
@@ -19,3 +19,4 @@
 indoc = "1.0.3"
 insta = "1.10.0"
 anyhow = "1.0.57"
+jrsonnet-stdlib = { path = "../jrsonnet-stdlib" }
modifiedcrates/jrsonnet-rowan-parser/jsonnet.ungramdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/jsonnet.ungram
+++ b/crates/jrsonnet-rowan-parser/jsonnet.ungram
@@ -1,7 +1,12 @@
+// This file describes structure of jsonnet source code
+// It is also used to generate files in src/generated
+
+// Token names ending with `!` are considered meta, and handled specifically
+
 SourceFile = Expr
 
 ExprBinary =
-    lhs:Expr
+    lhs:LhsExpr
     BinaryOperator
     rhs:Expr
 ExprUnary =
@@ -9,29 +14,23 @@
     rhs:Expr
 ExprSlice =
     Expr
-    '['
     SliceDesc
-    ']'
 ExprIndex =
     Expr
     '.'
     index:Name
 ExprIndexExpr =
-    base:Expr
+    base:LhsExpr
     '['
     index:Expr
     ']'
 ExprApply =
     Expr
-    '('
     ArgsDesc
-    ')'
     'tailstrict'?
 ExprObjExtend =
+    LhsExpr
     Expr
-    '{'
-    ObjBody
-    '}'
 ExprParened =
     '('
     Expr
@@ -64,13 +63,16 @@
     '['
     Expr
     ','?
-    ForSpec
     CompSpec*
     ']'
+
 ExprImport =
-    'importstr' String
-|   'importbin' String
-|   'import' String
+    ImportKind String
+
+ImportKind =
+    'importstr'
+|   'importbin'
+|   'import'
 
 ExprVar =
     name:Name
@@ -78,12 +80,15 @@
     'local'
     (Bind (',' Bind)* ','?)
     ';'
+    Expr
+
 ExprIfThenElse =
     'if'
     cond:Expr
     'then'
-    then:Expr
-    ('else' else_:Expr)?
+    then:TrueExpr
+    ('else' else_:FalseExpr)?
+
 ExprFunction =
     'function'
     '('
@@ -112,6 +117,7 @@
 |   ExprIntrinsic
 |   ExprString
 |   ExprNumber
+|   ExprLiteral
 |   ExprArray
 |   ExprObject
 |   ExprArrayComp
@@ -130,42 +136,48 @@
 |   '<<' | '>>'
 |   '+' | '-'
 |   '*' | '/' | '%'
+|   'ERROR_NO_OPERATOR!'
 
 UnaryOperator =
     '-' | '!' | '~'
 
+SliceDescEnd=Expr
+SliceDescStep=Expr
 SliceDesc =
+    '['
     from:Expr?
     ':'
     (
-        end:Expr?
+        end:SliceDescEnd?
         (
             ':'
-            step:Expr?
+            step:SliceDescStep?
         )?
     )?
+    ']'
 
 Name =
-    'ident'
+    'LIT_IDENT!'
 
 ArgsDesc =
+    '('
     (Arg (',' Arg)* ','?)?
+    ')'
 Arg =
     (name:Name '=')? Expr
 
 ObjBodyComp =
     pre:ObjLocalPostComma*
     '['
-    key:Expr
+    key:LhsExpr
     ']'
     '+'?
     ':'
     value:Expr
     post:ObjLocalPreComma*
-    ForSpec
     CompSpec*
 ObjBodyMemberList =
-    (Member (',' Member) ','?)?
+    (Member (',' Member)* ','?)?
 ObjBody =
     ObjBodyComp
 |   ObjBodyMemberList
@@ -196,9 +208,7 @@
     Expr
 FieldMethod =
     FieldName
-    '('
     ParamsDesc
-    ')'
     Visibility
     Expr
 Field =
@@ -230,14 +240,15 @@
 |   'super'
 
 String =
-    'string_double'
-|   'string_single'
-|   'string_double_verbatim'
-|   'string_single_verbatim'
-|   'string_block'
+    'LIT_STRING_DOUBLE!'
+|   'LIT_STRING_SINGLE!'
+|   'LIT_STRING_DOUBLE_VERBATIM!'
+|   'LIT_STRING_SINGLE_VERBATIM!'
+|   'LIT_STRING_BLOCK!'
 
 Number =
-    'number'
+    'LIT_FLOAT!'
+|   'META_FORCE_ENUM!'
 
 ForSpec =
     'for'
@@ -257,9 +268,7 @@
     value:Expr
 BindFunction =
     name:Name
-    '('
     params:ParamsDesc
-    ')'
     '='
     value:Expr
 Bind =
@@ -267,7 +276,9 @@
 |   BindFunction
 
 ParamsDesc =
+    '('
     (Param (',' Param)* ','?)?
+    ')'
 Param =
     Destruct
     (
@@ -277,30 +288,23 @@
 
 Assertion =
     'assert'
-    condition:Expr
+    condition:LhsExpr
     (
         ':'
-        Expr
+        message:Expr
     )?
 
 DestructFull =
-    into:Name
+    Name
 DestructSkip =
     '?'
 DestructArray =
     '['
-    start:(
-        Destruct
-        (',' Destruct)*
+    (
+        DestructArrayPart
+        (',' DestructArrayPart)*
         ','?
     )?
-    DestructRest?
-    ','?
-    end:(
-        Destruct
-        (',' Destruct)*
-        ','?
-    )
     ']'
 DestructObject =
     '{'
@@ -314,10 +318,16 @@
     '}'
 Destruct =
     DestructFull
-    DestructSkip
-    DestructArray
-    DestructObject
+|   DestructSkip
+|   DestructArray
+|   DestructObject
 
+DestructArrayElement =
+    Destruct
+DestructArrayPart =
+    DestructArrayElement
+|   DestructRest
+
 DestructRest =
     '...'
     into:Name?
@@ -332,3 +342,8 @@
         '='
         Expr
     )?
+
+// Aliases used to resolve node type conflicts
+TrueExpr=Expr
+FalseExpr=Expr
+LhsExpr=Expr
modifiedcrates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/ast.rs
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -1 +1,95 @@
-pub trait AstToken {}
+use std::marker::PhantomData;
+
+use crate::{SyntaxKind, SyntaxNode, SyntaxNodeChildren, SyntaxToken};
+
+/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
+/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
+/// the same representation: a pointer to the tree root and a pointer to the
+/// node itself.
+pub trait AstNode {
+	fn can_cast(kind: SyntaxKind) -> bool
+	where
+		Self: Sized;
+
+	fn cast(syntax: SyntaxNode) -> Option<Self>
+	where
+		Self: Sized;
+
+	fn syntax(&self) -> &SyntaxNode;
+	fn clone_for_update(&self) -> Self
+	where
+		Self: Sized,
+	{
+		Self::cast(self.syntax().clone_for_update()).unwrap()
+	}
+	fn clone_subtree(&self) -> Self
+	where
+		Self: Sized,
+	{
+		Self::cast(self.syntax().clone_subtree()).unwrap()
+	}
+}
+
+/// Like `AstNode`, but wraps tokens rather than interior nodes.
+pub trait AstToken {
+	fn can_cast(token: SyntaxKind) -> bool
+	where
+		Self: Sized;
+
+	fn cast(syntax: SyntaxToken) -> Option<Self>
+	where
+		Self: Sized;
+
+	fn syntax(&self) -> &SyntaxToken;
+
+	fn text(&self) -> &str {
+		self.syntax().text()
+	}
+}
+
+#[derive(Debug, Clone)]
+pub struct AstChildren<N> {
+	inner: SyntaxNodeChildren,
+	ph: PhantomData<N>,
+}
+
+impl<N> AstChildren<N> {
+	fn new(parent: &SyntaxNode) -> Self {
+		AstChildren {
+			inner: parent.children(),
+			ph: PhantomData,
+		}
+	}
+}
+
+impl<N: AstNode> Iterator for AstChildren<N> {
+	type Item = N;
+	fn next(&mut self) -> Option<N> {
+		self.inner.find_map(N::cast)
+	}
+}
+
+pub mod support {
+	use super::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken};
+
+	pub fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
+		parent.children().find_map(N::cast)
+	}
+	pub fn token_child<N: AstToken>(parent: &SyntaxNode) -> Option<N> {
+		parent.children_with_tokens().find_map(|n| match n {
+			rowan::NodeOrToken::Node(_) => None,
+			rowan::NodeOrToken::Token(t) => N::cast(t),
+		})
+	}
+
+	pub fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
+		AstChildren::new(parent)
+	}
+
+	pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
+		parent
+			.children_with_tokens()
+			.filter_map(|it| it.into_token())
+			.find(|it| it.kind() == kind)
+	}
+}
modifiedcrates/jrsonnet-rowan-parser/src/binary.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/binary.rs
+++ b/crates/jrsonnet-rowan-parser/src/binary.rs
@@ -20,6 +20,7 @@
 	Or,
 	In,
 	ObjectApply,
+	#[allow(dead_code)]
 	Invalid,
 }
 
addedcrates/jrsonnet-rowan-parser/src/classify.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/classify.rs
@@ -0,0 +1,51 @@
+use crate::SyntaxKind;
+
+impl SyntaxKind {
+	pub fn is_trivia(self) -> bool {
+		matches!(
+			self,
+			Self::WHITESPACE
+				| Self::MULTI_LINE_COMMENT
+				| Self::ERROR_COMMENT_TOO_SHORT
+				| Self::ERROR_COMMENT_UNTERMINATED
+				| Self::SINGLE_LINE_HASH_COMMENT
+				| Self::SINGLE_LINE_SLASH_COMMENT
+		)
+	}
+	pub fn is_string(self) -> bool {
+		matches!(
+			self,
+			Self::STRING_SINGLE
+				| Self::ERROR_STRING_SINGLE_UNTERMINATED
+				| Self::STRING_DOUBLE
+				| Self::ERROR_STRING_DOUBLE_UNTERMINATED
+				| Self::STRING_SINGLE_VERBATIM
+				| Self::ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED
+				| Self::STRING_DOUBLE_VERBATIM
+				| Self::ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED
+				| Self::STRING_BLOCK
+				| Self::ERROR_STRING_BLOCK_UNEXPECTED_END
+				| Self::ERROR_STRING_BLOCK_MISSING_NEW_LINE
+				| Self::ERROR_STRING_BLOCK_MISSING_TERMINATION
+				| Self::ERROR_STRING_BLOCK_MISSING_INDENT
+		)
+	}
+	pub fn is_number(self) -> bool {
+		matches!(
+			self,
+			Self::FLOAT
+				| Self::ERROR_FLOAT_JUNK_AFTER_POINT
+				| Self::ERROR_FLOAT_JUNK_AFTER_EXPONENT
+				| Self::ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN
+		)
+	}
+	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
+		)
+	}
+}
modifiedcrates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/event.rs
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -1,6 +1,6 @@
 use std::mem;
 
-use rowan::{GreenNode, GreenNodeBuilder, Language};
+use rowan::{GreenNodeBuilder, Language};
 
 use crate::{
 	lex::Lexeme,
@@ -10,14 +10,27 @@
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Event {
+	/// Used for unfinished markers
+	Pending,
+	/// After marker is completed, Pending event is replaced with Start
 	Start {
 		kind: SyntaxKind,
+		/// If marker is preceded or wrapped - instead of reordering events, we
+		/// insert start event in the end of events Vec instead, and store relative offset to this event here
 		forward_parent: Option<usize>,
 	},
-	Token,
-	Finish,
-	Placeholder,
+	/// Eat token
+	Token {
+		kind: SyntaxKind,
+	},
+	/// Position of finished node
+	Finish {
+		/// Same as forward_parent of Start, but for wrapping
+		wrapper: Option<usize>,
+	},
 	Error(SyntaxError),
+	/// Used for dropped markers and other things
+	Noop,
 }
 
 pub(super) struct Sink<'i> {
@@ -40,12 +53,17 @@
 	}
 
 	pub(super) fn finish(mut self) -> Parse {
+		let mut eat_start_whitespace = false;
+		let mut depth = 0;
 		for idx in 0..self.events.len() {
-			match mem::replace(&mut self.events[idx], Event::Placeholder) {
+			match mem::replace(&mut self.events[idx], Event::Noop) {
 				Event::Start {
 					kind,
 					forward_parent,
 				} => {
+					if depth != 0 {
+						self.skip_whitespace();
+					}
 					let mut kinds = vec![kind];
 
 					let mut idx = idx;
@@ -60,7 +78,7 @@
 						forward_parent = if let Event::Start {
 							kind,
 							forward_parent,
-						} = mem::replace(&mut self.events[idx], Event::Placeholder)
+						} = mem::replace(&mut self.events[idx], Event::Noop)
 						{
 							kinds.push(kind);
 							forward_parent
@@ -71,18 +89,46 @@
 
 					for kind in kinds.into_iter().rev() {
 						self.builder.start_node(JsonnetLanguage::kind_to_raw(kind));
+						depth += 1;
+						if depth == 1 {
+							self.skip_whitespace();
+						}
+					}
+
+					eat_start_whitespace = false;
+				}
+				Event::Token { kind } => {
+					if eat_start_whitespace {
+						self.skip_whitespace();
 					}
+					self.token(kind);
+					eat_start_whitespace = true;
 				}
-				Event::Token => self.token(),
-				Event::Finish => {
+				Event::Finish { wrapper } => {
 					self.builder.finish_node();
+					depth -= 1;
+					let mut idx = idx;
+					let mut wrapper = wrapper;
+					while let Some(w) = wrapper {
+						idx += w;
+						wrapper = if let Event::Finish { wrapper } =
+							mem::replace(&mut self.events[idx], Event::Noop)
+						{
+							self.builder.finish_node();
+							depth -= 1;
+							wrapper
+						} else {
+							unreachable!()
+						}
+					}
+					eat_start_whitespace = true;
 				}
-				Event::Placeholder => {}
+				Event::Pending => panic!("placeholder should not end in events"),
+				Event::Noop => {}
 				Event::Error(e) => {
 					self.errors.push(e);
 				}
 			}
-			self.skip_whitespace();
 		}
 
 		Parse {
@@ -90,10 +136,10 @@
 			errors: self.errors,
 		}
 	}
-	fn token(&mut self) {
+	fn token(&mut self, kind: SyntaxKind) {
 		let lexeme = self.lexemes[self.offset];
 		self.builder
-			.token(JsonnetLanguage::kind_to_raw(lexeme.kind), lexeme.text);
+			.token(JsonnetLanguage::kind_to_raw(kind), lexeme.text);
 		self.offset += 1;
 	}
 	fn skip_whitespace(&mut self) {
@@ -102,7 +148,7 @@
 				break;
 			}
 
-			self.token();
+			self.token(lexeme.kind);
 		}
 	}
 }
modifiedcrates/jrsonnet-rowan-parser/src/generated/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/generated/mod.rs
+++ b/crates/jrsonnet-rowan-parser/src/generated/mod.rs
@@ -1,3 +1,2 @@
-// mod tokens;
-// mod nodes;
+pub mod nodes;
 pub mod syntax_kinds;
modifiedcrates/jrsonnet-rowan-parser/src/generated/nodes.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
+++ b/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
@@ -1,9 +1,9 @@
 //! 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)]
+#![allow(non_snake_case, clippy::match_like_matches_macro)]
 use crate::{
-	ast::{self, support, AstChildren, AstNode},
+	ast::{support, AstChildren, AstNode, AstToken},
 	SyntaxKind::{self, *},
 	SyntaxNode, SyntaxToken, T,
 };
@@ -13,7 +13,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl SourceFile {
-	pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -21,35 +23,25 @@
 	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) }
+	pub fn lhs(&self) -> Option<LhsExpr> {
+		support::child(&self.syntax)
+	}
+	pub fn binary_operator(&self) -> Option<BinaryOperator> {
+		support::token_child(&self.syntax)
+	}
+	pub fn rhs(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct BinaryOperator {
+pub struct LhsExpr {
 	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![%]) }
+impl LhsExpr {
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -57,29 +49,25 @@
 	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,
+	pub fn unary_operator(&self) -> Option<UnaryOperator> {
+		support::token_child(&self.syntax)
+	}
+	pub fn rhs(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
-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![']']) }
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
+	pub fn slice_desc(&self) -> Option<SliceDesc> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -87,10 +75,24 @@
 	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) }
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!['['])
+	}
+	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<SliceDescEnd> {
+		support::child(&self.syntax)
+	}
+	pub fn step(&self) -> Option<SliceDescStep> {
+		support::child(&self.syntax)
+	}
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![']'])
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -98,9 +100,15 @@
 	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) }
+	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)]
@@ -108,7 +116,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl Name {
-	pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+	pub fn ident_lit(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, IDENT)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -116,10 +126,18 @@
 	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![']']) }
+	pub fn base(&self) -> Option<LhsExpr> {
+		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)]
@@ -127,11 +145,13 @@
 	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> {
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
+	pub fn args_desc(&self) -> Option<ArgsDesc> {
+		support::child(&self.syntax)
+	}
+	pub fn tailstrict_kw_token(&self) -> Option<SyntaxToken> {
 		support::token(&self.syntax, T![tailstrict])
 	}
 }
@@ -141,7 +161,15 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl ArgsDesc {
-	pub fn args(&self) -> AstChildren<Arg> { support::children(&self.syntax) }
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!['('])
+	}
+	pub fn args(&self) -> AstChildren<Arg> {
+		support::children(&self.syntax)
+	}
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![')'])
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -149,10 +177,12 @@
 	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!['}']) }
+	pub fn lhs_expr(&self) -> Option<LhsExpr> {
+		support::child(&self.syntax)
+	}
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -160,9 +190,15 @@
 	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![')']) }
+	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)]
@@ -170,20 +206,9 @@
 	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]) }
+	pub fn literal(&self) -> Option<Literal> {
+		support::token_child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -214,9 +239,15 @@
 	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![')']) }
+	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)]
@@ -224,28 +255,8 @@
 	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])
+	pub fn string(&self) -> Option<String> {
+		support::token_child(&self.syntax)
 	}
 }
 
@@ -254,25 +265,25 @@
 	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,
+	pub fn number(&self) -> Option<Number> {
+		support::token_child(&self.syntax)
+	}
 }
-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![']']) }
+	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)]
@@ -280,9 +291,15 @@
 	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!['}']) }
+	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)]
@@ -290,38 +307,34 @@
 	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,
+	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 comp_specs(&self) -> AstChildren<CompSpec> {
+		support::children(&self.syntax)
+	}
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![']'])
+	}
 }
-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 import_kind(&self) -> Option<ImportKind> {
+		support::token_child(&self.syntax)
 	}
-	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 string(&self) -> Option<String> {
+		support::token_child(&self.syntax)
 	}
-	pub fn import_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![import]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -329,7 +342,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl ExprVar {
-	pub fn name(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn name(&self) -> Option<Name> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -337,9 +352,18 @@
 	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![;]) }
+	pub fn local_kw_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![;])
+	}
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -347,12 +371,44 @@
 	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) }
+	pub fn if_kw_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![if])
+	}
+	pub fn cond(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
+	pub fn then_kw_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![then])
+	}
+	pub fn then(&self) -> Option<TrueExpr> {
+		support::child(&self.syntax)
+	}
+	pub fn else_kw_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![else])
+	}
+	pub fn else_(&self) -> Option<FalseExpr> {
+		support::child(&self.syntax)
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TrueExpr {
+	pub(crate) syntax: SyntaxNode,
+}
+impl TrueExpr {
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FalseExpr {
+	pub(crate) syntax: SyntaxNode,
+}
+impl FalseExpr {
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -360,13 +416,21 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl ExprFunction {
-	pub fn function_token(&self) -> Option<SyntaxToken> {
+	pub fn function_kw_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) }
+	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)]
@@ -374,7 +438,15 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl ParamsDesc {
-	pub fn params(&self) -> AstChildren<Param> { support::children(&self.syntax) }
+	pub fn l_paren_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!['('])
+	}
+	pub fn params(&self) -> AstChildren<Param> {
+		support::children(&self.syntax)
+	}
+	pub fn r_paren_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![')'])
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -382,9 +454,15 @@
 	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) }
+	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)]
@@ -392,10 +470,18 @@
 	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) }
+	pub fn assert_kw_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![assert])
+	}
+	pub fn condition(&self) -> Option<LhsExpr> {
+		support::child(&self.syntax)
+	}
+	pub fn colon_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![:])
+	}
+	pub fn message(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -403,18 +489,48 @@
 	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) }
+	pub fn error_kw_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 SliceDescEnd {
+	pub(crate) syntax: SyntaxNode,
+}
+impl SliceDescEnd {
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SliceDescStep {
+	pub(crate) syntax: SyntaxNode,
+}
+impl SliceDescStep {
+	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) }
+	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)]
@@ -422,16 +538,33 @@
 	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) }
+	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<LhsExpr> {
+		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 comp_specs(&self) -> AstChildren<CompSpec> {
+		support::children(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -439,8 +572,12 @@
 	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![,]) }
+	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)]
@@ -448,8 +585,12 @@
 	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) }
+	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)]
@@ -457,8 +598,9 @@
 	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![,]) }
+	pub fn members(&self) -> AstChildren<Member> {
+		support::children(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -466,8 +608,12 @@
 	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) }
+	pub fn local_kw_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)]
@@ -475,7 +621,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl MemberBindStmt {
-	pub fn obj_local(&self) -> Option<ObjLocal> { support::child(&self.syntax) }
+	pub fn obj_local(&self) -> Option<ObjLocal> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -483,7 +631,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl MemberAssertStmt {
-	pub fn assertion(&self) -> Option<Assertion> { support::child(&self.syntax) }
+	pub fn assertion(&self) -> Option<Assertion> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -491,7 +641,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl MemberField {
-	pub fn field(&self) -> Option<Field> { support::child(&self.syntax) }
+	pub fn field(&self) -> Option<Field> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -499,22 +651,18 @@
 	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 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::token_child(&self.syntax)
+	}
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
 	}
-	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)]
@@ -522,12 +670,18 @@
 	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) }
+	pub fn field_name(&self) -> Option<FieldName> {
+		support::child(&self.syntax)
+	}
+	pub fn params_desc(&self) -> Option<ParamsDesc> {
+		support::child(&self.syntax)
+	}
+	pub fn visibility(&self) -> Option<Visibility> {
+		support::token_child(&self.syntax)
+	}
+	pub fn expr(&self) -> Option<Expr> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -535,8 +689,12 @@
 	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) }
+	pub fn id(&self) -> Option<Name> {
+		support::child(&self.syntax)
+	}
+	pub fn string(&self) -> Option<String> {
+		support::token_child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -544,39 +702,63 @@
 	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![']']) }
+	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 struct ForSpec {
 	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) }
+impl ForSpec {
+	pub fn for_kw_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![for])
+	}
+	pub fn bind(&self) -> Option<Name> {
+		support::child(&self.syntax)
+	}
+	pub fn in_kw_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 BindDestruct {
+pub struct IfSpec {
 	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) }
+impl IfSpec {
+	pub fn if_kw_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 Destruct {
+pub struct BindDestruct {
 	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) }
+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)]
@@ -584,12 +766,18 @@
 	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) }
+	pub fn name(&self) -> Option<Name> {
+		support::child(&self.syntax)
+	}
+	pub fn params(&self) -> Option<ParamsDesc> {
+		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)]
@@ -597,9 +785,15 @@
 	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) }
+	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)]
@@ -607,7 +801,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl DestructFull {
-	pub fn into(&self) -> Option<Name> { support::child(&self.syntax) }
+	pub fn name(&self) -> Option<Name> {
+		support::child(&self.syntax)
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -615,7 +811,9 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl DestructSkip {
-	pub fn question_mark_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![?]) }
+	pub fn question_mark_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![?])
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -623,21 +821,15 @@
 	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) }
+	pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T!['['])
+	}
+	pub fn destruct_array_parts(&self) -> AstChildren<DestructArrayPart> {
+		support::children(&self.syntax)
+	}
+	pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+		support::token(&self.syntax, T![']'])
+	}
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -645,13 +837,21 @@
 	pub(crate) syntax: SyntaxNode,
 }
 impl DestructObject {
-	pub fn l_brace_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
+	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!['}']) }
+	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)]
@@ -659,12 +859,45 @@
 	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) }
+	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 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 DestructArrayElement {
+	pub(crate) syntax: SyntaxNode,
 }
+impl DestructArrayElement {
+	pub fn destruct(&self) -> Option<Destruct> {
+		support::child(&self.syntax)
+	}
+}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum Expr {
@@ -681,6 +914,7 @@
 	ExprIntrinsic(ExprIntrinsic),
 	ExprString(ExprString),
 	ExprNumber(ExprNumber),
+	ExprLiteral(ExprLiteral),
 	ExprArray(ExprArray),
 	ExprObject(ExprObject),
 	ExprArrayComp(ExprArrayComp),
@@ -729,8 +963,136 @@
 	FieldNameFixed(FieldNameFixed),
 	FieldNameDynamic(FieldNameDynamic),
 }
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Destruct {
+	DestructFull(DestructFull),
+	DestructSkip(DestructSkip),
+	DestructArray(DestructArray),
+	DestructObject(DestructObject),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum DestructArrayPart {
+	DestructArrayElement(DestructArrayElement),
+	DestructRest(DestructRest),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct BinaryOperator {
+	syntax: SyntaxToken,
+	kind: BinaryOperatorKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum BinaryOperatorKind {
+	Or,
+	And,
+	BitOr,
+	BitXor,
+	BitAnd,
+	Eq,
+	Ne,
+	Lt,
+	Gt,
+	Le,
+	Ge,
+	InKw,
+	Lhs,
+	Rhs,
+	Plus,
+	Minus,
+	Mul,
+	Div,
+	Modulo,
+	ErrorNoOperator,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UnaryOperator {
+	syntax: SyntaxToken,
+	kind: UnaryOperatorKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum UnaryOperatorKind {
+	Minus,
+	Not,
+	BitNot,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Literal {
+	syntax: SyntaxToken,
+	kind: LiteralKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum LiteralKind {
+	NullKw,
+	TrueKw,
+	FalseKw,
+	SelfKw,
+	Dollar,
+	SuperKw,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct String {
+	syntax: SyntaxToken,
+	kind: StringKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum StringKind {
+	StringDouble,
+	StringSingle,
+	StringDoubleVerbatim,
+	StringSingleVerbatim,
+	StringBlock,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Number {
+	syntax: SyntaxToken,
+	kind: NumberKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum NumberKind {
+	Float,
+	MetaForceEnum,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ImportKind {
+	syntax: SyntaxToken,
+	kind: ImportKindKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ImportKindKind {
+	ImportstrKw,
+	ImportbinKw,
+	ImportKw,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Visibility {
+	syntax: SyntaxToken,
+	kind: VisibilityKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum VisibilityKind {
+	Coloncoloncolon,
+	Coloncolon,
+	Colon,
+}
 impl AstNode for SourceFile {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == SOURCE_FILE
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -738,10 +1100,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprBinary {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_BINARY }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_BINARY
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -749,10 +1115,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for BinaryOperator {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == BINARY_OPERATOR }
+impl AstNode for LhsExpr {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == LHS_EXPR
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -760,10 +1130,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprUnary {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_UNARY }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_UNARY
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -771,21 +1145,14 @@
 			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
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ExprSlice {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_SLICE }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_SLICE
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -793,10 +1160,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for SliceDesc {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_DESC }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == SLICE_DESC
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -804,10 +1175,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprIndex {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INDEX }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_INDEX
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -815,10 +1190,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for Name {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == NAME }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == NAME
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -826,10 +1205,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprIndexExpr {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INDEX_EXPR }
+	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 })
@@ -837,10 +1220,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprApply {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_APPLY }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_APPLY
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -848,10 +1235,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ArgsDesc {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == ARGS_DESC }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == ARGS_DESC
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -859,10 +1250,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprObjExtend {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_OBJ_EXTEND }
+	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 })
@@ -870,10 +1265,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprParened {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_PARENED }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_PARENED
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -881,10 +1280,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ExprLiteral {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_LITERAL }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == EXPR_LITERAL
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -892,10 +1295,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Literal {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL }
+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 })
@@ -903,10 +1310,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprIntrinsicThisFile {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC_THIS_FILE }
+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 })
@@ -914,10 +1325,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprIntrinsicId {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC_ID }
+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 })
@@ -925,10 +1340,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprIntrinsic {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_INTRINSIC }
+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 })
@@ -936,10 +1355,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprString {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STRING }
+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 })
@@ -947,10 +1370,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for String {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == STRING }
+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 })
@@ -958,10 +1385,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprNumber {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_NUMBER }
+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 })
@@ -969,10 +1400,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Number {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == NUMBER }
+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 })
@@ -980,10 +1415,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprArray {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ARRAY }
+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 })
@@ -991,10 +1430,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprObject {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_OBJECT }
+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 })
@@ -1002,10 +1445,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprArrayComp {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ARRAY_COMP }
+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 })
@@ -1013,10 +1460,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ForSpec {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_SPEC }
+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 })
@@ -1024,10 +1475,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprImport {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_IMPORT }
+impl AstNode for TrueExpr {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == TRUE_EXPR
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1035,10 +1490,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprVar {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_VAR }
+impl AstNode for FalseExpr {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == FALSE_EXPR
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1046,10 +1505,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprLocal {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_LOCAL }
+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 })
@@ -1057,10 +1520,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprIfThenElse {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_IF_THEN_ELSE }
+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 })
@@ -1068,10 +1535,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprFunction {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_FUNCTION }
+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 })
@@ -1079,10 +1550,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ParamsDesc {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == PARAMS_DESC }
+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 })
@@ -1090,10 +1565,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprAssert {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ASSERT }
+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 })
@@ -1101,10 +1580,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Assertion {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == ASSERTION }
+impl AstNode for SliceDescEnd {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == SLICE_DESC_END
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1112,10 +1595,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for ExprError {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_ERROR }
+impl AstNode for SliceDescStep {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == SLICE_DESC_STEP
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1123,10 +1610,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for Arg {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == ARG }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == ARG
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1134,10 +1625,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ObjBodyComp {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_BODY_COMP }
+	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 })
@@ -1145,10 +1640,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ObjLocalPostComma {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL_POST_COMMA }
+	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 })
@@ -1156,10 +1655,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ObjLocalPreComma {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL_PRE_COMMA }
+	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 })
@@ -1167,10 +1670,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ObjBodyMemberList {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_BODY_MEMBER_LIST }
+	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 })
@@ -1178,10 +1685,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for ObjLocal {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == OBJ_LOCAL }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == OBJ_LOCAL
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1189,10 +1700,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for MemberBindStmt {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_BIND_STMT }
+	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 })
@@ -1200,10 +1715,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for MemberAssertStmt {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_ASSERT_STMT }
+	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 })
@@ -1211,10 +1730,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for MemberField {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == MEMBER_FIELD }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == MEMBER_FIELD
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1222,10 +1745,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for FieldNormal {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NORMAL }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == FIELD_NORMAL
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1233,10 +1760,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Visibility {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY }
+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 })
@@ -1244,10 +1775,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for FieldMethod {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_METHOD }
+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 })
@@ -1255,10 +1790,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for FieldNameFixed {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NAME_FIXED }
+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 })
@@ -1266,10 +1805,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for FieldNameDynamic {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_NAME_DYNAMIC }
+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 })
@@ -1277,10 +1820,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for IfSpec {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == IF_SPEC }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == IF_SPEC
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1288,10 +1835,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl AstNode for BindDestruct {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == BIND_DESTRUCT }
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == BIND_DESTRUCT
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1299,10 +1850,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Destruct {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT }
+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 })
@@ -1310,10 +1865,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for BindFunction {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == BIND_FUNCTION }
+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 })
@@ -1321,10 +1880,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for Param {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM }
+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 })
@@ -1332,10 +1895,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructFull {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_FULL }
+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 })
@@ -1343,10 +1910,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructSkip {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_SKIP }
+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 })
@@ -1354,10 +1925,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructArray {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_ARRAY }
+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 })
@@ -1365,10 +1940,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructRest {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_REST }
+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 })
@@ -1376,10 +1955,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructObject {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_OBJECT }
+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 })
@@ -1387,10 +1970,14 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
-impl AstNode for DestructObjectField {
-	fn can_cast(kind: SyntaxKind) -> bool { kind == DESTRUCT_OBJECT_FIELD }
+impl AstNode for DestructArrayElement {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		kind == DESTRUCT_ARRAY_ELEMENT
+	}
 	fn cast(syntax: SyntaxNode) -> Option<Self> {
 		if Self::can_cast(syntax.kind()) {
 			Some(Self { syntax })
@@ -1398,76 +1985,129 @@
 			None
 		}
 	}
-	fn syntax(&self) -> &SyntaxNode { &self.syntax }
+	fn syntax(&self) -> &SyntaxNode {
+		&self.syntax
+	}
 }
 impl From<ExprBinary> for Expr {
-	fn from(node: ExprBinary) -> Expr { Expr::ExprBinary(node) }
+	fn from(node: ExprBinary) -> Expr {
+		Expr::ExprBinary(node)
+	}
 }
 impl From<ExprUnary> for Expr {
-	fn from(node: ExprUnary) -> Expr { Expr::ExprUnary(node) }
+	fn from(node: ExprUnary) -> Expr {
+		Expr::ExprUnary(node)
+	}
 }
 impl From<ExprSlice> for Expr {
-	fn from(node: ExprSlice) -> Expr { Expr::ExprSlice(node) }
+	fn from(node: ExprSlice) -> Expr {
+		Expr::ExprSlice(node)
+	}
 }
 impl From<ExprIndex> for Expr {
-	fn from(node: ExprIndex) -> Expr { Expr::ExprIndex(node) }
+	fn from(node: ExprIndex) -> Expr {
+		Expr::ExprIndex(node)
+	}
 }
 impl From<ExprIndexExpr> for Expr {
-	fn from(node: ExprIndexExpr) -> Expr { Expr::ExprIndexExpr(node) }
+	fn from(node: ExprIndexExpr) -> Expr {
+		Expr::ExprIndexExpr(node)
+	}
 }
 impl From<ExprApply> for Expr {
-	fn from(node: ExprApply) -> Expr { Expr::ExprApply(node) }
+	fn from(node: ExprApply) -> Expr {
+		Expr::ExprApply(node)
+	}
 }
 impl From<ExprObjExtend> for Expr {
-	fn from(node: ExprObjExtend) -> Expr { Expr::ExprObjExtend(node) }
+	fn from(node: ExprObjExtend) -> Expr {
+		Expr::ExprObjExtend(node)
+	}
 }
 impl From<ExprParened> for Expr {
-	fn from(node: ExprParened) -> Expr { Expr::ExprParened(node) }
+	fn from(node: ExprParened) -> Expr {
+		Expr::ExprParened(node)
+	}
 }
 impl From<ExprIntrinsicThisFile> for Expr {
-	fn from(node: ExprIntrinsicThisFile) -> Expr { Expr::ExprIntrinsicThisFile(node) }
+	fn from(node: ExprIntrinsicThisFile) -> Expr {
+		Expr::ExprIntrinsicThisFile(node)
+	}
 }
 impl From<ExprIntrinsicId> for Expr {
-	fn from(node: ExprIntrinsicId) -> Expr { Expr::ExprIntrinsicId(node) }
+	fn from(node: ExprIntrinsicId) -> Expr {
+		Expr::ExprIntrinsicId(node)
+	}
 }
 impl From<ExprIntrinsic> for Expr {
-	fn from(node: ExprIntrinsic) -> Expr { Expr::ExprIntrinsic(node) }
+	fn from(node: ExprIntrinsic) -> Expr {
+		Expr::ExprIntrinsic(node)
+	}
 }
 impl From<ExprString> for Expr {
-	fn from(node: ExprString) -> Expr { Expr::ExprString(node) }
+	fn from(node: ExprString) -> Expr {
+		Expr::ExprString(node)
+	}
 }
 impl From<ExprNumber> for Expr {
-	fn from(node: ExprNumber) -> Expr { Expr::ExprNumber(node) }
+	fn from(node: ExprNumber) -> Expr {
+		Expr::ExprNumber(node)
+	}
 }
+impl From<ExprLiteral> for Expr {
+	fn from(node: ExprLiteral) -> Expr {
+		Expr::ExprLiteral(node)
+	}
+}
 impl From<ExprArray> for Expr {
-	fn from(node: ExprArray) -> Expr { Expr::ExprArray(node) }
+	fn from(node: ExprArray) -> Expr {
+		Expr::ExprArray(node)
+	}
 }
 impl From<ExprObject> for Expr {
-	fn from(node: ExprObject) -> Expr { Expr::ExprObject(node) }
+	fn from(node: ExprObject) -> Expr {
+		Expr::ExprObject(node)
+	}
 }
 impl From<ExprArrayComp> for Expr {
-	fn from(node: ExprArrayComp) -> Expr { Expr::ExprArrayComp(node) }
+	fn from(node: ExprArrayComp) -> Expr {
+		Expr::ExprArrayComp(node)
+	}
 }
 impl From<ExprImport> for Expr {
-	fn from(node: ExprImport) -> Expr { Expr::ExprImport(node) }
+	fn from(node: ExprImport) -> Expr {
+		Expr::ExprImport(node)
+	}
 }
 impl From<ExprVar> for Expr {
-	fn from(node: ExprVar) -> Expr { Expr::ExprVar(node) }
+	fn from(node: ExprVar) -> Expr {
+		Expr::ExprVar(node)
+	}
 }
 impl From<ExprLocal> for Expr {
-	fn from(node: ExprLocal) -> Expr { Expr::ExprLocal(node) }
+	fn from(node: ExprLocal) -> Expr {
+		Expr::ExprLocal(node)
+	}
 }
 impl From<ExprIfThenElse> for Expr {
-	fn from(node: ExprIfThenElse) -> Expr { Expr::ExprIfThenElse(node) }
+	fn from(node: ExprIfThenElse) -> Expr {
+		Expr::ExprIfThenElse(node)
+	}
 }
 impl From<ExprFunction> for Expr {
-	fn from(node: ExprFunction) -> Expr { Expr::ExprFunction(node) }
+	fn from(node: ExprFunction) -> Expr {
+		Expr::ExprFunction(node)
+	}
 }
 impl From<ExprAssert> for Expr {
-	fn from(node: ExprAssert) -> Expr { Expr::ExprAssert(node) }
+	fn from(node: ExprAssert) -> Expr {
+		Expr::ExprAssert(node)
+	}
 }
 impl From<ExprError> for Expr {
-	fn from(node: ExprError) -> Expr { Expr::ExprError(node) }
+	fn from(node: ExprError) -> Expr {
+		Expr::ExprError(node)
+	}
 }
 impl AstNode for Expr {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1485,6 +2125,7 @@
 			| EXPR_INTRINSIC
 			| EXPR_STRING
 			| EXPR_NUMBER
+			| EXPR_LITERAL
 			| EXPR_ARRAY
 			| EXPR_OBJECT
 			| EXPR_ARRAY_COMP
@@ -1515,6 +2156,7 @@
 			EXPR_INTRINSIC => Expr::ExprIntrinsic(ExprIntrinsic { syntax }),
 			EXPR_STRING => Expr::ExprString(ExprString { syntax }),
 			EXPR_NUMBER => Expr::ExprNumber(ExprNumber { syntax }),
+			EXPR_LITERAL => Expr::ExprLiteral(ExprLiteral { syntax }),
 			EXPR_ARRAY => Expr::ExprArray(ExprArray { syntax }),
 			EXPR_OBJECT => Expr::ExprObject(ExprObject { syntax }),
 			EXPR_ARRAY_COMP => Expr::ExprArrayComp(ExprArrayComp { syntax }),
@@ -1544,6 +2186,7 @@
 			Expr::ExprIntrinsic(it) => &it.syntax,
 			Expr::ExprString(it) => &it.syntax,
 			Expr::ExprNumber(it) => &it.syntax,
+			Expr::ExprLiteral(it) => &it.syntax,
 			Expr::ExprArray(it) => &it.syntax,
 			Expr::ExprObject(it) => &it.syntax,
 			Expr::ExprArrayComp(it) => &it.syntax,
@@ -1558,10 +2201,14 @@
 	}
 }
 impl From<ObjBodyComp> for ObjBody {
-	fn from(node: ObjBodyComp) -> ObjBody { ObjBody::ObjBodyComp(node) }
+	fn from(node: ObjBodyComp) -> ObjBody {
+		ObjBody::ObjBodyComp(node)
+	}
 }
 impl From<ObjBodyMemberList> for ObjBody {
-	fn from(node: ObjBodyMemberList) -> ObjBody { ObjBody::ObjBodyMemberList(node) }
+	fn from(node: ObjBodyMemberList) -> ObjBody {
+		ObjBody::ObjBodyMemberList(node)
+	}
 }
 impl AstNode for ObjBody {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1586,10 +2233,14 @@
 	}
 }
 impl From<ForSpec> for CompSpec {
-	fn from(node: ForSpec) -> CompSpec { CompSpec::ForSpec(node) }
+	fn from(node: ForSpec) -> CompSpec {
+		CompSpec::ForSpec(node)
+	}
 }
 impl From<IfSpec> for CompSpec {
-	fn from(node: IfSpec) -> CompSpec { CompSpec::IfSpec(node) }
+	fn from(node: IfSpec) -> CompSpec {
+		CompSpec::IfSpec(node)
+	}
 }
 impl AstNode for CompSpec {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1614,10 +2265,14 @@
 	}
 }
 impl From<BindDestruct> for Bind {
-	fn from(node: BindDestruct) -> Bind { Bind::BindDestruct(node) }
+	fn from(node: BindDestruct) -> Bind {
+		Bind::BindDestruct(node)
+	}
 }
 impl From<BindFunction> for Bind {
-	fn from(node: BindFunction) -> Bind { Bind::BindFunction(node) }
+	fn from(node: BindFunction) -> Bind {
+		Bind::BindFunction(node)
+	}
 }
 impl AstNode for Bind {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1642,13 +2297,19 @@
 	}
 }
 impl From<MemberBindStmt> for Member {
-	fn from(node: MemberBindStmt) -> Member { Member::MemberBindStmt(node) }
+	fn from(node: MemberBindStmt) -> Member {
+		Member::MemberBindStmt(node)
+	}
 }
 impl From<MemberAssertStmt> for Member {
-	fn from(node: MemberAssertStmt) -> Member { Member::MemberAssertStmt(node) }
+	fn from(node: MemberAssertStmt) -> Member {
+		Member::MemberAssertStmt(node)
+	}
 }
 impl From<MemberField> for Member {
-	fn from(node: MemberField) -> Member { Member::MemberField(node) }
+	fn from(node: MemberField) -> Member {
+		Member::MemberField(node)
+	}
 }
 impl AstNode for Member {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1675,10 +2336,14 @@
 	}
 }
 impl From<FieldNormal> for Field {
-	fn from(node: FieldNormal) -> Field { Field::FieldNormal(node) }
+	fn from(node: FieldNormal) -> Field {
+		Field::FieldNormal(node)
+	}
 }
 impl From<FieldMethod> for Field {
-	fn from(node: FieldMethod) -> Field { Field::FieldMethod(node) }
+	fn from(node: FieldMethod) -> Field {
+		Field::FieldMethod(node)
+	}
 }
 impl AstNode for Field {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1703,10 +2368,14 @@
 	}
 }
 impl From<FieldNameFixed> for FieldName {
-	fn from(node: FieldNameFixed) -> FieldName { FieldName::FieldNameFixed(node) }
+	fn from(node: FieldNameFixed) -> FieldName {
+		FieldName::FieldNameFixed(node)
+	}
 }
 impl From<FieldNameDynamic> for FieldName {
-	fn from(node: FieldNameDynamic) -> FieldName { FieldName::FieldNameDynamic(node) }
+	fn from(node: FieldNameDynamic) -> FieldName {
+		FieldName::FieldNameDynamic(node)
+	}
 }
 impl AstNode for FieldName {
 	fn can_cast(kind: SyntaxKind) -> bool {
@@ -1730,6 +2399,448 @@
 		}
 	}
 }
+impl From<DestructFull> for Destruct {
+	fn from(node: DestructFull) -> Destruct {
+		Destruct::DestructFull(node)
+	}
+}
+impl From<DestructSkip> for Destruct {
+	fn from(node: DestructSkip) -> Destruct {
+		Destruct::DestructSkip(node)
+	}
+}
+impl From<DestructArray> for Destruct {
+	fn from(node: DestructArray) -> Destruct {
+		Destruct::DestructArray(node)
+	}
+}
+impl From<DestructObject> for Destruct {
+	fn from(node: DestructObject) -> Destruct {
+		Destruct::DestructObject(node)
+	}
+}
+impl AstNode for Destruct {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			DESTRUCT_FULL | DESTRUCT_SKIP | DESTRUCT_ARRAY | DESTRUCT_OBJECT => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			DESTRUCT_FULL => Destruct::DestructFull(DestructFull { syntax }),
+			DESTRUCT_SKIP => Destruct::DestructSkip(DestructSkip { syntax }),
+			DESTRUCT_ARRAY => Destruct::DestructArray(DestructArray { syntax }),
+			DESTRUCT_OBJECT => Destruct::DestructObject(DestructObject { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			Destruct::DestructFull(it) => &it.syntax,
+			Destruct::DestructSkip(it) => &it.syntax,
+			Destruct::DestructArray(it) => &it.syntax,
+			Destruct::DestructObject(it) => &it.syntax,
+		}
+	}
+}
+impl From<DestructArrayElement> for DestructArrayPart {
+	fn from(node: DestructArrayElement) -> DestructArrayPart {
+		DestructArrayPart::DestructArrayElement(node)
+	}
+}
+impl From<DestructRest> for DestructArrayPart {
+	fn from(node: DestructRest) -> DestructArrayPart {
+		DestructArrayPart::DestructRest(node)
+	}
+}
+impl AstNode for DestructArrayPart {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			DESTRUCT_ARRAY_ELEMENT | DESTRUCT_REST => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxNode) -> Option<Self> {
+		let res = match syntax.kind() {
+			DESTRUCT_ARRAY_ELEMENT => {
+				DestructArrayPart::DestructArrayElement(DestructArrayElement { syntax })
+			}
+			DESTRUCT_REST => DestructArrayPart::DestructRest(DestructRest { syntax }),
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxNode {
+		match self {
+			DestructArrayPart::DestructArrayElement(it) => &it.syntax,
+			DestructArrayPart::DestructRest(it) => &it.syntax,
+		}
+	}
+}
+impl AstToken for BinaryOperator {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			OR | AND | BIT_OR | BIT_XOR | BIT_AND | EQ | NE | LT | GT | LE | GE | IN_KW | LHS
+			| RHS | PLUS | MINUS | MUL | DIV | MODULO | ERROR_NO_OPERATOR => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			OR => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Or,
+			},
+			AND => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::And,
+			},
+			BIT_OR => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::BitOr,
+			},
+			BIT_XOR => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::BitXor,
+			},
+			BIT_AND => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::BitAnd,
+			},
+			EQ => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Eq,
+			},
+			NE => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Ne,
+			},
+			LT => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Lt,
+			},
+			GT => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Gt,
+			},
+			LE => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Le,
+			},
+			GE => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Ge,
+			},
+			IN_KW => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::InKw,
+			},
+			LHS => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Lhs,
+			},
+			RHS => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Rhs,
+			},
+			PLUS => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Plus,
+			},
+			MINUS => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Minus,
+			},
+			MUL => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Mul,
+			},
+			DIV => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Div,
+			},
+			MODULO => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::Modulo,
+			},
+			ERROR_NO_OPERATOR => BinaryOperator {
+				syntax,
+				kind: BinaryOperatorKind::ErrorNoOperator,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl BinaryOperator {
+	pub fn kind(&self) -> BinaryOperatorKind {
+		self.kind
+	}
+}
+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 AstToken for UnaryOperator {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			MINUS | NOT | BIT_NOT => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			MINUS => UnaryOperator {
+				syntax,
+				kind: UnaryOperatorKind::Minus,
+			},
+			NOT => UnaryOperator {
+				syntax,
+				kind: UnaryOperatorKind::Not,
+			},
+			BIT_NOT => UnaryOperator {
+				syntax,
+				kind: UnaryOperatorKind::BitNot,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl UnaryOperator {
+	pub fn kind(&self) -> UnaryOperatorKind {
+		self.kind
+	}
+}
+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 AstToken for Literal {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			NULL_KW | TRUE_KW | FALSE_KW | SELF_KW | DOLLAR | SUPER_KW => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			NULL_KW => Literal {
+				syntax,
+				kind: LiteralKind::NullKw,
+			},
+			TRUE_KW => Literal {
+				syntax,
+				kind: LiteralKind::TrueKw,
+			},
+			FALSE_KW => Literal {
+				syntax,
+				kind: LiteralKind::FalseKw,
+			},
+			SELF_KW => Literal {
+				syntax,
+				kind: LiteralKind::SelfKw,
+			},
+			DOLLAR => Literal {
+				syntax,
+				kind: LiteralKind::Dollar,
+			},
+			SUPER_KW => Literal {
+				syntax,
+				kind: LiteralKind::SuperKw,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl Literal {
+	pub fn kind(&self) -> LiteralKind {
+		self.kind
+	}
+}
+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 AstToken for String {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			STRING_DOUBLE
+			| STRING_SINGLE
+			| STRING_DOUBLE_VERBATIM
+			| STRING_SINGLE_VERBATIM
+			| STRING_BLOCK => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			STRING_DOUBLE => String {
+				syntax,
+				kind: StringKind::StringDouble,
+			},
+			STRING_SINGLE => String {
+				syntax,
+				kind: StringKind::StringSingle,
+			},
+			STRING_DOUBLE_VERBATIM => String {
+				syntax,
+				kind: StringKind::StringDoubleVerbatim,
+			},
+			STRING_SINGLE_VERBATIM => String {
+				syntax,
+				kind: StringKind::StringSingleVerbatim,
+			},
+			STRING_BLOCK => String {
+				syntax,
+				kind: StringKind::StringBlock,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl String {
+	pub fn kind(&self) -> StringKind {
+		self.kind
+	}
+}
+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 Number {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			FLOAT | META_FORCE_ENUM => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			FLOAT => Number {
+				syntax,
+				kind: NumberKind::Float,
+			},
+			META_FORCE_ENUM => Number {
+				syntax,
+				kind: NumberKind::MetaForceEnum,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl Number {
+	pub fn kind(&self) -> NumberKind {
+		self.kind
+	}
+}
+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 ImportKind {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			IMPORTSTR_KW | IMPORTBIN_KW | IMPORT_KW => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			IMPORTSTR_KW => ImportKind {
+				syntax,
+				kind: ImportKindKind::ImportstrKw,
+			},
+			IMPORTBIN_KW => ImportKind {
+				syntax,
+				kind: ImportKindKind::ImportbinKw,
+			},
+			IMPORT_KW => ImportKind {
+				syntax,
+				kind: ImportKindKind::ImportKw,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl ImportKind {
+	pub fn kind(&self) -> ImportKindKind {
+		self.kind
+	}
+}
+impl std::fmt::Display for ImportKind {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl AstToken for Visibility {
+	fn can_cast(kind: SyntaxKind) -> bool {
+		match kind {
+			COLONCOLONCOLON | COLONCOLON | COLON => true,
+			_ => false,
+		}
+	}
+	fn cast(syntax: SyntaxToken) -> Option<Self> {
+		let res = match syntax.kind() {
+			COLONCOLONCOLON => Visibility {
+				syntax,
+				kind: VisibilityKind::Coloncoloncolon,
+			},
+			COLONCOLON => Visibility {
+				syntax,
+				kind: VisibilityKind::Coloncolon,
+			},
+			COLON => Visibility {
+				syntax,
+				kind: VisibilityKind::Colon,
+			},
+			_ => return None,
+		};
+		Some(res)
+	}
+	fn syntax(&self) -> &SyntaxToken {
+		&self.syntax
+	}
+}
+impl Visibility {
+	pub fn kind(&self) -> VisibilityKind {
+		self.kind
+	}
+}
+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 Expr {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
@@ -1765,31 +2876,36 @@
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for SourceFile {
+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 ExprBinary {
+impl std::fmt::Display for DestructArrayPart {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for BinaryOperator {
+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 ExprUnary {
+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 UnaryOperator {
+impl std::fmt::Display for LhsExpr {
 	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 ExprSlice {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
@@ -1840,11 +2956,6 @@
 		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)
@@ -1861,11 +2972,6 @@
 	}
 }
 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)
 	}
@@ -1875,47 +2981,47 @@
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for Number {
+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 ExprArray {
+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 ExprObject {
+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 ExprArrayComp {
+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 ForSpec {
+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 ExprImport {
+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 ExprVar {
+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 ExprLocal {
+impl std::fmt::Display for TrueExpr {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for ExprIfThenElse {
+impl std::fmt::Display for FalseExpr {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
@@ -1945,6 +3051,16 @@
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
+impl std::fmt::Display for SliceDescEnd {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		std::fmt::Display::fmt(self.syntax(), f)
+	}
+}
+impl std::fmt::Display for SliceDescStep {
+	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)
@@ -1991,11 +3107,6 @@
 	}
 }
 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)
 	}
@@ -2015,17 +3126,17 @@
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for IfSpec {
+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 BindDestruct {
+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 Destruct {
+impl std::fmt::Display for BindDestruct {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
@@ -2055,17 +3166,22 @@
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
 }
-impl std::fmt::Display for DestructRest {
+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)
 	}
 }
-impl std::fmt::Display for DestructObject {
+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 DestructObjectField {
+impl std::fmt::Display for DestructArrayElement {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		std::fmt::Display::fmt(self.syntax(), f)
 	}
modifiedcrates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs
+++ b/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs
@@ -1,7 +1,13 @@
 //! 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)]
+#![allow(
+	bad_style,
+	missing_docs,
+	unreachable_pub,
+	clippy::manual_non_exhaustive,
+	clippy::match_like_matches_macro
+)]
 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)]
@@ -89,6 +95,52 @@
 	INTRINSIC_ID,
 	#[token("$intrinsic")]
 	INTRINSIC,
+	#[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?")]
+	FLOAT,
+	#[regex("(?:0|[1-9][0-9]*)\\.[^0-9]")]
+	ERROR_FLOAT_JUNK_AFTER_POINT,
+	#[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?[eE][^+\\-0-9]")]
+	ERROR_FLOAT_JUNK_AFTER_EXPONENT,
+	#[regex("(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?[eE][+-][^0-9]")]
+	ERROR_FLOAT_JUNK_AFTER_EXPONENT_SIGN,
+	#[regex("\"(?s:[^\"\\\\]|\\\\.)*\"")]
+	STRING_DOUBLE,
+	#[regex("\"(?s:[^\"\\\\]|\\\\.)*")]
+	ERROR_STRING_DOUBLE_UNTERMINATED,
+	#[regex("'(?s:[^'\\\\]|\\\\.)*'")]
+	STRING_SINGLE,
+	#[regex("'(?s:[^'\\\\]|\\\\.)*")]
+	ERROR_STRING_SINGLE_UNTERMINATED,
+	#[regex("@\"(?:[^\"]|\"\")*\"")]
+	STRING_DOUBLE_VERBATIM,
+	#[regex("@\"(?:[^\"]|\"\")*")]
+	ERROR_STRING_DOUBLE_VERBATIM_UNTERMINATED,
+	#[regex("@'(?:[^']|'')*'")]
+	STRING_SINGLE_VERBATIM,
+	#[regex("@'(?:[^']|'')*")]
+	ERROR_STRING_SINGLE_VERBATIM_UNTERMINATED,
+	#[regex("@[^\"'\\s]\\S+")]
+	ERROR_STRING_VERBATIM_MISSING_QUOTES,
+	#[regex("\\|\\|\\|", crate::string_block::lex_str_block_test)]
+	STRING_BLOCK,
+	ERROR_STRING_BLOCK_UNEXPECTED_END,
+	ERROR_STRING_BLOCK_MISSING_NEW_LINE,
+	ERROR_STRING_BLOCK_MISSING_TERMINATION,
+	ERROR_STRING_BLOCK_MISSING_INDENT,
+	#[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,
+	#[regex("/\\*/")]
+	ERROR_COMMENT_TOO_SHORT,
+	#[regex("/\\*([^*]|\\*[^/])+")]
+	ERROR_COMMENT_UNTERMINATED,
 	#[token("tailstrict")]
 	TAILSTRICT_KW,
 	#[token("importstr")]
@@ -111,6 +163,7 @@
 	ERROR_KW,
 	#[token("in")]
 	IN_KW,
+	ERROR_NO_OPERATOR,
 	#[token("null")]
 	NULL_KW,
 	#[token("true")]
@@ -121,39 +174,17 @@
 	SELF_KW,
 	#[token("super")]
 	SUPER_KW,
+	META_FORCE_ENUM,
 	#[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,
+	LHS_EXPR,
 	EXPR_UNARY,
-	UNARY_OPERATOR,
 	EXPR_SLICE,
 	SLICE_DESC,
 	EXPR_INDEX,
@@ -164,26 +195,27 @@
 	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,
+	TRUE_EXPR,
+	FALSE_EXPR,
 	EXPR_FUNCTION,
 	PARAMS_DESC,
 	EXPR_ASSERT,
 	ASSERTION,
 	EXPR_ERROR,
+	SLICE_DESC_END,
+	SLICE_DESC_STEP,
 	ARG,
 	OBJ_BODY_COMP,
 	OBJ_LOCAL_POST_COMMA,
@@ -194,21 +226,21 @@
 	MEMBER_ASSERT_STMT,
 	MEMBER_FIELD,
 	FIELD_NORMAL,
-	VISIBILITY,
 	FIELD_METHOD,
 	FIELD_NAME_FIXED,
 	FIELD_NAME_DYNAMIC,
+	FOR_SPEC,
 	IF_SPEC,
 	BIND_DESTRUCT,
-	DESTRUCT,
 	BIND_FUNCTION,
 	PARAM,
 	DESTRUCT_FULL,
 	DESTRUCT_SKIP,
 	DESTRUCT_ARRAY,
-	DESTRUCT_REST,
 	DESTRUCT_OBJECT,
 	DESTRUCT_OBJECT_FIELD,
+	DESTRUCT_REST,
+	DESTRUCT_ARRAY_ELEMENT,
 	EXPR,
 	OBJ_BODY,
 	COMP_SPEC,
@@ -216,6 +248,15 @@
 	MEMBER,
 	FIELD,
 	FIELD_NAME,
+	DESTRUCT,
+	DESTRUCT_ARRAY_PART,
+	BINARY_OPERATOR,
+	UNARY_OPERATOR,
+	LITERAL,
+	STRING,
+	NUMBER,
+	IMPORT_KIND,
+	VISIBILITY,
 	#[doc(hidden)]
 	__LAST,
 }
@@ -223,76 +264,23 @@
 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,
+			| INTRINSIC_ID | INTRINSIC | 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 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 is_enum(self) -> bool {
+		match self {
+			EXPR | OBJ_BODY | COMP_SPEC | BIND | MEMBER | FIELD | FIELD_NAME | DESTRUCT
+			| DESTRUCT_ARRAY_PART | BINARY_OPERATOR | UNARY_OPERATOR | LITERAL | STRING
+			| NUMBER | IMPORT_KIND | VISIBILITY => true,
+			_ => false,
+		}
 	}
 	pub fn from_raw(r: u16) -> Self {
 		assert!(r < Self::__LAST as u16);
@@ -303,5 +291,5 @@
 	}
 }
 #[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 } ; }
+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 } }
 pub use T;
deletedcrates/jrsonnet-rowan-parser/src/generated/tokens.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/generated/tokens.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-//! 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 }
-}
modifiedcrates/jrsonnet-rowan-parser/src/lex.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lex.rs
+++ b/crates/jrsonnet-rowan-parser/src/lex.rs
@@ -6,40 +6,6 @@
 
 use crate::SyntaxKind;
 
-impl SyntaxKind {
-	pub fn is_trivia(self) -> bool {
-		matches!(
-			self,
-			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
-		)
-	}
-}
-
 pub struct Lexer<'a> {
 	inner: logos::Lexer<'a, SyntaxKind>,
 }
@@ -74,7 +40,7 @@
 	}
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 pub struct Lexeme<'i> {
 	pub kind: SyntaxKind,
 	pub text: &'i str,
modifiedcrates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lib.rs
+++ b/crates/jrsonnet-rowan-parser/src/lib.rs
@@ -2,6 +2,7 @@
 
 mod ast;
 mod binary;
+mod classify;
 mod event;
 mod generated;
 mod language;
@@ -13,8 +14,27 @@
 mod token_set;
 mod unary;
 
-pub use generated::syntax_kinds::SyntaxKind;
+pub use ast::{AstChildren, AstNode, AstToken};
+use event::Sink;
+use generated::nodes::SourceFile;
+pub use generated::{nodes, syntax_kinds::SyntaxKind};
 pub use language::{
 	JsonnetLanguage, PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode,
 	SyntaxNodeChildren, SyntaxToken,
 };
+use lex::lex;
+use parser::{Parser, SyntaxError};
+pub fn parse(input: &str) -> (SourceFile, Vec<SyntaxError>) {
+	let lexemes = lex(input);
+	let parser = Parser::new(&lexemes);
+	let events = parser.parse();
+	let sink = Sink::new(events, &lexemes);
+
+	let parse = sink.finish();
+	(
+		SourceFile {
+			syntax: parse.syntax(),
+		},
+		parse.errors,
+	)
+}
modifiedcrates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/marker.rs
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -7,7 +7,7 @@
 	pub pos: usize,
 }
 impl Ranger {
-	pub fn finish(mut self, p: &Parser) -> FinishedRanger {
+	pub fn finish(self, p: &Parser) -> FinishedRanger {
 		FinishedRanger {
 			start_token: self.pos,
 			end_token: self.pos.max(p.offset.saturating_sub(1)),
@@ -20,6 +20,7 @@
 	pub end_token: usize,
 }
 impl FinishedRanger {
+	#[allow(dead_code)]
 	pub fn had_error_since(&self, p: &Parser) -> bool {
 		p.last_error_token >= self.start_token
 	}
@@ -28,76 +29,94 @@
 #[must_use]
 pub struct Marker {
 	pub start_event_idx: usize,
-	pub token: usize,
 	bomb: DropBomb,
 }
 impl Marker {
-	pub fn new(pos: usize, token: usize) -> Self {
+	pub fn new(pos: usize) -> Self {
 		Self {
 			start_event_idx: pos,
-			token,
 			bomb: DropBomb::new("marked dropped while not completed"),
 		}
 	}
 	pub fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
 		self.bomb.defuse();
+		assert!(
+			!kind.is_enum(),
+			"{kind:?} is a enum kind, you should use variant kinds instead"
+		);
+		// TODO: is_parser should return true if enum variant has #[regex]/#[token] over it
+		// debug_assert!(
+		// 	!kind.is_parser(),
+		// 	"{kind:?} should be only emitted by parser, not used directly"
+		// );
 		let event_at_pos = &mut p.events[self.start_event_idx];
-		assert_eq!(*event_at_pos, Event::Placeholder);
+		assert_eq!(*event_at_pos, Event::Pending);
 
 		*event_at_pos = Event::Start {
 			kind,
 			forward_parent: None,
 		};
 
-		p.events.push(Event::Finish);
+		let finish_event_idx = p.events.len();
+		p.events.push(Event::Finish { wrapper: None });
 		p.entered -= 1;
 		p.clear_outdated_hints();
 		CompletedMarker {
 			start_event_idx: self.start_event_idx,
-			start_token: self.token,
-			end_token: self.token.max(p.offset.saturating_sub(1)),
+			finish_event_idx,
 		}
 	}
+	pub fn forget(mut self, p: &mut Parser) {
+		self.bomb.defuse();
+		let event_at_pos = &mut p.events[self.start_event_idx];
+		assert_eq!(*event_at_pos, Event::Pending);
+
+		*event_at_pos = Event::Noop;
+		p.entered -= 1;
+		p.clear_outdated_hints();
+	}
 }
 pub struct CompletedMarker {
 	start_event_idx: usize,
-	pub start_token: usize,
-	pub end_token: usize,
+	finish_event_idx: usize,
 }
 impl CompletedMarker {
 	pub(super) fn precede(self, p: &mut Parser) -> Marker {
-		let mut new_m = p.start();
-		new_m.token = self.start_token;
-
-		if let Event::Start {
-			ref mut forward_parent,
-			..
-		} = p.events[self.start_event_idx]
-		{
-			*forward_parent = Some(new_m.start_event_idx - self.start_event_idx);
-		} else {
-			unreachable!();
+		let new_m = p.start();
+		match &mut p.events[self.start_event_idx] {
+			Event::Start { forward_parent, .. } => {
+				*forward_parent = Some(new_m.start_event_idx - self.start_event_idx);
+			}
+			_ => unreachable!(),
 		}
 
 		new_m
 	}
+	/// Create new node around existing marker, not counting anything that comes after it
+	pub fn wrap(self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
+		let new_m = p.start();
+		match &mut p.events[self.start_event_idx] {
+			Event::Start { forward_parent, .. } => {
+				*forward_parent = Some(new_m.start_event_idx - self.start_event_idx);
+			}
+			_ => unreachable!(),
+		}
+
+		let completed = new_m.complete(p, kind);
+
+		match &mut p.events[self.finish_event_idx] {
+			Event::Finish { wrapper } => {
+				*wrapper = Some(completed.finish_event_idx - self.finish_event_idx);
+			}
+			_ => unreachable!(),
+		}
+		completed
+	}
 }
 
 pub trait AsRange {
 	fn as_range(&self, p: &Parser) -> TextRange;
 	fn end_token(&self) -> usize;
-}
-
-impl AsRange for CompletedMarker {
-	fn as_range(&self, p: &Parser) -> TextRange {
-		TextRange::new(
-			p.start_of_token(self.start_token),
-			p.end_of_token(self.end_token),
-		)
-	}
-	fn end_token(&self) -> usize {
-		self.end_token
-	}
 }
 
 impl AsRange for FinishedRanger {
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -5,9 +5,10 @@
 
 use crate::{
 	binary::BinaryOperator,
-	event::{Event, Sink},
-	lex::{lex, Lexeme},
+	event::Event,
+	lex::Lexeme,
 	marker::{AsRange, CompletedMarker, Marker, Ranger},
+	string_block::{lex_str_block, StringBlockError},
 	token_set::SyntaxKindSet,
 	unary::UnaryOperator,
 	SyntaxKind,
@@ -43,9 +44,10 @@
 	pub last_error_token: usize,
 	expected_syntax: Option<ExpectedSyntax>,
 	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntaxTrackingState>>,
+	steps: Cell<u64>,
 }
 
-const DEFAULT_RECOVERY_SET: SyntaxKindSet = TS![; ')' ']' '}' local];
+const DEFAULT_RECOVERY_SET: SyntaxKindSet = TS![];
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum SyntaxError {
@@ -68,9 +70,9 @@
 	},
 }
 
-impl Into<LabeledSpan> for SyntaxError {
-	fn into(self) -> LabeledSpan {
-		match self {
+impl From<SyntaxError> for LabeledSpan {
+	fn from(val: SyntaxError) -> Self {
+		match val {
 			SyntaxError::Unexpected {
 				expected,
 				found,
@@ -91,7 +93,7 @@
 			),
 			SyntaxError::Custom { error, range } | SyntaxError::Hint { error, range } => {
 				LabeledSpan::new_with_span(
-					Some(format!("{}", error)),
+					Some(error),
 					SourceSpan::new(
 						SourceOffset::from(usize::from(range.start())),
 						SourceOffset::from(usize::from(range.end() - range.start())),
@@ -103,7 +105,7 @@
 }
 
 impl<'i> Parser<'i> {
-	fn new(lexemes: &'i [Lexeme<'i>]) -> Self {
+	pub fn new(lexemes: &'i [Lexeme<'i>]) -> Self {
 		Self {
 			lexemes,
 			offset: 0,
@@ -115,6 +117,7 @@
 			expected_syntax_tracking_state: Rc::new(Cell::new(
 				ExpectedSyntaxTrackingState::Unnamed,
 			)),
+			steps: Cell::new(0),
 		}
 	}
 	pub fn clear_outdated_hints(&mut self) {
@@ -132,27 +135,21 @@
 			.set(ExpectedSyntaxTrackingState::Unnamed);
 	}
 	pub fn start(&mut self) -> Marker {
+		self.skip_trivia();
 		let start_event_idx = self.events.len();
-		self.events.push(Event::Placeholder);
+		self.events.push(Event::Pending);
 		self.entered += 1;
-		Marker::new(start_event_idx, self.offset)
+		Marker::new(start_event_idx)
 	}
 	pub fn start_ranger(&mut self) -> Ranger {
+		self.skip_trivia();
 		let pos = self.offset;
 		Ranger { pos }
 	}
-	fn parse(mut self) -> Vec<Event> {
+	pub fn parse(mut self) -> Vec<Event> {
 		let m = self.start();
 		expr(&mut self);
-		if !self.at_end() {
-			let ranger = self.start_ranger();
-
-			while self.peek().is_some() {
-				self.bump()
-			}
-			let end = ranger.finish(&self);
-			self.custom_error(end, "unexpected input after expression");
-		}
+		self.expect(EOF);
 		m.complete(&mut self, SOURCE_FILE);
 
 		self.events
@@ -168,7 +165,9 @@
 		recovery_set: SyntaxKindSet,
 	) {
 		if self.at(kind) {
-			self.bump();
+			if kind != EOF {
+				self.bump();
+			}
 		} else {
 			self.error_with_recovery_set(recovery_set);
 		}
@@ -180,9 +179,6 @@
 		} else {
 			self.error_with_no_skip();
 		}
-	}
-	pub(crate) fn last_token_range(&self) -> Option<TextRange> {
-		self.lexemes.last().map(|Lexeme { range, .. }| *range)
 	}
 	fn current_token(&self) -> Lexeme<'i> {
 		self.lexemes[self.offset]
@@ -236,15 +232,19 @@
 		&mut self,
 		recovery_set: SyntaxKindSet,
 	) -> Option<CompletedMarker> {
-		let expected_syntax = self.expected_syntax.take().unwrap();
+		let expected_syntax = self
+			.expected_syntax
+			.take()
+			.unwrap_or(ExpectedSyntax::Named("unknown"));
 		self.expected_syntax_tracking_state
 			.set(ExpectedSyntaxTrackingState::Unnamed);
 
-		if self.at_end() || self.at_set(recovery_set) {
+		self.skip_trivia();
+		if self.at_end() || self.at_ts(recovery_set) {
 			let range = self
 				.previous_token()
 				.map(|t| t.range)
-				.unwrap_or(TextRange::at(TextSize::from(0), TextSize::from(0)));
+				.unwrap_or_else(|| TextRange::at(TextSize::from(0), TextSize::from(0)));
 
 			self.events.push(Event::Error(SyntaxError::Missing {
 				expected: expected_syntax,
@@ -256,7 +256,7 @@
 		let current_token = self.current_token();
 
 		self.events.push(Event::Error(SyntaxError::Unexpected {
-			expected: expected_syntax.clone(),
+			expected: expected_syntax,
 			found: current_token.kind,
 			range: current_token.range,
 		}));
@@ -270,30 +270,85 @@
 
 	fn bump(&mut self) {
 		self.skip_trivia();
+		self.bump_remap(self.current());
+	}
+	fn bump_remap(&mut self, kind: SyntaxKind) {
+		self.skip_trivia();
 		assert_ne!(self.offset, self.lexemes.len(), "already at end");
-		self.events.push(Event::Token);
+		self.events.push(Event::Token { kind });
 		self.offset += 1;
 		self.clear_expected_syntaxes();
 	}
-	fn peek(&mut self) -> Option<SyntaxKind> {
-		self.skip_trivia();
-		self.peek_raw()
+	fn step(&self) {
+		use std::fmt::Write;
+		let steps = self.steps.get();
+		if steps >= 15000000 {
+			let mut out = "seems like parsing is stuck".to_owned();
+			{
+				let last = 20;
+				write!(out, "\n\nLast {} events:", last).unwrap();
+				for (i, event) in self
+					.events
+					.iter()
+					.skip(self.events.len().saturating_sub(last))
+					.enumerate()
+				{
+					write!(out, "\n{i}. {event:?}").unwrap();
+				}
+			}
+			{
+				let next = 20;
+				write!(out, "\n\nNext {next} tokens:").unwrap();
+				for (i, tok) in self.lexemes.iter().skip(self.offset).take(next).enumerate() {
+					write!(out, "\n{i}. {tok:?}").unwrap();
+				}
+			}
+			panic!("{out}")
+		}
+		self.steps.set(steps + 1);
+	}
+	fn nth(&self, i: usize) -> SyntaxKind {
+		self.step();
+		let mut offset = self.offset;
+		for _ in 0..i {
+			while self
+				.lexemes
+				.get(offset)
+				.map(|l| l.kind.is_trivia())
+				.unwrap_or(false)
+			{
+				offset += 1;
+			}
+			offset += 1;
+		}
+		while self
+			.lexemes
+			.get(offset)
+			.map(|l| l.kind.is_trivia())
+			.unwrap_or(false)
+		{
+			offset += 1;
+		}
+		self.lexemes.get(offset).map(|l| l.kind).unwrap_or(EOF)
 	}
-	pub fn peek_token(&mut self) -> Option<&Lexeme<'i>> {
-		self.skip_trivia();
-		self.peek_token_raw()
+	fn current(&self) -> SyntaxKind {
+		self.nth(0)
 	}
 	fn skip_trivia(&mut self) {
-		while self.peek_raw().map(|c| c.is_trivia()).unwrap_or(false) {
+		while self.peek_raw().is_trivia() {
 			self.offset += 1;
 		}
-	}
-	fn peek_raw(&mut self) -> Option<SyntaxKind> {
-		self.lexemes.get(self.offset).map(|l| l.kind)
 	}
-	fn peek_token_raw(&mut self) -> Option<&Lexeme<'i>> {
+	fn current_lexeme(&mut self) -> Option<&Lexeme> {
+		self.skip_trivia();
 		self.lexemes.get(self.offset)
 	}
+	fn peek_raw(&mut self) -> SyntaxKind {
+		self.lexemes
+			.get(self.offset)
+			.map(|l| l.kind)
+			.unwrap_or(SyntaxKind::EOF)
+	}
 	#[must_use]
 	pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {
 		self.expected_syntax_tracking_state
@@ -303,16 +358,19 @@
 		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))
 	}
 	pub fn at(&mut self, kind: SyntaxKind) -> bool {
+		self.nth_at(0, kind)
+	}
+	pub fn nth_at(&mut self, n: usize, kind: SyntaxKind) -> bool {
 		if let ExpectedSyntaxTrackingState::Unnamed = self.expected_syntax_tracking_state.get() {
 			self.expected_syntax = Some(ExpectedSyntax::Unnamed(kind));
 		}
-		self.peek() == Some(kind)
+		self.nth(n) == kind
 	}
-	pub fn at_set(&mut self, set: SyntaxKindSet) -> bool {
-		self.peek().map_or(false, |k| set.contains(k))
+	pub fn at_ts(&mut self, set: SyntaxKindSet) -> bool {
+		set.contains(self.current())
 	}
 	pub fn at_end(&mut self) -> bool {
-		self.peek().is_none()
+		self.at(EOF)
 	}
 }
 pub(crate) struct ExpectedSyntaxGuard {
@@ -352,8 +410,8 @@
 	}}
 }
 
-fn expr(p: &mut Parser) {
-	expr_binding_power(p, 0);
+fn expr(p: &mut Parser) -> Option<CompletedMarker> {
+	expr_binding_power(p, 0)
 }
 fn expr_binding_power(p: &mut Parser, minimum_binding_power: u8) -> Option<CompletedMarker> {
 	let mut lhs = lhs(p)?;
@@ -392,7 +450,7 @@
 			p.bump();
 		}
 
-		let m = lhs.precede(p);
+		let m = lhs.wrap(p, LHS_EXPR).precede(p);
 		let parsed_rhs = expr_binding_power(p, right_binding_power).is_some();
 		lhs = m.complete(
 			p,
@@ -414,11 +472,11 @@
 	if p.at(T![for]) {
 		let m = p.start();
 		p.bump();
-		p.expect(IDENT);
+		name(p);
 		p.expect(T![in]);
 		expr(p);
 		m.complete(p, FOR_SPEC);
-	} else if p.at(T![in]) {
+	} else if p.at(T![if]) {
 		let m = p.start();
 		p.bump();
 		expr(p);
@@ -439,7 +497,7 @@
 	if p.at(T![,]) {
 		p.bump();
 		true
-	} else if p.at_set(set) {
+	} else if p.at_ts(set) {
 		p.expect_with_no_skip(T![,]);
 		p.bump();
 		true
@@ -449,18 +507,76 @@
 }
 fn field_name(p: &mut Parser) {
 	let _e = p.expected_syntax_name("field name");
+	let m = p.start();
 	if p.at(T!['[']) {
 		p.bump();
 		expr(p);
 		p.expect(T![']']);
+		m.complete(p, FIELD_NAME_DYNAMIC);
 	} else if p.at(IDENT) {
+		name(p);
+		m.complete(p, FIELD_NAME_FIXED);
+	} else if p.current().is_string() {
+		string(p);
+		m.complete(p, FIELD_NAME_FIXED);
+	} else {
+		p.error_with_recovery_set(TS![;]);
+	}
+}
+fn visibility(p: &mut Parser) {
+	if p.at_ts(TS![: :: :::]) {
 		p.bump()
 	} else {
-		p.error_with_recovery_set(TS![;]);
+		p.error_with_recovery_set(TS![]);
 	}
 }
+fn field(p: &mut Parser) {
+	let m = p.start();
+	field_name(p);
+	let plus = if p.at(T![+]) {
+		let r = p.start_ranger();
+		p.bump();
+		Some(r.finish(p))
+	} else {
+		None
+	};
+	let params = if p.at(T!['(']) {
+		if let Some(plus) = plus {
+			p.custom_error(plus, "can't extend with method");
+		}
+		params_desc(p);
+		if p.at(T![+]) {
+			let r = p.start_ranger();
+			p.bump();
+			p.custom_error(r.finish(p), "can't extend with method");
+		}
+		true
+	} else {
+		false
+	};
+	visibility(p);
+	expr(p);
+
+	if params {
+		m.complete(p, FIELD_METHOD)
+	} else {
+		m.complete(p, FIELD_NORMAL)
+	};
+}
+fn assertion(p: &mut Parser) {
+	assert!(p.at(T![assert]));
+	let m = p.start();
+	p.bump();
+	expr(p).map(|c| c.wrap(p, LHS_EXPR));
+	if p.at(T![:]) {
+		p.bump();
+		expr(p);
+	}
+	m.complete(p, ASSERTION);
+}
 fn object(p: &mut Parser) -> CompletedMarker {
 	assert!(p.at(T!['{']));
+	let m_t = p.start();
 	let m = p.start();
 	p.bump();
 
@@ -470,13 +586,19 @@
 			break;
 		}
 		let m = p.start();
-		field_name(p);
-		p.expect(T![,]);
-		expr(p);
-		while p.at(T![for]) || p.at(T![if]) {
-			compspec(p)
-		}
-		m.complete(p, MEMBER);
+		if p.at(T![local]) {
+			obj_local(p);
+			m.complete(p, MEMBER_BIND_STMT)
+		} else if p.at(T![assert]) {
+			assertion(p);
+			m.complete(p, MEMBER_ASSERT_STMT)
+		} else {
+			field(p);
+			while p.at(T![for]) || p.at(T![if]) {
+				compspec(p)
+			}
+			m.complete(p, MEMBER_FIELD)
+		};
 		if comma_with_alternatives(p, SyntaxKindSet::new(&[T![=]])) {
 			continue;
 		}
@@ -484,10 +606,19 @@
 		break;
 	}
 
-	m.complete(p, OBJ_BODY)
+	m.complete(p, OBJ_BODY_MEMBER_LIST);
+	m_t.complete(p, EXPR_OBJECT)
+}
+fn param(p: &mut Parser) {
+	let m = p.start();
+	destruct(p);
+	if p.at(T![=]) {
+		p.bump();
+		expr(p);
+	}
+	m.complete(p, PARAM);
 }
-
-fn params(p: &mut Parser) -> CompletedMarker {
+fn params_desc(p: &mut Parser) -> CompletedMarker {
 	assert!(p.at(T!['(']));
 	let m = p.start();
 	p.bump();
@@ -496,14 +627,8 @@
 		if p.at(T![')']) {
 			p.bump();
 			break;
-		}
-		let m = p.start();
-		p.expect(IDENT);
-		if p.at(T![=]) {
-			p.bump();
-			expr(p);
 		}
-		m.complete(p, PARAM);
+		param(p);
 		if comma(p) {
 			continue;
 		}
@@ -513,48 +638,39 @@
 
 	m.complete(p, PARAMS_DESC)
 }
-fn args(p: &mut Parser) {
+fn args_desc(p: &mut Parser) {
+	let m = p.start();
 	assert!(p.at(T!['(']));
 	p.bump();
 
-	let mut error_positional_start = None::<Marker>;
-	let mut started_named = Cell::new(false);
-	let mut on_positional = |p: &mut Parser, m: Marker| {
-		let c = m.complete(p, ARG);
-		if started_named.get() && error_positional_start.is_none() {
-			error_positional_start = Some(c.precede(p));
-		}
-	};
+	let started_named = Cell::new(false);
+
 	loop {
 		if p.at(T![')']) {
 			break;
 		}
 
 		let m = p.start();
-		if p.at(IDENT) {
+		if p.at(IDENT) && p.nth_at(1, T![=]) {
+			name(p);
 			p.bump();
-			if p.at(T![=]) {
-				p.bump();
-				expr(p);
-				m.complete(p, ARG);
-				started_named.set(true);
-			} else {
-				on_positional(p, m);
-			}
+			expr(p);
+			m.complete(p, ARG);
+			started_named.set(true);
 		} else {
 			expr(p);
-			on_positional(p, m);
+			m.complete(p, ARG);
 		}
 		if comma(p) {
 			continue;
 		}
 		break;
 	}
-	if let Some(error_positional_start) = error_positional_start {
-		let c = error_positional_start.complete(p, ERROR);
-		p.custom_error(c, "positional arguments can't be placed after named")
+	p.expect(T![')']);
+	if p.at(T![tailstrict]) {
+		p.bump()
 	}
-	p.expect(T![')']);
+	m.complete(p, ARGS_DESC);
 }
 
 fn array(p: &mut Parser) -> CompletedMarker {
@@ -605,7 +721,45 @@
 		m.complete(p, EXPR_ARRAY)
 	}
 }
-
+/// Returns true if it was slice, false if just index
+#[must_use]
+fn slice_desc_or_index(p: &mut Parser) -> bool {
+	let m = p.start();
+	p.bump();
+	// TODO: do not treat :, ::, ::: as full tokens?
+	// Start
+	if !p.at(T![:]) && !p.at(T![::]) {
+		expr(p);
+	}
+	if p.at(T![:]) {
+		p.bump();
+		// End
+		if !p.at(T![']']) {
+			expr(p).map(|c| c.wrap(p, SLICE_DESC_END));
+		}
+		if p.at(T![:]) {
+			p.bump();
+			// Step
+			if !p.at(T![']']) {
+				expr(p).map(|c| c.wrap(p, SLICE_DESC_STEP));
+			}
+		}
+	} else if p.at(T![::]) {
+		p.bump();
+		// End
+		if !p.at(T![']']) {
+			expr(p).map(|c| c.wrap(p, SLICE_DESC_END));
+		}
+	} else {
+		// It was not a slice
+		p.expect(T![']']);
+		m.forget(p);
+		return false;
+	}
+	p.expect(T![']']);
+	m.complete(p, SLICE_DESC);
+	true
+}
 fn lhs(p: &mut Parser) -> Option<CompletedMarker> {
 	let mut lhs = lhs_basic(p)?;
 
@@ -613,34 +767,20 @@
 		if p.at(T![.]) {
 			let m = lhs.precede(p);
 			p.bump();
-			p.expect(IDENT);
+			name(p);
 			lhs = m.complete(p, EXPR_INDEX);
 		} else if p.at(T!['[']) {
-			let m = lhs.precede(p);
-			p.bump();
-			// Start
-			if !p.at(T![:]) {
-				expr(p);
-			}
-			if p.at(T![:]) {
-				p.bump();
-				// End
-				if !p.at(T![']']) && !p.at(T![:]) {
-					expr(p);
-				}
-				if p.at(T![:]) {
-					p.bump();
-					// Step
-					if !p.at(T![']']) {
-						expr(p);
-					}
-				}
+			if slice_desc_or_index(p) {
+				lhs = lhs.precede(p).complete(p, EXPR_SLICE);
+			} else {
+				lhs = lhs
+					.wrap(p, LHS_EXPR)
+					.precede(p)
+					.complete(p, EXPR_INDEX_EXPR);
 			}
-			p.expect(T![']']);
-			lhs = m.complete(p, EXPR_SLICE);
 		} else if p.at(T!['(']) {
 			let m = lhs.precede(p);
-			args(p);
+			args_desc(p);
 			lhs = m.complete(p, EXPR_APPLY);
 		} else {
 			break;
@@ -649,25 +789,199 @@
 
 	Some(lhs)
 }
-
+fn name(p: &mut Parser) {
+	let m = p.start();
+	p.expect(IDENT);
+	m.complete(p, NAME);
+}
+fn destruct_rest(p: &mut Parser) {
+	assert!(p.at(T![...]));
+	p.bump();
+	let m = p.start();
+	if p.at(IDENT) {
+		p.bump()
+	}
+	m.complete(p, DESTRUCT_REST);
+}
+fn destruct_object_field(p: &mut Parser) {
+	let m = p.start();
+	name(p);
+	if p.at(T![:]) {
+		p.bump();
+		destruct(p);
+	};
+	if p.at(T![=]) {
+		p.bump();
+		expr(p);
+	}
+	m.complete(p, DESTRUCT_OBJECT_FIELD);
+}
+fn obj_local(p: &mut Parser) {
+	assert!(p.at(T![local]));
+	let m = p.start();
+	p.bump();
+	bind(p);
+	m.complete(p, OBJ_LOCAL);
+}
+fn destruct(p: &mut Parser) -> CompletedMarker {
+	let m = p.start();
+	if p.at(T![?]) {
+		p.bump();
+		m.complete(p, DESTRUCT_SKIP)
+	} else if p.at(T!['[']) {
+		p.bump();
+		let mut had_rest = false;
+		loop {
+			if p.at(T![']']) {
+				p.bump();
+				break;
+			} else if p.at(T![...]) {
+				let m_err = p.start_ranger();
+				destruct_rest(p);
+				if had_rest {
+					p.custom_error(m_err.finish(p), "only one rest can be present in array");
+				}
+				had_rest = true;
+			} else {
+				destruct(p);
+			}
+			if p.at(T![,]) {
+				p.bump();
+				continue;
+			}
+			p.expect(T![']']);
+			break;
+		}
+		m.complete(p, DESTRUCT_ARRAY)
+	} else if p.at(T!['{']) {
+		p.bump();
+		let mut had_rest = false;
+		loop {
+			if p.at(T!['}']) {
+				p.bump();
+				break;
+			} else if p.at(T![...]) {
+				let m_err = p.start_ranger();
+				destruct_rest(p);
+				if had_rest {
+					p.custom_error(m_err.finish(p), "only one rest can be present in object");
+				}
+				had_rest = true;
+			} else {
+				if had_rest {
+					p.error_with_recovery_set(TS![]);
+				}
+				destruct_object_field(p);
+			}
+			if p.at(T![,]) {
+				p.bump();
+				continue;
+			}
+			p.expect(T!['}']);
+			break;
+		}
+		m.complete(p, DESTRUCT_OBJECT)
+	} else if p.at(IDENT) {
+		name(p);
+		m.complete(p, DESTRUCT_FULL)
+	} else {
+		m.complete(p, ERROR)
+	}
+}
+fn bind(p: &mut Parser) {
+	let m = p.start();
+	if p.at(IDENT) && p.nth_at(1, T!['(']) {
+		name(p);
+		params_desc(p);
+		p.expect(T![=]);
+		expr(p);
+		m.complete(p, BIND_FUNCTION)
+	} else {
+		destruct(p);
+		p.expect(T![=]);
+		expr(p);
+		m.complete(p, BIND_DESTRUCT)
+	};
+}
+fn string(p: &mut Parser) {
+	assert!(p.current().is_string());
+	if p.at(STRING_BLOCK) {
+		// We use custom lexer, which skips enough bytes, but not returns error
+		// Instead we should call lexer again to verify if there is something wrong with string block
+		let mut lexer = logos::Lexer::<SyntaxKind>::new(dbg!(
+			&p.current_lexeme().expect("parser is at string block").text
+		));
+		// In kinds, string blocks is parsed at least as `|||`
+		lexer.bump(3);
+		let res = lex_str_block(&mut lexer);
+		debug_assert!(lexer.next().is_none(), "str_block is lexed");
+		match res {
+			Ok(_) => {
+				p.bump();
+			}
+			Err(e) => p.bump_remap(match e {
+				StringBlockError::UnexpectedEnd => ERROR_STRING_BLOCK_UNEXPECTED_END,
+				StringBlockError::MissingNewLine => ERROR_STRING_BLOCK_MISSING_NEW_LINE,
+				StringBlockError::MissingTermination => ERROR_STRING_BLOCK_MISSING_TERMINATION,
+				StringBlockError::MissingIndent => ERROR_STRING_BLOCK_MISSING_INDENT,
+			}),
+		}
+	} else {
+		p.bump();
+	}
+}
+fn number(p: &mut Parser) {
+	assert!(p.current().is_number());
+	p.bump();
+}
+fn literal(p: &mut Parser) {
+	assert!(p.current().is_literal());
+	p.bump();
+}
 fn lhs_basic(p: &mut Parser) -> Option<CompletedMarker> {
 	let _e = p.expected_syntax_name("value");
-	Some(if p.peek().map(|l| l.is_literal()).unwrap_or(false) {
+	Some(if p.current().is_literal() {
 		let m = p.start();
-		p.bump();
+		literal(p);
 		m.complete(p, EXPR_LITERAL)
-	} else if p.peek().map(|l| l.is_string()).unwrap_or(false) {
+	} else if p.current().is_string() {
 		let m = p.start();
-		p.bump();
+		string(p);
 		m.complete(p, EXPR_STRING)
-	} else if p.peek().map(|l| l.is_number()).unwrap_or(false) {
+	} else if p.current().is_number() {
 		let m = p.start();
-		p.bump();
+		number(p);
 		m.complete(p, EXPR_NUMBER)
 	} else if p.at(IDENT) {
 		let m = p.start();
-		p.bump();
+		name(p);
 		m.complete(p, EXPR_VAR)
+	} else if p.at(INTRINSIC_THIS_FILE) {
+		let m = p.start();
+		p.bump();
+		m.complete(p, EXPR_INTRINSIC_THIS_FILE)
+	} else if p.at(INTRINSIC_ID) {
+		let m = p.start();
+		p.bump();
+		m.complete(p, EXPR_INTRINSIC_ID)
+	} else if p.at(INTRINSIC) {
+		let m = p.start();
+		p.bump();
+		p.expect(T!['(']);
+		name(p);
+		p.expect(T![')']);
+		m.complete(p, EXPR_INTRINSIC)
+	} else if p.at(T![if]) {
+		let m = p.start();
+		p.bump();
+		expr(p);
+		p.expect(T![then]);
+		expr(p).map(|c| c.wrap(p, TRUE_EXPR));
+		if p.at(T![else]) {
+			p.bump();
+			expr(p).map(|c| c.wrap(p, FALSE_EXPR));
+		}
+		m.complete(p, EXPR_IF_THEN_ELSE)
 	} else if p.at(T!['[']) {
 		array(p)
 	} else if p.at(T!['{']) {
@@ -675,35 +989,26 @@
 	} else if p.at(T![local]) {
 		let m = p.start();
 		p.bump();
-		let mut sus_local = None;
 		loop {
-			p.expect_with_recovery_set(IDENT, TS![= ; local]);
-			if p.at(T!['(']) {
-				params(p);
+			if p.at(T![;]) {
+				p.bump();
+				break;
 			}
-
-			let sus_local_candidate = p.start_ranger();
-			p.expect_with_recovery_set(T![=], TS![; local]);
+			bind(p);
 
-			sus_local = p.at(T![local]).then(|| sus_local_candidate.finish(p));
-			expr(p);
-
-			if !comma(p) {
-				break;
+			if p.at(T![,]) {
+				p.bump();
+				continue;
 			}
-		}
-		p.expect(T![;]);
-		if let Some(sus_local) = sus_local {
-			if sus_local.had_error_since(p) {
-				p.custom_error(sus_local, "unusal local placement, missing ';' ?")
-			}
+			p.expect(T![;]);
+			break;
 		}
 		expr(p);
-		m.complete(p, T![local])
+		m.complete(p, EXPR_LOCAL)
 	} else if p.at(T![function]) {
 		let m = p.start();
 		p.bump();
-		args(p);
+		params_desc(p);
 		expr(p);
 		m.complete(p, EXPR_FUNCTION)
 	} else if p.at(T![error]) {
@@ -713,20 +1018,17 @@
 		m.complete(p, EXPR_ERROR)
 	} else if p.at(T![assert]) {
 		let m = p.start();
-		p.bump();
+		assertion(p);
+		p.expect(T![;]);
 		expr(p);
-		if p.at(T![:]) {
-			p.bump();
-			expr(p);
-		}
 		m.complete(p, EXPR_ASSERT)
 	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {
 		let m = p.start();
 		p.bump();
-		expr(p);
+		string(p);
 		m.complete(p, EXPR_IMPORT)
 	} else if p.at(T![-]) || p.at(T![!]) || p.at(T![~]) {
-		let op = match p.peek().unwrap() {
+		let op = match p.current() {
 			T![-] => UnaryOperator::Minus,
 			T![!] => UnaryOperator::Not,
 			T![~] => UnaryOperator::BitNegate,
@@ -746,7 +1048,7 @@
 		p.bump();
 		m.complete(p, EXPR_PARENED)
 	} else {
-		p.error_with_no_skip();
+		p.error_with_recovery_set(TS![]);
 		return None;
 	})
 }
@@ -755,14 +1057,4 @@
 	pub fn syntax(&self) -> SyntaxNode {
 		SyntaxNode::new_root(self.green_node.clone())
 	}
-}
-
-pub fn parse(input: &str) -> Parse {
-	let lexemes = lex(input);
-	let parser = Parser::new(&lexemes);
-	let events = parser.parse();
-	dbg!(&events);
-	let sink = Sink::new(events, &lexemes);
-
-	sink.finish()
 }
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp.snap
@@ -1,43 +1,34 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "[a for a in [1, 2, 3]]\n"
-
 ---
-Root@0..23
-  Array@0..23
-    SymbolLeftBracket@0..1 "["
-    ArrayElem@1..21
-      BodyDef@1..3
-        Ident@1..3
-          Ident@1..2 "a"
-          Whitespace@2..3 " "
-      CompspecFor@3..21
-        KeywordFor@3..6 "for"
-        Whitespace@6..7 " "
-        Ident@7..8 "a"
-        Whitespace@8..9 " "
-        OpIn@9..11 "in"
-        Whitespace@11..12 " "
-        Array@12..21
-          SymbolLeftBracket@12..13 "["
-          ArrayElem@13..14
-            BodyDef@13..14
-              Literal@13..14
-                Number@13..14 "1"
-          SymbolComma@14..15 ","
-          Whitespace@15..16 " "
-          ArrayElem@16..17
-            BodyDef@16..17
-              Literal@16..17
-                Number@16..17 "2"
-          SymbolComma@17..18 ","
-          Whitespace@18..19 " "
-          ArrayElem@19..20
-            BodyDef@19..20
-              Literal@19..20
-                Number@19..20 "3"
-          SymbolRightBracket@20..21 "]"
-    SymbolRightBracket@21..22 "]"
-    Whitespace@22..23 "\n"
+SOURCE_FILE@0..22
+  EXPR_ARRAY_COMP@0..22
+    L_BRACK@0..1 "["
+    EXPR_VAR@1..2
+      NAME@1..2
+        IDENT@1..2 "a"
+    WHITESPACE@2..3 " "
+    FOR_SPEC@3..21
+      FOR_KW@3..6 "for"
+      WHITESPACE@6..7 " "
+      NAME@7..8
+        IDENT@7..8 "a"
+      WHITESPACE@8..9 " "
+      IN_KW@9..11 "in"
+      WHITESPACE@11..12 " "
+      EXPR_ARRAY@12..21
+        L_BRACK@12..13 "["
+        EXPR_NUMBER@13..14
+          FLOAT@13..14 "1"
+        COMMA@14..15 ","
+        WHITESPACE@15..16 " "
+        EXPR_NUMBER@16..17
+          FLOAT@16..17 "2"
+        COMMA@17..18 ","
+        WHITESPACE@18..19 " "
+        EXPR_NUMBER@19..20
+          FLOAT@19..20 "3"
+        R_BRACK@20..21 "]"
+    R_BRACK@21..22 "]"
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp_incompatible_with_multiple_elems.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp_incompatible_with_multiple_elems.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__array_comp_incompatible_with_multiple_elems.snap
@@ -1,58 +1,48 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "[a for a in [1, 2, 3], b]\n"
-
 ---
-Root@0..26
-  Array@0..26
-    SymbolLeftBracket@0..1 "["
-    ArrayElem@1..21
-      BodyDef@1..3
-        Ident@1..3
-          Ident@1..2 "a"
-          Whitespace@2..3 " "
-      CompspecFor@3..21
-        KeywordFor@3..6 "for"
-        Whitespace@6..7 " "
-        Ident@7..8 "a"
-        Whitespace@8..9 " "
-        OpIn@9..11 "in"
-        Whitespace@11..12 " "
-        Array@12..21
-          SymbolLeftBracket@12..13 "["
-          ArrayElem@13..14
-            BodyDef@13..14
-              Literal@13..14
-                Number@13..14 "1"
-          SymbolComma@14..15 ","
-          Whitespace@15..16 " "
-          ArrayElem@16..17
-            BodyDef@16..17
-              Literal@16..17
-                Number@16..17 "2"
-          SymbolComma@17..18 ","
-          Whitespace@18..19 " "
-          ArrayElem@19..20
-            BodyDef@19..20
-              Literal@19..20
-                Number@19..20 "3"
-          SymbolRightBracket@20..21 "]"
-    SymbolComma@21..22 ","
-    Whitespace@22..23 " "
-    ArrayElem@23..24
-      BodyDef@23..24
-        Ident@23..24
-          Ident@23..24 "b"
-    SymbolRightBracket@24..25 "]"
-    Whitespace@25..26 "\n"
+SOURCE_FILE@0..25
+  EXPR_ARRAY@0..25
+    L_BRACK@0..1 "["
+    EXPR_VAR@1..2
+      NAME@1..2
+        IDENT@1..2 "a"
+    WHITESPACE@2..3 " "
+    FOR_SPEC@3..21
+      FOR_KW@3..6 "for"
+      WHITESPACE@6..7 " "
+      NAME@7..8
+        IDENT@7..8 "a"
+      WHITESPACE@8..9 " "
+      IN_KW@9..11 "in"
+      WHITESPACE@11..12 " "
+      EXPR_ARRAY@12..21
+        L_BRACK@12..13 "["
+        EXPR_NUMBER@13..14
+          FLOAT@13..14 "1"
+        COMMA@14..15 ","
+        WHITESPACE@15..16 " "
+        EXPR_NUMBER@16..17
+          FLOAT@16..17 "2"
+        COMMA@17..18 ","
+        WHITESPACE@18..19 " "
+        EXPR_NUMBER@19..20
+          FLOAT@19..20 "3"
+        R_BRACK@20..21 "]"
+    COMMA@21..22 ","
+    WHITESPACE@22..23 " "
+    EXPR_VAR@23..24
+      NAME@23..24
+        IDENT@23..24 "b"
+    R_BRACK@24..25 "]"
 ===
 Custom { error: "compspec may only be used if there is only one array element", range: 3..21 }
 ===
-  × syntax error
-   ╭────
- 1 │ [a for a in [1, 2, 3], b]
-   ·    ─────────┬────────
-   ·             ╰── compspec may only be used if there is only one array element
-   ╰────
+  x syntax error
+   ,----
+ 1 | [a for a in [1, 2, 3], b]
+   :    ^^^^^^^^^|^^^^^^^^
+   :             `-- compspec may only be used if there is only one array element
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__empty.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__empty.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__empty.snap
@@ -1,18 +1,14 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: " "
-
 ---
-Root@0..1
-  Whitespace@0..1 " "
+SOURCE_FILE@0..1
+  WHITESPACE@0..1 " "
 ===
 Missing { expected: Named("value"), offset: 1 }
 ===
-  × syntax error
-   ╭────
- 1 │  
-   ·  ▲
-   ·  ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 |  
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function.snap
@@ -1,34 +1,38 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "function(a, b = 1) a + b\n"
-
 ---
-Root@0..25
-  FunctionDef@0..25
-    KeywordFunction@0..8 "function"
-    LParen@8..9 "("
-    DefPositionalArg@9..10
-      Ident@9..10 "a"
-    SymbolComma@10..11 ","
-    Whitespace@11..12 " "
-    DefNamedArg@12..17
-      Ident@12..13 "b"
-      Whitespace@13..14 " "
-      SymbolAssign@14..15 "="
-      Whitespace@15..16 " "
-      Literal@16..17
-        Number@16..17 "1"
-    RParen@17..18 ")"
-    Whitespace@18..19 " "
-    BodyDef@19..25
-      BinOp@19..25
-        Ident@19..21
-          Ident@19..20 "a"
-          Whitespace@20..21 " "
-        OpPlus@21..22 "+"
-        Whitespace@22..23 " "
-        Ident@23..25
-          Ident@23..24 "b"
-          Whitespace@24..25 "\n"
+SOURCE_FILE@0..24
+  EXPR_FUNCTION@0..24
+    FUNCTION_KW@0..8 "function"
+    PARAMS_DESC@8..18
+      L_PAREN@8..9 "("
+      PARAM@9..10
+        DESTRUCT_FULL@9..10
+          NAME@9..10
+            IDENT@9..10 "a"
+      COMMA@10..11 ","
+      WHITESPACE@11..12 " "
+      PARAM@12..17
+        DESTRUCT_FULL@12..13
+          NAME@12..13
+            IDENT@12..13 "b"
+        WHITESPACE@13..14 " "
+        ASSIGN@14..15 "="
+        WHITESPACE@15..16 " "
+        EXPR_NUMBER@16..17
+          FLOAT@16..17 "1"
+      R_PAREN@17..18 ")"
+    WHITESPACE@18..19 " "
+    EXPR_BINARY@19..24
+      LHS_EXPR@19..20
+        EXPR_VAR@19..20
+          NAME@19..20
+            IDENT@19..20 "a"
+      WHITESPACE@20..21 " "
+      PLUS@21..22 "+"
+      WHITESPACE@22..23 " "
+      EXPR_VAR@23..24
+        NAME@23..24
+          IDENT@23..24 "b"
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_body.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_body.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_body.snap
@@ -1,29 +1,30 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "function(a, b)\n"
-
 ---
-Root@0..15
-  FunctionDef@0..15
-    KeywordFunction@0..8 "function"
-    LParen@8..9 "("
-    DefPositionalArg@9..10
-      Ident@9..10 "a"
-    SymbolComma@10..11 ","
-    Whitespace@11..12 " "
-    DefPositionalArg@12..13
-      Ident@12..13 "b"
-    RParen@13..14 ")"
-    Whitespace@14..15 "\n"
-    BodyDef@15..15
+SOURCE_FILE@0..14
+  EXPR_FUNCTION@0..14
+    FUNCTION_KW@0..8 "function"
+    PARAMS_DESC@8..14
+      L_PAREN@8..9 "("
+      PARAM@9..10
+        DESTRUCT_FULL@9..10
+          NAME@9..10
+            IDENT@9..10 "a"
+      COMMA@10..11 ","
+      WHITESPACE@11..12 " "
+      PARAM@12..13
+        DESTRUCT_FULL@12..13
+          NAME@12..13
+            IDENT@12..13 "b"
+      R_PAREN@13..14 ")"
 ===
 Missing { expected: Named("value"), offset: 14 }
 ===
-  × syntax error
-   ╭────
- 1 │ function(a, b)
-   ·               ▲
-   ·               ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 | function(a, b)
+   :               ^
+   :               `-- missing value
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_no_value.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_no_value.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_no_value.snap
@@ -1,41 +1,39 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "function(a, b = ) a + b\n"
-
 ---
-Root@0..24
-  FunctionDef@0..24
-    KeywordFunction@0..8 "function"
-    LParen@8..9 "("
-    DefPositionalArg@9..10
-      Ident@9..10 "a"
-    SymbolComma@10..11 ","
-    Whitespace@11..12 " "
-    DefNamedArg@12..16
-      Ident@12..13 "b"
-      Whitespace@13..14 " "
-      SymbolAssign@14..15 "="
-      Whitespace@15..16 " "
-    RParen@16..17 ")"
-    Whitespace@17..18 " "
-    BodyDef@18..24
-      BinOp@18..24
-        Ident@18..20
-          Ident@18..19 "a"
-          Whitespace@19..20 " "
-        OpPlus@20..21 "+"
-        Whitespace@21..22 " "
-        Ident@22..24
-          Ident@22..23 "b"
-          Whitespace@23..24 "\n"
+SOURCE_FILE@0..24
+  EXPR_FUNCTION@0..24
+    FUNCTION_KW@0..8 "function"
+    PARAMS_DESC@8..18
+      L_PAREN@8..9 "("
+      PARAM@9..10
+        IDENT@9..10 "a"
+      COMMA@10..11 ","
+      WHITESPACE@11..12 " "
+      PARAM@12..16
+        IDENT@12..13 "b"
+        WHITESPACE@13..14 " "
+        ASSIGN@14..15 "="
+        WHITESPACE@15..16 " "
+      R_PAREN@16..17 ")"
+      WHITESPACE@17..18 " "
+    EXPR_BINARY@18..24
+      EXPR_VAR@18..20
+        IDENT@18..19 "a"
+        WHITESPACE@19..20 " "
+      PLUS@20..21 "+"
+      WHITESPACE@21..22 " "
+      EXPR_VAR@22..24
+        IDENT@22..23 "b"
+        WHITESPACE@23..24 "\n"
 ===
 Missing { expected: Named("value"), offset: 15 }
 ===
-  × syntax error
-   ╭────
- 1 │ function(a, b = ) a + b
-   ·                ▲
-   ·                ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 | function(a, b = ) a + b
+   :                ^
+   :                `-- missing value
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_rparen.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_rparen.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__function_error_rparen.snap
@@ -1,30 +1,31 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "function(a, b\n"
-
 ---
-Root@0..14
-  FunctionDef@0..14
-    KeywordFunction@0..8 "function"
-    LParen@8..9 "("
-    DefPositionalArg@9..10
-      Ident@9..10 "a"
-    SymbolComma@10..11 ","
-    Whitespace@11..12 " "
-    DefPositionalArg@12..14
-      Ident@12..13 "b"
-      Whitespace@13..14 "\n"
-    BodyDef@14..14
+SOURCE_FILE@0..13
+  EXPR_FUNCTION@0..13
+    FUNCTION_KW@0..8 "function"
+    PARAMS_DESC@8..13
+      L_PAREN@8..9 "("
+      PARAM@9..10
+        DESTRUCT_FULL@9..10
+          NAME@9..10
+            IDENT@9..10 "a"
+      COMMA@10..11 ","
+      WHITESPACE@11..12 " "
+      PARAM@12..13
+        DESTRUCT_FULL@12..13
+          NAME@12..13
+            IDENT@12..13 "b"
 ===
-Missing { expected: Unnamed(RParen), offset: 13 }
+Missing { expected: Unnamed(R_PAREN), offset: 13 }
 Missing { expected: Named("value"), offset: 13 }
 ===
-  × syntax error
-   ╭────
- 1 │ function(a, b
-   ·              ▲
-   ·              │╰── missing value
-   ·              ╰── missing RParen
-   ╰────
+  x syntax error
+   ,----
+ 1 | function(a, b
+   :              ^
+   :              |`-- missing value
+   :              `-- missing R_PAREN
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap
@@ -1,47 +1,39 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "local a =\nlocal b = 3;\n1\n"
-
 ---
-Root@0..25
-  Local@0..25
-    KeywordLocal@0..5 "local"
-    Whitespace@5..6 " "
-    Ident@6..7 "a"
-    Whitespace@7..8 " "
-    SymbolAssign@8..9 "="
-    Whitespace@9..10 "\n"
-    Local@10..25
-      KeywordLocal@10..15 "local"
-      Whitespace@15..16 " "
-      Ident@16..17 "b"
-      Whitespace@17..18 " "
-      SymbolAssign@18..19 "="
-      Whitespace@19..20 " "
-      Literal@20..21
-        Number@20..21 "3"
-      SymbolSemi@21..22 ";"
-      Whitespace@22..23 "\n"
-      BodyDef@23..25
-        Literal@23..25
-          Number@23..24 "1"
-          Whitespace@24..25 "\n"
-    BodyDef@25..25
+SOURCE_FILE@0..25
+  LOCAL_KW@0..25
+    LOCAL_KW@0..5 "local"
+    WHITESPACE@5..6 " "
+    IDENT@6..7 "a"
+    WHITESPACE@7..8 " "
+    ASSIGN@8..9 "="
+    WHITESPACE@9..10 "\n"
+    LOCAL_KW@10..25
+      LOCAL_KW@10..15 "local"
+      WHITESPACE@15..16 " "
+      IDENT@16..17 "b"
+      WHITESPACE@17..18 " "
+      ASSIGN@18..19 "="
+      WHITESPACE@19..20 " "
+      EXPR_NUMBER@20..21
+        NUMBER@20..21 "3"
+      SEMI@21..22 ";"
+      WHITESPACE@22..23 "\n"
+      EXPR_NUMBER@23..25
+        NUMBER@23..24 "1"
+        WHITESPACE@24..25 "\n"
 ===
-Missing { expected: Unnamed(SymbolSemi), offset: 24 }
-Custom { error: "unusal local placement, missing ';' ?", range: 8..9 }
+Missing { expected: Unnamed(SEMI), offset: 24 }
 Missing { expected: Named("value"), offset: 24 }
 ===
-  × syntax error
-   ╭─[1:1]
- 1 │ local a =
-   ·         ┬
-   ·         ╰── unusal local placement, missing ';' ?
- 2 │ local b = 3;
- 3 │ 1
-   ·  ▲
-   ·  │╰── missing value
-   ·  ╰── missing SymbolSemi
-   ╰────
+  x syntax error
+   ,-[2:1]
+ 2 | local b = 3;
+ 3 | 1
+   :  ^
+   :  |`-- missing value
+   :  `-- missing SEMI
+   `----
 
deletedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap.newdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_no_value_recovery.snap.new
+++ /dev/null
@@ -1,43 +0,0 @@
----
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
-expression: "local a =\nlocal b = 3;\n1\n"
-
----
-Root@0..25
-  Local@0..25
-    KeywordLocal@0..5 "local"
-    Whitespace@5..6 " "
-    Ident@6..7 "a"
-    Whitespace@7..8 " "
-    SymbolAssign@8..9 "="
-    Whitespace@9..10 "\n"
-    Local@10..25
-      KeywordLocal@10..15 "local"
-      Whitespace@15..16 " "
-      Ident@16..17 "b"
-      Whitespace@17..18 " "
-      SymbolAssign@18..19 "="
-      Whitespace@19..20 " "
-      Literal@20..21
-        Number@20..21 "3"
-      SymbolSemi@21..22 ";"
-      Whitespace@22..23 "\n"
-      BodyDef@23..25
-        Literal@23..25
-          Number@23..24 "1"
-          Whitespace@24..25 "\n"
-    BodyDef@25..25
-===
-Missing { expected: Unnamed(SymbolSemi), offset: 24 }
-Missing { expected: Named("value"), offset: 24 }
-===
-  × syntax error
-   ╭─[2:1]
- 2 │ local b = 3;
- 3 │ 1
-   ·  ▲
-   ·  │╰── missing value
-   ·  ╰── missing SymbolSemi
-   ╰────
-
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_novalue.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_novalue.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_novalue.snap
@@ -1,29 +1,26 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "local a =\n"
-
 ---
-Root@0..10
-  Local@0..10
-    KeywordLocal@0..5 "local"
-    Whitespace@5..6 " "
-    Ident@6..7 "a"
-    Whitespace@7..8 " "
-    SymbolAssign@8..9 "="
-    Whitespace@9..10 "\n"
-    BodyDef@10..10
+SOURCE_FILE@0..10
+  LOCAL_KW@0..10
+    LOCAL_KW@0..5 "local"
+    WHITESPACE@5..6 " "
+    IDENT@6..7 "a"
+    WHITESPACE@7..8 " "
+    ASSIGN@8..9 "="
+    WHITESPACE@9..10 "\n"
 ===
 Missing { expected: Named("value"), offset: 9 }
-Missing { expected: Unnamed(SymbolSemi), offset: 9 }
+Missing { expected: Unnamed(SEMI), offset: 9 }
 Missing { expected: Named("value"), offset: 9 }
 ===
-  × syntax error
-   ╭────
- 1 │ local a =
-   ·          ▲
-   ·          ╰── missing value
-   ·          │╰── missing SymbolSemi
-   ·          ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 | local a =
+   :          ^
+   :          `-- missing value
+   :          |`-- missing SEMI
+   :          `-- missing value
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__named_before_positional.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__named_before_positional.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__named_before_positional.snap
@@ -1,63 +1,61 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "a(1, 2, b=4, 3, 5, k = 12, 6)\n"
-
 ---
-Root@0..30
-  FunctionCall@0..30
-    Ident@0..1
-      Ident@0..1 "a"
-    LParen@1..2 "("
-    DefPositionalArg@2..3
-      Literal@2..3
-        Number@2..3 "1"
-    SymbolComma@3..4 ","
-    Whitespace@4..5 " "
-    DefPositionalArg@5..6
-      Literal@5..6
-        Number@5..6 "2"
-    SymbolComma@6..7 ","
-    Whitespace@7..8 " "
-    DefNamedArg@8..11
-      Ident@8..9 "b"
-      SymbolAssign@9..10 "="
-      Literal@10..11
-        Number@10..11 "4"
-    SymbolComma@11..12 ","
-    Whitespace@12..13 " "
-    ErrorPositionalAfterNamed@13..28
-      DefPositionalArg@13..14
-        Literal@13..14
-          Number@13..14 "3"
-      SymbolComma@14..15 ","
-      Whitespace@15..16 " "
-      DefPositionalArg@16..17
-        Literal@16..17
-          Number@16..17 "5"
-      SymbolComma@17..18 ","
-      Whitespace@18..19 " "
-      DefNamedArg@19..25
-        Ident@19..20 "k"
-        Whitespace@20..21 " "
-        SymbolAssign@21..22 "="
-        Whitespace@22..23 " "
-        Literal@23..25
-          Number@23..25 "12"
-      SymbolComma@25..26 ","
-      Whitespace@26..27 " "
-      DefPositionalArg@27..28
-        Literal@27..28
-          Number@27..28 "6"
-    RParen@28..29 ")"
-    Whitespace@29..30 "\n"
+SOURCE_FILE@0..30
+  EXPR_APPLY@0..30
+    EXPR_VAR@0..1
+      IDENT@0..1 "a"
+    L_PAREN@1..2 "("
+    ARG@2..3
+      EXPR_NUMBER@2..3
+        NUMBER@2..3 "1"
+    COMMA@3..4 ","
+    WHITESPACE@4..5 " "
+    ARG@5..6
+      EXPR_NUMBER@5..6
+        NUMBER@5..6 "2"
+    COMMA@6..7 ","
+    WHITESPACE@7..8 " "
+    ARG@8..11
+      IDENT@8..9 "b"
+      ASSIGN@9..10 "="
+      EXPR_NUMBER@10..11
+        NUMBER@10..11 "4"
+    COMMA@11..12 ","
+    WHITESPACE@12..13 " "
+    ERROR@13..28
+      ARG@13..14
+        EXPR_NUMBER@13..14
+          NUMBER@13..14 "3"
+      COMMA@14..15 ","
+      WHITESPACE@15..16 " "
+      ARG@16..17
+        EXPR_NUMBER@16..17
+          NUMBER@16..17 "5"
+      COMMA@17..18 ","
+      WHITESPACE@18..19 " "
+      ARG@19..25
+        IDENT@19..20 "k"
+        WHITESPACE@20..21 " "
+        ASSIGN@21..22 "="
+        WHITESPACE@22..23 " "
+        EXPR_NUMBER@23..25
+          NUMBER@23..25 "12"
+      COMMA@25..26 ","
+      WHITESPACE@26..27 " "
+      ARG@27..28
+        EXPR_NUMBER@27..28
+          NUMBER@27..28 "6"
+    R_PAREN@28..29 ")"
+    WHITESPACE@29..30 "\n"
 ===
 Custom { error: "positional arguments can't be placed after named", range: 13..28 }
 ===
-  × syntax error
-   ╭────
- 1 │ a(1, 2, b=4, 3, 5, k = 12, 6)
-   ·              ───────┬───────
-   ·                     ╰── positional arguments can't be placed after named
-   ╰────
+  x syntax error
+   ,----
+ 1 | a(1, 2, b=4, 3, 5, k = 12, 6)
+   :              ^^^^^^^|^^^^^^^
+   :                     `-- positional arguments can't be placed after named
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_lhs.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_lhs.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_lhs.snap
@@ -1,23 +1,19 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "+ 2\n"
-
 ---
-Root@0..4
-  OpPlus@0..1 "+"
-  Whitespace@1..2 " "
-  Number@2..3 "2"
-  Whitespace@3..4 "\n"
+SOURCE_FILE@0..2
+  PLUS@0..1 "+"
+  WHITESPACE@1..2 " "
 ===
 Missing { expected: Named("value"), offset: 0 }
-Custom { error: "unexpected input after expression", range: 0..3 }
+Custom { error: "unexpected input after expression", range: 0..1 }
 ===
-  × syntax error
-   ╭────
- 1 │ + 2
-   · ▲─┬─
-   · │╰── unexpected input after expression
-   · ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 | + 2
+   : ^
+   : |`-- unexpected input after expression
+   : `-- missing value
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_operator.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_operator.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_operator.snap
@@ -1,22 +1,20 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "2 2\n"
-
 ---
-Root@0..4
-  Literal@0..2
-    Number@0..1 "2"
-    Whitespace@1..2 " "
-  Number@2..3 "2"
-  Whitespace@3..4 "\n"
+SOURCE_FILE@0..3
+  EXPR_NUMBER@0..1
+    FLOAT@0..1 "2"
+  WHITESPACE@1..2 " "
+  ERROR@2..3
+    FLOAT@2..3 "2"
 ===
-Custom { error: "unexpected input after expression", range: 2..3 }
+Unexpected { expected: Unnamed(EOF), found: FLOAT, range: 2..3 }
 ===
-  × syntax error
-   ╭────
- 1 │ 2 2
-   ·   ┬
-   ·   ╰── unexpected input after expression
-   ╰────
+  x syntax error
+   ,----
+ 1 | 2 2
+   :   |
+   :   `-- expected EOF, found FLOAT
+   `----
 
modifiedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_rhs.snapdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_rhs.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__no_rhs.snap
@@ -1,23 +1,22 @@
 ---
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
+source: crates/jrsonnet-rowan-parser/src/tests.rs
 expression: "a +\n"
-
 ---
-Root@0..4
-  BinOp@0..4
-    Ident@0..2
-      Ident@0..1 "a"
-      Whitespace@1..2 " "
-    OpPlus@2..3 "+"
-    Whitespace@3..4 "\n"
+SOURCE_FILE@0..3
+  EXPR_BINARY@0..3
+    LHS_EXPR@0..1
+      EXPR_VAR@0..1
+        NAME@0..1
+          IDENT@0..1 "a"
+    WHITESPACE@1..2 " "
+    PLUS@2..3 "+"
 ===
 Missing { expected: Named("value"), offset: 3 }
 ===
-  × syntax error
-   ╭────
- 1 │ a +
-   ·    ▲
-   ·    ╰── missing value
-   ╰────
+  x syntax error
+   ,----
+ 1 | a +
+   :    ^
+   :    `-- missing value
+   `----
 
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snap
@@ -0,0 +1,52 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "std.substr(a, 0, std.length(b)) == b\n"
+---
+SOURCE_FILE@0..36
+  EXPR_BINARY@0..36
+    LHS_EXPR@0..31
+      EXPR_APPLY@0..31
+        EXPR_INDEX@0..10
+          EXPR_VAR@0..3
+            NAME@0..3
+              IDENT@0..3 "std"
+          DOT@3..4 "."
+          NAME@4..10
+            IDENT@4..10 "substr"
+        ARGS_DESC@10..31
+          L_PAREN@10..11 "("
+          ARG@11..12
+            EXPR_VAR@11..12
+              NAME@11..12
+                IDENT@11..12 "a"
+          COMMA@12..13 ","
+          WHITESPACE@13..14 " "
+          ARG@14..15
+            EXPR_NUMBER@14..15
+              FLOAT@14..15 "0"
+          COMMA@15..16 ","
+          WHITESPACE@16..17 " "
+          ARG@17..30
+            EXPR_APPLY@17..30
+              EXPR_INDEX@17..27
+                EXPR_VAR@17..20
+                  NAME@17..20
+                    IDENT@17..20 "std"
+                DOT@20..21 "."
+                NAME@21..27
+                  IDENT@21..27 "length"
+              ARGS_DESC@27..30
+                L_PAREN@27..28 "("
+                ARG@28..29
+                  EXPR_VAR@28..29
+                    NAME@28..29
+                      IDENT@28..29 "b"
+                R_PAREN@29..30 ")"
+          R_PAREN@30..31 ")"
+    WHITESPACE@31..32 " "
+    EQ@32..34 "=="
+    WHITESPACE@34..35 " "
+    EXPR_VAR@35..36
+      NAME@35..36
+        IDENT@35..36 "b"
+
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__stdlib.snapdiffbeforeafterboth

no changes

addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\n"
+---
+SOURCE_FILE@0..4
+  EXPR_STRING@0..4
+    ERROR_STRING_BLOCK_MISSING_INDENT@0..4 "|||\n"
+
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent_text.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_indent_text.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\nhello\n"
+---
+SOURCE_FILE@0..10
+  EXPR_STRING@0..10
+    ERROR_STRING_BLOCK_MISSING_INDENT@0..10 "|||\nhello\n"
+
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_newline.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_newline.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||hello\n"
+---
+SOURCE_FILE@0..9
+  EXPR_STRING@0..9
+    ERROR_STRING_BLOCK_MISSING_NEW_LINE@0..9 "|||hello\n"
+
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_termination.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__str_block_missing_termination.snap
@@ -0,0 +1,8 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||\n\thello\n"
+---
+SOURCE_FILE@0..11
+  EXPR_STRING@0..11
+    ERROR_STRING_BLOCK_UNEXPECTED_END@0..11 "|||\n\thello\n"
+
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap
@@ -0,0 +1,30 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{\n\ta: 1;\n\tb: 2;\n}\n"
+---
+SOURCE_FILE@0..10
+  OBJ_BODY@0..7
+    L_BRACE@0..1 "{"
+    WHITESPACE@1..3 "\n\t"
+    MEMBER@3..7
+      IDENT@3..4 "a"
+      COLON@4..5 ":"
+      WHITESPACE@5..6 " "
+      EXPR_NUMBER@6..7
+        NUMBER@6..7 "1"
+  SEMI@7..8 ";"
+  WHITESPACE@8..10 "\n\t"
+===
+Missing { expected: Unnamed(R_BRACE), offset: 7 }
+Custom { error: "unexpected input after expression", range: 7..8 }
+===
+  x syntax error
+   ,-[1:1]
+ 1 | {
+ 2 | 	a: 1;
+   :      ^
+   :      |`-- unexpected input after expression
+   :      `-- missing R_BRACE
+ 3 | 	b: 2;
+   `----
+
deletedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap.newdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__wrong_field_end.snap.new
+++ /dev/null
@@ -1,41 +0,0 @@
----
-source: crates/jrsonnet-rowan-parser/src/lib.rs
-assertion_line: 88
-expression: "{\n\ta: 1;\n\tb: 2;\n}\n"
-
----
-Root@0..18
-  Object@0..7
-    SymbolLeftBrace@0..1 "{"
-    Whitespace@1..3 "\n\t"
-    Field@3..7
-      Ident@3..4 "a"
-      SymbolColon@4..5 ":"
-      Whitespace@5..6 " "
-      Literal@6..7
-        Number@6..7 "1"
-  SymbolSemi@7..8 ";"
-  Whitespace@8..10 "\n\t"
-  Ident@10..11 "b"
-  SymbolColon@11..12 ":"
-  Whitespace@12..13 " "
-  Number@13..14 "2"
-  SymbolSemi@14..15 ";"
-  Whitespace@15..16 "\n"
-  SymbolRightBrace@16..17 "}"
-  Whitespace@17..18 "\n"
-===
-Missing { expected: Unnamed(SymbolRightBrace), offset: 7 }
-Custom { error: "unexpected input after expression", range: 7..17 }
-===
-  × syntax error
-   ╭─[1:1]
- 1 │     {
- 2 │ ╭─▶ 	a: 1;
-   · ││     ▲
-   · ││     ╰── missing SymbolRightBrace
- 3 │ │   	b: 2;
- 4 │ ├─▶ }
-   · ╰──── unexpected input after expression
-   ╰────
-
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
@@ -1,23 +1,23 @@
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum StringBlockToken {
-	Valid,
-	UnexpectedEndOfString,
-	MissingTextBlockNewLine,
-	MissingTextBlockTermination,
-	MissingTextBlockIndent,
+pub enum StringBlockError {
+	UnexpectedEnd,
+	MissingNewLine,
+	MissingTermination,
+	MissingIndent,
 }
 
 use std::ops::Range;
 
-use StringBlockToken::*;
+use logos::Lexer;
+use StringBlockError::*;
 
 use crate::SyntaxKind;
 
-pub fn lex_str_block_test<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>) {
-	lex_str_block(lex);
+pub fn lex_str_block_test(lex: &mut Lexer<SyntaxKind>) {
+	let _ = lex_str_block(lex);
 }
 
-fn lex_str_block<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>) -> StringBlockToken {
+pub fn lex_str_block(lex: &mut Lexer<SyntaxKind>) -> Result<(), StringBlockError> {
 	struct Context<'a> {
 		source: &'a str,
 		index: usize,
@@ -116,7 +116,7 @@
 		a.len()
 	}
 
-	fn guess_token_end_and_bump<'a>(lex: &mut logos::Lexer<'a, SyntaxKind>, ctx: &Context<'a>) {
+	fn guess_token_end_and_bump<'a>(lex: &mut Lexer<'a, SyntaxKind>, ctx: &Context<'a>) {
 		let end_index = ctx
 			.rest()
 			.find("|||")
@@ -140,12 +140,12 @@
 		Some('\n') => (),
 		None => {
 			guess_token_end_and_bump(lex, &ctx);
-			return UnexpectedEndOfString;
+			return Err(UnexpectedEnd);
 		}
 		// Text block requires new line after |||.
 		Some(_) => {
 			guess_token_end_and_bump(lex, &ctx);
-			return MissingTextBlockNewLine;
+			return Err(MissingNewLine);
 		}
 	}
 
@@ -160,7 +160,7 @@
 	if num_whitespace == 0 {
 		// Text block's first line must start with whitespace
 		guess_token_end_and_bump(lex, &ctx);
-		return MissingTextBlockIndent;
+		return Err(MissingIndent);
 	}
 
 	loop {
@@ -171,7 +171,7 @@
 			match ctx.next() {
 				None => {
 					guess_token_end_and_bump(lex, &ctx);
-					return UnexpectedEndOfString;
+					return Err(UnexpectedEnd);
 				}
 				Some('\n') => break,
 				Some(_) => (),
@@ -188,26 +188,21 @@
 		if num_whitespace == 0 {
 			// End of the text block
 			let mut term_indent = String::with_capacity(num_whitespace);
-			loop {
-				match ctx.peek() {
-					Some(' ') | Some('\t') => {
-						term_indent.push(ctx.next().unwrap());
-					}
-					_ => break,
-				}
+			while let Some(' ' | '\t') = ctx.peek() {
+				term_indent.push(ctx.next().unwrap());
 			}
 
 			if !ctx.rest().starts_with("|||") {
 				// Text block not terminated with |||
 				let pos = ctx.pos();
-				if pos.len() == 0 {
+				if pos.is_empty() {
 					// eof
 					lex.bump(ctx.index);
-					return UnexpectedEndOfString;
+					return Err(UnexpectedEnd);
 				}
 
 				guess_token_end_and_bump(lex, &ctx);
-				return MissingTextBlockTermination;
+				return Err(MissingTermination);
 			}
 
 			// Skip '|||'
@@ -217,5 +212,5 @@
 	}
 
 	lex.bump(ctx.index);
-	Valid
+	Ok(())
 }
modifiedcrates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/tests.rs
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -1,9 +1,11 @@
 #![cfg(test)]
 
-use miette::{Diagnostic, GraphicalReportHandler, LabeledSpan};
+use miette::{
+	Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,
+};
 use thiserror::Error;
 
-use crate::parser::parse;
+use crate::{parse, AstNode};
 
 #[derive(Debug, Error)]
 #[error("syntax error")]
@@ -44,19 +46,22 @@
 fn process(text: &str) -> String {
 	use std::fmt::Write;
 	let mut out = String::new();
-	let node = parse(text);
+	let (node, errors) = parse(text);
 	write!(out, "{:#?}", node.syntax()).unwrap();
-	if !node.errors.is_empty() && !text.is_empty() {
+	if !errors.is_empty() && !text.is_empty() {
 		writeln!(out, "===").unwrap();
-		for err in &node.errors {
+		for err in &errors {
 			writeln!(out, "{:?}", err).unwrap();
 		}
 		let diag = MyDiagnostic {
 			code: text.to_string(),
-			spans: node.errors.into_iter().map(|e| e.into()).collect(),
+			spans: errors.into_iter().map(|e| e.into()).collect(),
 		};
 
-		let handler = GraphicalReportHandler::new();
+		let handler = GraphicalReportHandler::new_themed(GraphicalTheme {
+			characters: ThemeCharacters::ascii(),
+			styles: ThemeStyles::none(),
+		});
 
 		write!(out, "===").unwrap();
 		handler.render_report(&mut out, &diag).unwrap();
@@ -77,51 +82,94 @@
 mk_test!(
 	empty => r#" "#
 	function => r#"
-			function(a, b = 1) a + b
-		"#
+		function(a, b = 1) a + b
+	"#
 	function_error_no_value => r#"
-			function(a, b = ) a + b
-		"#
+		function(a, b = ) a + b
+	"#
 	function_error_rparen => r#"
-			function(a, b
-		"#
+		function(a, b
+	"#
 	function_error_body => r#"
-			function(a, b)
-		"#
+		function(a, b)
+	"#
 	local_novalue => r#"
-			local a =
-		"#
+		local a =
+	"#
 	local_no_value_recovery => r#"
-			local a =
-			local b = 3;
-			1
-		"#
+		local a =
+		local b = 3;
+		1
+	"#
 
 	array_comp => r#"
-			[a for a in [1, 2, 3]]
-		"#
+		[a for a in [1, 2, 3]]
+	"#
 	array_comp_incompatible_with_multiple_elems => r#"
-			[a for a in [1, 2, 3], b]
-		"#
+		[a for a in [1, 2, 3], b]
+	"#
 
 	no_rhs => r#"
-			a +
-		"#
+		a +
+	"#
 	no_lhs => r#"
-			+ 2
-		"#
+		+ 2
+	"#
 	no_operator => "
-			2 2
-		"
+		2 2
+	"
 
 	named_before_positional => "
-			a(1, 2, b=4, 3, 5, k = 12, 6)
-		"
+		a(1, 2, b=4, 3, 5, k = 12, 6)
+	"
 
 	wrong_field_end => "
-			{
-				a: 1;
-				b: 2;
-			}
-		"
+		{
+			a: 1;
+			b: 2;
+		}
+	"
+
+
+	plain_call => "
+		std.substr(a, 0, std.length(b)) == b
+	"
+
+	destruct => "
+		local [a, b, c] = arr;
+		local [a, ...] = arr_rest;
+		local [..., a] = rest_arr;
+		local [...] = rest_in_arr;
+		local [a, ...n] = arr_rest_n;
+		local [...n, a] = rest_arr_n;
+		local [...n] = rest_in_arr_n;
+
+		local {a, b, c} = obj;
+		local {a, b, c, ...} = obj_rest;
+		local {a, b, c, ...n} = obj_rest_n;
+
+		null
+	"
+
+	str_block_missing_indent => "
+		|||
+	"
+	str_block_missing_termination => "
+		|||
+			hello
+	"
+	str_block_missing_newline => "
+		|||hello
+	"
+	str_block_missing_indent_text => "
+		|||
+		hello
+	"
 );
+
+#[test]
+fn stdlib() {
+	let src = jrsonnet_stdlib::STDLIB_STR;
+	let result = process(src);
+	insta::assert_snapshot!("stdlib", result, src);
+}
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,14 +1,15 @@
 use crate::SyntaxKind;
 
 #[derive(Clone, Copy, Default)]
-pub struct SyntaxKindSet(u64);
+pub struct SyntaxKindSet(u128);
 
 impl SyntaxKindSet {
+	#[allow(dead_code)]
 	pub const EMPTY: Self = Self(0);
-	pub const ALL: Self = Self(u64::MAX);
+	pub const ALL: Self = Self(u128::MAX);
 
 	pub const fn new(kinds: &[SyntaxKind]) -> SyntaxKindSet {
-		let mut res = 0u64;
+		let mut res = 0u128;
 		let mut i = 0;
 		while i < kinds.len() {
 			res |= mask(kinds[i]);
@@ -26,8 +27,8 @@
 	}
 }
 
-const fn mask(kind: SyntaxKind) -> u64 {
-	1u64 << (kind as usize)
+const fn mask(kind: SyntaxKind) -> u128 {
+	1u128 << (kind as u128)
 }
 
 #[macro_export]
@@ -40,3 +41,11 @@
 		])
 	};
 }
+
+#[test]
+fn sanity() {
+	assert!(
+		(SyntaxKind::ERROR as u32) < 127,
+		"can't keep KindSet as bitset"
+	);
+}
modifiedxtask/Cargo.tomldiffbeforeafterboth
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -5,6 +5,7 @@
 
 [dependencies]
 anyhow = "1.0.57"
+indexmap = "1.9.0"
 itertools = "0.10.3"
 proc-macro2 = "1.0.39"
 quote = "1.0.18"
modifiedxtask/src/sourcegen/ast.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/ast.rs
+++ b/xtask/src/sourcegen/ast.rs
@@ -1,7 +1,13 @@
+use std::collections::{BTreeSet, HashMap};
+
 use proc_macro2::TokenStream;
-use quote::{format_ident, quote};
+use quote::format_ident;
+use ungrammar::{Grammar, Rule};
 
-use super::{escape_token_macro, KindsSrc};
+use super::{
+	util::{pluralize, to_lower_snake_case},
+	KindsSrc,
+};
 
 impl AstNodeSrc {
 	pub fn remove_field(&mut self, to_remove: Vec<usize>) {
@@ -14,9 +20,9 @@
 #[allow(dead_code)]
 #[derive(Default, Debug)]
 pub struct AstSrc {
-	pub tokens: Vec<String>,
 	pub nodes: Vec<AstNodeSrc>,
 	pub enums: Vec<AstEnumSrc>,
+	pub token_enums: Vec<AstTokenEnumSrc>,
 }
 #[derive(Debug)]
 pub struct AstNodeSrc {
@@ -38,7 +44,11 @@
 
 #[derive(Debug, Eq, PartialEq)]
 pub enum Cardinality {
+	/// This field may not exist in code
 	Optional,
+	/// This field should exist in correctly parsed code
+	Required,
+	/// There may be multiple field values of this kind
 	Many,
 }
 
@@ -50,6 +60,13 @@
 	pub variants: Vec<String>,
 }
 
+#[derive(Debug, Clone)]
+pub struct AstTokenEnumSrc {
+	pub doc: Vec<String>,
+	pub name: String,
+	pub variants: Vec<String>,
+}
+
 impl Field {
 	pub fn is_many(&self) -> bool {
 		matches!(
@@ -60,25 +77,29 @@
 			}
 		)
 	}
-	pub fn token_kind(&self) -> Option<TokenStream> {
+
+	pub fn token_name(&self) -> Option<String> {
+		match self {
+			Field::Token(token) => Some(token.clone()),
+			_ => None,
+		}
+	}
+	pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {
 		match self {
-			Field::Token(token) => {
-				let token: TokenStream = escape_token_macro(token);
-				Some(quote! { T![#token] })
-			}
+			Field::Token(token) => Some(kinds.token(token).expect("token exists").reference()),
 			_ => None,
 		}
 	}
+	pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {
+		match self {
+			Field::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),
+			_ => false,
+		}
+	}
 
 	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::Token(name) => kinds.token(name).expect("token exists").method_name(),
 			Field::Node { name, .. } => {
 				format_ident!("{}", name)
 			}
@@ -91,3 +112,275 @@
 		}
 	}
 }
+
+pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {
+	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 => match lower_token_enum(grammar, rule) {
+				Some(variants) => {
+					let tokens_enum_src = AstTokenEnumSrc {
+						doc: Vec::new(),
+						name,
+						variants,
+					};
+					res.token_enums.push(tokens_enum_src);
+				}
+				None => {
+					let mut fields = Vec::new();
+					lower_rule(&mut fields, grammar, None, rule, false);
+					let mut types = HashMap::new();
+					for field in fields.iter().filter(|f| f.token_name().is_none()) {
+						if let Some(old) = types.insert(field.ty(), field.method_name(kinds)) {
+							panic!("{name}.{} has same type as {name}.{}, resolve conflict by wrapping one field: {}", old, field.method_name(kinds), field.ty());
+						}
+					}
+					res.nodes.push(AstNodeSrc {
+						doc: Vec::new(),
+						name,
+						traits: Vec::new(),
+						fields,
+					});
+				}
+			},
+		}
+	}
+
+	deduplicate_fields(&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_token_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::Token(it) => variants.push(grammar[*it].name.clone()),
+			_ => return None,
+		}
+	}
+	Some(variants)
+}
+
+fn lower_rule(
+	acc: &mut Vec<Field>,
+	grammar: &Grammar,
+	label: Option<&String>,
+	rule: &Rule,
+	in_optional: bool,
+) {
+	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: if in_optional {
+					Cardinality::Optional
+				} else {
+					Cardinality::Required
+				},
+			};
+			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, in_optional);
+		}
+		Rule::Seq(rules) | Rule::Alt(rules) => {
+			for rule in rules {
+				lower_rule(acc, grammar, label, rule, in_optional)
+			}
+		}
+		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),
+	}
+}
+
+// (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_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 {
+		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!("could not find struct {var} for enum {}::{var}", enm.name)
+						})
+				})
+		});
+
+		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();
+	}
+}
addedxtask/src/sourcegen/kinds.rsdiffbeforeafterboth
--- /dev/null
+++ b/xtask/src/sourcegen/kinds.rs
@@ -0,0 +1,274 @@
+#[derive(Debug)]
+pub struct KindsSrc {
+	/// Key - how this token appears in ungrammar
+	defined_tokens: IndexMap<String, TokenKind>,
+	defined_node_names: HashSet<String>,
+	pub nodes: Vec<String>,
+}
+
+#[derive(Debug, Clone)]
+pub enum TokenKind {
+	/// May exist in token tree, but never in source code
+	Meta { grammar_name: String, name: String },
+	/// Specific parsing errors may be emitted as this type of kind
+	Error {
+		grammar_name: String,
+		name: String,
+		regex: Option<String>,
+		priority: Option<u32>,
+	},
+	/// Keyword - literal match of token
+	Keyword {
+		/// How this keyword appears in grammar/code, should be same as Kinds key
+		code: String,
+		name: String,
+	},
+	/// Literal - something defined by user, i.e strings, identifiers, smth
+	Literal {
+		/// How this keyword appears in grammar, should be same as Kinds key
+		grammar_name: String,
+		name: String,
+		/// Regex for Logos lexer
+		regex: String,
+		/// Path to custom lexer
+		lexer: Option<String>,
+	},
+}
+
+impl TokenKind {
+	pub fn grammar_name(&self) -> &str {
+		match self {
+			TokenKind::Keyword { code, .. } => code,
+			TokenKind::Literal { grammar_name, .. } => grammar_name,
+			TokenKind::Meta { grammar_name, .. } => grammar_name,
+			TokenKind::Error { grammar_name, .. } => grammar_name,
+		}
+	}
+	/// How this keyword should appear in kinds enum, screaming snake cased
+	pub fn name(&self) -> &str {
+		match self {
+			TokenKind::Keyword { name, .. } => name,
+			TokenKind::Literal { name, .. } => name,
+			TokenKind::Meta { name, .. } => name,
+			TokenKind::Error { name, .. } => name,
+		}
+	}
+	pub fn expand_kind(&self) -> TokenStream {
+		let name = format_ident!("{}", self.name());
+		let attr = match self {
+			TokenKind::Keyword { code, .. } => quote! {#[token(#code)]},
+			TokenKind::Literal { regex, lexer, .. } => {
+				let lexer = lexer
+					.as_deref()
+					.map(TokenStream::from_str)
+					.map(|r| r.expect("path is correct"));
+				quote! {#[regex(#regex, #lexer)]}
+			}
+			TokenKind::Error {
+				regex, priority, ..
+			} if regex.is_some() => {
+				let priority = priority.map(|p| quote! {, priority = #p});
+				quote! {#[regex(#regex #priority)]}
+			}
+			_ => quote! {},
+		};
+		quote! {
+			#attr
+			#name
+		}
+	}
+	pub fn expand_t_macros(&self) -> Option<TokenStream> {
+		match self {
+			TokenKind::Keyword { code, name } => {
+				let code = escape_token_macro(code);
+				let name = format_ident!("{name}");
+				Some(quote! {
+					[#code] => {$crate::SyntaxKind::#name}
+				})
+			}
+			// Meta items should not appear in T![_]
+			_ => None,
+		}
+	}
+
+	/// How this token should be referenced in code
+	/// Keywords are referenced with `T![_]` macro,
+	/// and literals are referenced directly by name
+	pub fn reference(&self) -> TokenStream {
+		match self {
+			TokenKind::Keyword { code, .. } => {
+				let code = escape_token_macro(code);
+				quote! {T![#code]}
+			}
+			_ => {
+				let name = self.name();
+				let ident = format_ident!("{name}");
+				quote! {#ident}
+			}
+		}
+	}
+
+	pub fn method_name(&self) -> Ident {
+		match self {
+			TokenKind::Keyword { name, .. } => {
+				format_ident!("{}_token", name.to_lowercase())
+			}
+			TokenKind::Literal { name, .. } => {
+				format_ident!("{}_lit", name.to_lowercase())
+			}
+			TokenKind::Meta { name, .. } => format_ident!("{}_meta", name.to_lowercase()),
+			TokenKind::Error { name, .. } => format_ident!("{}_error", name.to_lowercase()),
+		}
+	}
+}
+
+#[macro_export]
+macro_rules! define_kinds {
+	($into:ident = lit($name:literal) => $regex:literal $(, $lexer:literal)? $(; $($rest:tt)*)?) => {{
+		$into.define_token(TokenKind::Literal {
+			grammar_name: format!("LIT_{}!", $name),
+			name: $name.to_owned(),
+			regex: $regex.to_owned(),
+			lexer: None $(.or_else(|| Some($lexer.to_string())))?,
+		});
+		$(define_kinds!($into = $($rest)*))?
+	}};
+	($into:ident = error($name:literal$(, priority = $priority:literal)?) $(=> $regex:literal)? $(; $($rest:tt)*)?) => {{
+		$into.define_token(TokenKind::Error {
+			grammar_name: format!("ERROR_{}!", $name),
+			name: format!("ERROR_{}", $name),
+			regex: None$(.or(Some($regex.to_owned())))?,
+			priority: None$(.or(Some($priority)))?,
+		});
+		$(define_kinds!($into = $($rest)*))?
+	}};
+	($into:ident = $tok:literal => $name:literal $(; $($rest:tt)*)?) => {{
+		$into.define_token(TokenKind::Keyword {
+			code: format!("{}", $tok),
+			name: $name.to_owned(),
+		});
+		$(define_kinds!($into = $($rest)*))?
+	}};
+	($into:ident =) => {{}}
+}
+use std::{collections::HashSet, str::FromStr};
+
+pub use define_kinds;
+use indexmap::IndexMap;
+use proc_macro2::{Ident, TokenStream};
+use quote::{format_ident, quote};
+
+use super::escape_token_macro;
+
+impl KindsSrc {
+	pub fn new() -> Self {
+		Self {
+			defined_tokens: IndexMap::new(),
+			defined_node_names: HashSet::new(),
+			nodes: Vec::new(),
+		}
+	}
+	pub fn define_token(&mut self, token: TokenKind) {
+		assert!(
+			self.defined_node_names.insert(token.name().to_owned()),
+			"node name already defined: {}",
+			token.name()
+		);
+		assert!(
+			self.defined_tokens
+				.insert(token.grammar_name().to_owned(), token.clone())
+				.is_none(),
+			"token already defined: {}",
+			token.grammar_name()
+		)
+	}
+	pub fn define_node(&mut self, node: &str) {
+		assert!(
+			self.defined_node_names.insert(node.to_owned()),
+			"node name already defined: {}",
+			node
+		);
+		self.nodes.push(node.to_string())
+	}
+	pub fn token(&self, tok: &str) -> Option<&TokenKind> {
+		self.defined_tokens.get(tok)
+	}
+	pub fn is_token(&self, tok: &str) -> bool {
+		self.defined_tokens.contains_key(tok)
+	}
+	pub fn tokens(&self) -> impl Iterator<Item = &TokenKind> {
+		self.defined_tokens.iter().map(|(_, v)| v)
+	}
+}
+
+pub fn jsonnet_kinds() -> KindsSrc {
+	let mut kinds = KindsSrc::new();
+	define_kinds![kinds =
+		"||" => "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";
+		// Literals
+		lit("FLOAT") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?";
+		error("FLOAT_JUNK_AFTER_POINT") => r"(?:0|[1-9][0-9]*)\.[^0-9]";
+		error("FLOAT_JUNK_AFTER_EXPONENT") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][^+\-0-9]";
+		error("FLOAT_JUNK_AFTER_EXPONENT_SIGN") => r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?[eE][+-][^0-9]";
+		lit("STRING_DOUBLE") => "\"(?s:[^\"\\\\]|\\\\.)*\"";
+		error("STRING_DOUBLE_UNTERMINATED") => "\"(?s:[^\"\\\\]|\\\\.)*";
+		lit("STRING_SINGLE") => "'(?s:[^'\\\\]|\\\\.)*'";
+		error("STRING_SINGLE_UNTERMINATED") => "'(?s:[^'\\\\]|\\\\.)*";
+		lit("STRING_DOUBLE_VERBATIM") => "@\"(?:[^\"]|\"\")*\"";
+		error("STRING_DOUBLE_VERBATIM_UNTERMINATED") => "@\"(?:[^\"]|\"\")*";
+		lit("STRING_SINGLE_VERBATIM") => "@'(?:[^']|'')*'";
+		error("STRING_SINGLE_VERBATIM_UNTERMINATED") => "@'(?:[^']|'')*";
+		error("STRING_VERBATIM_MISSING_QUOTES") => "@[^\"'\\s]\\S+";
+		lit("STRING_BLOCK") => r"\|\|\|", "crate::string_block::lex_str_block_test";
+		error("STRING_BLOCK_UNEXPECTED_END");
+		error("STRING_BLOCK_MISSING_NEW_LINE");
+		error("STRING_BLOCK_MISSING_TERMINATION");
+		error("STRING_BLOCK_MISSING_INDENT");
+		lit("IDENT") => r"[_a-zA-Z][_a-zA-Z0-9]*";
+		lit("WHITESPACE") => r"[ \t\n\r]+";
+		lit("SINGLE_LINE_SLASH_COMMENT") => r"//[^\r\n]*(\r\n|\n)?";
+		lit("SINGLE_LINE_HASH_COMMENT") => r"#[^\r\n]*(\r\n|\n)?";
+		lit("MULTI_LINE_COMMENT") => r"/\*([^*]|\*[^/])*\*/";
+		error("COMMENT_TOO_SHORT") => r"/\*/";
+		error("COMMENT_UNTERMINATED") =>  r"/\*([^*]|\*[^/])+";
+	];
+	kinds
+}
modifiedxtask/src/sourcegen/mod.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/mod.rs
+++ b/xtask/src/sourcegen/mod.rs
@@ -1,22 +1,36 @@
-use std::{
-	collections::{BTreeSet, HashSet},
-	path::PathBuf,
-};
+use std::path::PathBuf;
 
 use anyhow::Result;
-use ast::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field};
+use ast::{lower, AstSrc};
 use itertools::Itertools;
+use kinds::{KindsSrc, TokenKind};
 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,
-};
+use ungrammar::Grammar;
+use util::{ensure_file_contents, reformat, to_pascal_case, to_upper_snake_case};
 
 mod ast;
+mod kinds;
 mod util;
 
+enum SpecialName {
+	Literal,
+	Meta,
+	Error,
+}
+fn classify_special(name: &str) -> Option<(SpecialName, &str)> {
+	let name = name.strip_suffix('!')?;
+	Some(if let Some(name) = name.strip_prefix("LIT_") {
+		(SpecialName::Literal, name)
+	} else if let Some(name) = name.strip_prefix("META_") {
+		(SpecialName::Meta, name)
+	} else if let Some(name) = name.strip_prefix("ERROR_") {
+		(SpecialName::Error, name)
+	} else {
+		return None;
+	})
+}
+
 pub fn generate_ungrammar() -> Result<()> {
 	let grammar: Grammar = include_str!(concat!(
 		env!("CARGO_MANIFEST_DIR"),
@@ -24,93 +38,51 @@
 	))
 	.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 mut kinds = kinds::jsonnet_kinds();
 	let ast = lower(&kinds, &grammar);
 
+	for token in grammar.tokens() {
+		let token = &grammar[token];
+		let token = &token.name.clone();
+		if !kinds.is_token(token) {
+			if let Some((special, name)) = classify_special(token) {
+				match special {
+					SpecialName::Literal => panic!("literal is not defined: {name}"),
+					SpecialName::Meta => kinds.define_token(TokenKind::Meta {
+						grammar_name: token.to_owned(),
+						name: format!("META_{}", name),
+					}),
+					SpecialName::Error => kinds.define_token(TokenKind::Error {
+						grammar_name: token.to_owned(),
+						name: format!("ERROR_{}", name),
+						regex: None,
+						priority: None,
+					}),
+				};
+				continue;
+			};
+			let name = to_upper_snake_case(token);
+			kinds.define_token(TokenKind::Keyword {
+				code: token.to_owned(),
+				name: format!("{name}_KW"),
+			});
+		}
+	}
 	for node in &ast.nodes {
 		let name = to_upper_snake_case(&node.name);
-		if !kinds.is_literal(&name) {
-			kinds.nodes.push(name);
-		}
+		kinds.define_node(&name);
 	}
 	for enum_ in &ast.enums {
 		let name = to_upper_snake_case(&enum_.name);
-		if !kinds.is_literal(&name) {
-			kinds.nodes.push(name);
-		}
+		kinds.define_node(&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());
-		}
+	for token_enum in &ast.token_enums {
+		let name = to_upper_snake_case(&token_enum.name);
+		kinds.define_node(&name);
 	}
 
-	let syntax_kinds = generate_syntax_kinds(&kinds)?;
+	let syntax_kinds = generate_syntax_kinds(&kinds, &ast)?;
 
-	let tokens = generate_tokens(&ast)?;
-
 	let nodes = generate_nodes(&kinds, &ast)?;
 	ensure_file_contents(
 		&PathBuf::from(concat!(
@@ -118,13 +90,6 @@
 			"/../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!(
@@ -134,123 +99,37 @@
 		&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();
+fn generate_syntax_kinds(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {
+	let t_macros = kinds.tokens().filter_map(TokenKind::expand_t_macros);
+	let token_kinds = kinds.tokens().map(TokenKind::expand_kind);
 
-	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 keywords = kinds
+		.tokens()
+		.filter(|k| matches!(k, TokenKind::Keyword { .. }))
+		.map(TokenKind::name)
+		.map(|n| format_ident!("{n}"));
 
-	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
+	let nodes = kinds
+		.nodes
 		.iter()
-		.map(|s| x(&**s))
+		.map(|name| format_ident!("{}", name))
 		.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
+	let enums = grammar
+		.enums
 		.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<_>>();
+		.map(|e| format_ident!("{}", to_upper_snake_case(&e.name)))
+		.chain(
+			grammar
+				.token_enums
+				.iter()
+				.map(|e| format_ident!("{}", to_upper_snake_case(&e.name))),
+		);
 
 	let ast = quote! {
-		#![allow(bad_style, missing_docs, unreachable_pub)]
+		#![allow(bad_style, missing_docs, unreachable_pub, clippy::manual_non_exhaustive, clippy::match_like_matches_macro)]
 		use logos::Logos;
 
 		/// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.
@@ -261,9 +140,7 @@
 			TOMBSTONE,
 			#[doc(hidden)]
 			EOF,
-			#(#punctuation_enum,)*
-			#(#all_keywords_enum,)*
-			#(#tokens_enum,)*
+			#(#token_kinds,)*
 			#[error]
 			ERROR,
 			#(#nodes,)*
@@ -275,32 +152,15 @@
 		impl SyntaxKind {
 			pub fn is_keyword(self) -> bool {
 				match self {
-					#(#all_keywords)|* => true,
+					#(#keywords)|* => true,
 					_ => false,
 				}
 			}
-
-			pub fn is_punct(self) -> bool {
+			pub fn is_enum(self) -> bool {
 				match self {
-					#(#punctuation)|* => true,
+					#(#enums)|* => 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 {
@@ -313,71 +173,13 @@
 		}
 
 		#[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 };
-		}
+		macro_rules! T {#(#t_macros);*}
 		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
@@ -400,12 +202,18 @@
 							support::children(&self.syntax)
 						}
 					}
-				} else if let Some(token_kind) = field.token_kind() {
+				} else if let Some(token_kind) = field.token_kind(kinds) {
 					quote! {
 						pub fn #method_name(&self) -> Option<#ty> {
 							support::token(&self.syntax, #token_kind)
 						}
 					}
+				} else if field.is_token_enum(grammar) {
+					quote! {
+						pub fn #method_name(&self) -> Option<#ty> {
+							support::token_child(&self.syntax)
+						}
+					}
 				} else {
 					quote! {
 						pub fn #method_name(&self) -> Option<#ty> {
@@ -513,6 +321,80 @@
 		})
 		.unzip();
 
+	let (token_enum_defs, token_enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+		.token_enums
+		.iter()
+		.map(|en| {
+			let variants: Vec<_> = en
+				.variants
+				.iter()
+				.map(|token| {
+					format_ident!(
+						"{}",
+						to_pascal_case(kinds.token(token).expect("token exists").name())
+					)
+				})
+				.collect();
+			let name = format_ident!("{}", en.name);
+			let kind_name = format_ident!("{}Kind", en.name);
+			let kinds: Vec<_> = variants
+				.iter()
+				.map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
+				.collect();
+
+			let ast_node = quote! {
+				impl AstToken for #name {
+					fn can_cast(kind: SyntaxKind) -> bool {
+						match kind {
+							#(#kinds)|* => true,
+							_ => false,
+						}
+					}
+					fn cast(syntax: SyntaxToken) -> Option<Self> {
+						let res = match syntax.kind() {
+							#(
+							#kinds => #name { syntax, kind: #kind_name::#variants },
+							)*
+							_ => return None,
+						};
+						Some(res)
+					}
+					fn syntax(&self) -> &SyntaxToken {
+						&self.syntax
+					}
+				}
+			};
+
+			(
+				quote! {
+					#[pretty_doc_comment_placeholder_workaround]
+					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+					pub struct #name { syntax: SyntaxToken, kind: #kind_name }
+
+					#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+					pub enum #kind_name {
+						#(#variants,)*
+					}
+				},
+				quote! {
+					#ast_node
+
+					impl #name {
+						pub fn kind(&self) -> #kind_name {
+							self.kind
+						}
+					}
+
+					impl std::fmt::Display for #name {
+						fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+							std::fmt::Display::fmt(self.syntax(), f)
+						}
+					}
+				},
+			)
+		})
+		.unzip();
+
 	let (any_node_defs, any_node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
 		.nodes
 		.iter()
@@ -581,32 +463,22 @@
 			}
 		});
 
-	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)]
+		#![allow(non_snake_case, clippy::match_like_matches_macro)]
+
 		use crate::{
 			SyntaxNode, SyntaxToken, SyntaxKind::{self, *},
-			ast::{self, AstNode, AstChildren, support},
+			ast::{AstNode, AstToken, AstChildren, support},
 			T,
 		};
 
 		#(#node_defs)*
 		#(#enum_defs)*
+		#(#token_enum_defs)*
 		#(#any_node_defs)*
 		#(#node_boilerplate_impls)*
 		#(#enum_boilerplate_impls)*
+		#(#token_enum_boilerplate_impls)*
 		#(#any_node_boilerplate_impls)*
 		#(#display_impls)*
 	};
@@ -636,276 +508,6 @@
 	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();
 	}
 }
 
modifiedxtask/src/sourcegen/util.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/util.rs
+++ b/xtask/src/sourcegen/util.rs
@@ -1,6 +1,6 @@
 use std::{fs, path::Path};
 
-use anyhow::{bail, Result};
+use anyhow::Result;
 use xshell::{cmd, Shell};
 
 /// Checks that the `file` has the specified `contents`. If that is not the
@@ -15,13 +15,13 @@
 
 	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");
+		eprintln!("NOTE: run `cargo xtask` 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");
+	Ok(())
 }
 
 // Eww, someone configured git to use crlf?
@@ -80,9 +80,7 @@
 	// let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
 	// rustfmt()?;
 	let sh = Shell::new()?;
-	let stdout = cmd!(sh, "rustfmt --config fn_single_line=true")
-		.stdin(text)
-		.read()?;
+	let stdout = cmd!(sh, "rustfmt").stdin(text).read()?;
 	Ok(format!(
 		"{}\n\n{}\n",
 		"//! This is a generated file, please do not edit manually. Changes can be