-
Notifications
You must be signed in to change notification settings - Fork 27.1k
feat(transpiler): handle named params #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,40 @@ | ||
| import {describe, it, expect} from 'test_lib/test_lib'; | ||
| import {describe, ddescribe, it, iit, expect} from 'test_lib/test_lib'; | ||
|
|
||
| function sum(a, b) { | ||
| return a + b; | ||
| } | ||
|
|
||
|
|
||
| export function main() { | ||
| describe('functions', function() { | ||
| it('should work', function() { | ||
| expect(sum(1, 2)).toBe(3); | ||
| }); | ||
|
|
||
| describe("named parameters", function() { | ||
| it('should pass named params as named params by using identifier keys', function() { | ||
| function f(a, {b, c}) {return a + b + c;} | ||
| expect(f(1, {b: 2, c: 3})).toBe(6); | ||
| }); | ||
|
|
||
| it('should pass named params as a map by using quoted keys', function() { | ||
| function f(m) {return m["a"] + m["b"];} | ||
|
|
||
| expect(f({"a": 1, "b": 2})).toBe(3); | ||
| }); | ||
|
|
||
| it('should compile initializers', function() { | ||
| function f({a=1, b=2}) {return a + b;} | ||
| expect(f({a:10})).toBe(12); | ||
| }); | ||
|
|
||
| it("should call function with named params without passing any" + | ||
| "params by providing an empty object initializer", function() { | ||
| function f({a=1, b=2}={}) {return a + b;} | ||
|
|
||
| expect(f({a: 10})).toBe(12); | ||
| expect(f()).toBe(3); | ||
| }); | ||
| }); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import {ParseTree} from 'traceur/src/syntax/trees/ParseTree'; | ||
|
|
||
| export class NamedParams extends ParseTree { | ||
| constructor(location, propertyNameAndValues) { | ||
| this.location = location; | ||
| this.propertyNameAndValues = propertyNameAndValues; | ||
| } | ||
|
|
||
| visit(visitor) { | ||
| visitor.visitNamedParamsExpression(this); | ||
| } | ||
|
|
||
| transform(transformer) { | ||
| return this; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import {BindingElement} from 'traceur/src/syntax/trees/ParseTrees'; | ||
|
|
||
| export class ObjectPatternBindingElement extends BindingElement { | ||
| visit(visitor) { | ||
| visitor.visitObjectPatternBindingElement(this); | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Newline is missing |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name should either have
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean the filename...
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do we check that the keys are quoted? |
||
| import { | ||
| BINDING_ELEMENT, | ||
| OBJECT_PATTERN, | ||
| OBJECT_LITERAL_EXPRESSION | ||
| } from 'traceur/src/syntax/trees/ParseTreeType'; | ||
|
|
||
| import {NamedParams} from '../ast/named_params'; | ||
| import {ObjectPatternBindingElement} from '../ast/object_pattern_binding_element'; | ||
|
|
||
| /** | ||
| * Transforms maps into named parameters: | ||
| * | ||
| * First, it transform all calls where the last argument is an object literal | ||
| * with identifier keys, as follows: | ||
| * | ||
| * f({a: 1, b: 2}) -> f(a: 1, b: 2) | ||
| * | ||
| * Second, it removes the empty object initializer from the function definition: | ||
| * | ||
| * function f({a:1, b:2} = {}){} -> f({a:1, b:2}){} | ||
| */ | ||
| export class NamedParamsTransformer extends ParseTreeTransformer { | ||
| /** | ||
| * @param {CallExpression} tree | ||
| * @return {ParseTree} | ||
| */ | ||
| transformCallExpression(tree) { | ||
| tree = super.transformCallExpression(tree); | ||
| if (this._isLastArgAnNonEmptyObjectLiteral(tree) && | ||
| ! this._isLastArgObjectLiteralWithQuotedKeys(tree)) { | ||
| this._replaceLastArgWithNamedParams(tree); | ||
| } | ||
| return tree; | ||
| } | ||
|
|
||
| _isLastArgAnNonEmptyObjectLiteral(tree) { | ||
| var lastArg = this._last(tree.args.args); | ||
| if (!lastArg) return false; | ||
|
|
||
| var pairs = lastArg.propertyNameAndValues; | ||
| if (!pairs || pairs.length == 0) return false; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| _isLastArgObjectLiteralWithQuotedKeys(tree) { | ||
| var pairs = this._last(tree.args.args).propertyNameAndValues; | ||
|
|
||
| for (var pair of pairs) { | ||
| var key = pair.name.literalToken.value; | ||
| if (key.charAt(0) == '"' || key.charAt(0) == "'") return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| _replaceLastArgWithNamedParams(tree) { | ||
| var args = tree.args.args; | ||
| var last = this._last(args); | ||
| args[args.length - 1] = new NamedParams(last.location, last.propertyNameAndValues); | ||
| } | ||
|
|
||
| /** | ||
| * @param {ObjectPattern} tree | ||
| * @return {ParseTree} | ||
| */ | ||
| transformObjectPattern(tree) { | ||
| tree = super.transformObjectPattern(tree); | ||
| tree.fields = tree.fields.map( | ||
| (e) => new ObjectPatternBindingElement(e.location, e.binding, e.initializer)); | ||
| return tree; | ||
| } | ||
|
|
||
| /** | ||
| * @param {FormalParameterList} tree | ||
| * @return {ParseTree} | ||
| */ | ||
| transformFormalParameterList(tree) { | ||
| tree = super.transformFormalParameterList(tree); | ||
| var last = this._last(tree.parameters); | ||
| if (last && this._isObjectPatternWithAnEmptyObjectInit(last.parameter)) { | ||
| last.parameter = last.parameter.binding; | ||
| } | ||
| return tree; | ||
| } | ||
|
|
||
| _isObjectPatternWithAnEmptyObjectInit(tree) { | ||
| return tree.type === BINDING_ELEMENT && | ||
| tree.binding.type === OBJECT_PATTERN && | ||
| this._isEmptyObjectInitializer(tree.initializer) | ||
| } | ||
|
|
||
| _isEmptyObjectInitializer(initializer) { | ||
| return initializer && | ||
| initializer.type == OBJECT_LITERAL_EXPRESSION && | ||
| initializer.propertyNameAndValues.length == 0; | ||
| } | ||
|
|
||
| _last(array) { | ||
| if (!array || array.length == 0) return undefined; | ||
| return array[array.length - 1]; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newline is missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tbosch I have always wonder why new line at eof are important ?