from datetime import datetime
import re
from textwrap import indent, dedent
from ast import *
from get_node_id import has_get_node_id_impl
from get_span import has_get_span_impl
from util import *
def prec_name_to_expr(name, inc):
inc_str = '' if not inc else ' + 1'
if name.isupper():
return 'parser::PREC_%s%s' % (name, inc_str)
else:
return 'parser::AssocOp::%s.precedence() as i8%s' % (name, inc_str)
def field_prec_expr(f, first, suffix='1'):
prec_val = 'parser::PREC_RESET'
prec = f.attrs.get('prec')
if prec:
prec_val = prec_name_to_expr(prec, False)
prec_inc = f.attrs.get('prec_inc')
if prec_inc:
prec_val = prec_name_to_expr(prec_inc, True)
left_of = f.attrs.get('prec_left_of_binop')
if left_of:
return 'binop_left_prec(%s)' % (left_of + suffix)
right_of = f.attrs.get('prec_right_of_binop')
if right_of:
return 'binop_right_prec(%s)' % (right_of + suffix)
prec_first = f.attrs.get('prec_first')
if first and prec_first:
prec_val = prec_name_to_expr(prec_first, False)
ctor = f.attrs.get('prec_special', 'Normal')
return 'ExprPrec::%s(%s)' % (ctor, prec_val)
SELF_FIELD_RE = re.compile(r'\bself.([a-zA-Z0-9_]+)\b')
def rewrite_field_expr(expr, fmt):
def repl(m):
var_name = fmt % m.group(1)
return '(*%s)' % var_name
return SELF_FIELD_RE.sub(repl, expr)
DEFAULT_GEN_TRAITS = {'Rewrite', 'MaybeRewriteSeq', 'RecoverChildren'}
DEFAULT_STRUCT_ENUM_GEN_TRAITS = {'Recursive'}
def type_has_impl(d, trait):
skip = d.attrs.get('rewrite_skip')
if skip is not None and trait in skip.split(','):
return False
gen = d.attrs.get('rewrite_gen')
if gen is not None and trait in gen.split(','):
return True
custom = d.attrs.get('rewrite_custom')
if custom is not None and trait in custom.split(','):
return True
if 'rewrite_print' in d.attrs and trait in ('PrintParse', 'Splice'):
return True
if 'rewrite_print_recover' in d.attrs and trait in ('PrintParse', 'Splice', 'Recover'):
return True
if 'rewrite_seq_item' in d.attrs and trait == 'SeqItem':
return True
if trait in DEFAULT_GEN_TRAITS:
return True
if isinstance(d, (Struct, Enum)) and trait in DEFAULT_STRUCT_ENUM_GEN_TRAITS:
return True
return False
def type_needs_generated_impl(d, trait):
skip = d.attrs.get('rewrite_skip')
if skip is not None and trait in skip.split(','):
return False
gen = d.attrs.get('rewrite_gen')
if gen is not None and trait in gen.split(','):
return True
if 'rewrite_seq_item' in d.attrs and trait == 'SeqItem':
return True
if trait in DEFAULT_GEN_TRAITS:
return True
if isinstance(d, (Struct, Enum)) and trait in DEFAULT_STRUCT_ENUM_GEN_TRAITS:
return True
return False
def get_rewrite_strategies(d):
strats_str = d.attrs.get('rewrite_strategies')
if strats_str is not None:
return strats_str.split(',')
strats = []
if isinstance(d, Flag):
strats.append('equal')
else:
if type_has_impl(d, 'Recursive'):
strats.append('recursive')
extra_strats = d.attrs.get('rewrite_extra_strategies')
if extra_strats is not None:
strats.extend(extra_strats.split(','))
if all(type_has_impl(d, t) for t in ('PrintParse', 'RecoverChildren', 'Splice')):
strats.append('print')
return strats
@linewise
def do_record_node_span(d, span_node, id_node, rcx):
has_id = has_get_node_id_impl(d)
has_span = has_get_span_impl(d)
if not has_id or not has_span:
return
yield '{'
yield ' let span = %s.get_span();' % span_node
yield ' let id = %s.get_node_id();' % id_node
yield ' %s.record_node_span(span, id);' % rcx
yield '}'
@linewise
def do_rewrite_impl(d):
if 'rewrite_ignore' in d.attrs:
yield '#[allow(unused)]'
yield 'impl Rewrite for %s {' % d.name
yield ' fn rewrite(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {'
yield ' // Rewrite mode: ignore'
yield ' true'
yield ' }'
yield '}'
return
yield '#[allow(unused)]'
yield 'impl Rewrite for %s {' % d.name
yield ' fn rewrite(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {'
if has_field(d, 'id'):
yield ' trace!("{:?}: rewrite: begin (%s)", new.id);' % d.name
for strat in get_rewrite_strategies(d):
yield ' let mark = rcx.mark();'
if has_field(d, 'id'):
yield ' trace!("{:?}: rewrite: try %s", new.id);' % strat
yield ' let ok = strategy::%s::rewrite(old, new, rcx.borrow());' % strat
yield ' if ok {'
if has_field(d, 'id'):
yield ' trace!("{:?}: rewrite: %s succeeded", new.id);' % strat
yield ' return true;'
yield ' } else {'
if has_field(d, 'id'):
yield ' trace!("{:?}: rewrite: %s FAILED", new.id);' % strat
yield ' rcx.rewind(mark);'
yield ' }'
yield ''
if has_field(d, 'id'):
yield ' trace!("{:?}: rewrite: ran out of strategies!", new.id);'
yield ' false'
yield ' }'
yield '}'
@linewise
def generate_rewrite_impls(decls):
yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
yield '// Produced %s by process_ast.py' % (datetime.now(),)
yield ''
for d in decls:
if type_needs_generated_impl(d, 'Rewrite'):
yield do_rewrite_impl(d)
@linewise
def do_recursive_body(se, target1, target2):
contains_expr = 'prec_contains_expr' in se.attrs
yield 'match (%s, %s) {' % (target1, target2)
for v, path in variants_paths(se):
yield ' (&%s,' % struct_pattern(v, path, '1')
yield ' &%s) => {' % struct_pattern(v, path, '2')
for f in v.fields:
if 'rewrite_ignore' in f.attrs:
continue
seq_rewrite_mode = f.attrs.get('seq_rewrite')
if seq_rewrite_mode is None and 'seq_rewrite_outer_span' in f.attrs:
seq_rewrite_mode = ''
if seq_rewrite_mode is None:
mk_rewrite = lambda old, new: \
'Rewrite::rewrite({old}, {new}, rcx.borrow())'.format(
old=old, new=new)
else:
outer_span_expr = f.attrs.get('seq_rewrite_outer_span')
if outer_span_expr is not None:
outer_span_expr = rewrite_field_expr(outer_span_expr, '%s2')
else:
outer_span_expr = 'DUMMY_SP'
mk_rewrite = lambda old, new: \
'rewrite_seq({old}, {new}, {outer}, rcx.borrow())'.format(
old=old, new=new, outer=outer_span_expr)
yield ' ({'
if 'prec_first' in f.attrs:
yield ' let old = rcx.replace_expr_prec(%s);' % \
field_prec_expr(f, True)
yield ' let ok = Rewrite::rewrite(&%s1[0], &%s2[0], ' \
'rcx.borrow());' % (f.name, f.name)
yield ' rcx.replace_expr_prec(%s);' % field_prec_expr(f, False)
rewrite_expr = mk_rewrite('&%s1[1..]' % f.name, '&%s2[1..]' % f.name)
yield ' let ok = ok && %s;' % rewrite_expr
yield ' rcx.replace_expr_prec(old);'
yield ' ok'
else:
if contains_expr:
yield ' let old = rcx.replace_expr_prec(%s);' % \
field_prec_expr(f, False)
rewrite_expr = mk_rewrite('%s1' % f.name, '%s2' % f.name)
yield ' let ok = %s;' % rewrite_expr
if contains_expr:
yield ' rcx.replace_expr_prec(old);'
yield ' ok'
yield ' }) &&'
yield ' true'
yield ' }'
yield ' (_, _) => false,'
yield '}'
@linewise
def do_recursive_impl(d):
if 'rewrite_ignore' in d.attrs:
yield '#[allow(unused)]'
yield 'impl Recursive for %s {' % d.name
yield ' fn recursive(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {'
yield indent(do_record_node_span(d, 'old', 'new', 'rcx'), ' ')
yield ' true'
yield ' }'
yield '}'
yield '#[allow(unused)]'
yield 'impl Recursive for %s {' % d.name
yield ' fn recursive(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {'
yield indent(do_record_node_span(d, 'old', 'new', 'rcx'), ' ')
yield indent(do_recursive_body(d, 'old', 'new'), ' ')
yield ' }'
yield '}'
@linewise
def generate_recursive_impls(decls):
yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
yield '// Produced %s by process_ast.py' % (datetime.now(),)
yield ''
for d in decls:
if type_needs_generated_impl(d, 'Recursive'):
yield do_recursive_impl(d)
@linewise
def do_recover_children_match(d):
if not isinstance(d, (Struct, Enum)) or 'rewrite_ignore' in d.attrs:
return
contains_expr = 'prec_contains_expr' in d.attrs
yield 'match (reparsed, new) {'
for v, path in variants_paths(d):
yield ' (&%s,' % struct_pattern(v, path, '_r')
yield ' &%s) => {' % struct_pattern(v, path, '_n')
for f in v.fields:
if 'rewrite_ignore' in f.attrs:
continue
if 'prec_first' in f.attrs:
yield ' let old = rcx.replace_expr_prec(%s);' % \
field_prec_expr(f, True, suffix='_n')
yield ' RecoverChildren::recover_node_and_children(' \
'&%s_r[0], &%s_n[0], rcx.borrow());' % (f.name, f.name)
yield ' rcx.replace_expr_prec(%s);' % \
field_prec_expr(f, False, suffix='_n')
yield ' RecoverChildren::recover_node_and_children(' \
'&%s_r[1..], &%s_n[1..], rcx.borrow());' % (f.name, f.name)
yield ' rcx.replace_expr_prec(old);'
else:
if contains_expr:
yield ' let old = rcx.replace_expr_prec(%s);' % \
field_prec_expr(f, False, suffix='_n')
yield ' RecoverChildren::recover_node_and_children(' \
'%s_r, %s_n, rcx.borrow());' % (f.name, f.name)
if contains_expr:
yield ' rcx.replace_expr_prec(old);'
yield ' },'
yield ' _ => panic!("new and reparsed ASTs don\'t match"),'
yield '}'
@linewise
def do_recover_children_impl(d):
impl_recover = type_has_impl(d, 'Recover')
yield '#[allow(unused)]'
yield 'impl RecoverChildren for %s {' % d.name
yield ' fn recover_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {'
yield indent(do_record_node_span(d, 'reparsed', 'new', 'rcx'), ' ')
yield indent(do_recover_children_match(d), ' ')
yield ' }'
yield ' fn recover_node_and_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {'
if impl_recover:
yield ' if recover(None, reparsed, new, rcx.borrow()) {'
yield ' return;'
yield ' }'
yield ' <Self as RecoverChildren>::recover_children(reparsed, new, rcx);'
yield ' }'
yield ' fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {'
if impl_recover:
yield ' if recover(Some(old_span), reparsed, new, rcx.borrow()) {'
yield ' return;'
yield ' }'
yield ' <Self as RecoverChildren>::recover_children(reparsed, new, rcx);'
yield ' }'
yield '}'
@linewise
def generate_recover_children_impls(decls):
yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
yield '// Produced %s by process_ast.py' % (datetime.now(),)
yield ''
for d in decls:
if type_needs_generated_impl(d, 'RecoverChildren'):
yield do_recover_children_impl(d)
@linewise
def do_seq_item_impl(d):
yield '#[allow(unused)]'
yield 'impl SeqItem for %s {' % d.name
yield ' fn seq_item_id(&self) -> SeqItemId {'
yield ' SeqItemId::Node(self.id)'
yield ' }'
yield '}'
@linewise
def generate_seq_item_impls(decls):
yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
yield '// Produced %s by process_ast.py' % (datetime.now(),)
yield ''
for d in decls:
if type_needs_generated_impl(d, 'SeqItem'):
yield do_seq_item_impl(d)
@linewise
def do_maybe_rewrite_seq_impl(d):
supported = type_has_impl(d, 'SeqItem')
for ty in (d.name, 'P<%s>' % d.name):
yield '#[allow(unused)]'
yield 'impl MaybeRewriteSeq for %s {' % ty
if supported:
yield ' fn maybe_rewrite_seq(old: &[Self],'
yield ' new: &[Self],'
yield ' outer_span: Span,'
yield ' rcx: RewriteCtxtRef) -> bool {'
yield ' trace!("try sequence rewriting for %s");' % d.name
yield ' rewrite_seq(old, new, outer_span, rcx)'
yield ' }'
yield '}'
@linewise
def generate_maybe_rewrite_seq_impls(decls):
yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
yield '// Produced %s by process_ast.py' % (datetime.now(),)
yield ''
for d in decls:
if type_needs_generated_impl(d, 'MaybeRewriteSeq'):
yield do_maybe_rewrite_seq_impl(d)