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: ifnot isinstance(p, Type): raise ValueError("type parameters must be types, got {!r}".format(p)) return TypeBase.__new__(cls, name, 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 isNoneor token == current
current = next(tokens, None) if current isnotNone:
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 isnotNoneand ident_re.match(current) isnotNone
def parse_params():
params = [] while current != '>':
params.append(parse_ty()) if current == ',':
consume(',') return params
def parse_ty(): ifnot 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 isnotNone: raise ValueError("parse error in type {!r} (extra stuff at end)".format(ty)) return result
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> = Resultalloc, T>, AstError>;
/// Values that can be converted to StackValues, fallibly.
pub trait TryIntoStack<'alloc> {
type Error;
fn try_into_stack(self) -> Result<StackValue<'alloc>, Self::Error>;
} """)
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};
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");
}
}
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 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 isNone: returnNone 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): yieldfrom 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 isNone:
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, "")
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 isNone:
ty.has_lifetime = "computing"
ty.has_lifetime = any(
field_ty isnotNoneand 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 isnotNone 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)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.