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
31 changes: 28 additions & 3 deletions lib/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ token NULL TRUE FALSE

/* keywords */
token BREAK CASE CATCH CONST CONTINUE DEBUGGER DEFAULT DELETE DO ELSE ENUM
token FINALLY FOR FUNCTION IF IN INSTANCEOF NEW RETURN SWITCH THIS THROW TRY
token TYPEOF VAR VOID WHILE WITH
token FINALLY FOR FUNCTION IF IN INSTANCEOF LET NEW RETURN SWITCH
token THIS THROW TRY TYPEOF VAR VOID WHILE WITH

/* punctuators */
token EQEQ NE /* == and != */
Expand Down Expand Up @@ -67,6 +67,7 @@ rule
| WithStatement
| SwitchStatement
| LabelledStatement
| LetStatement
| ThrowStatement
| TryStatement
| DebuggerStatement
Expand Down Expand Up @@ -543,7 +544,7 @@ rule
;

VariableDeclarationNoIn:
IDENT { result = VarDeclNode.new(val[0],nil) }
IDENT { result = VarDeclNode.new(val[0], nil) }
| IDENT InitializerNoIn { result = VarDeclNode.new(val[0], val[1]) }
;

Expand Down Expand Up @@ -571,6 +572,30 @@ rule
| IDENT Initializer { result = VarDeclNode.new(val[0], val[1], true) }
;

LetStatement:
LET LetDeclarationList ';' {
result = LetStatementNode.new(val[1])
debug(result)
}
| LET LetDeclarationList error {
result = LetStatementNode.new(val[1])
debug(result)
yyerror unless allow_auto_semi?(val.last)
}
;

LetDeclarationList:
LetDeclaration { result = val }
| LetDeclarationList ',' LetDeclaration {
result = [val.first, val.last].flatten
}
;

LetDeclaration:
IDENT { result = VarDeclNode.new(val[0], nil, :let) }
| IDENT Initializer { result = VarDeclNode.new(val[0], val[1], :let) }
;

Initializer:
'=' AssignmentExpr { result = AssignExprNode.new(val[1]) }
;
Expand Down
13 changes: 8 additions & 5 deletions lib/recma/nodes/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,14 @@ def to_real_sexp
end
end

%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
Arguments Attr Continue ConstStatement UnaryPlus VarStatement].each do |node|
%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 Arguments Attr Continue ConstStatement LetStatement
UnaryPlus VarStatement
].each do |node|
eval "class #{node}Node < Node; end"
end
end
Expand Down
9 changes: 7 additions & 2 deletions lib/recma/nodes/var_decl_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ module RECMA
module Nodes
class VarDeclNode < Node
attr_accessor :name, :type
# constant can be true (for const), false (for var), or :let (for let).
def initialize(name, value, constant = false)
super(value)
@name = name
@constant = constant
end

def constant?; @constant; end
def variable?; !@constant; end
def const?; @constant == true; end
def let?; @constant == :let; end
def var?; @constant == false; end

alias constant? const?
alias variable? var?
end
end
end
2 changes: 1 addition & 1 deletion lib/recma/tokenizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Tokenizer
if in instanceof new return switch this throw try typeof var void while
with

const true false null debugger
const true false let null debugger
}

