summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-ast/generate_ast.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xthird_party/rust/jsparagus-ast/generate_ast.py1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-ast/generate_ast.py b/third_party/rust/jsparagus-ast/generate_ast.py
new file mode 100755
index 0000000000..dab3f96aac
--- /dev/null
+++ b/third_party/rust/jsparagus-ast/generate_ast.py
@@ -0,0 +1,1061 @@
+#!/usr/bin/env python3
+
+import json
+import os
+import re
+import collections
+import textwrap
+
+
+RUST_BUILTIN_TYPES = {
+ 'bool',
+ 'f64',
+}
+
+RUST_PARAMETERIZED_TYPES = {
+ 'Option',
+ 'Vec',
+}
+
+
+# name is a string; params is a tuple of 0 or more Types.
+TypeBase = collections.namedtuple("Type", "name params")
+
+
+class Type(TypeBase):
+ def __new__(cls, name, params=()):
+ params = tuple(params)
+ for p in params:
+ if not isinstance(p, Type):
+ raise ValueError("type parameters must be types, got {!r}".format(p))
+ return TypeBase.__new__(cls, name, params)
+
+ def __str__(self):
+ if self.params == ():
+ return self.name
+ return self.name + "<{}>".format(", ".join(map(str, self.params)))
+
+ def __repr__(self):
+ if self.params == ():
+ return 'Type({!r})'.format(self.name)
+ return 'Type({!r}, {!r})'.format(self.name, list(self.params))
+
+ def to_rust_type(self, ast):
+ params_str = ", ".join(p.to_rust_type(ast) for p in self.params)
+ if self.name == 'Option':
+ return "Option<{}>".format(params_str)
+ if self.name == 'Box':
+ return "arena::Box<'alloc, {}>".format(params_str)
+ if self.name == 'Vec':
+ return "arena::Vec<'alloc, {}>".format(params_str)
+ if self.name in RUST_PARAMETERIZED_TYPES:
+ return "{}<{}>".format(self.name, params_str)
+ if self.params:
+ return "{}<'alloc, {}>".format(self.name, params_str)
+ if self.name in RUST_BUILTIN_TYPES:
+ return self.name
+ if self.name == 'Token':
+ return "Token"
+ if self.name in ast.type_decls and ast.type_decls[self.name].has_lifetime:
+ return "{}<'alloc>".format(self.name)
+ return self.name
+
+ def rust_variant_name(self):
+ if self.name == 'Vec':
+ return 'Vec' + self.params[0].rust_variant_name()
+ if self.name == 'Box':
+ return self.params[0].rust_variant_name()
+ return self.name
+
+
+def parse_type(ty):
+ """Parse a type, in the minilanguage used by ast.json, into a Type object.
+
+ A simple type like String parses as `Type("String", ())`; a parameterized type
+ like `Vec<String>` parses as `Type("Vec", ("String",))`;
+ nested parameterized types parse as nested Type objects.
+ """
+ ident_re = re.compile(r'^(?:\w|_)+$')
+ token_re = re.compile(r'(?s)\s*((?:\w|_)+|.)\s*')
+ tokens = token_re.finditer(ty)
+
+ current = None
+
+ def consume(token=None):
+ nonlocal current
+ assert token is None or token == current
+ current = next(tokens, None)
+ if current is not None:
+ current = current.group(1)
+
+ consume(None) # load the first token into `current`
+
+ def is_ident():
+ """True if the current token is an identifier"""
+ return current is not None and ident_re.match(current) is not None
+
+ def parse_params():
+ params = []
+ while current != '>':
+ params.append(parse_ty())
+ if current == ',':
+ consume(',')
+ return params
+
+ def parse_ty():
+ if not is_ident():
+ raise ValueError("parse error in type {!r}".format(ty))
+ name = current
+ consume()
+ if current == '<':
+ consume('<')
+ params = parse_params()
+ if current != '>':
+ raise ValueError("parse error in type {!r} (expected `>`)".format(ty))
+ consume('>')
+ return Type(name, params)
+ return Type(name)
+
+ result = parse_ty()
+ if current is not None:
+ raise ValueError("parse error in type {!r} (extra stuff at end)".format(ty))
+ return result
+
+
+assert parse_type('Statement') == Type('Statement')
+assert parse_type('Box<T>') == Type('Box', [Type('T')])
+assert parse_type("Vec<Box<Expression>>") == Type('Vec', [Type('Box', [Type('Expression')])])
+
+
+def write_impl(f, indentation, string, *format_args):
+ if len(format_args) == 0:
+ formatted = string
+ if '\n' in formatted:
+ cut = formatted.rindex("\n")
+ if formatted[cut:].isspace():
+ formatted = formatted[:cut]
+ formatted = textwrap.dedent(formatted.rstrip())
+ else:
+ formatted = string.format(*format_args)
+ f.write(" " * indentation + formatted + "\n")
+
+
+def to_snek_case(ident):
+ # https://stackoverflow.com/questions/1175208
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', ident)
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+
+EXTRA_STACK_VALUE_TYPE_NAMES = [
+ "Token",
+ "Vec<SwitchCase>",
+ "Vec<Statement>",
+ "Vec<VariableDeclarator>",
+ "Vec<ArrayExpressionElement>",
+ "Vec<Box<ClassElement>>",
+ "Vec<BindingProperty>",
+ "Vec<Option<Parameter>>",
+]
+
+
+def collect_stack_value_types(ast):
+ types = {}
+ for name, type_decl in ast.type_decls.items():
+ ty = parse_type(name)
+ if ty in types:
+ raise ValueError("type occurs twice with different spellings: {!r} and {!r}"
+ .format(name, types[ty]))
+ types[ty] = name
+
+ types = set(types.keys())
+ for name in EXTRA_STACK_VALUE_TYPE_NAMES:
+ types.add(parse_type(name))
+
+ return sorted(types)
+
+
+def stack_value(ast):
+ types = collect_stack_value_types(ast)
+ with open("../generated_parser/src/stack_value_generated.rs", "w+") as f:
+ def write(*args):
+ write_impl(f, *args)
+ write(0, """\
+ // WARNING: This file is auto-generated by crates/ast/generate_ast.py.
+
+ use crate::token::Token;
+ use ast::arena;
+ use ast::types::*;
+ use std::convert::Infallible;
+
+ pub type AstError = String;
+ type AstResult<'alloc, T> = Result<arena::Box<'alloc, T>, AstError>;
+
+ #[derive(Debug)]
+ pub enum StackValue<'alloc> {
+ """)
+
+ for ty in types:
+ write(1, "{}({}),", ty.rust_variant_name(), Type('Box', [ty]).to_rust_type(ast))
+
+ write(0, """\
+ }
+
+ impl<'alloc> StackValue<'alloc> {
+ pub fn to_ast<T: StackValueItem<'alloc>>(self) -> AstResult<'alloc, T> {
+ T::to_ast(self)
+ }
+ }
+
+ pub trait StackValueItem<'alloc>: Sized {
+ fn to_ast(sv: StackValue<'alloc>) -> AstResult<'alloc, Self>;
+ }
+
+ /// Values that can be converted to StackValues, fallibly.
+ pub trait TryIntoStack<'alloc> {
+ type Error;
+ fn try_into_stack(self) -> Result<StackValue<'alloc>, Self::Error>;
+ }
+ """)
+
+ for ty in types:
+ write(0, "impl<'alloc> StackValueItem<'alloc> for {} {{", ty.to_rust_type(ast))
+ write(1, "fn to_ast(sv: StackValue<'alloc>) -> AstResult<'alloc, Self> {")
+ write(2, "match sv {")
+ write(3, "StackValue::{}(v) => Ok(v),", ty.rust_variant_name())
+ write(3, "_ => Err(format!(\"StackValue expected {}, got {{:?}}\", sv)),", ty)
+ write(2, "}")
+ write(1, "}")
+ write(0, "}")
+ write(0, "")
+ for ty in types:
+ rust_ty = ty.to_rust_type(ast)
+ write(0, "impl<'alloc> TryIntoStack<'alloc> for arena::Box<'alloc, {}> {{", rust_ty)
+ write(1, "type Error = Infallible;")
+ write(1, "fn try_into_stack(self) -> Result<StackValue<'alloc>, Infallible> {")
+ write(2, "Ok(StackValue::{}(self))", ty.rust_variant_name())
+ write(1, "}")
+ write(0, "}")
+ write(0, "")
+ write(0, """\
+ impl<'alloc, T, E> TryIntoStack<'alloc> for Result<T, E>
+ where
+ T: TryIntoStack<'alloc>,
+ E: From<T::Error>,
+ {
+ type Error = E;
+ fn try_into_stack(self) -> Result<StackValue<'alloc>, E> {
+ Ok(self?.try_into_stack()?)
+ }
+ }
+ """)
+
+
+def loc_trait(ast):
+ types = collect_stack_value_types(ast)
+ with open("src/source_location_accessor_generated.rs", "w+") as f:
+ def write(*args):
+ write_impl(f, *args)
+ write(0, """\
+ // WARNING: This file is auto-generated by crates/ast/generate_ast.py.
+
+ use crate::SourceLocation;
+ use crate::arena;
+ use crate::types::*;
+ use std::borrow::{Borrow, BorrowMut};
+
+ pub trait SourceLocationAccessor {
+ fn set_loc(&mut self, start: SourceLocation, end: SourceLocation);
+ fn get_loc(&self) -> SourceLocation;
+ }
+
+ """)
+
+ extra_types = []
+
+ def define_accessor(ty):
+ if ty.name in ['Box', 'Token', 'Vec', 'Void']:
+ return
+
+ if ty.name not in ast.type_decls:
+ raise ValueError("unhandlable type {!r}".format(ty.name))
+
+ rust_ty = ty.to_rust_type(ast)
+ decl = ast.type_decls[ty.name]
+
+ write(0, "impl<'alloc> SourceLocationAccessor for {} {{", rust_ty)
+ if isinstance(decl, Struct):
+ write(1, "fn set_loc(&mut self, start: SourceLocation, end: SourceLocation) {")
+ write(2, "self.loc.start = start.start;")
+ write(2, "self.loc.end = end.end;")
+ write(1, "}")
+ write(0, "")
+ write(1, "fn get_loc(&self) -> SourceLocation {")
+ write(2, "self.loc")
+ write(1, "}")
+ elif isinstance(decl, Enum):
+ write(1, "fn set_loc(&mut self, start: SourceLocation, end: SourceLocation) {")
+ write(2, "match self {")
+ for variant_name, variant_ty in decl.variants.items():
+ if variant_ty is None:
+ write(3, "{}::{} {{ mut loc }} => {{", ty.name, variant_name)
+ write(4, "loc.start = start.start;")
+ write(4, "loc.end = end.end;")
+ write(3, "}}", ty.name, variant_name)
+ elif isinstance(variant_ty, dict):
+ write(3, "{}::{} {{ mut loc, .. }} => {{", ty.name, variant_name)
+ write(4, "loc.start = start.start;")
+ write(4, "loc.end = end.end;")
+ write(3, "}")
+ else:
+ write(3, "{}::{}(content) => {{ content.set_loc(start, end) }}",
+ ty.name, variant_name)
+ if variant_ty not in extra_types and variant_ty not in types:
+ extra_types.append(variant_ty)
+ write(2, "}")
+ write(1, "}")
+ write(0, "")
+ write(1, "fn get_loc(&self) -> SourceLocation {")
+ write(2, "match self {")
+ for variant_name, variant_ty in decl.variants.items():
+ if variant_ty is None:
+ write(3, "{}::{} {{ loc }} => {{", ty.name, variant_name)
+ write(4, "*loc")
+ write(3, "}}", ty.name, variant_name)
+ elif isinstance(variant_ty, dict):
+ write(3, "{}::{} {{ loc, .. }} => {{", ty.name, variant_name)
+ write(4, "*loc")
+ write(3, "}")
+ else:
+ write(3, "{}::{}(content) => {{ content.get_loc() }}",
+ ty.name, variant_name)
+ write(2, "}")
+ write(1, "}")
+ else:
+ raise ValueError("unhandlable type {!r}".format(types[ty]))
+
+ write(0, "}")
+ write(0, "")
+
+ for ty in types:
+ define_accessor(ty)
+
+ for ty in extra_types:
+ define_accessor(ty)
+
+ write(0, "impl<'alloc, T> SourceLocationAccessor for arena::Box<'alloc, T>")
+ write(0, "where")
+ write(1, "T: SourceLocationAccessor,")
+ write(0, "{")
+ write(1, "fn set_loc(&mut self, start: SourceLocation, end: SourceLocation) {")
+ write(2, "(self.borrow_mut() as &mut T).set_loc(start, end)")
+ write(1, "}")
+ write(0, "")
+ write(1, "fn get_loc(&self) -> SourceLocation {")
+ write(2, "(self.borrow() as &T).get_loc()")
+ write(1, "}")
+ write(0, "}")
+
+
+def pass_(ast):
+ with open("src/visit_generated.rs", "w+") as f:
+ def write(*args):
+ write_impl(f, *args)
+
+ def to_method_name(name):
+ return "visit_{}".format(to_snek_case(name))
+
+ def to_enum_method_name(enum_name, variant_name):
+ return "visit_enum_{}_variant_{}".format(to_snek_case(enum_name),
+ to_snek_case(variant_name))
+
+ def to_enter_method_name(name):
+ return "enter_{}".format(to_snek_case(name))
+
+ def to_enter_enum_method_name(enum_name, variant_name):
+ return "enter_enum_{}_variant_{}".format(to_snek_case(enum_name),
+ to_snek_case(variant_name))
+
+ def to_leave_method_name(name):
+ return "leave_{}".format(to_snek_case(name))
+
+ def to_leave_enum_method_name(enum_name, variant_name):
+ return "leave_enum_{}_variant_{}".format(to_snek_case(enum_name),
+ to_snek_case(variant_name))
+
+ # --- Pass ---
+
+ def emit_call(indent, ty, var):
+ if ty.name == 'Vec':
+ write(indent, "for item in {} {{", var)
+ emit_call(indent + 1, ty.params[0], "item")
+ write(indent, "}")
+ elif ty.name == 'Option':
+ write(indent, "if let Some(item) = {} {{", var)
+ emit_call(indent + 1, ty.params[0], "item")
+ write(indent, "}")
+ elif ty.name in RUST_BUILTIN_TYPES:
+ pass
+ elif ty.name == "SourceAtomSetIndex":
+ pass
+ elif ty.name == "SourceSliceIndex":
+ pass
+ elif ty.name == 'Box':
+ write(indent, "self.{}({});", to_method_name(ty.params[0].name), var)
+ else:
+ write(indent, "self.{}({});", to_method_name(ty.name), var)
+
+ def emit_variant_dict_call(indent, enum_name, variant_name,
+ variant_type):
+ write(indent, "self.{}(",
+ to_enum_method_name(enum_name, variant_name))
+ for field_name, field_ty in variant_type.items():
+ write(indent + 1, "{},", field_name)
+ write(indent, ")")
+
+ def emit_variant_tuple_call(indent, enum_name, variant_name, param):
+ write(indent, "self.{}({})",
+ to_enum_method_name(enum_name, variant_name),
+ param)
+
+ def emit_variant_none_call(indent, enum_name, variant_name):
+ write(indent, "self.{}()",
+ to_enum_method_name(enum_name, variant_name))
+
+ write(0, "// WARNING: This file is auto-generated.")
+ write(0, "")
+ write(0, "#![allow(unused_mut)]")
+ write(0, "#![allow(unused_parens)]")
+ write(0, "#![allow(unused_variables)]")
+ write(0, "#![allow(dead_code)]")
+ write(0, "")
+ write(0, "use crate::arena;")
+ write(0, "use crate::source_atom_set::SourceAtomSetIndex;")
+ write(0, "use crate::source_slice_list::SourceSliceIndex;")
+ write(0, "use crate::types::*;")
+ write(0, "")
+ write(0, "pub trait Pass<'alloc> {")
+ for name, type_decl in ast.type_decls.items():
+ if name == "Void":
+ # Hack in a quick fix
+ continue
+
+ write(1, "fn {}(&mut self, ast: &'alloc {}) {{",
+ to_method_name(type_decl.name), Type(name).to_rust_type(ast))
+
+ write(2, "self.{}(ast);",
+ to_enter_method_name(type_decl.name))
+
+ type_decl.write_rust_pass_method_body(write, emit_call,
+ emit_variant_dict_call,
+ emit_variant_tuple_call,
+ emit_variant_none_call)
+
+ write(2, "self.{}(ast);",
+ to_leave_method_name(type_decl.name))
+
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(&mut self, ast: &'alloc {}) {{",
+ to_enter_method_name(type_decl.name),
+ Type(name).to_rust_type(ast))
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(&mut self, ast: &'alloc {}) {{",
+ to_leave_method_name(type_decl.name),
+ Type(name).to_rust_type(ast))
+ write(1, "}")
+ write(0, "")
+
+ if isinstance(type_decl, Enum):
+ for variant_name, variant_type in type_decl.variants.items():
+ if variant_type is None:
+ write(1, "fn {}(&mut self) {{",
+ to_enum_method_name(type_decl.name,
+ variant_name)),
+ write(1, "}")
+ write(0, "")
+ elif isinstance(variant_type, dict):
+ def write_field_params(indent, write, variant_type,
+ ast):
+ for field_name, field_ty in variant_type.items():
+ write(2, "{}: &'alloc {},",
+ field_name, field_ty.to_rust_type(ast))
+
+ write(1, "fn {}(",
+ to_enum_method_name(type_decl.name,
+ variant_name)),
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+
+ write(2, "self.{}(",
+ to_enter_enum_method_name(type_decl.name,
+ variant_name))
+ for field_name, field_ty in variant_type.items():
+ write(3, "{},", field_name)
+ write(2, ");")
+
+ type_decl.write_rust_pass_variant_dict_method_body(
+ emit_call, variant_type)
+
+ write(2, "self.{}(",
+ to_leave_enum_method_name(type_decl.name,
+ variant_name))
+ for field_name, field_ty in variant_type.items():
+ write(3, "{},", field_name)
+ write(2, ");")
+
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(",
+ to_enter_enum_method_name(type_decl.name,
+ variant_name))
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(",
+ to_leave_enum_method_name(type_decl.name,
+ variant_name))
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+ write(1, "}")
+ write(0, "")
+ else:
+ def write_field_params(indent, write, variant_type,
+ ast):
+ write(2, "ast: &'alloc {},",
+ variant_type.to_rust_type(ast))
+
+ write(1, "fn {}(",
+ to_enum_method_name(type_decl.name,
+ variant_name)),
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+
+ write(2, "self.{}(ast);",
+ to_enter_enum_method_name(type_decl.name,
+ variant_name))
+
+ type_decl.write_rust_pass_variant_tuple_method_body(
+ emit_call, variant_type, "ast")
+
+ write(2, "self.{}(ast);",
+ to_leave_enum_method_name(type_decl.name,
+ variant_name))
+
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(",
+ to_enter_enum_method_name(type_decl.name,
+ variant_name))
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+ write(1, "}")
+ write(0, "")
+
+ write(1, "fn {}(",
+ to_leave_enum_method_name(type_decl.name,
+ variant_name))
+ write(2, "&mut self,")
+ write_field_params(2, write, variant_type, ast)
+ write(1, ") {")
+ write(1, "}")
+ write(0, "")
+
+ write(0, "}")
+ write(0, "")
+
+
+def ast_(ast):
+ with open("src/types_generated.rs", "w+") as f:
+ def write(*args):
+ write_impl(f, *args)
+ write(0, "// WARNING: This file is auto-generated.")
+ write(0, "")
+ write(0, "use crate::source_location::SourceLocation;")
+ write(0, "use crate::arena;")
+ write(0, "use crate::source_atom_set::SourceAtomSetIndex;")
+ write(0, "use crate::source_slice_list::SourceSliceIndex;")
+ write(0, "")
+ for type_decl in ast.type_decls.values():
+ type_decl.write_rust_type_decl(ast, write)
+
+
+def type_id(ast):
+ with open("src/type_id_generated.rs", "w+") as f:
+ def write(*args):
+ write_impl(f, *args)
+ write(0, "// WARNING: This file is auto-generated.")
+ write(0, "")
+ write(0, "use crate::types::*;")
+ write(0, "")
+
+ write(0, "#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]")
+ write(0, "pub enum NodeTypeId {")
+
+ for type_decl in ast.type_decls.values():
+ if type_decl.name in ['Box', 'Token', 'Vec', 'Void']:
+ continue
+
+ write(1, "{},", type_decl.name)
+
+ write(0, "}")
+ write(0, "")
+
+ write(0, "pub trait NodeTypeIdAccessor {")
+ write(1, "fn get_type_id(&self) -> NodeTypeId;")
+ write(0, "}")
+ write(0, "")
+
+ for type_decl in ast.type_decls.values():
+ if type_decl.name in ['Box', 'Token', 'Vec', 'Void']:
+ continue
+
+ write(0, "impl<'alloc> NodeTypeIdAccessor for {}{} {{",
+ type_decl.name,
+ type_decl.lifetime_params())
+ write(1, "fn get_type_id(&self) -> NodeTypeId {")
+ write(2, "NodeTypeId::{}", type_decl.name)
+ write(1, "}")
+ write(0, "}")
+ write(0, "")
+
+
+def dump(ast):
+ with open('src/dump_generated.rs', 'w+') as f:
+ def write(*args):
+ write_impl(f, *args)
+
+ write(0, """\
+ // WARNING: This file is auto-generated by crates/ast/generate_ast.py.
+
+ #![allow(unused_variables)]
+
+ use crate::arena;
+ use crate::source_atom_set::{SourceAtomSet, SourceAtomSetIndex};
+ use crate::source_slice_list::{SourceSliceList, SourceSliceIndex};
+ use crate::types::*;
+ use std::ops::Deref;
+ use std::io;
+
+ fn newline<W>(out: &mut W, depth: usize)
+ where
+ W: io::Write,
+ {
+ writeln!(out, "").expect("failed to dump");
+ for i in 0..depth {
+ write!(out, " ").expect("failed to dump");
+ }
+ }
+
+ pub trait ASTDump {
+ fn dump_with_atoms<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList
+ )
+ where
+ W: io::Write,
+ {
+ self.dump_with_atoms_at(out, atoms, slices, 0);
+ writeln!(out, "").expect("failed to dump");
+ }
+
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where W: io::Write;
+ }
+ """)
+
+ for type_decl in ast.type_decls.values():
+ if type_decl.name in ['Box', 'Token', 'Vec', 'Void']:
+ continue
+
+ write(0, 'impl<\'alloc> ASTDump for {}{} {{',
+ type_decl.name,
+ type_decl.lifetime_params())
+ write(1,
+ 'fn dump_with_atoms_at<W>(&self, out: &mut W, '
+ 'atoms: &SourceAtomSet, slices: &SourceSliceList, '
+ 'depth: usize)')
+ write(2, 'where W: io::Write')
+ write(1, '{')
+
+ type_decl.write_rust_dump_method_body(write)
+ write(1, '}')
+ write(0, '}')
+ write(0, '')
+
+ write(0, """\
+ impl<'alloc, T> ASTDump for arena::Vec<'alloc, T>
+ where
+ T: ASTDump,
+ {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ write!(out, "[").expect("failed to dump");
+ if self.len() > 0 {
+ for item in self {
+ newline(out, depth + 1);
+ item.dump_with_atoms_at(out, atoms, slices, depth + 1);
+ }
+ newline(out, depth);
+ }
+ write!(out, "]").expect("failed to dump");
+ }
+ }
+
+ impl<T> ASTDump for Option<T>
+ where
+ T: ASTDump,
+ {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ match self {
+ Some(v) => {
+ v.dump_with_atoms_at(out, atoms, slices, depth);
+ }
+ None => {
+ write!(out, "None").expect("failed to dump");
+ }
+ }
+ }
+ }
+
+ impl<'alloc, T> ASTDump for arena::Box<'alloc, T>
+ where
+ T: ASTDump,
+ {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ self.deref().dump_with_atoms_at(out, atoms, slices, depth);
+ }
+ }
+
+ impl ASTDump for bool {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ if *self {
+ write!(out, "true").expect("failed to dump");
+ } else {
+ write!(out, "false").expect("failed to dump");
+ }
+ }
+ }
+
+ impl ASTDump for SourceAtomSetIndex {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ write!(out, "{:?}", atoms.get(self.clone()))
+ .expect("failed to dump");
+ }
+ }
+
+ impl ASTDump for SourceSliceIndex {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ write!(out, "{:?}", slices.get(self.clone()))
+ .expect("failed to dump");
+ }
+ }
+
+ impl ASTDump for f64 {
+ fn dump_with_atoms_at<W>(
+ &self,
+ out: &mut W,
+ atoms: &SourceAtomSet,
+ slices: &SourceSliceList,
+ depth: usize,
+ )
+ where
+ W: io::Write,
+ {
+ write!(out, "{}", self).expect("failed to dump");
+ }
+ }
+ """)
+
+
+class AggregateTypeDecl:
+ def __init__(self):
+ self.has_lifetime = None
+
+ def lifetime_params(self):
+ if self.has_lifetime:
+ return "<'alloc>"
+ return ""
+
+
+class Struct(AggregateTypeDecl):
+ def __init__(self, name, struct_json):
+ AggregateTypeDecl.__init__(self)
+ self.name = name
+ self.fields = {
+ name: parse_type(ty)
+ for name, ty in struct_json.items()
+ if name != '_type'
+ }
+
+ def field_types(self):
+ return iter(self.fields.values())
+
+ def write_rust_type_decl(self, ast, write):
+ if len(self.fields) == 0:
+ write(0, "#[derive(Default, Debug, PartialEq)]")
+ else:
+ write(0, "#[derive(Debug, PartialEq)]")
+ lifetime_params = self.lifetime_params()
+ write(0, "pub struct {}{} {{", self.name, lifetime_params)
+ for field, field_type in self.fields.items():
+ write(1, "pub {}: {},", field, field_type.to_rust_type(ast))
+ write(1, "pub loc: SourceLocation,")
+ write(0, "}")
+ write(0, "")
+
+ def write_rust_pass_method_body(self, write, emit_call,
+ emit_variant_dict_call,
+ emit_variant_tuple_call,
+ emit_variant_none_call):
+ for name, ty in self.fields.items():
+ emit_call(2, ty, "&ast.{}".format(name))
+
+ def write_rust_dump_method_body(self, write):
+ write(2, 'write!(out, "({}").expect("failed to dump");', self.name)
+ for name, ty in self.fields.items():
+ if len(self.fields.items()) > 1:
+ write(2, 'newline(out, depth + 1);')
+ else:
+ write(2, 'write!(out, " ").expect("failed to dump");')
+ write(2, 'write!(out, "{}=").expect("failed to dump");', name)
+ write(2, 'self.{}.dump_with_atoms_at(out, atoms, slices, depth + 1);', name)
+ write(2, 'write!(out, ")").expect("failed to dump");')
+
+
+class Enum(AggregateTypeDecl):
+ def __init__(self, name, enum_json):
+ AggregateTypeDecl.__init__(self)
+
+ def parse_maybe_type(ty):
+ if ty is None:
+ return None
+ if isinstance(ty, dict):
+ return {name: parse_type(field_ty) for name, field_ty in ty.items()}
+ return parse_type(ty)
+
+ self.name = name
+ self.variants = {
+ name: parse_maybe_type(ty)
+ for name, ty in enum_json.items()
+ if name != '_type'
+ }
+ self.has_lifetime = None
+
+ def field_types(self):
+ for var in self.variants.values():
+ if isinstance(var, dict):
+ yield from var.values()
+ else:
+ yield var
+
+ def write_rust_type_decl(self, ast, write):
+ write(0, "#[derive(Debug, PartialEq)]")
+ lifetime_params = self.lifetime_params()
+ write(0, "pub enum {}{} {{", self.name, lifetime_params)
+ for variant_name, ty in self.variants.items():
+ if ty is None:
+ write(1, "{} {{", variant_name)
+ write(2, "loc: SourceLocation,")
+ write(1, "},")
+ elif isinstance(ty, dict):
+ write(1, "{} {{", variant_name)
+ for field_name, field_ty in ty.items():
+ write(2, "{}: {},", field_name, field_ty.to_rust_type(ast))
+ write(2, "loc: SourceLocation,")
+ write(1, "},")
+ else:
+ write(1, "{}({}),", variant_name, ty.to_rust_type(ast))
+ write(0, "}")
+ write(0, "")
+
+ def write_rust_pass_method_body(self, write, emit_call,
+ emit_variant_dict_call,
+ emit_variant_tuple_call,
+ emit_variant_none_call):
+ write(2, "match ast {")
+ for variant_name, variant_type in self.variants.items():
+ if variant_type is None:
+ write(3, "{}::{} {{ .. }} => {{", self.name, variant_name)
+ emit_variant_none_call(4, self.name, variant_name)
+ write(3, "}")
+ elif isinstance(variant_type, dict):
+ write(3, "{}::{} {{ {}, .. }} => {{",
+ self.name, variant_name, ', '.join(variant_type.keys()))
+ emit_variant_dict_call(4, self.name, variant_name, variant_type)
+ write(3, "}")
+ else:
+ write(3, "{}::{}(ast) => {{", self.name, variant_name)
+ emit_variant_tuple_call(4, self.name, variant_name, "ast")
+ write(3, "}")
+ write(2, "}")
+
+ def write_rust_pass_variant_dict_method_body(self, emit_call,
+ variant_type):
+ for field_name, field_ty in variant_type.items():
+ if field_ty.name == 'Vec':
+ emit_call(2, field_ty, '{}.iter()'.format(field_name))
+ else:
+ emit_call(2, field_ty, field_name)
+
+ def write_rust_pass_variant_tuple_method_body(self, emit_call,
+ variant_type, param):
+ emit_call(2, variant_type, param)
+
+ def write_rust_dump_method_body(self, write):
+ write(2, 'match self {')
+ for variant_name, variant_type in self.variants.items():
+ if variant_type is None:
+ write(3, '{}::{} {{ .. }} => {{', self.name, variant_name)
+ write(4, 'write!(out, "{}").expect("failed to dump");', variant_name)
+ write(3, '}')
+ elif isinstance(variant_type, dict):
+ write(3, '{}::{} {{ {}, .. }} => {{',
+ self.name, variant_name, ', '.join(variant_type.keys()))
+ write(4, 'write!(out, "({}").expect("failed to dump");', variant_name)
+
+ for field_name, field_ty in variant_type.items():
+ if len(variant_type.items()) > 1:
+ write(4, 'newline(out, depth + 1);')
+ else:
+ write(4, 'write!(out, " ").expect("failed to dump");')
+ write(4, 'write!(out, "{}=").expect("failed to dump");', field_name)
+ write(4, '{}.dump_with_atoms_at(out, atoms, slices, depth + 1);', field_name)
+
+ write(4, 'write!(out, ")").expect("failed to dump");')
+ write(3, '}')
+ else:
+ write(3, '{}::{}(ast) => {{', self.name, variant_name)
+ write(4, 'ast.dump_with_atoms_at(out, atoms, slices, depth);')
+ write(3, '}')
+ write(2, '}')
+
+
+class Ast:
+ def __init__(self, ast_json):
+ self.type_decls = {}
+ for name, contents in ast_json.items():
+ _type = contents["_type"]
+ if _type == "struct":
+ t = Struct(name, contents)
+ elif _type == "enum":
+ t = Enum(name, contents)
+ else:
+ raise ValueError("unrecognized _type: " + repr(_type))
+ self.type_decls[name] = t
+
+ for name in self.type_decls:
+ self._has_lifetime(name)
+
+ def _has_lifetime(self, name):
+ ty = self.type_decls[name]
+ if ty.has_lifetime == "computing":
+ raise ValueError("invalid AST structure: {} contains itself. Try adding a Box."
+ .format(name))
+ if ty.has_lifetime is None:
+ ty.has_lifetime = "computing"
+ ty.has_lifetime = any(
+ field_ty is not None and self.type_has_lifetime(field_ty)
+ for field_ty in ty.field_types()
+ )
+ return ty.has_lifetime
+
+ def type_has_lifetime(self, ty):
+ return (
+ ty is not None
+ and (ty.name == 'Box'
+ or ty.name == 'Vec'
+ or any(self.type_has_lifetime(u)
+ for u in ty.params)
+ or (ty.name in self.type_decls
+ and self._has_lifetime(ty.name))))
+
+
+def main():
+ with open("ast.json", "r") as json_file:
+ ast_json = json.load(json_file)
+ ast = Ast(ast_json)
+
+ ast_(ast)
+ type_id(ast)
+ stack_value(ast)
+ loc_trait(ast)
+ pass_(ast)
+ dump(ast)
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(os.path.abspath(__file__)))
+ main()