Source code
Revision control
Copy as Markdown
Other Tools
#!/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):
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()