From 7da45bd9a0a2e42fa0df8da591f0bc1900bc651f Mon Sep 17 00:00:00 2001 From: Matthias Thoemmes Date: Wed, 26 Oct 2016 22:44:17 +0200 Subject: [PATCH 1/4] Initial default arguments for arrow functions --- core/src/codegen.rs | 6 +++++- core/src/grammar.rs | 3 ++- core/src/parser.rs | 19 ++++++++++++++++--- core/tests/parser.rs | 37 ++++++++++++++++++++++++++++--------- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/core/src/codegen.rs b/core/src/codegen.rs index 115d6a8..7a28a40 100644 --- a/core/src/codegen.rs +++ b/core/src/codegen.rs @@ -320,7 +320,11 @@ impl Code for ObjectKey { impl Code for Parameter { #[inline] fn to_code(&self, gen: &mut Generator) { - gen.write_bytes(self.name.as_bytes()); + gen.write(&self.name); + if let Some(ref expression) = self.expression { + gen.write_min(b" = ", b"="); + gen.write(expression); + } } } diff --git a/core/src/grammar.rs b/core/src/grammar.rs index 4db3a9a..246082e 100644 --- a/core/src/grammar.rs +++ b/core/src/grammar.rs @@ -13,9 +13,10 @@ pub enum Value { } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone)] pub struct Parameter { pub name: OwnedSlice, + pub expression: Option> } #[derive(Debug, PartialEq, Clone, Copy)] diff --git a/core/src/parser.rs b/core/src/parser.rs index f611531..7faef60 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -272,15 +272,27 @@ impl<'a> Parser<'a> { let params: Vec = match p { None => Vec::new(), Some(Expression::Identifier(name)) => { - vec![Parameter { name: name }] + vec![Parameter { name: name, expression: None }] }, Some(Expression::Sequence(mut list)) => { let mut params = Vec::with_capacity(list.len()); for expression in list.drain(..) { match expression { + Expression::Binary { + operator: Assign, + left, + right, + .. + } => { + let name = match *left { + Expression::Identifier(value) => value, + _ => unexpected_token!(self) + }; + params.push(Parameter { name: name, expression: Some(right) }); + }, Expression::Identifier(name) => { - params.push(Parameter { name: name }); + params.push(Parameter { name: name, expression: None }); }, _ => unexpected_token!(self) } @@ -863,7 +875,8 @@ impl<'a> Parser<'a> { ParenClose => break, Identifier(name) => { list.push(Parameter { - name: name + name: name, + expression: None }); }, _ => unexpected_token!(self) diff --git a/core/tests/parser.rs b/core/tests/parser.rs index 37a46d8..f831297 100644 --- a/core/tests/parser.rs +++ b/core/tests/parser.rs @@ -42,8 +42,9 @@ macro_rules! ident { } macro_rules! param { - ($name:expr) => (Parameter { - name: $name.into() + ($name:expr, $expr:expr) => (Parameter { + name: $name.into(), + expression: $expr.into() }) } @@ -370,9 +371,9 @@ fn function_with_params_statement() { ", Statement::Function { name: "foo".into(), params: vec![ - param!("a"), - param!("b"), - param!("c"), + param!("a", None), + param!("b", None), + param!("c", None), ], body: vec![ Statement::Return { @@ -663,7 +664,7 @@ fn arrow_function() { fn arrow_function_shorthand() { assert_expression!("n => n * n", Expression::ArrowFunction { params: vec![ - param!("n") + param!("n", None) ], body: Box::new(Statement::Expression { value: Expression::binary( @@ -685,9 +686,9 @@ fn arrow_function_with_params() { ", Expression::ArrowFunction { params: vec![ - param!("a"), - param!("b"), - param!("c"), + param!("a", None), + param!("b", None), + param!("c", None), ], body: Box::new(Statement::Block { body: vec![Statement::Expression { @@ -697,6 +698,24 @@ fn arrow_function_with_params() { }); } +#[test] +fn arrow_function_with_default_params() { + assert_expression!(" + + (a, b, c = 2) => bar; + + ", Expression::ArrowFunction { + params: vec![ + param!("a", None), + param!("b", None), + param!("c", Some(Box::new(Expression::Literal(Value::Number("2".into()))))), + ], + body: Box::new(Statement::Expression { + value: ident!("bar") + }) + }); +} + #[test] fn function_expression() { assert_expression!(" From 38de758c58a5066a7ff3459dcd63367ae0d1e0a2 Mon Sep 17 00:00:00 2001 From: Matthias Thoemmes Date: Thu, 27 Oct 2016 09:24:23 +0200 Subject: [PATCH 2/4] Adding support for default params in functions --- core/src/codegen.rs | 2 +- core/src/grammar.rs | 2 +- core/src/parser.rs | 60 +++++++++++++++++++++++++++++++++++--------- core/tests/parser.rs | 2 +- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/core/src/codegen.rs b/core/src/codegen.rs index 7a28a40..bd4ee50 100644 --- a/core/src/codegen.rs +++ b/core/src/codegen.rs @@ -321,7 +321,7 @@ impl Code for Parameter { #[inline] fn to_code(&self, gen: &mut Generator) { gen.write(&self.name); - if let Some(ref expression) = self.expression { + if let Some(ref expression) = self.default { gen.write_min(b" = ", b"="); gen.write(expression); } diff --git a/core/src/grammar.rs b/core/src/grammar.rs index 246082e..2fbc1bb 100644 --- a/core/src/grammar.rs +++ b/core/src/grammar.rs @@ -16,7 +16,7 @@ pub enum Value { #[derive(Debug, PartialEq, Clone)] pub struct Parameter { pub name: OwnedSlice, - pub expression: Option> + pub default: Option> } #[derive(Debug, PartialEq, Clone, Copy)] diff --git a/core/src/parser.rs b/core/src/parser.rs index 7faef60..41d13d7 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -272,11 +272,26 @@ impl<'a> Parser<'a> { let params: Vec = match p { None => Vec::new(), Some(Expression::Identifier(name)) => { - vec![Parameter { name: name, expression: None }] + vec![Parameter { name: name, default: None }] + }, + Some(Expression::Binary { + operator: Assign, + left, + right, + .. + }) => { + let mut params = Vec::with_capacity(1); + + let name = match *left { + Expression::Identifier(value) => value, + _ => unexpected_token!(self) + }; + params.push(Parameter { name: name, default: Some(right) }); + params }, Some(Expression::Sequence(mut list)) => { let mut params = Vec::with_capacity(list.len()); - + let mut default_params = false; for expression in list.drain(..) { match expression { Expression::Binary { @@ -289,10 +304,14 @@ impl<'a> Parser<'a> { Expression::Identifier(value) => value, _ => unexpected_token!(self) }; - params.push(Parameter { name: name, expression: Some(right) }); + params.push(Parameter { name: name, default: Some(right) }); + default_params = true; }, Expression::Identifier(name) => { - params.push(Parameter { name: name, expression: None }); + if default_params { + unexpected_token!(self) + } + params.push(Parameter { name: name, default: None }); }, _ => unexpected_token!(self) } @@ -869,18 +888,35 @@ impl<'a> Parser<'a> { fn parameter_list(&mut self) -> Result> { let mut list = Vec::new(); + let mut default_params = false; loop { - match next!(self) { + let name = match next!(self) { ParenClose => break, - Identifier(name) => { - list.push(Parameter { - name: name, - expression: None - }); - }, + Identifier(name) => name, _ => unexpected_token!(self) - } + }; + + list.push(match peek!(self) { + Operator(Assign) => { + self.consume(); + let expression = try!(self.expression(0)); + default_params = true; + Parameter { + name: name, + default: Some(Box::new(expression)) + } + } + _ => { + if default_params { + unexpected_token!(self); + } + Parameter { + name: name, + default: None + } + } + }); match next!(self) { ParenClose => break, diff --git a/core/tests/parser.rs b/core/tests/parser.rs index f831297..672c48f 100644 --- a/core/tests/parser.rs +++ b/core/tests/parser.rs @@ -44,7 +44,7 @@ macro_rules! ident { macro_rules! param { ($name:expr, $expr:expr) => (Parameter { name: $name.into(), - expression: $expr.into() + default: $expr.into() }) } From da3034e4b8483c2ba3d8a4bb33f2291e7defd796 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz Date: Wed, 2 Nov 2016 23:20:16 +0100 Subject: [PATCH 3/4] transform parameters with default values --- core/src/grammar.rs | 1 - core/src/parser.rs | 44 +++++--- core/src/transformer.rs | 216 +++++++++++++++++++++++++++++++++++++++- core/tests/codegen.rs | 20 ++++ core/tests/parser.rs | 2 +- 5 files changed, 262 insertions(+), 21 deletions(-) diff --git a/core/src/grammar.rs b/core/src/grammar.rs index 2fbc1bb..1a881a8 100644 --- a/core/src/grammar.rs +++ b/core/src/grammar.rs @@ -12,7 +12,6 @@ pub enum Value { RawQuasi(OwnedSlice), } - #[derive(Debug, PartialEq, Clone)] pub struct Parameter { pub name: OwnedSlice, diff --git a/core/src/parser.rs b/core/src/parser.rs index 41d13d7..451c6ff 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -271,29 +271,36 @@ impl<'a> Parser<'a> { fn arrow_function_expression(&mut self, p: Option) -> Result { let params: Vec = match p { None => Vec::new(), + Some(Expression::Identifier(name)) => { - vec![Parameter { name: name, default: None }] + vec![Parameter { + name : name, + default : None, + }] }, + Some(Expression::Binary { - operator: Assign, + parenthesized : true, + operator : Assign, left, right, - .. }) => { - let mut params = Vec::with_capacity(1); - let name = match *left { Expression::Identifier(value) => value, _ => unexpected_token!(self) }; - params.push(Parameter { name: name, default: Some(right) }); - params + + vec![Parameter { + name : name, + default : Some(right), + }] }, + Some(Expression::Sequence(mut list)) => { let mut params = Vec::with_capacity(list.len()); - let mut default_params = false; + for expression in list.drain(..) { - match expression { + params.push(match expression { Expression::Binary { operator: Assign, left, @@ -302,19 +309,24 @@ impl<'a> Parser<'a> { } => { let name = match *left { Expression::Identifier(value) => value, - _ => unexpected_token!(self) + _ => unexpected_token!(self) }; - params.push(Parameter { name: name, default: Some(right) }); - default_params = true; + + Parameter { + name : name, + default : Some(right), + } }, + Expression::Identifier(name) => { - if default_params { - unexpected_token!(self) + Parameter { + name : name, + default : None } - params.push(Parameter { name: name, default: None }); }, + _ => unexpected_token!(self) - } + }) } params diff --git a/core/src/transformer.rs b/core/src/transformer.rs index 8f01719..a073f4a 100644 --- a/core/src/transformer.rs +++ b/core/src/transformer.rs @@ -8,6 +8,7 @@ pub struct Settings { pub transform_block_scope: bool, pub transform_arrow: bool, pub transform_object: bool, + pub transform_default_parameters: bool, pub transform_exponentation: bool, pub transform_class_properties: bool, pub transform_class: bool, @@ -50,6 +51,7 @@ impl Settings { settings.transform_block_scope = true; settings.transform_arrow = true; + settings.transform_default_parameters = true; settings.transform_object = true; settings.transform_class = true; settings.transform_template_strings = true; @@ -71,6 +73,7 @@ impl Settings { transform_block_scope: false, transform_arrow: false, transform_object: false, + transform_default_parameters: false, transform_exponentation: false, transform_class_properties: false, transform_class: false, @@ -122,6 +125,33 @@ impl Transformable for Box { impl Transformable for Parameter {} +#[inline] +fn transform_default_parameters(params: &mut Vec, body: &mut Vec) { + for param in params.iter_mut() { + match param.default.take() { + Some(value) => { + body.insert(0, Statement::Expression { + value: Expression::binary( + Expression::binary( + param.name, + StrictEquality, + "undefined".into(), + ), + LogicalAnd, + Expression::Binary { + parenthesized : true, + operator : Assign, + left : Box::new(param.name.into()), + right : value, + } + ) + }); + }, + None => {}, + } + } +} + impl Transformable for Expression { fn transform(&mut self, settings: &Settings) { *self = match *self { @@ -132,13 +162,43 @@ impl Transformable for Expression { params.transform(settings); body.transform(settings); + if settings.transform_default_parameters { + let has_defaults = params.iter().any(|param| match *param { + Parameter { + default: Some(_), + .. + } => true, + _ => false, + }); + + if has_defaults { + let mut new_body = match **body { + Statement::Block { ref mut body } => body.take(), + Statement::Expression { ref mut value } => vec![ + Statement::Return { + value: Some(value.take()) + } + ], + ref statement => { + panic!("Invalid arrow function body {:#?}", statement); + } + }; + + transform_default_parameters(params, &mut new_body); + + **body = Statement::Block { + body: new_body + }; + } + } + // transformation flag check if !settings.transform_arrow { return; } let body = match **body { - Statement::Block { ref mut body } => body.split_off(0), + Statement::Block { ref mut body } => body.take(), Statement::Expression { ref mut value } => vec![ Statement::Return { value: Some(value.take()) @@ -164,6 +224,21 @@ impl Transformable for Expression { } }, + Expression::Function { + ref mut params, + ref mut body, + .. + } => { + params.transform(settings); + body.transform(settings); + + if settings.transform_default_parameters { + transform_default_parameters(params, body); + } + + return; + }, + Expression::Array(ref mut items) => { items.transform(settings); return; @@ -364,7 +439,61 @@ impl Transformable for Expression { } }, - _ => return, + Expression::Sequence(ref mut expressions) => { + expressions.transform(settings); + + return; + }, + + Expression::Member { + ref mut object, + .. + } => { + object.transform(settings); + + return; + }, + + Expression::ComputedMember { + ref mut object, + ref mut property, + } => { + object.transform(settings); + property.transform(settings); + + return; + }, + + Expression::Prefix { + ref mut operand, + .. + } + | + Expression::Postfix { + ref mut operand, + .. + } => { + operand.transform(settings); + + return; + }, + + Expression::Conditional { + ref mut test, + ref mut consequent, + ref mut alternate, + } => { + test.transform(settings); + consequent.transform(settings); + alternate.transform(settings); + + return; + }, + + Expression::This | + Expression::Literal(..) | + Expression::RegEx {..} | + Expression::Identifier(..) => return, } } @@ -625,6 +754,21 @@ impl Transformable for Statement { return; }, + Statement::Function { + ref mut params, + ref mut body, + .. + } => { + params.transform(settings); + body.transform(settings); + + if settings.transform_default_parameters { + transform_default_parameters(params, body); + } + + return; + }, + Statement::Class { ref name, ref mut body, @@ -726,9 +870,75 @@ impl Transformable for Statement { } else { constructor } + }, + + Statement::Transparent { + ref mut body, + } => { + body.transform(settings); + + return; + }, + + Statement::Return { + ref mut value, + } => { + value.transform(settings); + + return; + }, + + Statement::Throw { + ref mut value, + } => { + value.transform(settings); + + return; + }, + + Statement::While { + ref mut test, + ref mut body, + } => { + test.transform(settings); + body.transform(settings); + + return; + }, + + Statement::For { + ref mut init, + ref mut test, + ref mut update, + ref mut body, + } => { + init.transform(settings); + test.transform(settings); + update.transform(settings); + body.transform(settings); + + return; + }, + + Statement::ForIn { + ref mut left, + ref mut right, + ref mut body, } + | + Statement::ForOf { + ref mut left, + ref mut right, + ref mut body, + } => { + left.transform(settings); + right.transform(settings); + body.transform(settings); + + return; + }, - _ => return, + Statement::Break { .. } => return, } } diff --git a/core/tests/codegen.rs b/core/tests/codegen.rs index 8052f0e..3183466 100644 --- a/core/tests/codegen.rs +++ b/core/tests/codegen.rs @@ -43,6 +43,11 @@ fn convert_let_to_var_in_block() { assert_compile!(program, expected); } +#[test] +fn exponent_in_sequence() { + assert_compile!("(1, 2 ** 2)", "(1,Math.pow(2,2));"); +} + #[test] fn regex() { assert_compile!("/foo/gi.test();", "/foo/gi.test();"); @@ -83,6 +88,21 @@ fn template_strings_tagged() { assert_compile!("foo`bar${1}baz${2}`;", r#"foo(["bar","baz",""],1,2);"#); } +#[test] +fn function_statement_default_parameters() { + assert_compile!("function foo(a,b=1){}", "function foo(a,b){b===undefined&&(b=1);}"); +} + +#[test] +fn function_expression_default_parameters() { + assert_compile!("(function(a,b=1){})", "(function(a,b){b===undefined&&(b=1);});"); +} + +#[test] +fn arrow_function_default_parameters() { + assert_compile!("(a,b=1)=>{}", "(function(a,b){b===undefined&&(b=1);});"); +} + #[test] fn empty_class() { assert_compile!("class Foo {}", "function Foo(){}"); diff --git a/core/tests/parser.rs b/core/tests/parser.rs index 672c48f..dd9e20f 100644 --- a/core/tests/parser.rs +++ b/core/tests/parser.rs @@ -708,7 +708,7 @@ fn arrow_function_with_default_params() { params: vec![ param!("a", None), param!("b", None), - param!("c", Some(Box::new(Expression::Literal(Value::Number("2".into()))))), + param!("c", Some(Box::new(num!("2")))), ], body: Box::new(Statement::Expression { value: ident!("bar") From cc173bb428fd1652535c0fd3d052860bbd855ea7 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz Date: Thu, 3 Nov 2016 08:20:20 +0100 Subject: [PATCH 4/4] Transform Object and Class methods and bump the version number --- core/Cargo.toml | 2 +- core/src/transformer.rs | 8 ++++++++ core/tests/codegen.rs | 40 +++++++++++++++++++++++++--------------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index f3ca1cc..d769e9c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratel" -version = "0.5.5" +version = "0.6.0" authors = ["Maciej Hirsz "] license = "MIT/Apache-2.0" description = "JavaScript transpiler in Rust" diff --git a/core/src/transformer.rs b/core/src/transformer.rs index a073f4a..8128463 100644 --- a/core/src/transformer.rs +++ b/core/src/transformer.rs @@ -584,6 +584,10 @@ impl Transformable for ObjectMember { body.transform(settings); params.transform(settings); + if settings.transform_default_parameters { + transform_default_parameters(params, body); + } + // transformation flag check if !settings.transform_object { return; @@ -632,6 +636,10 @@ impl Transformable for ClassMember { } => { params.transform(settings); body.transform(settings); + + if settings.transform_default_parameters { + transform_default_parameters(params, body); + } }, Property { diff --git a/core/tests/codegen.rs b/core/tests/codegen.rs index 3183466..08e676e 100644 --- a/core/tests/codegen.rs +++ b/core/tests/codegen.rs @@ -88,21 +88,6 @@ fn template_strings_tagged() { assert_compile!("foo`bar${1}baz${2}`;", r#"foo(["bar","baz",""],1,2);"#); } -#[test] -fn function_statement_default_parameters() { - assert_compile!("function foo(a,b=1){}", "function foo(a,b){b===undefined&&(b=1);}"); -} - -#[test] -fn function_expression_default_parameters() { - assert_compile!("(function(a,b=1){})", "(function(a,b){b===undefined&&(b=1);});"); -} - -#[test] -fn arrow_function_default_parameters() { - assert_compile!("(a,b=1)=>{}", "(function(a,b){b===undefined&&(b=1);});"); -} - #[test] fn empty_class() { assert_compile!("class Foo {}", "function Foo(){}"); @@ -172,3 +157,28 @@ fn class_with_static_methods() { ", "function Foo(){}Foo.foo=function(){};Foo.bar=function(){};"); } + +#[test] +fn function_statement_default_parameters() { + assert_compile!("function foo(a,b=1){}", "function foo(a,b){b===undefined&&(b=1);}"); +} + +#[test] +fn function_expression_default_parameters() { + assert_compile!("(function(a,b=1){})", "(function(a,b){b===undefined&&(b=1);});"); +} + +#[test] +fn arrow_function_default_parameters() { + assert_compile!("(a,b=1)=>{}", "(function(a,b){b===undefined&&(b=1);});"); +} + +#[test] +fn object_method_default_parameters() { + assert_compile!("({foo(a,b=1){}})", "({foo:function(a,b){b===undefined&&(b=1);}});"); +} + +#[test] +fn class_method_default_parameters() { + assert_compile!("class Foo{bar(a,b=1){}}", "function Foo(){}Foo.prototype.bar=function(a,b){b===undefined&&(b=1);};") +}