Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lib/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ token RSHIFTEQUAL URSHIFTEQUAL /* >>= and >>>= */
token ANDEQUAL MODEQUAL /* &= and %= */
token XOREQUAL OREQUAL /* ^= and |= */

token DOTDOTDOT /* ... */

/* Terminal types */
token REGEXP
token NUMBER
Expand Down Expand Up @@ -848,8 +850,20 @@ rule
;

FormalParameterList:
FunctionRestParameter { result = [val[0]] }
| FormalsList { result = val[0] }
| FormalsList ',' FunctionRestParameter {
result = [val.first, val.last].flatten
}
;

FunctionRestParameter:
DOTDOTDOT IDENT { result = RestParameterNode.new(val[1]) }
;

FormalsList:
IDENT { result = [ParameterNode.new(val[0])] }
| FormalParameterList ',' IDENT {
| FormalsList ',' IDENT {
result = [val.first, ParameterNode.new(val.last)].flatten
}
;
Expand Down
2 changes: 1 addition & 1 deletion lib/rkelly/nodes/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def to_real_sexp
%w[EmptyStatement Parenthetical ExpressionStatement True Delete Return TypeOf
SourceElements Number LogicalNot AssignExpr FunctionBody
ObjectLiteral UnaryMinus Throw This BitwiseNot Element String
Array CaseBlock Null Break Parameter Block False Void Regexp
Array CaseBlock Null Break Parameter RestParameter Block False Void Regexp
Arguments Attr Continue ConstStatement UnaryPlus VarStatement].each do |node|
eval "class #{node}Node < Node; end"
end
Expand Down
3 changes: 2 additions & 1 deletion lib/rkelly/tokenizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class enum export extends import super
'-=' => :MINUSEQUAL,
'*=' => :MULTEQUAL,
'/=' => :DIVEQUAL,
'...' => :DOTDOTDOT,
}

# Some keywords can be followed by regular expressions (eg, return and throw).
Expand Down Expand Up @@ -175,7 +176,7 @@ def initialize(&block)
literal_chars = LITERALS.keys.map {|k| k.slice(0,1) }.uniq
literal_regex = Regexp.new(LITERALS.keys.sort_by { |x|
x.length
}.reverse.map { |x| "#{x.gsub(/([|+*^])/, '\\\\\1')}" }.join('|'))
}.reverse.map { |x| "#{x.gsub(/([|+*^.])/, '\\\\\1')}" }.join('|'))
token(:LITERALS, literal_regex, literal_chars) do |type, value|
[LITERALS[value], value]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rkelly/visitors/dot_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def initialize
## Terminal nodes
%w{
BreakNode ContinueNode EmptyStatementNode FalseNode
NullNode NumberNode ParameterNode RegexpNode ResolveNode StringNode
NullNode NumberNode ParameterNode RegexpNode ResolveNode RestParameterNode StringNode
ThisNode TrueNode
}.each do |type|
define_method(:"visit_#{type}") do |o|
Expand Down
4 changes: 4 additions & 0 deletions lib/rkelly/visitors/ecma_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ def visit_ParameterNode(o)
o.value
end

def visit_RestParameterNode(o)
"...#{o.value}"
end

def visit_FunctionBodyNode(o)
@indent += 1
"{\n#{o.value.accept(self)}\n#{@indent -=1; indent}}"
Expand Down
88 changes: 88 additions & 0 deletions lib/rkelly/visitors/es_6_5_visitor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
require 'rkelly/nodes'

module RKelly
module Visitors


class ES_6_5_Visitor < ECMAVisitor

include RKelly::Nodes


#
# converts function(...args) { ... }
# into
# function() { var args = Array.prototype.slice(arguments, ?); ... }
def fxtransform(o)


return o unless o.arguments.last.is_a? RestParameterNode

*args, rest = o.arguments

# function body -> source elements -> array
body = o.function_body.value.value.clone

restName = rest.value
restOffset = args.length

# var #{restName} = Array.prototype.slice(argument, #{restOffset});
newCode = VarStatementNode.new([
VarDeclNode.new(
restName,
AssignExprNode.new(
FunctionCallNode.new(
DotAccessorNode.new(
DotAccessorNode.new(
DotAccessorNode.new(
ResolveNode.new("Array"),
"prototype"
),
"slice"
),
"call"
),
ArgumentsNode.new([
ResolveNode.new("arguments"),
NumberNode.new(restOffset)
])
)
)
)
])

#puts newCode.to_sexp.to_s

body.unshift newCode

