forked from mirrors/gecko-dev
Import the improvements made in mp4parse-rust repo. The changes would save some redundant copy when calling avif related APIs and provide the ability to get the alpha data of the parsed avif image. Differential Revision: https://phabricator.services.mozilla.com/D98950
1061 lines
38 KiB
Python
Executable file
1061 lines
38 KiB
Python
Executable file
#!/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()
|