468 lines
15 KiB
Rust
468 lines
15 KiB
Rust
use crate::gen::schema::*;
|
|
use crate::syntax::block::constructors::*;
|
|
use crate::syntax::block::Item;
|
|
use crate::*;
|
|
|
|
use super::context::FunctionContext;
|
|
use super::context::ModuleContext;
|
|
use super::context::ModuleContextMode;
|
|
use super::context::RefRenderStyle;
|
|
use super::names;
|
|
use super::types::*;
|
|
|
|
#[derive(Debug)]
|
|
pub struct ParserPlugin;
|
|
|
|
impl compiler::Plugin for ParserPlugin {
|
|
fn generate_definition(
|
|
&self,
|
|
module_ctxt: &mut ModuleContext,
|
|
definition_name: &str,
|
|
definition: &Definition,
|
|
) {
|
|
if let ModuleContextMode::TargetGeneric = module_ctxt.mode {
|
|
gen_definition_parser(module_ctxt, definition_name, definition)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn gen_definition_parser(m: &mut ModuleContext, n: &str, d: &Definition) {
|
|
let ty = definition_type(&m.module_path, Purpose::Codegen, n, d);
|
|
|
|
m.define_function(n, |mut ctxt| {
|
|
let mut body = vec![];
|
|
|
|
match d {
|
|
Definition::Or {
|
|
pattern_0,
|
|
pattern_1,
|
|
pattern_n,
|
|
} => {
|
|
let mut ps = vec![&**pattern_0, &**pattern_1];
|
|
ps.extend(pattern_n);
|
|
for NamedAlternative {
|
|
variant_label: name,
|
|
pattern: pat,
|
|
} in ps
|
|
{
|
|
let fname = seq![
|
|
"parse_",
|
|
names::render_fieldname(n),
|
|
"_",
|
|
names::render_fieldname(name)
|
|
];
|
|
let ctorname = item(name![
|
|
names::render_constructor(n),
|
|
names::render_constructor(name)
|
|
]);
|
|
ctxt.m
|
|
.define_function(&(n.to_owned() + "::" + name), |mut ctxt| {
|
|
let mut body = Vec::new();
|
|
let dest = pattern_parser(&mut ctxt, pat, "value", None, &mut body);
|
|
let dest = dest.as_ref().map(String::as_str);
|
|
construct(&ctxt, ctorname, false, &pattern_type(pat), dest, &mut body);
|
|
|
|
item(seq![
|
|
"fn ",
|
|
fname.clone(),
|
|
ctxt.m.parse_unparse_generic_decls(&ty),
|
|
"(_ctxt: _L, value: &",
|
|
ctxt.m.any_type(),
|
|
") -> ",
|
|
"std::result::Result<",
|
|
names::render_constructor(n),
|
|
ty.generic_arg(ctxt.m),
|
|
", _support::ParseError> ",
|
|
codeblock(body)
|
|
])
|
|
});
|
|
body.push(item(seq![
|
|
"if let Ok(r) = ",
|
|
fname,
|
|
"(_ctxt, value) { return Ok(r); }"
|
|
]));
|
|
}
|
|
body.push(item(seq![ctxt.err_code()]));
|
|
}
|
|
Definition::And {
|
|
pattern_0,
|
|
pattern_1,
|
|
pattern_n,
|
|
} => {
|
|
let mut ps = vec![&**pattern_0, &**pattern_1];
|
|
ps.extend(pattern_n);
|
|
for e in &ps {
|
|
named_pattern_parser(&mut ctxt, e, "value", None, &mut body);
|
|
}
|
|
construct(
|
|
&ctxt,
|
|
item(names::render_constructor(n)),
|
|
true,
|
|
&record_type(&ps),
|
|
None,
|
|
&mut body,
|
|
);
|
|
}
|
|
Definition::Pattern(p) => {
|
|
let dest = pattern_parser(&mut ctxt, p, "value", None, &mut body);
|
|
let dest = dest.as_ref().map(String::as_str);
|
|
construct(
|
|
&ctxt,
|
|
item(names::render_constructor(n)),
|
|
true,
|
|
&pattern_type(p),
|
|
dest,
|
|
&mut body,
|
|
);
|
|
}
|
|
}
|
|
|
|
item(seq![
|
|
"impl",
|
|
ctxt.m.parse_unparse_generic_decls(&ty),
|
|
" _support::Parse",
|
|
anglebrackets!["_L", ctxt.m.any_type()],
|
|
" for ",
|
|
names::render_constructor(n),
|
|
ty.generic_arg(ctxt.m),
|
|
" ",
|
|
codeblock![seq![
|
|
"fn parse(_ctxt: _L, value: &",
|
|
ctxt.m.any_type(),
|
|
")",
|
|
" -> std::result::Result<Self, _support::ParseError> ",
|
|
codeblock(body)
|
|
]]
|
|
])
|
|
});
|
|
}
|
|
|
|
fn construct(
|
|
ctxt: &FunctionContext,
|
|
ctorname: Item,
|
|
is_struct: bool,
|
|
ty: &TSimple,
|
|
dest: Option<&str>,
|
|
body: &mut Vec<Item>,
|
|
) {
|
|
match ty {
|
|
TSimple::Field(TField::Unit) => body.push(item(seq!["Ok(", ctorname, ")"])),
|
|
TSimple::Field(fieldty) => body.push(item(seq![
|
|
"Ok(",
|
|
ctorname,
|
|
parens![store_wrap(is_struct, fieldty, dest.unwrap())],
|
|
")"
|
|
])),
|
|
TSimple::Record(_) => body.push(item(seq![
|
|
"Ok(",
|
|
ctorname,
|
|
" ",
|
|
braces(
|
|
ctxt.captures
|
|
.iter()
|
|
.map(|c| item(seq![
|
|
c.field_name.clone(),
|
|
": ",
|
|
store_wrap(is_struct, &c.ty, &c.source_expr)
|
|
]))
|
|
.collect()
|
|
),
|
|
")"
|
|
])),
|
|
}
|
|
}
|
|
|
|
fn store_wrap(is_struct: bool, ty: &TField, expr: &str) -> String {
|
|
match ty {
|
|
TField::Unit | TField::Array(_) | TField::Set(_) | TField::Map(_, _) => expr.to_owned(),
|
|
TField::Ref(_) => {
|
|
if is_struct {
|
|
expr.to_owned()
|
|
} else {
|
|
format!("std::boxed::Box::new({})", expr)
|
|
}
|
|
}
|
|
TField::Base(_) | TField::Any | TField::Embedded => format!("{}.clone()", expr),
|
|
}
|
|
}
|
|
|
|
fn simple_pattern_parser(
|
|
ctxt: &mut FunctionContext,
|
|
p: &SimplePattern,
|
|
src: &str,
|
|
sequence_base: Option<usize>,
|
|
body: &mut Vec<Item>,
|
|
) -> String {
|
|
let dest = ctxt.gentempname();
|
|
match p {
|
|
SimplePattern::Any => {
|
|
ctxt.define_atom(body, &dest, item(src.to_owned()));
|
|
dest
|
|
}
|
|
SimplePattern::Atom { atom_kind: k } => {
|
|
let converter = match &**k {
|
|
AtomKind::Boolean => "to_boolean",
|
|
AtomKind::Double => "to_double",
|
|
AtomKind::SignedInteger => "to_signedinteger",
|
|
AtomKind::String => "to_string",
|
|
AtomKind::ByteString => "to_bytestring",
|
|
AtomKind::Symbol => "to_symbol",
|
|
};
|
|
ctxt.define_atom(
|
|
body,
|
|
&dest,
|
|
item(seq![src.to_owned(), ".value().", converter, "()?"]),
|
|
);
|
|
dest
|
|
}
|
|
SimplePattern::Embedded { .. } => {
|
|
ctxt.define_atom(
|
|
body,
|
|
&dest,
|
|
item(seq![parens![seq![
|
|
src.to_owned(),
|
|
".value().to_embedded()?"
|
|
]]]),
|
|
);
|
|
dest
|
|
}
|
|
SimplePattern::Lit { value } => {
|
|
body.push(item(seq![
|
|
"if ",
|
|
src.to_owned(),
|
|
" != ",
|
|
ctxt.m.define_literal(value),
|
|
" { return ",
|
|
ctxt.err_code(),
|
|
"; }"
|
|
]));
|
|
ctxt.define_atom(body, &dest, item("()"));
|
|
dest
|
|
}
|
|
SimplePattern::Seqof { pattern } => {
|
|
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
|
let tmp = ctxt.gentempname();
|
|
let mut inner = Vec::new();
|
|
let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner);
|
|
inner.push(item(seq![
|
|
dest.to_owned(),
|
|
".push(",
|
|
store_wrap(true, &field_type(pattern), &item_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &dest, item("std::vec::Vec::new()"));
|
|
body.push(item(seq![
|
|
"for ",
|
|
tmp.to_owned(),
|
|
" in &",
|
|
src.to_owned(),
|
|
brackets![seq![n.to_string(), ".."]],
|
|
" ",
|
|
codeblock(inner)
|
|
]));
|
|
dest
|
|
}
|
|
SimplePattern::Setof { pattern } => {
|
|
let tmp = ctxt.gentempname();
|
|
let mut inner = Vec::new();
|
|
let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner);
|
|
inner.push(item(seq![
|
|
dest.to_owned(),
|
|
".insert(",
|
|
store_wrap(true, &field_type(pattern), &item_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &dest, item("preserves::value::Set::new()"));
|
|
body.push(item(seq![
|
|
"for ",
|
|
tmp.to_owned(),
|
|
" in ",
|
|
src.to_owned(),
|
|
".value().to_set()?",
|
|
" ",
|
|
codeblock(inner)
|
|
]));
|
|
dest
|
|
}
|
|
SimplePattern::Dictof { key, value } => {
|
|
let tmp_key = ctxt.gentempname();
|
|
let tmp_value = ctxt.gentempname();
|
|
let mut inner = Vec::new();
|
|
let key_dest = simple_pattern_parser(ctxt, key, &tmp_key, None, &mut inner);
|
|
let value_dest = simple_pattern_parser(ctxt, value, &tmp_value, None, &mut inner);
|
|
inner.push(item(seq![
|
|
dest.to_owned(),
|
|
".insert(",
|
|
store_wrap(true, &field_type(key), &key_dest),
|
|
", ",
|
|
store_wrap(true, &field_type(value), &value_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &dest, item("preserves::value::Map::new()"));
|
|
body.push(item(seq![
|
|
"for (",
|
|
tmp_key.to_owned(),
|
|
", ",
|
|
tmp_value.to_owned(),
|
|
")",
|
|
" in ",
|
|
src.to_owned(),
|
|
".value().to_dictionary()?",
|
|
" ",
|
|
codeblock(inner)
|
|
]));
|
|
dest
|
|
}
|
|
SimplePattern::Ref(r) => {
|
|
let tf = name![ctxt.m.render_ref(&**r, RefRenderStyle::Bare), "parse"];
|
|
ctxt.define_atom(
|
|
body,
|
|
&dest,
|
|
item(seq![tf, parens!["_ctxt", src.to_owned()], "?"]),
|
|
);
|
|
dest
|
|
}
|
|
}
|
|
}
|
|
|
|
fn sequenceify(
|
|
ctxt: &mut FunctionContext,
|
|
src: &str,
|
|
sequence_base: Option<usize>,
|
|
body: &mut Vec<Item>,
|
|
) -> (String, usize) {
|
|
match sequence_base {
|
|
Some(n) => (src.to_owned(), n),
|
|
None => {
|
|
let tmp = ctxt.gentempname();
|
|
ctxt.define_atom(
|
|
body,
|
|
&tmp,
|
|
item(seq![src.to_owned(), ".value().to_sequence()?"]),
|
|
);
|
|
(tmp, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fixed_sequence_parser(
|
|
ctxt: &mut FunctionContext,
|
|
base: usize,
|
|
ps: &[NamedPattern],
|
|
src: &str,
|
|
body: &mut Vec<Item>,
|
|
) {
|
|
let mut i = base;
|
|
let required_count = ps.len();
|
|
if required_count > 0 {
|
|
body.push(item(seq![
|
|
"if ",
|
|
src.to_owned(),
|
|
".len()",
|
|
if base > 0 {
|
|
seq![" - ", base.to_string()]
|
|
} else {
|
|
seq![]
|
|
},
|
|
" < ",
|
|
required_count.to_string(),
|
|
" { return ",
|
|
ctxt.err_code(),
|
|
"; }"
|
|
]));
|
|
}
|
|
for p in ps {
|
|
named_pattern_parser(ctxt, p, &format!("(&{}[{}])", src, i), None, body);
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
fn named_pattern_parser(
|
|
ctxt: &mut FunctionContext,
|
|
p: &NamedPattern,
|
|
src: &str,
|
|
sequence_base: Option<usize>,
|
|
body: &mut Vec<Item>,
|
|
) {
|
|
match p {
|
|
NamedPattern::Anonymous(p) => {
|
|
pattern_parser(ctxt, p, src, sequence_base, body);
|
|
}
|
|
NamedPattern::Named(b) => {
|
|
let Binding { name, pattern } = &**b;
|
|
let dest = simple_pattern_parser(ctxt, pattern, src, sequence_base, body);
|
|
let capture_ty = field_type(pattern);
|
|
ctxt.capture(names::render_fieldname(name), capture_ty, dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pattern_parser(
|
|
ctxt: &mut FunctionContext,
|
|
p: &Pattern,
|
|
src: &str,
|
|
sequence_base: Option<usize>,
|
|
body: &mut Vec<Item>,
|
|
) -> Option<String> {
|
|
match p {
|
|
Pattern::SimplePattern(s) => Some(simple_pattern_parser(ctxt, s, src, sequence_base, body)),
|
|
Pattern::CompoundPattern(c) => {
|
|
match &**c {
|
|
CompoundPattern::Rec { label, fields } => {
|
|
let rtmp = ctxt.gentempname();
|
|
ctxt.define_atom(
|
|
body,
|
|
&rtmp,
|
|
item(seq![src.to_owned(), ".value().to_record(None)?"]),
|
|
);
|
|
named_pattern_parser(ctxt, &**label, &format!("{}.label()", rtmp), None, body);
|
|
named_pattern_parser(
|
|
ctxt,
|
|
&**fields,
|
|
&format!("{}.fields()", rtmp),
|
|
Some(0),
|
|
body,
|
|
);
|
|
}
|
|
CompoundPattern::Tuple { patterns } => {
|
|
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
|
fixed_sequence_parser(ctxt, n, patterns, &src, body);
|
|
}
|
|
CompoundPattern::TuplePrefix { fixed, variable } => {
|
|
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
|
fixed_sequence_parser(ctxt, n, fixed, &src, body);
|
|
named_pattern_parser(
|
|
ctxt,
|
|
&promote(variable),
|
|
&src,
|
|
Some(n + fixed.len()),
|
|
body,
|
|
);
|
|
}
|
|
CompoundPattern::Dict { entries } => {
|
|
let dtmp = ctxt.gentempname();
|
|
ctxt.define_atom(
|
|
body,
|
|
&dtmp,
|
|
item(seq![src.to_owned(), ".value().to_dictionary()?"]),
|
|
);
|
|
for (key_lit, value_pat) in entries.0.iter() {
|
|
let vtmp = ctxt.gentempname();
|
|
let init_expr = item(seq![
|
|
dtmp.to_owned(),
|
|
".get",
|
|
parens![ctxt.m.define_literal(key_lit)],
|
|
".ok_or_else(|| ",
|
|
ctxt.conformance_err_code(),
|
|
")?"
|
|
]);
|
|
ctxt.define_atom(body, &vtmp, init_expr);
|
|
named_pattern_parser(ctxt, &promote(value_pat), &vtmp, None, body);
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}
|