diff --git a/semantic/semantic.go b/semantic/semantic.go index 0c93ab4..37831e0 100644 --- a/semantic/semantic.go +++ b/semantic/semantic.go @@ -40,18 +40,17 @@ func (s *Semantic) exitScope() { } // declare will keep track of declare variables -func (s *Semantic) declare(node *ast.VarStatement) { +func (s *Semantic) declare(name string) { if s.scopeStack.IsEmpty() { return } peek, _ := s.scopeStack.Peek() - (*peek)[node.Name.Value] = false + (*peek)[name] = false } // resolve after a variable been resolve we mark it as resolved. -func (s *Semantic) resolve(node *ast.VarStatement) { - name := node.Name.Value +func (s *Semantic) resolve(name string) { peek, ok := s.scopeStack.Peek() if !ok { return @@ -104,21 +103,30 @@ func (s *Semantic) analysis(node ast.Node) ast.Node { case *ast.Identifier: s.expectIdentifierDeclare(node) case *ast.VarStatement: - s.declare(node) + s.declare(node.Name.Value) if _, ok := node.Value.(ast.Expression); ok { s.analysis(node.Value) } - s.resolve(node) + s.resolve(node.Name.Value) case *ast.ExpressionStatement: s.analysis(node.Expression) + case *ast.PrefixExpression: + s.analysis(node.Right) + case *ast.InfixExpression: + s.analysis(node.Left) + s.analysis(node.Right) case *ast.FunctionLiteral: + s.newScope() + for _, arg := range node.Parameters { + s.declare(arg.Value) + s.resolve(arg.Value) + } s.analysis(node.Body) + s.exitScope() case *ast.BlockStatement: - s.newScope() for _, b := range node.Statements { s.analysis(b) } - s.exitScope() } return node } diff --git a/semantic/semantic_test.go b/semantic/semantic_test.go index 9c8fe66..feee6ac 100644 --- a/semantic/semantic_test.go +++ b/semantic/semantic_test.go @@ -21,6 +21,30 @@ func TestScopeVariable(t *testing.T) { { `function () { var a = true; var b = a; }`, }, + { + `var a = 1`, + }, + { + `var a = true`, + }, + { + `var a = []`, + }, + { + `var a = {}`, + }, + { + `function (x) { return x; }`, + }, + { + `function () { function (y) { var a = y + 1; } }`, + }, + { + `function () { var a = -1; }`, + }, + { + `function () { var a = !true; }`, + }, } for i, tt := range tests { @@ -43,6 +67,10 @@ func TestScopeVariablesWrong(t *testing.T) { `function () { var a = a; }`, "Can't read local variable \"a\" in its own initializer IDENT at [Line: 1, Offset: 24]", }, + { + `function () { var a = a + 1 }`, + "Can't read local variable \"a\" in its own initializer IDENT at [Line: 1, Offset: 24]", + }, { `function () { var a = [1, b]; }`, "Variable \"b\" not declare yet IDENT at [Line: 1, Offset: 28]", @@ -75,6 +103,18 @@ func TestScopeVariablesWrong(t *testing.T) { `function () { var a = "local"; function () { var a = a; } }`, "Can't read local variable \"a\" in its own initializer IDENT at [Line: 1, Offset: 55]", }, + { + `function () { var a = "local"; function () { function () { var a = a; } } }`, + "Can't read local variable \"a\" in its own initializer IDENT at [Line: 1, Offset: 69]", + }, + { + `function () { function (y) { var a = x + 1; } }`, + `Variable "x" not declare yet IDENT at [Line: 1, Offset: 39]`, + }, + { + `function () { var a = !x; }`, + `Variable "x" not declare yet IDENT at [Line: 1, Offset: 25]`, + }, } for i, tt := range tests {