fxbody = FunctionBodyNode.new(SourceElementsNode.new(body))
node = FunctionExprNode.new(o.value, fxbody, args)

return node
end

def visit_FunctionDeclNode(o)
node = fxtransform(o)
super(node)
end

def visit_FunctionExprNode(o)
node = fxtransform(o)
super(node)
end

# this should never have any arguments...
def visit_GetterPropertyNode(o)
node = GetterProperyNode.new(o.name, fxtransform(o.value))
super(node)
end

# just in case...
def visit_SetterPropertyNode(o)
node = SetterProperyNode.new(o.name, fxtransform(o.value))
super(node)
end

end
end
end
2 changes: 1 addition & 1 deletion lib/rkelly/visitors/evaluation_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def visit_UnaryMinusNode(o)
OpLShiftEqualNode OpMinusEqualNode OpModEqualNode
OpMultiplyEqualNode OpOrEqualNode OpRShiftEqualNode
OpURShiftEqualNode OpXOrEqualNode ParameterNode
PropertyNode RegexpNode RightShiftNode
PropertyNode RegexpNode RestParameterNode RightShiftNode
SetterPropertyNode StrictEqualNode
SwitchNode ThrowNode TryNode
UnsignedRightShiftNode
Expand Down
2 changes: 1 addition & 1 deletion lib/rkelly/visitors/function_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def visit_FunctionDeclNode(o)
OpEqualNode OpLShiftEqualNode OpMinusEqualNode OpModEqualNode
OpMultiplyEqualNode OpOrEqualNode OpPlusEqualNode OpRShiftEqualNode
OpURShiftEqualNode OpXOrEqualNode ParameterNode PostfixNode PrefixNode
PropertyNode RegexpNode ResolveNode ReturnNode RightShiftNode
PropertyNode RegexpNode ResolveNode RestParameterNode ReturnNode RightShiftNode
SetterPropertyNode StrictEqualNode StringNode
SubtractNode SwitchNode ThisNode ThrowNode TrueNode TryNode TypeOfNode
UnaryMinusNode UnaryPlusNode UnsignedRightShiftNode VarDeclNode
Expand Down
4 changes: 4 additions & 0 deletions lib/rkelly/visitors/sexp_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ def visit_ParameterNode(o)
[:param, o.value]
end

def visit_RestParameterNode(o)
[:rest_param, o.value]
end

def visit_BreakNode(o)
[:break, o.value].compact
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rkelly/visitors/visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module RKelly
module Visitors
class Visitor
TERMINAL_NODES = %w{
Break Continue EmptyStatement False Null Number Parameter Regexp Resolve
Break Continue EmptyStatement False Null Number Parameter RestParameter Regexp Resolve
String This True
}
SINGLE_VALUE_NODES = %w{
Expand Down
42 changes: 42 additions & 0 deletions test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ def test_function_expr_anon
@parser.parse("var foo = function(a,b) { }"))
end

def test_function_expr_anon_rest_arg
assert_sexp(
[[:var,
[[:var_decl, :foo, [:assign,
[:func_expr, "function", [[:rest_param, "a"]], [:func_body, []]]
]]]
]],
@parser.parse("var foo = function(...a) { }"))
end

def test_function_expr_anon_arg_rest_arg
assert_sexp(
[[:var,
[[:var_decl, :foo, [:assign,
[:func_expr, "function", [[:param, "a"], [:rest_param, "b"]], [:func_body, []]]
]]]
]],
@parser.parse("var foo = function(a, ...b) { }"))
end


def test_function_expr_no_args
assert_sexp(
[[:var,
Expand All @@ -87,6 +108,27 @@ def test_function_expr_with_args
@parser.parse("var foo = function aaron(a, b) { }"))
end

def test_function_expr_with_rest_args
assert_sexp(
[[:var,
[[:var_decl, :foo, [:assign,
[:func_expr, 'aaron', [[:rest_param, 'a']], [:func_body, []]]
]]]
]],
@parser.parse("var foo = function aaron(...a) { }"))
end

def test_function_expr_with_args_rest_args
assert_sexp(
[[:var,
[[:var_decl, :foo, [:assign,
[:func_expr, 'aaron', [[:param, 'a'], [:rest_param, 'b']], [:func_body, []]]
]]]
]],
@parser.parse("var foo = function aaron(a, ...b) { }"))
end


def test_labelled_statement
assert_sexp([[:label, :foo, [:var, [[:var_decl, :x, [:assign, [:lit, 10]]]]]]],
@parser.parse('foo: var x = 10;'))
Expand Down