RESERVED = %w{
Expand Down
3 changes: 2 additions & 1 deletion lib/recma/visitors/dot_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def initialize
# Array Value Nodes
%w{
ArgumentsNode ArrayNode CaseBlockNode ConstStatementNode
ObjectLiteralNode SourceElementsNode VarStatementNode
LetStatementNode ObjectLiteralNode SourceElementsNode
VarStatementNode
}.each do |type|
define_method(:"visit_#{type}") do |o|
node = Node.new(@node_index += 1, [type])
Expand Down
12 changes: 8 additions & 4 deletions lib/recma/visitors/ecma_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ def visit_SourceElementsNode(o)
o.value.map { |x| "#{indent}#{x.accept(self)}" }.join("\n")
end

def visit_VarStatementNode(o)
"var #{o.value.map { |x| x.accept(self) }.join(', ')};"
end

def visit_ConstStatementNode(o)
"const #{o.value.map { |x| x.accept(self) }.join(', ')};"
end

def visit_LetStatementNode(o)
"let #{o.value.map { |x| x.accept(self) }.join(', ')};"
end

def visit_VarStatementNode(o)
"var #{o.value.map { |x| x.accept(self) }.join(', ')};"
end

def visit_VarDeclNode(o)
"#{o.name}#{o.value ? o.value.accept(self) : nil}"
end
Expand Down
2 changes: 1 addition & 1 deletion lib/recma/visitors/evaluation_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def visit_UnaryMinusNode(o)
ForInNode ForNode
FunctionExprNode GetterPropertyNode GreaterNode GreaterOrEqualNode
InNode InstanceOfNode LabelNode LeftShiftNode LessNode
LessOrEqualNode LogicalAndNode LogicalOrNode
LessOrEqualNode LetStatementNode LogicalAndNode LogicalOrNode
NotEqualNode NotStrictEqualNode
ObjectLiteralNode OpAndEqualNode OpDivideEqualNode
OpLShiftEqualNode OpMinusEqualNode OpModEqualNode
Expand Down
27 changes: 14 additions & 13 deletions lib/recma/visitors/function_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,23 @@ def visit_FunctionDeclNode(o)
AddNode ArgumentsNode ArrayNode AssignExprNode BitAndNode BitOrNode
BitXOrNode BitwiseNotNode BlockNode BracketAccessorNode BreakNode
CaseBlockNode CaseClauseNode CommaNode ConditionalNode
ConstStatementNode ContinueNode DeleteNode DivideNode
DoWhileNode DotAccessorNode ElementNode EmptyStatementNode EqualNode
ConstStatementNode ContinueNode DeleteNode DivideNode DoWhileNode
DotAccessorNode ElementNode EmptyStatementNode EqualNode
ExpressionStatementNode FalseNode ForInNode ForNode FunctionBodyNode
FunctionExprNode GetterPropertyNode GreaterNode GreaterOrEqualNode
IfNode InNode InstanceOfNode LabelNode LeftShiftNode LessNode
LessOrEqualNode LogicalAndNode LogicalNotNode LogicalOrNode ModulusNode
MultiplyNode NewExprNode NotEqualNode NotStrictEqualNode NullNode
NumberNode ObjectLiteralNode OpAndEqualNode OpDivideEqualNode
OpEqualNode OpLShiftEqualNode OpMinusEqualNode OpModEqualNode
OpMultiplyEqualNode OpOrEqualNode OpPlusEqualNode OpRShiftEqualNode
OpURShiftEqualNode OpXOrEqualNode ParameterNode PostfixNode PrefixNode
PropertyNode RegexpNode ResolveNode ReturnNode RightShiftNode
SetterPropertyNode StrictEqualNode StringNode
SubtractNode SwitchNode ThisNode ThrowNode TrueNode TryNode TypeOfNode
UnaryMinusNode UnaryPlusNode UnsignedRightShiftNode VarDeclNode
VarStatementNode VoidNode WhileNode WithNode
LessOrEqualNode LetStatementNode LogicalAndNode LogicalNotNode
LogicalOrNode ModulusNode MultiplyNode NewExprNode NotEqualNode
NotStrictEqualNode NullNode NumberNode ObjectLiteralNode
OpAndEqualNode OpDivideEqualNode OpEqualNode OpLShiftEqualNode
OpMinusEqualNode OpModEqualNode OpMultiplyEqualNode OpOrEqualNode
OpPlusEqualNode OpRShiftEqualNode OpURShiftEqualNode OpXOrEqualNode
ParameterNode PostfixNode PrefixNode PropertyNode RegexpNode
ResolveNode ReturnNode RightShiftNode SetterPropertyNode
StrictEqualNode StringNode SubtractNode SwitchNode ThisNode ThrowNode
TrueNode TryNode TypeOfNode UnaryMinusNode UnaryPlusNode
UnsignedRightShiftNode VarDeclNode VarStatementNode VoidNode WhileNode
WithNode
}.each do |type|
define_method(:"visit_#{type}") do |o|
end
Expand Down
6 changes: 5 additions & 1 deletion lib/recma/visitors/sexp_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def visit_AssignExprNode(o)
end

def visit_VarDeclNode(o)
[ o.constant? ? :const_decl : :var_decl ] + super(o)
[ o.const? ? :const_decl : o.let? ? :let_decl : :var_decl ] + super(o)
end

def visit_VarStatementNode(o)
Expand Down Expand Up @@ -61,6 +61,10 @@ def visit_ConstStatementNode(o)
[:const, super]
end

def visit_LetStatementNode(o)
[:let, super]
end

def visit_MultiplyNode(o)
[:multiply, *super]
end
Expand Down
4 changes: 2 additions & 2 deletions lib/recma/visitors/visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class Visitor
While With
}
ARRAY_VALUE_NODES = %w{
Arguments Array CaseBlock ConstStatement ObjectLiteral SourceElements
VarStatement
Arguments Array CaseBlock ConstStatement LetStatement ObjectLiteral
SourceElements VarStatement
}
NAME_VALUE_NODES = %w{
Label Property GetterProperty SetterProperty VarDecl
Expand Down
13 changes: 13 additions & 0 deletions test/test_const_statement_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ def test_to_sexp
)
end
end

class LetStatementNodeTest < NodeTestCase
def test_to_sexp
initializer = AssignExprNode.new(NumberNode.new(10))
decl = VarDeclNode.new('foo', initializer, :let)
stmt = LetStatementNode.new([decl])

assert_sexp(
[:let, [[:let_decl, :foo, [:assign, [:lit, 10]]]]],
stmt
)
end
end
32 changes: 32 additions & 0 deletions test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,38 @@ def test_const_statement_error
)
end

def test_let_statement
assert_sexp(
[[:let, [[:let_decl, :foo, [:assign, [:lit, 10]]]]]],
@parser.parse('let foo = 10;')
)
end

def test_let_decl_list
assert_sexp(
[[:let,
[
[:let_decl, :foo, [:assign, [:lit, 10]]],
[:let_decl, :bar, [:assign, [:lit, 1]]],
]]],
@parser.parse('let foo = 10, bar = 1;')
)
end

def test_let_decl_no_init
assert_sexp(
[[:let, [[:let_decl, :foo, nil]]]],
@parser.parse('let foo;')
)
end

def test_let_statement_error
assert_sexp(
[[:let, [[:let_decl, :foo, [:assign, [:lit, 10]]]]]],
@parser.parse('let foo = 10')
)
end

def test_variable_statement
assert_sexp(
[[:var, [[:var_decl, :foo, [:assign, [:lit, 10]]]]]],
Expand Down
6 changes: 3 additions & 3 deletions test/test_tokenizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def assert_tokens(expected, actual)
if in instanceof new return switch this throw try typeof var void while
with

const true false null debugger
const let true false null debugger
}.each do |kw|
define_method(:"test_keyword_#{kw}") do
tokens = @tokenizer.tokenize(kw)
Expand All @@ -174,8 +174,8 @@ def assert_tokens(expected, actual)
'!=' => :NE,
'===' => :STREQ,
'!==' => :STRNEQ,
'<=' => :LE,
'>=' => :GE,
'<=' => :LE,
'>=' => :GE,
'||' => :OR,
'&&' => :AND,
'++' => :PLUSPLUS,
Expand Down