diff options
| author | bors <bors@rust-lang.org> | 2013-03-27 21:51:53 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-03-27 21:51:53 -0700 |
| commit | 84ddff3909b5920228642649b7f5cc011c0b900a (patch) | |
| tree | 25cfe9b7ddd20bc08ea56de4fe87dbabf9a885f0 /src/libsyntax | |
| parent | 4954d3e50177c46d260a5340ff91bfada8590ef0 (diff) | |
| parent | c317d3f6fa9475e65b6276743c09444441059ca7 (diff) | |
| download | rust-84ddff3909b5920228642649b7f5cc011c0b900a.tar.gz rust-84ddff3909b5920228642649b7f5cc011c0b900a.zip | |
auto merge of #5578 : erickt/rust/incoming, r=jbclements,erickt
Hey folks,
This patch series does some work on the json decoder, specifically with auto decoding of enums. Previously, we would take this code:
```
enum A {
B,
C(~str, uint)
}
```
and would encode a value of this enum to either `["B", []]` or `["C", ["D", 123]]`. I've changed this to `"B"` or `["C", "D", 123]`. This matches the style of the O'Caml json library [json-wheel](http://mjambon.com/json-wheel.html). I've added tests to make sure all this work.
In order to make this change, I added passing a `&[&str]` vec to `Decode::emit_enum_variant` so the json decoder can convert the name of a variant into it's position. I also changed the impl of `Encodable` for `Option<T>` to have the right upper casing.
I also did some work on the parser, which allows for `fn foo<T: ::cmp::Eq>() { ... }` statements (#5572), fixed the pretty printer properly expanding `debug!("...")` expressions, and removed `ast::expr_vstore_fixed`, which doesn't appear to be used anymore.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/ext/auto_encode.rs | 96 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/ext/quote.rs | 167 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 39 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 41 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 2 |
9 files changed, 292 insertions, 87 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 12d4e9d5e24..6b788810a79 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -386,7 +386,6 @@ pub enum vstore { #[auto_decode] #[deriving(Eq)] pub enum expr_vstore { - expr_vstore_fixed(Option<uint>), // [1,2,3,4] expr_vstore_uniq, // ~[1,2,3,4] expr_vstore_box, // @[1,2,3,4] expr_vstore_mut_box, // @mut [1,2,3,4] @@ -546,12 +545,6 @@ pub struct expr { #[auto_encode] #[auto_decode] #[deriving(Eq)] -pub enum log_level { error, debug, log_other } -// 0 = error, 1 = debug, 2 = log_other - -#[auto_encode] -#[auto_decode] -#[deriving(Eq)] pub enum CallSugar { NoSugar, DoSugar, @@ -598,7 +591,7 @@ pub enum expr_ { expr_break(Option<ident>), expr_again(Option<ident>), expr_ret(Option<@expr>), - expr_log(log_level, @expr, @expr), + expr_log(@expr, @expr), expr_inline_asm(@~str, // asm ~[(@~str, @expr)], // inputs diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs index e81e460e832..2a112f106a8 100644 --- a/src/libsyntax/ext/auto_encode.rs +++ b/src/libsyntax/ext/auto_encode.rs @@ -1059,6 +1059,18 @@ fn mk_enum_deser_body( name: ast::ident, variants: ~[ast::variant] ) -> @ast::expr { + let expr_arm_names = build::mk_base_vec_e( + ext_cx, + span, + do variants.map |variant| { + build::mk_base_str( + ext_cx, + span, + ext_cx.str_of(variant.node.name) + ) + } + ); + let mut arms = do variants.mapi |v_idx, variant| { let body = match variant.node.kind { ast::tuple_variant_kind(ref args) => { @@ -1152,13 +1164,13 @@ fn mk_enum_deser_body( ) ); - // ast for `__d.read_enum_variant($(expr_lambda))` + // ast for `__d.read_enum_variant($expr_arm_names, $(expr_lambda))` let expr_lambda = ext_cx.lambda_expr( ext_cx.expr_method_call( span, ext_cx.expr_var(span, ~"__d"), ext_cx.ident_of(~"read_enum_variant"), - ~[expr_lambda] + ~[expr_arm_names, expr_lambda] ) ); @@ -1174,9 +1186,9 @@ fn mk_enum_deser_body( ) } - #[cfg(test)] mod test { + use core::option::{None, Some}; use std::serialize::Encodable; use std::serialize::Encoder; @@ -1190,6 +1202,9 @@ mod test { CallToEmitNil, CallToEmitStruct(~str,uint), CallToEmitField(~str,uint), + CallToEmitOption, + CallToEmitOptionNone, + CallToEmitOptionSome, // all of the ones I was too lazy to handle: CallToOther } @@ -1281,6 +1296,18 @@ mod test { fn emit_tup_elt(&self, +_idx: uint, f: &fn()) { self.add_unknown_to_log(); f(); } + + fn emit_option(&self, f: &fn()) { + self.add_to_log(CallToEmitOption); + f(); + } + fn emit_option_none(&self) { + self.add_to_log(CallToEmitOptionNone); + } + fn emit_option_some(&self, f: &fn()) { + self.add_to_log(CallToEmitOptionSome); + f(); + } } @@ -1296,13 +1323,58 @@ mod test { Magazine(~str) } - #[test] fn encode_enum_test () { - assert_eq!(to_call_log(Book(34,44)), - ~[CallToEmitEnum (~"Written"), - CallToEmitEnumVariant (~"Book",0,2), - CallToEmitEnumVariantArg (0), - CallToEmitUint (34), - CallToEmitEnumVariantArg (1), - CallToEmitUint (44)]); - } + #[test] + fn test_encode_enum() { + assert_eq!( + to_call_log(Book(34,44)), + ~[ + CallToEmitEnum(~"Written"), + CallToEmitEnumVariant(~"Book",0,2), + CallToEmitEnumVariantArg(0), + CallToEmitUint(34), + CallToEmitEnumVariantArg(1), + CallToEmitUint(44), + ] + ); + } + + pub struct BPos(uint); + + #[auto_encode] + pub struct HasPos { pos : BPos } + + #[test] + fn test_encode_newtype() { + assert_eq!( + to_call_log(HasPos { pos:BPos(48) }), + ~[ + CallToEmitStruct(~"HasPos",1), + CallToEmitField(~"pos",0), + CallToEmitUint(48), + ] + ); + } + + #[test] + fn test_encode_option() { + let mut v = None; + + assert_eq!( + to_call_log(v), + ~[ + CallToEmitOption, + CallToEmitOptionNone, + ] + ); + + v = Some(54u); + assert_eq!( + to_call_log(v), + ~[ + CallToEmitOption, + CallToEmitOptionSome, + CallToEmitUint(54) + ] + ); + } } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index ad71441e046..9499f95f0e7 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -152,11 +152,6 @@ pub fn mk_slice_vec_e(cx: @ext_ctxt, sp: span, +exprs: ~[@ast::expr]) mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::expr_vstore_slice) } -pub fn mk_fixed_vec_e(cx: @ext_ctxt, sp: span, +exprs: ~[@ast::expr]) - -> @ast::expr { - mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), - ast::expr_vstore_fixed(None)) -} pub fn mk_base_str(cx: @ext_ctxt, sp: span, +s: ~str) -> @ast::expr { let lit = ast::lit_str(@s); return mk_lit(cx, sp, lit); diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 6044c3ad3d2..a6f078d07b4 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -42,8 +42,7 @@ pub mod rt { pub use ast::*; pub use parse::token::*; pub use parse::new_parser_from_tts; - pub use codemap::BytePos; - pub use codemap::span; + pub use codemap::{BytePos, span, dummy_spanned}; use print::pprust; use print::pprust::{item_to_str, ty_to_str}; @@ -89,7 +88,7 @@ pub mod rt { } } - impl ToSource for ~[@ast::item] { + impl<'self> ToSource for &'self [@ast::item] { fn to_source(&self, cx: @ext_ctxt) -> ~str { str::connect(self.map(|i| i.to_source(cx)), ~"\n\n") } @@ -101,7 +100,7 @@ pub mod rt { } } - impl ToSource for ~[@ast::Ty] { + impl<'self> ToSource for &'self [@ast::Ty] { fn to_source(&self, cx: @ext_ctxt) -> ~str { str::connect(self.map(|i| i.to_source(cx)), ~", ") } @@ -119,6 +118,90 @@ pub mod rt { } } + impl ToSource for ast::blk { + fn to_source(&self, cx: @ext_ctxt) -> ~str { + pprust::block_to_str(self, cx.parse_sess().interner) + } + } + + impl<'self> ToSource for &'self str { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_str(@str::from_slice(*self))); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for int { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_int(*self as i64, ast::ty_i)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for i8 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_int(*self as i64, ast::ty_i8)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for i16 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_int(*self as i64, ast::ty_i16)); + pprust::lit_to_str(@lit) + } + } + + + impl ToSource for i32 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_int(*self as i64, ast::ty_i32)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for i64 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_int(*self as i64, ast::ty_i64)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for uint { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_uint(*self as u64, ast::ty_u)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for u8 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_uint(*self as u64, ast::ty_u8)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for u16 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_uint(*self as u64, ast::ty_u16)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for u32 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_uint(*self as u64, ast::ty_u32)); + pprust::lit_to_str(@lit) + } + } + + impl ToSource for u64 { + fn to_source(&self, _cx: @ext_ctxt) -> ~str { + let lit = dummy_spanned(ast::lit_uint(*self as u64, ast::ty_u64)); + pprust::lit_to_str(@lit) + } + } + // Alas ... we write these out instead. All redundant. impl ToTokens for ast::ident { @@ -133,7 +216,7 @@ pub mod rt { } } - impl ToTokens for ~[@ast::item] { + impl<'self> ToTokens for &'self [@ast::item] { fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { cx.parse_tts(self.to_source(cx)) } @@ -145,7 +228,7 @@ pub mod rt { } } - impl ToTokens for ~[@ast::Ty] { + impl<'self> ToTokens for &'self [@ast::Ty] { fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { cx.parse_tts(self.to_source(cx)) } @@ -163,6 +246,78 @@ pub mod rt { } } + impl ToTokens for ast::blk { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl<'self> ToTokens for &'self str { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for int { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for i8 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for i16 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for i32 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for i64 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for uint { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for u8 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for u16 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for u32 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + + impl ToTokens for u64 { + fn to_tokens(&self, cx: @ext_ctxt) -> ~[token_tree] { + cx.parse_tts(self.to_source(cx)) + } + } + pub trait ExtParseUtils { fn parse_item(&self, s: ~str) -> @ast::item; fn parse_expr(&self, s: ~str) -> @ast::expr; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9d4cf4e8939..279d57a291e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -552,9 +552,8 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ { expr_ret(ref e) => { expr_ret(e.map(|x| fld.fold_expr(*x))) } - expr_log(i, lv, e) => { + expr_log(lv, e) => { expr_log( - i, fld.fold_expr(lv), fld.fold_expr(e) ) diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index a0c73668a05..0ddf9111e02 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -326,19 +326,32 @@ mod test { @~"fn foo (x : int) { x; }", ~[], new_parse_sess(None)); - assert_eq!(to_json_str(@tts), - ~"[[\"tt_tok\",[null,[\"IDENT\",[\"fn\",false]]]],\ - [\"tt_tok\",[null,[\"IDENT\",[\"foo\",false]]]],\ - [\"tt_delim\",[[[\"tt_tok\",[null,[\"LPAREN\",[]]]],\ - [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\ - [\"tt_tok\",[null,[\"COLON\",[]]]],\ - [\"tt_tok\",[null,[\"IDENT\",[\"int\",false]]]],\ - [\"tt_tok\",[null,[\"RPAREN\",[]]]]]]],\ - [\"tt_delim\",[[[\"tt_tok\",[null,[\"LBRACE\",[]]]],\ - [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\ - [\"tt_tok\",[null,[\"SEMI\",[]]]],\ - [\"tt_tok\",[null,[\"RBRACE\",[]]]]]]]]" - ); + assert_eq!( + to_json_str(@tts), + ~"[\ + [\"tt_tok\",null,[\"IDENT\",\"fn\",false]],\ + [\"tt_tok\",null,[\"IDENT\",\"foo\",false]],\ + [\ + \"tt_delim\",\ + [\ + [\"tt_tok\",null,\"LPAREN\"],\ + [\"tt_tok\",null,[\"IDENT\",\"x\",false]],\ + [\"tt_tok\",null,\"COLON\"],\ + [\"tt_tok\",null,[\"IDENT\",\"int\",false]],\ + [\"tt_tok\",null,\"RPAREN\"]\ + ]\ + ],\ + [\ + \"tt_delim\",\ + [\ + [\"tt_tok\",null,\"LBRACE\"],\ + [\"tt_tok\",null,[\"IDENT\",\"x\",false]],\ + [\"tt_tok\",null,\"SEMI\"],\ + [\"tt_tok\",null,\"RBRACE\"]\ + ]\ + ]\ + ]" + ); let ast1 = new_parser_from_tts(new_parse_sess(None),~[],tts) .parse_item(~[]); let ast2 = parse_item_from_source_str( diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c2e7ecacd20..6ca91791ffd 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -28,7 +28,7 @@ use ast::{expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac}; use ast::{expr_method_call, expr_paren, expr_path, expr_repeat}; use ast::{expr_ret, expr_swap, expr_struct, expr_tup, expr_unary}; use ast::{expr_vec, expr_vstore, expr_vstore_mut_box, expr_inline_asm}; -use ast::{expr_vstore_fixed, expr_vstore_slice, expr_vstore_box}; +use ast::{expr_vstore_slice, expr_vstore_box}; use ast::{expr_vstore_mut_slice, expr_while, extern_fn, field, fn_decl}; use ast::{expr_vstore_uniq, TyClosure, TyBareFn, Onceness, Once, Many}; use ast::{foreign_item, foreign_item_const, foreign_item_fn, foreign_mod}; @@ -1223,7 +1223,7 @@ pub impl Parser { let lvl = self.parse_expr(); self.expect(&token::COMMA); let e = self.parse_expr(); - ex = expr_log(ast::log_other, lvl, e); + ex = expr_log(lvl, e); hi = self.span.hi; self.expect(&token::RPAREN); } else if self.eat_keyword(&~"return") { @@ -2721,8 +2721,9 @@ pub impl Parser { } self.bump(); } - token::IDENT(*) => { + token::MOD_SEP | token::IDENT(*) => { let maybe_bound = match *self.token { + token::MOD_SEP => None, token::IDENT(copy sid, _) => { match *self.id_to_str(sid) { ~"send" | @@ -2750,7 +2751,7 @@ pub impl Parser { result.push(bound); } None => { - let ty = self.parse_ty(false); + let ty = self.parse_ty(true); result.push(TraitTyParamBound(ty)); } } @@ -3099,14 +3100,6 @@ pub impl Parser { // impl<T> Foo { ... } // impl<T> ToStr for ~[T] { ... } fn parse_item_impl(&self, visibility: ast::visibility) -> item_info { - fn wrap_path(p: &Parser, pt: @path) -> @Ty { - @Ty { - id: p.get_id(), - node: ty_path(pt, p.get_id()), - span: pt.span, - } - } - // First, parse type parameters if necessary. let generics = self.parse_generics(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9fffed7074b..cb142ef3f54 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -325,7 +325,7 @@ pub fn commasep<IN>(s: @ps, b: breaks, elts: &[IN], op: &fn(@ps, IN)) { pub fn commasep_cmnt<IN>(s: @ps, b: breaks, elts: &[IN], op: &fn(@ps, IN), get_span: &fn(IN) -> codemap::span) { box(s, 0u, b); - let len = vec::len::<IN>(elts); + let len = elts.len(); let mut i = 0u; for elts.each |elt| { maybe_print_comment(s, get_span(*elt).hi); @@ -1029,8 +1029,6 @@ pub fn print_vstore(s: @ps, t: ast::vstore) { pub fn print_expr_vstore(s: @ps, t: ast::expr_vstore) { match t { - ast::expr_vstore_fixed(Some(i)) => word(s.s, fmt!("%u", i)), - ast::expr_vstore_fixed(None) => word(s.s, ~"_"), ast::expr_vstore_uniq => word(s.s, ~"~"), ast::expr_vstore_box => word(s.s, ~"@"), ast::expr_vstore_mut_box => { @@ -1105,16 +1103,9 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { let ann_node = node_expr(s, expr); (s.ann.pre)(ann_node); match expr.node { - ast::expr_vstore(e, v) => match v { - ast::expr_vstore_fixed(_) => { - print_expr(s, e); - word(s.s, ~"/"); - print_expr_vstore(s, v); - } - _ => { - print_expr_vstore(s, v); - print_expr(s, e); - } + ast::expr_vstore(e, v) => { + print_expr_vstore(s, v); + print_expr(s, e); }, ast::expr_vec(ref exprs, mutbl) => { ibox(s, indent_unit); @@ -1391,20 +1382,14 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { _ => () } } - ast::expr_log(lvl, lexp, expr) => { - match lvl { - ast::debug => { word_nbsp(s, ~"log"); print_expr(s, expr); } - ast::error => { word_nbsp(s, ~"log_err"); print_expr(s, expr); } - ast::log_other => { - word_nbsp(s, ~"log"); - popen(s); - print_expr(s, lexp); - word(s.s, ~","); - space_if_not_bol(s); - print_expr(s, expr); - pclose(s); - } - } + ast::expr_log(lexp, expr) => { + word(s.s, ~"__log"); + popen(s); + print_expr(s, lexp); + word(s.s, ~","); + space_if_not_bol(s); + print_expr(s, expr); + pclose(s); } ast::expr_inline_asm(a, in, out, c, v, _) => { if v { @@ -2139,7 +2124,7 @@ pub fn print_comment(s: @ps, cmnt: comments::cmnt) { } } -pub fn print_string(s: @ps, st: ~str) { +pub fn print_string(s: @ps, st: &str) { word(s.s, ~"\""); word(s.s, str::escape_default(st)); word(s.s, ~"\""); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index a159c98d21b..2bdf26fba58 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -559,7 +559,7 @@ pub fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) { expr_break(_) => (), expr_again(_) => (), expr_ret(eo) => visit_expr_opt(eo, e, v), - expr_log(_, lv, x) => { + expr_log(lv, x) => { (v.visit_expr)(lv, e, v); (v.visit_expr)(x, e, v); } |
