From 8f84de20911abd7ef3e26237089ad5a39df40c09 Mon Sep 17 00:00:00 2001 From: boohyunsik Date: Wed, 27 Feb 2019 00:40:33 +0900 Subject: [PATCH 1/5] Implements skel functions for scope refactoring --- symbol/scope.go | 134 +++++++++++++++--------------- symbol/scope_test.go | 192 +++++++------------------------------------ 2 files changed, 97 insertions(+), 229 deletions(-) diff --git a/symbol/scope.go b/symbol/scope.go index 9c0162dc..ac480275 100644 --- a/symbol/scope.go +++ b/symbol/scope.go @@ -16,93 +16,95 @@ package symbol -import ( - "bytes" - "fmt" - "sort" -) +import "github.com/DE-labtory/koa/ast" + +type Scope struct { + store map[string]Symbol + + child []*Scope + parent *Scope +} -// NewScope() makes a new scope. func NewScope() *Scope { - s := make(map[string]Symbol) return &Scope{ - store: s, - inner: make([]*Scope, 0), - outer: nil, + store: make(map[string]Symbol), + parent: nil, } } -// NewEnclosedScope makes a new scope which has outer scope. -func NewEnclosedScope(outer *Scope) *Scope { - scope := NewScope() - scope.outer = outer - return scope +func NewEnclosedScope(p *Scope) *Scope { + s := &Scope{ + store: make(map[string]Symbol), + child: make([]*Scope, 0), + parent: p, + } + p.child = append(p.child, s) + return s } -// Scope represent a variable's scope. -type Scope struct { - store map[string]Symbol - - inner []*Scope - outer *Scope +func GenerateScope(c *ast.Contract) *Scope { + s := NewScope() + for _, f := range c.Functions { + ScopingFunctionParameter(f, s) + ScopingFunctionBody(f, s) + } + return nil } -// Getter return's variable's scope. -// If a scope doesn't have a variable, -// Program will search in outer scope. -func (s *Scope) Get(name string) Symbol { - var scope = s - for scope != nil { - obj, ok := scope.store[name] - if ok { - return obj +func ScopingFunctionParameter(f *ast.FunctionLiteral, s *Scope) { + for _, p := range f.Parameters { + switch p.Type { + case ast.IntType: + { + s.store[p.Identifier.Name] = &Integer{Name: p.Identifier} + } + case ast.StringType: + { + s.store[p.Identifier.Name] = &String{Name: p.Identifier} + } + case ast.BoolType: + { + s.store[p.Identifier.Name] = &Boolean{Name: p.Identifier} + } } - scope = scope.outer } - return nil } -// Setter set a variable to target scope's map -func (s *Scope) Set(name string, val Symbol) Symbol { - s.store[name] = val - return val -} +func ScopingFunctionBody(b *ast.FunctionLiteral, s *Scope) { + for _, stmt := range b.Body.Statements { + switch implStmt := stmt.(type) { + case *ast.AssignStatement: + { -func (s *Scope) SetOuter(outer *Scope) { - s.outer = outer -} + } + case *ast.ReturnStatement: + { -func (s *Scope) GetOuter() *Scope { - return s.outer -} + } + case *ast.BlockStatement: + { -func (s *Scope) AppendInner(in *Scope) { - s.inner = append(s.inner, in) -} + } + case *ast.ExpressionStatement: + { -func (s *Scope) GetInner() []*Scope { - return s.inner -} + } + case *ast.ReassignStatement: + { -func (s *Scope) String() string { - var out bytes.Buffer - scope := s + } + case *ast.IfStatement: + { - for scope != nil { - // sort scope.store's keys alphabetically - keys := make([]string, 0) - for k := range scope.store { - keys = append(keys, k) + } } - sort.Strings(keys) + } +} - out.WriteString("[ Scope ]\n") - for _, k := range keys { - out.WriteString(fmt.Sprintf("key:%s, symbol:%s\n", - k, scope.store[k].String())) - } +func ScopingExpression(exp *ast.Expression, scope *Scope) { + +} + +func ScopingIdentifier(ident *ast.Identifier, scope *Scope) { - scope = scope.outer - } - return out.String() } diff --git a/symbol/scope_test.go b/symbol/scope_test.go index 7eee5c98..9f56063d 100644 --- a/symbol/scope_test.go +++ b/symbol/scope_test.go @@ -17,193 +17,59 @@ package symbol import ( - "testing" - "github.com/DE-labtory/koa/ast" + "reflect" + "testing" ) -func TestNewEnclosedScope(t *testing.T) { - outer := NewScope() - s := NewEnclosedScope(outer) - - if s.outer != outer { - t.Fatalf("testNewEnclosedScope() failed. outer must be set") - } - - if len(s.store) > 0 { - t.Fatalf("testNewEnclosedScope() failed. store's size must be 0") - } -} - func TestNewScope(t *testing.T) { - s := NewScope() - if s.outer != nil { - t.Fatalf("testNewScope() failed. outer must be nil") - } - - if len(s.store) > 0 { - t.Fatalf("testNewScope() failed. store's size must be 0") - } -} - -func TestScopeGetter(t *testing.T) { - tests := []struct { - scope Scope - want string - expectedSym Symbol - }{ - { - Scope{ - store: map[string]Symbol{ - "a": &Integer{&ast.Identifier{Name: "a"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - }, - }, - "a", - &Integer{&ast.Identifier{Name: "a"}}, - }, - { - Scope{ - store: map[string]Symbol{ - "a": &Integer{&ast.Identifier{Name: "a"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - }, - outer: &Scope{ - store: map[string]Symbol{ - "c": &String{&ast.Identifier{Name: "c"}}, - }, - }, - }, - "c", - &String{&ast.Identifier{Name: "c"}}, - }, - { - Scope{ - store: map[string]Symbol{ - "a": &Integer{&ast.Identifier{Name: "a"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - }, - }, - "c", - nil, - }, - } - for i, test := range tests { - sym := test.scope.Get(test.want) - if sym != nil && test.expectedSym.String() != sym.String() { - t.Fatalf("test[%d] testScopeGetter() returns invalid symbol.\n"+ - "expected=%s\n"+ - "got=%s", i, test.expectedSym.String(), sym.String()) - } - - if sym != nil && test.expectedSym == nil { - t.Fatalf("test[%d] testScopeGetter() returns invalid symbol.\n"+ - "expected=nil\n"+ - "got=%s", i, sym.String()) - } - } } -func TestScopeSetter(t *testing.T) { - tests := []struct { - Scope *Scope - Name string - Symbol Symbol - }{ - { - &Scope{ - store: map[string]Symbol{}, - outer: &Scope{}, - }, - "testInt", - &Integer{&ast.Identifier{Name: "testInt"}}, - }, - { - &Scope{ - store: map[string]Symbol{}, - outer: &Scope{}, - }, - "testBool", - &Boolean{&ast.Identifier{Name: "testBool"}}, - }, - { - &Scope{ - store: map[string]Symbol{}, - outer: &Scope{}, - }, - "testString", - &String{&ast.Identifier{Name: "testString"}}, - }, - } - - for i, test := range tests { - symbol := test.Scope.Set(test.Name, test.Symbol) - if symbol.String() != test.Symbol.String() { - t.Fatalf("test[%d] - TestScopeSetter() wrong result.\n"+ - "expected=%s\n"+ - "got=%s", i, test.Symbol.String(), symbol.String()) - } - - if sym := test.Scope.Get(test.Name); sym.String() != test.Symbol.String() { - t.Fatalf("test[%d] - TestScopeSetter() must set in scope store", i) - } - } +func TestNewEnclosedScope(t *testing.T) { } -func TestScopeString(t *testing.T) { +func TestGenerateScope(t *testing.T) { tests := []struct { - scope Scope - expected string + contract ast.Contract + expected Scope }{ { - Scope{ - store: map[string]Symbol{ - "a": &Integer{&ast.Identifier{Name: "a"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - }, - }, - "[ Scope ]\nkey:a, symbol:a\nkey:b, symbol:b\n", - }, - { - Scope{ - store: map[string]Symbol{ - "a": &Integer{&ast.Identifier{Name: "a"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - }, - outer: &Scope{ - store: map[string]Symbol{ - "c": &String{&ast.Identifier{Name: "c"}}, + // contract which has one function named "foo", and it has one integer parameter named "a" + contract: ast.Contract{ + Functions: []*ast.FunctionLiteral{ + { + Name: &ast.Identifier{Name: "foo"}, + Parameters: []*ast.ParameterLiteral{ + { + Type: ast.IntType, + Identifier: &ast.Identifier{Name: "a"}, + }, + }, }, - outer: nil, }, }, - "[ Scope ]\nkey:a, symbol:a\nkey:b, symbol:b\n[ Scope ]\nkey:c, symbol:c\n", - }, - { - Scope{ + expected: Scope{ store: map[string]Symbol{ - "c": &Integer{&ast.Identifier{Name: "c"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - "a": &Integer{&ast.Identifier{Name: "a"}}, + "foo": &Function{Name: "foo"}, }, - outer: &Scope{ - store: map[string]Symbol{ - "d": &String{&ast.Identifier{Name: "d"}}, + parent: nil, + child: []*Scope{ + { + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, }, }, }, - "[ Scope ]\nkey:a, symbol:a\nkey:b, symbol:b\nkey:c, symbol:c\n[ Scope ]\nkey:d, symbol:d\n", }, } - for i, test := range tests { - str := test.scope.String() - if str != test.expected { - t.Fatalf("test[%d] - TestScopeString() wrong result.\n"+ - "expected :\n%s\n"+ - "got :\n%s\n", i, test.expected, str) + for _, test := range tests { + scope := GenerateScope(&test.contract) + if !reflect.DeepEqual(test.expected, scope) { + t.Fatalf("") } } } From 916ce5ef29f46745e2b90e2f3fe14301796e28ca Mon Sep 17 00:00:00 2001 From: boohyunsik Date: Wed, 27 Feb 2019 01:36:34 +0900 Subject: [PATCH 2/5] Implements default tcs for scoping assign, if --- symbol/scope.go | 48 ++++++++++++-------------- symbol/scope_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 26 deletions(-) diff --git a/symbol/scope.go b/symbol/scope.go index ac480275..c11cd402 100644 --- a/symbol/scope.go +++ b/symbol/scope.go @@ -70,41 +70,37 @@ func ScopingFunctionParameter(f *ast.FunctionLiteral, s *Scope) { } } -func ScopingFunctionBody(b *ast.FunctionLiteral, s *Scope) { - for _, stmt := range b.Body.Statements { - switch implStmt := stmt.(type) { - case *ast.AssignStatement: - { - - } - case *ast.ReturnStatement: - { - - } - case *ast.BlockStatement: - { - - } - case *ast.ExpressionStatement: - { +func ScopingFunctionBody(f *ast.FunctionLiteral, scope *Scope) { + ScopingBlockStatement(f.Body, scope) +} - } - case *ast.ReassignStatement: - { +func ScopingStatement(stmt ast.Statement, scope *Scope) { + switch implStmt := stmt.(type) { + case *ast.AssignStatement: + ScopingAssignStatement(implStmt, scope) + case *ast.BlockStatement: + ScopingBlockStatement(implStmt, scope) + case *ast.ExpressionStatement: + { - } - case *ast.IfStatement: - { + } + case *ast.IfStatement: + { - } } } } -func ScopingExpression(exp *ast.Expression, scope *Scope) { +func ScopingAssignStatement(stmt *ast.AssignStatement, scope *Scope) { } -func ScopingIdentifier(ident *ast.Identifier, scope *Scope) { +func ScopingBlockStatement(bStmt *ast.BlockStatement, scope *Scope) { + for _, stmt := range bStmt.Statements { + ScopingStatement(stmt, scope) + } +} + +func ScopingIfStatement(iStmt *ast.IfStatement, scope *Scope) { } diff --git a/symbol/scope_test.go b/symbol/scope_test.go index 9f56063d..8fd67ec7 100644 --- a/symbol/scope_test.go +++ b/symbol/scope_test.go @@ -22,6 +22,12 @@ import ( "testing" ) +type setupScopeFn func() *Scope + +func defaultScope() *Scope { + return &Scope{} +} + func TestNewScope(t *testing.T) { } @@ -73,3 +79,77 @@ func TestGenerateScope(t *testing.T) { } } } + +func TestScopingAssignStatement(t *testing.T) { + tests := []struct { + setupScopeFn + input *ast.AssignStatement + expected *Scope + }{ + { + setupScopeFn: defaultScope, + input: &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + }, + } + + for i, test := range tests { + s := test.setupScopeFn() + ScopingAssignStatement(test.input, s) + if test.expected != s { + t.Fatalf("test [%d] - TestScopingAssignStatement failed", i) + } + } +} + +func TestScopingIfStatement(t *testing.T) { + tests := []struct { + setupScopeFn + input *ast.IfStatement + expected *Scope + }{ + { + setupScopeFn: defaultScope, + input: &ast.IfStatement{ + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + }, + } + + for i, test := range tests { + s := test.setupScopeFn() + ScopingIfStatement(test.input, s) + if test.expected != s { + t.Fatalf("test [%d] - TestScopingAssignStatement failed", i) + } + } + +} From 6f84f820e00dcd2ef78e8ca141f730800d0f7a1e Mon Sep 17 00:00:00 2001 From: boohyunsik Date: Wed, 27 Feb 2019 22:28:55 +0900 Subject: [PATCH 3/5] Implements ScopingAssignmentStatement and tcs --- symbol/scope.go | 98 ++++++++++++--- symbol/scope_test.go | 278 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 330 insertions(+), 46 deletions(-) diff --git a/symbol/scope.go b/symbol/scope.go index c11cd402..61737541 100644 --- a/symbol/scope.go +++ b/symbol/scope.go @@ -16,8 +16,22 @@ package symbol -import "github.com/DE-labtory/koa/ast" +import ( + "fmt" + "github.com/DE-labtory/koa/ast" +) + +// Define scope errors +type DupError struct { + Identifier ast.Identifier +} + +func (e DupError) Error() string { + return fmt.Sprintf("%s is already defined.", e.Identifier.Name) +} + +// Define scope structure type Scope struct { store map[string]Symbol @@ -42,17 +56,30 @@ func NewEnclosedScope(p *Scope) *Scope { return s } -func GenerateScope(c *ast.Contract) *Scope { - s := NewScope() +func ScopingContract(c *ast.Contract, s *Scope) error { for _, f := range c.Functions { - ScopingFunctionParameter(f, s) - ScopingFunctionBody(f, s) + if err := ScopingFunction(f, s); err != nil { + return err + } + } + return nil +} + +func ScopingFunction(f *ast.FunctionLiteral, s *Scope) error { + s = NewEnclosedScope(s) + if err := ScopingFunctionParameter(f.Parameters, s); err != nil { + return nil + } + + if err := ScopingFunctionBody(f.Body, s); err != nil { + return err } + return nil } -func ScopingFunctionParameter(f *ast.FunctionLiteral, s *Scope) { - for _, p := range f.Parameters { +func ScopingFunctionParameter(f []*ast.ParameterLiteral, s *Scope) error { + for _, p := range f { switch p.Type { case ast.IntType: { @@ -68,18 +95,23 @@ func ScopingFunctionParameter(f *ast.FunctionLiteral, s *Scope) { } } } + return nil } -func ScopingFunctionBody(f *ast.FunctionLiteral, scope *Scope) { - ScopingBlockStatement(f.Body, scope) +func ScopingFunctionBody(f *ast.BlockStatement, scope *Scope) error { + if err := ScopingBlockStatement(f, scope); err != nil { + return err + } + return nil } -func ScopingStatement(stmt ast.Statement, scope *Scope) { +func ScopingStatement(stmt ast.Statement, scope *Scope) error { + var err error switch implStmt := stmt.(type) { case *ast.AssignStatement: - ScopingAssignStatement(implStmt, scope) + err = ScopingAssignStatement(implStmt, scope) case *ast.BlockStatement: - ScopingBlockStatement(implStmt, scope) + err = ScopingBlockStatement(implStmt, scope) case *ast.ExpressionStatement: { @@ -89,18 +121,54 @@ func ScopingStatement(stmt ast.Statement, scope *Scope) { } } + + if err != nil { + return err + } + return nil } -func ScopingAssignStatement(stmt *ast.AssignStatement, scope *Scope) { +func ScopingAssignStatement(stmt *ast.AssignStatement, scope *Scope) error { + if ok := scope.store[stmt.Variable.Name]; ok != nil { + return DupError{ + Identifier: stmt.Variable, + } + } + if err := ScopingIdentifier(stmt.Variable, stmt.Type, scope); err != nil { + return err + } + return nil } -func ScopingBlockStatement(bStmt *ast.BlockStatement, scope *Scope) { +func ScopingBlockStatement(bStmt *ast.BlockStatement, scope *Scope) error { + scope = NewEnclosedScope(scope) for _, stmt := range bStmt.Statements { - ScopingStatement(stmt, scope) + if err := ScopingStatement(stmt, scope); err != nil { + return err + } } + return nil } func ScopingIfStatement(iStmt *ast.IfStatement, scope *Scope) { } + +func ScopingIdentifier(identifier ast.Identifier, ds ast.DataStructure, scope *Scope) error { + if ok := scope.store[identifier.Name]; ok != nil { + return DupError{Identifier: identifier} + } + + name := identifier.Name + switch ds { + case ast.IntType: + scope.store[name] = &Integer{Name: &ast.Identifier{Name: name}} + case ast.StringType: + scope.store[name] = &String{Name: &ast.Identifier{Name: name}} + case ast.BoolType: + scope.store[name] = &Boolean{Name: &ast.Identifier{Name: name}} + } + + return nil +} diff --git a/symbol/scope_test.go b/symbol/scope_test.go index 8fd67ec7..7dbe13d6 100644 --- a/symbol/scope_test.go +++ b/symbol/scope_test.go @@ -17,15 +17,16 @@ package symbol import ( - "github.com/DE-labtory/koa/ast" "reflect" "testing" + + "github.com/DE-labtory/koa/ast" ) type setupScopeFn func() *Scope func defaultScope() *Scope { - return &Scope{} + return NewScope() } func TestNewScope(t *testing.T) { @@ -36,55 +37,111 @@ func TestNewEnclosedScope(t *testing.T) { } -func TestGenerateScope(t *testing.T) { +func TestScopingFunction(t *testing.T) { + +} + +func TestScopingFunctionParameter(t *testing.T) { tests := []struct { - contract ast.Contract - expected Scope + setupScopeFn + input []*ast.ParameterLiteral + expected *Scope + err error }{ { - // contract which has one function named "foo", and it has one integer parameter named "a" - contract: ast.Contract{ - Functions: []*ast.FunctionLiteral{ - { - Name: &ast.Identifier{Name: "foo"}, - Parameters: []*ast.ParameterLiteral{ - { - Type: ast.IntType, - Identifier: &ast.Identifier{Name: "a"}, - }, - }, + setupScopeFn: defaultScope, + input: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", }, + Type: ast.IntType, }, }, - expected: Scope{ + expected: &Scope{ store: map[string]Symbol{ - "foo": &Function{Name: "foo"}, + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, }, - parent: nil, - child: []*Scope{ - { - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - }, + }, + err: nil, + }, + { + setupScopeFn: defaultScope, + input: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{ + Name: "b", + }, + Type: ast.BoolType, + }, + { + Identifier: &ast.Identifier{ + Name: "c", }, + Type: ast.StringType, + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Boolean{Name: &ast.Identifier{Name: "b"}}, + "c": &String{Name: &ast.Identifier{Name: "c"}}, }, }, + err: nil, + }, + { + setupScopeFn: defaultScope, + input: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + }, + expected: nil, + err: DupError{Identifier: ast.Identifier{Name: "a"}}, }, } - for _, test := range tests { - scope := GenerateScope(&test.contract) - if !reflect.DeepEqual(test.expected, scope) { - t.Fatalf("") + for i, test := range tests { + s := test.setupScopeFn() + err := ScopingFunctionParameter(test.input, s) + + if err == nil && !reflect.DeepEqual(test.expected, s) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, s) + } + if err != nil && reflect.DeepEqual(test.err, err) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) } } } +func TestScopingFunctionBody(t *testing.T) { + +} + func TestScopingAssignStatement(t *testing.T) { tests := []struct { setupScopeFn input *ast.AssignStatement expected *Scope + err error }{ { setupScopeFn: defaultScope, @@ -101,13 +158,54 @@ func TestScopingAssignStatement(t *testing.T) { }, }, }, + { + setupScopeFn: defaultScope, + input: &ast.AssignStatement{ + Type: ast.StringType, + Variable: ast.Identifier{Name: "str"}, + Value: &ast.StringLiteral{ + Value: "testString", + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "str": &String{Name: &ast.Identifier{Name: "str"}}, + }, + }, + }, + { + setupScopeFn: func() *Scope { + return &Scope{ + store: map[string]Symbol{ + "str": &String{Name: &ast.Identifier{Name: "str"}}, + }, + } + }, + input: &ast.AssignStatement{ + Type: ast.StringType, + Variable: ast.Identifier{Name: "str"}, + Value: &ast.StringLiteral{ + Value: "testString", + }, + }, + expected: nil, + err: DupError{ + Identifier: ast.Identifier{Name: "str"}, + }, + }, } for i, test := range tests { s := test.setupScopeFn() - ScopingAssignStatement(test.input, s) - if test.expected != s { - t.Fatalf("test [%d] - TestScopingAssignStatement failed", i) + err := ScopingAssignStatement(test.input, s) + + if err == nil && !reflect.DeepEqual(s, test.expected) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, s) + } + if err != nil && !reflect.DeepEqual(err, test.err) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) } } } @@ -117,6 +215,7 @@ func TestScopingIfStatement(t *testing.T) { setupScopeFn input *ast.IfStatement expected *Scope + err error }{ { setupScopeFn: defaultScope, @@ -142,6 +241,70 @@ func TestScopingIfStatement(t *testing.T) { }, }, }, + { + setupScopeFn: defaultScope, + input: &ast.IfStatement{ + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "b"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + }, + }, + { + setupScopeFn: defaultScope, + input: &ast.IfStatement{ + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "b"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + }, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + }, + }, } for i, test := range tests { @@ -151,5 +314,58 @@ func TestScopingIfStatement(t *testing.T) { t.Fatalf("test [%d] - TestScopingAssignStatement failed", i) } } +} +func TestScopingIdentifier(t *testing.T) { + tests := []struct { + setupScopeFn + idf ast.Identifier + ds ast.DataStructure + expected *Scope + err error + }{ + { + setupScopeFn: defaultScope, + idf: ast.Identifier{ + Name: "a", + }, + ds: ast.IntType, + expected: &Scope{ + store: map[string]Symbol{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + err: nil, + }, + { + setupScopeFn: func() *Scope { + return &Scope{ + store: map[string]Symbol{ + "a": &Boolean{Name: &ast.Identifier{Name: "a"}}, + }, + } + }, + idf: ast.Identifier{ + Name: "a", + }, + ds: ast.IntType, + expected: nil, + err: DupError{ + Identifier: ast.Identifier{Name: "a"}, + }, + }, + } + + for i, test := range tests { + s := test.setupScopeFn() + err := ScopingIdentifier(test.idf, test.ds, s) + if err == nil && !reflect.DeepEqual(s, test.expected) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, s) + } + if err != nil && !reflect.DeepEqual(err, test.err) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } } From 69a9ce2622d067a2087bdf8f2c05657be9a12dac Mon Sep 17 00:00:00 2001 From: boohyunsik Date: Thu, 28 Feb 2019 00:58:10 +0900 Subject: [PATCH 4/5] Add resolving assignment stmt functions and tcs --- ast/ast.go | 10 +- parse/parser_internal_test.go | 66 +-- symbol/resolver.go | 313 +++++++++++- symbol/resolver_test.go | 865 ++++++++++++++++++++++++++++++++++ symbol/scope.go | 174 ------- symbol/scope_test.go | 371 --------------- symbol/symbol.go | 35 +- symbol/symbol_test.go | 35 +- 8 files changed, 1243 insertions(+), 626 deletions(-) create mode 100644 symbol/resolver_test.go delete mode 100644 symbol/scope.go delete mode 100644 symbol/scope_test.go diff --git a/ast/ast.go b/ast/ast.go index 5bdc7761..801f3053 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -124,13 +124,15 @@ const ( StringType BoolType VoidType + InvalidType ) var DataStructureMap = map[DataStructure]string{ - IntType: "int", - StringType: "string", - BoolType: "bool", - VoidType: "void", + IntType: "int", + StringType: "string", + BoolType: "bool", + VoidType: "void", + InvalidType: "invalid", } func (ds DataStructure) String() string { diff --git a/parse/parser_internal_test.go b/parse/parser_internal_test.go index b2105ef5..52ffd30c 100644 --- a/parse/parser_internal_test.go +++ b/parse/parser_internal_test.go @@ -2194,7 +2194,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -2228,7 +2228,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -2262,7 +2262,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -2297,7 +2297,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -2332,7 +2332,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -2428,7 +2428,7 @@ func TestParseAssignStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -2812,7 +2812,7 @@ func TestParseIfStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -2865,7 +2865,7 @@ func TestParseIfStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -2878,7 +2878,7 @@ func TestParseIfStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3018,7 +3018,7 @@ func TestParseBlockStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3057,7 +3057,7 @@ string b = abc`, return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3070,7 +3070,7 @@ string b = abc`, return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3115,7 +3115,7 @@ bool c = true`, return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3128,7 +3128,7 @@ bool c = true`, return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3141,7 +3141,7 @@ bool c = true`, return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -3231,7 +3231,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3265,7 +3265,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3301,7 +3301,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3333,7 +3333,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3367,7 +3367,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3399,7 +3399,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3431,7 +3431,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -3465,7 +3465,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -3497,7 +3497,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -3529,7 +3529,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -3639,7 +3639,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3681,7 +3681,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3728,7 +3728,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -3741,7 +3741,7 @@ func TestParseStatement(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -4140,7 +4140,7 @@ func TestUpdateScopeSymbol(t *testing.T) { return false } - if sym.Type() != symbol.IntegerSymbol { + if sym.Type() != symbol.IntegerObject { return false } @@ -4162,7 +4162,7 @@ func TestUpdateScopeSymbol(t *testing.T) { return false } - if sym.Type() != symbol.BooleanSymbol { + if sym.Type() != symbol.BooleanObject { return false } @@ -4184,7 +4184,7 @@ func TestUpdateScopeSymbol(t *testing.T) { return false } - if sym.Type() != symbol.StringSymbol { + if sym.Type() != symbol.StringObject { return false } @@ -4206,7 +4206,7 @@ func TestUpdateScopeSymbol(t *testing.T) { return false } - if sym.Type() != symbol.FunctionSymbol { + if sym.Type() != symbol.FunctionObject { return false } diff --git a/symbol/resolver.go b/symbol/resolver.go index ed9c384f..c93d57dd 100644 --- a/symbol/resolver.go +++ b/symbol/resolver.go @@ -17,41 +17,336 @@ package symbol import ( + "fmt" + "github.com/DE-labtory/koa/ast" ) +var objTypeMap = map[ast.DataStructure]ObjectType{ + ast.IntType: IntegerObject, + ast.StringType: StringObject, + ast.BoolType: BooleanObject, + ast.VoidType: VoidObject, +} + // Resolver traverse AST and resolve symbols, (1) check symbol's scope // (2) check type of symbol type Resolver struct { scope *Scope // types manage expression its own type - types map[ast.Expression]SymbolType + types map[ast.Expression]ObjectType // defs manage identifier its own object - defs map[*ast.Identifier]Symbol + defs map[*ast.Identifier]Object + + // fns manager function its own type + fns map[*ast.FunctionLiteral]ObjectType +} + +// Define scope error +type DupError struct { + object Object +} + +func (e DupError) Error() string { + return fmt.Sprintf("%s is already defined.", e.object.String()) +} + +// Define data type error +type TypeError struct { + target ObjectType + object ObjectType +} + +func (e TypeError) Error() string { + return fmt.Sprintf("Type mismatched : %s, %s", e.target, e.object) +} + +// Define not exists error +type NoExistError struct { + identifier *ast.Identifier +} + +func (e NoExistError) Error() string { + return fmt.Sprintf("Object not exists : %s", e.identifier.Name) +} + +// Define scope structure +type Scope struct { + store map[string]Object + + child []*Scope + parent *Scope +} + +func (s *Scope) Get(name string) Object { + scope := s + for scope.parent != nil { + if obj, ok := scope.store[name]; ok { + return obj + } + scope = scope.parent + } + + if obj, ok := scope.store[name]; ok { + return obj + } + scope = scope.parent + + return nil } func NewResolver() *Resolver { + s := NewScope() return &Resolver{ - scope: NewScope(), - types: make(map[ast.Expression]SymbolType), - defs: make(map[*ast.Identifier]Symbol), + scope: s, + types: make(map[ast.Expression]ObjectType), + defs: make(map[*ast.Identifier]Object), + fns: make(map[*ast.FunctionLiteral]ObjectType), } } // typeof returns the type of expression exp, // or return InvalidSymbol if not found -func (r *Resolver) typeOf(exp ast.Expression) SymbolType { - return "" +func (r *Resolver) typeOf(exp ast.Expression) ObjectType { + if st, ok := r.types[exp]; ok { + return st + } + return InvalidSymbol } // objectOf returns the object denoted by the specified identifier // or return nil if not found -func (r *Resolver) objectOf(id *ast.Identifier) Symbol { +func (r *Resolver) objectOf(id *ast.Identifier) Object { + if sym, ok := r.defs[id]; ok { + return sym + } return nil } -func (r *Resolver) ResolveContract(c *ast.Contract) error { +func NewScope() *Scope { + return &Scope{ + store: make(map[string]Object), + parent: nil, + } +} + +func NewEnclosedScope(p *Scope) *Scope { + s := &Scope{ + store: make(map[string]Object), + child: make([]*Scope, 0), + parent: p, + } + p.child = append(p.child, s) + return s +} + +func ResolveContract(c *ast.Contract) error { + r := NewResolver() + + for _, f := range c.Functions { + name := f.Name.Name + obj := &Function{Name: name} + + r.scope.store[name] = obj + r.fns[f] = FunctionObject + r.defs[f.Name] = obj + } + + for _, f := range c.Functions { + if err := ResolveFunction(f, r); err != nil { + return err + } + } return nil } + +func ResolveFunction(f *ast.FunctionLiteral, r *Resolver) error { + r.scope = NewEnclosedScope(r.scope) + + if err := ResolveFunctionParameter(f.Parameters, r); err != nil { + return nil + } + + if err := ResolveFunctionBody(f.Body, r); err != nil { + return err + } + + r.scope = r.scope.parent + return nil +} + +func ResolveFunctionParameter(p []*ast.ParameterLiteral, r *Resolver) error { + for _, p := range p { + if obj, ok := r.scope.store[p.Identifier.Name]; ok { + return DupError{ + object: obj, + } + } + + var obj Object + var t ObjectType + switch p.Type { + case ast.IntType: + obj = &Integer{Name: p.Identifier} + t = IntegerObject + case ast.StringType: + obj = &String{Name: p.Identifier} + t = StringObject + case ast.BoolType: + obj = &Boolean{Name: p.Identifier} + t = BooleanObject + } + r.scope.store[p.Identifier.Name] = obj + r.types[p] = t + r.defs[p.Identifier] = obj + } + return nil +} + +func ResolveFunctionBody(b *ast.BlockStatement, r *Resolver) error { + if err := ResolveBlockStatement(b, r); err != nil { + return err + } + return nil +} + +func ResolveStatement(stmt ast.Statement, r *Resolver) error { + var err error + switch implStmt := stmt.(type) { + case *ast.AssignStatement: + err = ResolveAssignStatement(implStmt, r) + case *ast.BlockStatement: + err = ResolveBlockStatement(implStmt, r) + case *ast.ExpressionStatement: + // TODO + case *ast.IfStatement: + err = ResolveIfStatement(implStmt, r) + } + + if err != nil { + return err + } + return nil +} + +func ResolveAssignStatement(aStmt *ast.AssignStatement, r *Resolver) error { + // check if variable which has same name exists + if obj, ok := r.scope.store[aStmt.Variable.Name]; ok { + return DupError{ + object: obj, + } + } + + name := aStmt.Variable.Name + switch aStmt.Type { + case ast.IntType: + r.scope.store[name] = &Integer{Name: &ast.Identifier{Name: name}} + case ast.BoolType: + r.scope.store[name] = &Boolean{Name: &ast.Identifier{Name: name}} + case ast.StringType: + r.scope.store[name] = &String{Name: &ast.Identifier{Name: name}} + } + + ot, err := ResolveExpression(aStmt.Value, r) + if err != nil { + return err + } + if ot != objTypeMap[aStmt.Type] { + return TypeError{ + target: objTypeMap[aStmt.Type], + object: ot, + } + } + + switch aStmt.Type { + case ast.IntType: + r.types[aStmt.Value] = IntegerObject + r.defs[&aStmt.Variable] = &Integer{Name: &aStmt.Variable} + case ast.BoolType: + r.types[aStmt.Value] = BooleanObject + r.defs[&aStmt.Variable] = &Boolean{Name: &aStmt.Variable} + case ast.StringType: + r.types[aStmt.Value] = StringObject + r.defs[&aStmt.Variable] = &String{Name: &aStmt.Variable} + } + + return nil +} + +func ResolveBlockStatement(bStmt *ast.BlockStatement, r *Resolver) error { + r.scope = NewEnclosedScope(r.scope) + for _, stmt := range bStmt.Statements { + if err := ResolveStatement(stmt, r); err != nil { + return err + } + } + r.scope = r.scope.parent + return nil +} + +func ResolveIfStatement(ifStmt *ast.IfStatement, r *Resolver) error { + if ifStmt.Consequence != nil { + if err := ResolveBlockStatement(ifStmt.Consequence, r); err != nil { + return err + } + } + if ifStmt.Alternative != nil { + if err := ResolveBlockStatement(ifStmt.Alternative, r); err != nil { + return err + } + } + return nil +} + +// ResolveIdentifier checks whether target identifier is in the scope. +func ResolveIdentifier(identifier *ast.Identifier, r *Resolver) (ObjectType, error) { + if obj := r.scope.Get(identifier.Name); obj != nil { + return obj.Type(), nil + } + + return InvalidSymbol, NoExistError{ + identifier: identifier, + } +} + +func ResolveExpression(exp ast.Expression, r *Resolver) (ObjectType, error) { + switch implExp := exp.(type) { + case *ast.PrefixExpression: + return ResolvePrefixExpression(implExp, r) + case *ast.InfixExpression: + return ResolveInfixExpression(implExp, r) + case *ast.Identifier: + return ResolveIdentifier(implExp, r) + case *ast.CallExpression: + return ResolveCallExpression(implExp, r) + case *ast.IntegerLiteral: + return IntegerObject, nil + case *ast.BooleanLiteral: + return BooleanObject, nil + case *ast.StringLiteral: + return StringObject, nil + } + return InvalidSymbol, nil +} + +func ResolveCallExpression(exp *ast.CallExpression, r *Resolver) (ObjectType, error) { + return InvalidSymbol, nil +} + +func ResolvePrefixExpression(exp ast.Expression, r *Resolver) (ObjectType, error) { + return InvalidSymbol, nil +} + +func ResolveInfixExpression(exp *ast.InfixExpression, r *Resolver) (ObjectType, error) { + if ot, err := ResolveExpression(exp.Left, r); err != nil { + return ot, err + } + + if ot, err := ResolveExpression(exp.Right, r); err != nil { + return ot, err + } + + return InvalidSymbol, nil +} diff --git a/symbol/resolver_test.go b/symbol/resolver_test.go new file mode 100644 index 00000000..7c909a95 --- /dev/null +++ b/symbol/resolver_test.go @@ -0,0 +1,865 @@ +/* +// * Copyright 2018 De-labtory +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * https://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +package symbol + +import ( + "reflect" + "testing" + + "github.com/DE-labtory/koa/ast" +) + +type setupResolverFn func() *Resolver + +func defaultResolver() *Resolver { + return NewResolver() +} + +func postMakeScope(s *Scope) *Scope { + for _, child := range s.child { + child.parent = s + postMakeScope(child) + } + return s +} + +func TestNewScope(t *testing.T) { + +} + +func TestNewEnclosedScope(t *testing.T) { + +} + +func TestGet(t *testing.T) { + tests := []struct { + wanted string + input *Scope + expected Object + }{ + { + wanted: "a", + input: &Scope{ + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + expected: &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + } + + for i, test := range tests { + obj := test.input.Get(test.wanted) + if !reflect.DeepEqual(obj, test.expected) { + t.Fatalf("[test %d] - TestGet failed.\nexpected=%v\ngot=%v", + i, test.expected, obj) + } + } +} + +func TestResolveFunction(t *testing.T) { + vars := []struct { + fl *ast.FunctionLiteral + }{ + { + fl: &ast.FunctionLiteral{ + Name: &ast.Identifier{Name: "testFunction"}, + Parameters: []*ast.ParameterLiteral{}, + Body: &ast.BlockStatement{}, + ReturnType: ast.VoidType, + }, + }, + { + fl: &ast.FunctionLiteral{ + Name: &ast.Identifier{Name: "add"}, + Parameters: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{Name: "a"}, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{Name: "b"}, + Type: ast.IntType, + }, + }, + Body: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.ReturnStatement{ + ReturnValue: &ast.InfixExpression{ + Left: &ast.Identifier{ + Name: "a", + }, + Operator: ast.Plus, + Right: &ast.Identifier{ + Name: "b", + }, + }, + }, + }, + }, + ReturnType: ast.IntType, + }, + }, + } + + tests := []struct { + setupResolverFn + input *ast.FunctionLiteral + expected *Resolver + err error + }{ + { + // test case 1 + // void testFunction() + setupResolverFn: defaultResolver, + input: vars[0].fl, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{}, + child: []*Scope{ + { + store: make(map[string]Object, 0), + child: []*Scope{ + { + store: make(map[string]Object, 0), + child: []*Scope{}, + }, + }, + }, + }, + }, + types: map[ast.Expression]ObjectType{}, + defs: map[*ast.Identifier]Object{}, + fns: map[*ast.FunctionLiteral]ObjectType{}, + }, + err: nil, + }, + { + // test case 2 + // int add(int a, int b) { return a + b } + setupResolverFn: defaultResolver, + input: vars[1].fl, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{}, + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + child: []*Scope{ + { + store: make(map[string]Object, 0), + child: []*Scope{}, + }, + }, + }, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[1].fl.Parameters[0]: IntegerObject, + vars[1].fl.Parameters[1]: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + vars[1].fl.Parameters[0].Identifier: &Integer{Name: &ast.Identifier{Name: "a"}}, + vars[1].fl.Parameters[1].Identifier: &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + fns: map[*ast.FunctionLiteral]ObjectType{}, + }, + }, + } + for i, test := range tests { + r := test.setupResolverFn() + postMakeScope(test.expected.scope) + err := ResolveFunction(test.input, r) + if err == nil && !reflect.DeepEqual(test.expected, r) { + t.Fatalf("test [%d] - TestResolveFunction failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + } +} + +func TestResolveFunctionParameter(t *testing.T) { + vars := []struct { + pls []*ast.ParameterLiteral + }{ + { + pls: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + }, + }, + { + pls: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{ + Name: "b", + }, + Type: ast.BoolType, + }, + { + Identifier: &ast.Identifier{ + Name: "c", + }, + Type: ast.StringType, + }, + }, + }, + { + pls: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{ + Name: "a", + }, + Type: ast.IntType, + }, + }, + }, + } + tests := []struct { + setupResolverFn + input []*ast.ParameterLiteral + expected *Resolver + err error + }{ + { + // test case 1 + // function ... (int a) + setupResolverFn: defaultResolver, + input: vars[0].pls, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[0].pls[0]: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + vars[0].pls[0].Identifier: &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + err: nil, + }, + { + // test case 2 + // function ... (int a, bool b, string c) + setupResolverFn: defaultResolver, + input: vars[1].pls, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Boolean{Name: &ast.Identifier{Name: "b"}}, + "c": &String{Name: &ast.Identifier{Name: "c"}}, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[1].pls[0]: IntegerObject, + vars[1].pls[1]: BooleanObject, + vars[1].pls[2]: StringObject, + }, + defs: map[*ast.Identifier]Object{ + vars[1].pls[0].Identifier: &Integer{Name: &ast.Identifier{Name: "a"}}, + vars[1].pls[1].Identifier: &Boolean{Name: &ast.Identifier{Name: "b"}}, + vars[1].pls[2].Identifier: &String{Name: &ast.Identifier{Name: "c"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + err: nil, + }, + { + setupResolverFn: defaultResolver, + input: vars[2].pls, + expected: nil, + err: DupError{ + object: &Integer{ + Name: &ast.Identifier{ + Name: "a", + }, + }, + }, + }, + } + + for i, test := range tests { + r := test.setupResolverFn() + err := ResolveFunctionParameter(test.input, r) + if err == nil && !reflect.DeepEqual(test.expected, r) { + t.Fatalf("test [%d] - TestScopingFunctionParameter failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + if err != nil && !reflect.DeepEqual(test.err, err) { + t.Fatalf("test [%d] - TestScopingFunctionParameter failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } +} + +func TestResolveFunctionBody(t *testing.T) { + +} + +func TestResolveAssignStatement(t *testing.T) { + vars := []struct { + as *ast.AssignStatement + }{ + { + as: &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + { + as: &ast.AssignStatement{ + Type: ast.StringType, + Variable: ast.Identifier{Name: "str"}, + Value: &ast.StringLiteral{ + Value: "testString", + }, + }, + }, + {}, + } + + tests := []struct { + setupResolverFn + input *ast.AssignStatement + expected *Resolver + err error + }{ + { + // test case 1 + // int a = 0 + setupResolverFn: defaultResolver, + input: vars[0].as, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[0].as.Value: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[0].as.Variable: &Integer{Name: &vars[0].as.Variable}, + }, + }, + }, + { + // test case 2 + // string str = "str" + setupResolverFn: defaultResolver, + input: vars[1].as, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "str": &String{Name: &ast.Identifier{Name: "str"}}, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[1].as.Value: StringObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[1].as.Variable: &String{Name: &vars[1].as.Variable}, + }, + }, + }, + { + // test case 3 + // string str = "str" + // but val named str is already defined + setupResolverFn: func() *Resolver { + return &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "str": &String{Name: &ast.Identifier{Name: "str"}}, + }, + }, + } + }, + input: &ast.AssignStatement{ + Type: ast.StringType, + Variable: ast.Identifier{Name: "str"}, + Value: &ast.StringLiteral{ + Value: "testString", + }, + }, + expected: nil, + err: DupError{ + object: &String{ + Name: &ast.Identifier{ + Name: "str", + }, + }, + }, + }, + { + // test case 4 + // int a = "str" + setupResolverFn: defaultResolver, + input: &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.StringLiteral{ + Value: "str", + }, + }, + expected: nil, + err: &TypeError{ + target: IntegerObject, + object: StringObject, + }, + }, + } + + for i, test := range tests { + r := test.setupResolverFn() + err := ResolveAssignStatement(test.input, r) + + if err == nil && !reflect.DeepEqual(r.defs, test.expected.defs) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + if err != nil && err.Error() != test.err.Error() { + t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } +} + +func TestResolveBlockStatement(t *testing.T) { + vars := []struct { + bs *ast.BlockStatement + }{ + { + bs: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "b"}, + Value: &ast.IntegerLiteral{ + Value: 1, + }, + }, + }, + }, + }, + { + &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 1, + }, + }, + }, + }, + }, + } + + tests := []struct { + setupResolverFn + input *ast.BlockStatement + expected *Resolver + err error + }{ + { + // test case 1 + // int a = 0 + // int b = 1 + setupResolverFn: defaultResolver, + input: vars[0].bs, + expected: &Resolver{ + scope: &Scope{ + store: make(map[string]Object, 0), + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + child: []*Scope{}, + }, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[0].bs.Statements[0].(*ast.AssignStatement).Value: IntegerObject, + vars[0].bs.Statements[1].(*ast.AssignStatement).Value: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[0].bs.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, + &vars[0].bs.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + err: nil, + }, + { + // test case 2 + // int a = 0 + // int a = 1 + // This is an error case. + setupResolverFn: defaultResolver, + input: vars[1].bs, + expected: nil, + err: DupError{ + object: &Integer{ + Name: &ast.Identifier{ + Name: "a", + }, + }, + }, + }, + } + + for i, test := range tests { + if test.expected != nil { + postMakeScope(test.expected.scope) + } + + r := test.setupResolverFn() + err := ResolveBlockStatement(test.input, r) + if err == nil && !reflect.DeepEqual(r, test.expected) { + t.Fatalf("test [%d] - TestScopingBlockStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + + if err != nil && !reflect.DeepEqual(test.err, err) { + t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } +} + +func TestResolveIfStatement(t *testing.T) { + vars := []*ast.IfStatement{ + { + // case 1 + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + Alternative: nil, + }, + { + // case 2 + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "b"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + }, + { + // case 3 + Condition: &ast.BooleanLiteral{ + Value: true, + }, + Consequence: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "b"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + Alternative: &ast.BlockStatement{ + Statements: []ast.Statement{ + &ast.AssignStatement{ + Type: ast.IntType, + Variable: ast.Identifier{Name: "a"}, + Value: &ast.IntegerLiteral{ + Value: 0, + }, + }, + }, + }, + }, + } + tests := []struct { + setupResolverFn + input *ast.IfStatement + expected *Resolver + err error + }{ + { + // Test case 1 + // if (true) { + // int a = 0 + // } + setupResolverFn: defaultResolver, + input: vars[0], + expected: &Resolver{ + scope: &Scope{ + store: make(map[string]Object, 0), + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + child: []*Scope{}, + }, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[0].Consequence.Statements[0].(*ast.AssignStatement).Value: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[0].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + }, + { + // Test case 2 + // if (true) { + // int a = 0 + // int b = 0 + // } + setupResolverFn: defaultResolver, + input: vars[1], + expected: &Resolver{ + scope: &Scope{ + store: make(map[string]Object, 0), + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + child: []*Scope{}, + }, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[1].Consequence.Statements[0].(*ast.AssignStatement).Value: IntegerObject, + vars[1].Consequence.Statements[1].(*ast.AssignStatement).Value: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[1].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, + &vars[1].Consequence.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + }, + { + // Test case 3 + // if (true) { + // int a = 0 + // int b = 0 + // } else { + // int a = 0 + // } + setupResolverFn: defaultResolver, + input: vars[2], + expected: &Resolver{ + scope: &Scope{ + store: make(map[string]Object, 0), + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + child: []*Scope{}, + }, + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + child: []*Scope{}, + }, + }, + }, + types: map[ast.Expression]ObjectType{ + vars[2].Consequence.Statements[0].(*ast.AssignStatement).Value: IntegerObject, + vars[2].Consequence.Statements[1].(*ast.AssignStatement).Value: IntegerObject, + vars[2].Alternative.Statements[0].(*ast.AssignStatement).Value: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + &vars[2].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, + &vars[2].Consequence.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, + &vars[2].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + }, + } + + for i, test := range tests { + if test.expected != nil { + postMakeScope(test.expected.scope) + } + r := test.setupResolverFn() + err := ResolveIfStatement(test.input, r) + if err == nil && !reflect.DeepEqual(test.expected.scope, r.scope) { + t.Fatalf("test [%d] - TestScopingIfStatement failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + + if err != nil && !reflect.DeepEqual(test.err, err) { + t.Fatalf("test [%d] - TestScopingIfStatement failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } +} + +func TestResolveCallExpression(t *testing.T) { + //vars := []* +} + +/* +func TestResolveIdentifier() { + tests := []struct { + setupResolverFn + idf ast.Identifier + ds ast.DataStructure + expected *Resolver + err error + }{ + { + setupResolverFn: defaultResolver, + idf: ast.Identifier{ + Name: "a", + }, + ds: ast.IntType, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + }, + }, + types: make(map[ast.Expression]ObjectType), + defs: make(map[*ast.Identifier]Object), + fns: make(map[*ast.FunctionLiteral]ObjectType), + }, + err: nil, + }, + { + setupResolverFn: func() *Resolver { + return &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "a": &Boolean{Name: &ast.Identifier{Name: "a"}}, + }, + }, + } + }, + idf: ast.Identifier{ + Name: "a", + }, + ds: ast.IntType, + expected: nil, + err: DupError{ + object: &Boolean{ + Name: &ast.Identifier{ + Name: "a", + }, + }, + }, + }, + } + + for i, test := range tests { + r := test.setupResolverFn() + _, err := ResolveIdentifier(test.idf, test.ds, r) + if err == nil && !reflect.DeepEqual(r, test.expected) { + t.Fatalf("test [%d] - TestScopingIdentifier failed.\nexpected=%v\ngot=%v", + i, test.expected, r) + } + if err != nil && !reflect.DeepEqual(err, test.err) { + t.Fatalf("test [%d] - TestScopingIdentifier failed (err case).\nexpected=%v\ngot=%v", + i, test.err, err) + } + } +} +*/ diff --git a/symbol/scope.go b/symbol/scope.go deleted file mode 100644 index 61737541..00000000 --- a/symbol/scope.go +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2018 De-labtory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package symbol - -import ( - "fmt" - - "github.com/DE-labtory/koa/ast" -) - -// Define scope errors -type DupError struct { - Identifier ast.Identifier -} - -func (e DupError) Error() string { - return fmt.Sprintf("%s is already defined.", e.Identifier.Name) -} - -// Define scope structure -type Scope struct { - store map[string]Symbol - - child []*Scope - parent *Scope -} - -func NewScope() *Scope { - return &Scope{ - store: make(map[string]Symbol), - parent: nil, - } -} - -func NewEnclosedScope(p *Scope) *Scope { - s := &Scope{ - store: make(map[string]Symbol), - child: make([]*Scope, 0), - parent: p, - } - p.child = append(p.child, s) - return s -} - -func ScopingContract(c *ast.Contract, s *Scope) error { - for _, f := range c.Functions { - if err := ScopingFunction(f, s); err != nil { - return err - } - } - return nil -} - -func ScopingFunction(f *ast.FunctionLiteral, s *Scope) error { - s = NewEnclosedScope(s) - if err := ScopingFunctionParameter(f.Parameters, s); err != nil { - return nil - } - - if err := ScopingFunctionBody(f.Body, s); err != nil { - return err - } - - return nil -} - -func ScopingFunctionParameter(f []*ast.ParameterLiteral, s *Scope) error { - for _, p := range f { - switch p.Type { - case ast.IntType: - { - s.store[p.Identifier.Name] = &Integer{Name: p.Identifier} - } - case ast.StringType: - { - s.store[p.Identifier.Name] = &String{Name: p.Identifier} - } - case ast.BoolType: - { - s.store[p.Identifier.Name] = &Boolean{Name: p.Identifier} - } - } - } - return nil -} - -func ScopingFunctionBody(f *ast.BlockStatement, scope *Scope) error { - if err := ScopingBlockStatement(f, scope); err != nil { - return err - } - return nil -} - -func ScopingStatement(stmt ast.Statement, scope *Scope) error { - var err error - switch implStmt := stmt.(type) { - case *ast.AssignStatement: - err = ScopingAssignStatement(implStmt, scope) - case *ast.BlockStatement: - err = ScopingBlockStatement(implStmt, scope) - case *ast.ExpressionStatement: - { - - } - case *ast.IfStatement: - { - - } - } - - if err != nil { - return err - } - return nil -} - -func ScopingAssignStatement(stmt *ast.AssignStatement, scope *Scope) error { - if ok := scope.store[stmt.Variable.Name]; ok != nil { - return DupError{ - Identifier: stmt.Variable, - } - } - - if err := ScopingIdentifier(stmt.Variable, stmt.Type, scope); err != nil { - return err - } - return nil -} - -func ScopingBlockStatement(bStmt *ast.BlockStatement, scope *Scope) error { - scope = NewEnclosedScope(scope) - for _, stmt := range bStmt.Statements { - if err := ScopingStatement(stmt, scope); err != nil { - return err - } - } - return nil -} - -func ScopingIfStatement(iStmt *ast.IfStatement, scope *Scope) { - -} - -func ScopingIdentifier(identifier ast.Identifier, ds ast.DataStructure, scope *Scope) error { - if ok := scope.store[identifier.Name]; ok != nil { - return DupError{Identifier: identifier} - } - - name := identifier.Name - switch ds { - case ast.IntType: - scope.store[name] = &Integer{Name: &ast.Identifier{Name: name}} - case ast.StringType: - scope.store[name] = &String{Name: &ast.Identifier{Name: name}} - case ast.BoolType: - scope.store[name] = &Boolean{Name: &ast.Identifier{Name: name}} - } - - return nil -} diff --git a/symbol/scope_test.go b/symbol/scope_test.go deleted file mode 100644 index 7dbe13d6..00000000 --- a/symbol/scope_test.go +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2018 De-labtory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package symbol - -import ( - "reflect" - "testing" - - "github.com/DE-labtory/koa/ast" -) - -type setupScopeFn func() *Scope - -func defaultScope() *Scope { - return NewScope() -} - -func TestNewScope(t *testing.T) { - -} - -func TestNewEnclosedScope(t *testing.T) { - -} - -func TestScopingFunction(t *testing.T) { - -} - -func TestScopingFunctionParameter(t *testing.T) { - tests := []struct { - setupScopeFn - input []*ast.ParameterLiteral - expected *Scope - err error - }{ - { - setupScopeFn: defaultScope, - input: []*ast.ParameterLiteral{ - { - Identifier: &ast.Identifier{ - Name: "a", - }, - Type: ast.IntType, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - }, - }, - err: nil, - }, - { - setupScopeFn: defaultScope, - input: []*ast.ParameterLiteral{ - { - Identifier: &ast.Identifier{ - Name: "a", - }, - Type: ast.IntType, - }, - { - Identifier: &ast.Identifier{ - Name: "b", - }, - Type: ast.BoolType, - }, - { - Identifier: &ast.Identifier{ - Name: "c", - }, - Type: ast.StringType, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - "b": &Boolean{Name: &ast.Identifier{Name: "b"}}, - "c": &String{Name: &ast.Identifier{Name: "c"}}, - }, - }, - err: nil, - }, - { - setupScopeFn: defaultScope, - input: []*ast.ParameterLiteral{ - { - Identifier: &ast.Identifier{ - Name: "a", - }, - Type: ast.IntType, - }, - { - Identifier: &ast.Identifier{ - Name: "a", - }, - Type: ast.IntType, - }, - }, - expected: nil, - err: DupError{Identifier: ast.Identifier{Name: "a"}}, - }, - } - - for i, test := range tests { - s := test.setupScopeFn() - err := ScopingFunctionParameter(test.input, s) - - if err == nil && !reflect.DeepEqual(test.expected, s) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", - i, test.expected, s) - } - if err != nil && reflect.DeepEqual(test.err, err) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", - i, test.err, err) - } - } -} - -func TestScopingFunctionBody(t *testing.T) { - -} - -func TestScopingAssignStatement(t *testing.T) { - tests := []struct { - setupScopeFn - input *ast.AssignStatement - expected *Scope - err error - }{ - { - setupScopeFn: defaultScope, - input: &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "a"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - }, - }, - }, - { - setupScopeFn: defaultScope, - input: &ast.AssignStatement{ - Type: ast.StringType, - Variable: ast.Identifier{Name: "str"}, - Value: &ast.StringLiteral{ - Value: "testString", - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "str": &String{Name: &ast.Identifier{Name: "str"}}, - }, - }, - }, - { - setupScopeFn: func() *Scope { - return &Scope{ - store: map[string]Symbol{ - "str": &String{Name: &ast.Identifier{Name: "str"}}, - }, - } - }, - input: &ast.AssignStatement{ - Type: ast.StringType, - Variable: ast.Identifier{Name: "str"}, - Value: &ast.StringLiteral{ - Value: "testString", - }, - }, - expected: nil, - err: DupError{ - Identifier: ast.Identifier{Name: "str"}, - }, - }, - } - - for i, test := range tests { - s := test.setupScopeFn() - err := ScopingAssignStatement(test.input, s) - - if err == nil && !reflect.DeepEqual(s, test.expected) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", - i, test.expected, s) - } - if err != nil && !reflect.DeepEqual(err, test.err) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", - i, test.err, err) - } - } -} - -func TestScopingIfStatement(t *testing.T) { - tests := []struct { - setupScopeFn - input *ast.IfStatement - expected *Scope - err error - }{ - { - setupScopeFn: defaultScope, - input: &ast.IfStatement{ - Condition: &ast.BooleanLiteral{ - Value: true, - }, - Consequence: &ast.BlockStatement{ - Statements: []ast.Statement{ - &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "a"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - }, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - }, - }, - }, - { - setupScopeFn: defaultScope, - input: &ast.IfStatement{ - Condition: &ast.BooleanLiteral{ - Value: true, - }, - Consequence: &ast.BlockStatement{ - Statements: []ast.Statement{ - &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "a"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "b"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - }, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - "b": &Integer{Name: &ast.Identifier{Name: "b"}}, - }, - }, - }, - { - setupScopeFn: defaultScope, - input: &ast.IfStatement{ - Condition: &ast.BooleanLiteral{ - Value: true, - }, - Consequence: &ast.BlockStatement{ - Statements: []ast.Statement{ - &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "a"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - &ast.AssignStatement{ - Type: ast.IntType, - Variable: ast.Identifier{Name: "b"}, - Value: &ast.IntegerLiteral{ - Value: 0, - }, - }, - }, - }, - }, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - "b": &Integer{Name: &ast.Identifier{Name: "b"}}, - }, - }, - }, - } - - for i, test := range tests { - s := test.setupScopeFn() - ScopingIfStatement(test.input, s) - if test.expected != s { - t.Fatalf("test [%d] - TestScopingAssignStatement failed", i) - } - } -} - -func TestScopingIdentifier(t *testing.T) { - tests := []struct { - setupScopeFn - idf ast.Identifier - ds ast.DataStructure - expected *Scope - err error - }{ - { - setupScopeFn: defaultScope, - idf: ast.Identifier{ - Name: "a", - }, - ds: ast.IntType, - expected: &Scope{ - store: map[string]Symbol{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, - }, - }, - err: nil, - }, - { - setupScopeFn: func() *Scope { - return &Scope{ - store: map[string]Symbol{ - "a": &Boolean{Name: &ast.Identifier{Name: "a"}}, - }, - } - }, - idf: ast.Identifier{ - Name: "a", - }, - ds: ast.IntType, - expected: nil, - err: DupError{ - Identifier: ast.Identifier{Name: "a"}, - }, - }, - } - - for i, test := range tests { - s := test.setupScopeFn() - err := ScopingIdentifier(test.idf, test.ds, s) - if err == nil && !reflect.DeepEqual(s, test.expected) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed.\nexpected=%v\ngot=%v", - i, test.expected, s) - } - if err != nil && !reflect.DeepEqual(err, test.err) { - t.Fatalf("test [%d] - TestScopingAssignStatement failed (err case).\nexpected=%v\ngot=%v", - i, test.err, err) - } - } -} diff --git a/symbol/symbol.go b/symbol/symbol.go index 07d6d263..b26b8f64 100644 --- a/symbol/symbol.go +++ b/symbol/symbol.go @@ -22,17 +22,19 @@ import ( "github.com/DE-labtory/koa/ast" ) -type SymbolType string +type ObjectType string const ( - IntegerSymbol = "INTEGER" - BooleanSymbol = "BOOLEAN" - StringSymbol = "STRING" - FunctionSymbol = "FUNCTION" + IntegerObject = "INTEGER" + BooleanObject = "BOOLEAN" + StringObject = "STRING" + FunctionObject = "FUNCTION" + VoidObject = "VOID" + InvalidSymbol = "INVALID" ) -type Symbol interface { - Type() SymbolType +type Object interface { + Type() ObjectType String() string } @@ -41,8 +43,8 @@ type Integer struct { Name *ast.Identifier } -func (i *Integer) Type() SymbolType { - return IntegerSymbol +func (i *Integer) Type() ObjectType { + return IntegerObject } // String() returns symbol's name @@ -55,8 +57,8 @@ type Boolean struct { Name *ast.Identifier } -func (b *Boolean) Type() SymbolType { - return BooleanSymbol +func (b *Boolean) Type() ObjectType { + return BooleanObject } func (b *Boolean) String() string { @@ -68,8 +70,8 @@ type String struct { Name *ast.Identifier } -func (s *String) Type() SymbolType { - return StringSymbol +func (s *String) Type() ObjectType { + return StringObject } func (s *String) String() string { @@ -80,12 +82,11 @@ func (s *String) String() string { // Name represents function's name. // Scope represents function value's scope. type Function struct { - Name string - Scope *Scope + Name string } -func (f *Function) Type() SymbolType { - return FunctionSymbol +func (f *Function) Type() ObjectType { + return FunctionObject } func (f *Function) String() string { diff --git a/symbol/symbol_test.go b/symbol/symbol_test.go index c5569cee..ceb798bd 100644 --- a/symbol/symbol_test.go +++ b/symbol/symbol_test.go @@ -24,13 +24,13 @@ import ( func TestInteger(t *testing.T) { tests := []struct { - input Symbol + input Object expectedStr string - expectedSymbol SymbolType + expectedSymbol ObjectType }{ - {&Integer{&ast.Identifier{Name: "testName"}}, "testName", IntegerSymbol}, - {&Integer{&ast.Identifier{Name: "a"}}, "a", IntegerSymbol}, - {&Integer{&ast.Identifier{Name: "b"}}, "b", IntegerSymbol}, + {&Integer{&ast.Identifier{Name: "testName"}}, "testName", IntegerObject}, + {&Integer{&ast.Identifier{Name: "a"}}, "a", IntegerObject}, + {&Integer{&ast.Identifier{Name: "b"}}, "b", IntegerObject}, } for i, test := range tests { @@ -53,13 +53,13 @@ func TestInteger(t *testing.T) { func TestString(t *testing.T) { tests := []struct { - input Symbol + input Object expectedStr string - expectedSymbol SymbolType + expectedSymbol ObjectType }{ - {&String{&ast.Identifier{Name: "testName"}}, "testName", StringSymbol}, - {&String{&ast.Identifier{Name: "a"}}, "a", StringSymbol}, - {&String{&ast.Identifier{Name: "b"}}, "b", StringSymbol}, + {&String{&ast.Identifier{Name: "testName"}}, "testName", StringObject}, + {&String{&ast.Identifier{Name: "a"}}, "a", StringObject}, + {&String{&ast.Identifier{Name: "b"}}, "b", StringObject}, } for i, test := range tests { @@ -82,12 +82,12 @@ func TestString(t *testing.T) { func TestBoolean(t *testing.T) { tests := []struct { - input Symbol + input Object expectedStr string - expectedObj SymbolType + expectedObj ObjectType }{ - {&Boolean{&ast.Identifier{Name: "testName"}}, "testName", BooleanSymbol}, - {&Boolean{&ast.Identifier{Name: "a"}}, "a", BooleanSymbol}, + {&Boolean{&ast.Identifier{Name: "testName"}}, "testName", BooleanObject}, + {&Boolean{&ast.Identifier{Name: "a"}}, "a", BooleanObject}, } for i, test := range tests { @@ -110,17 +110,16 @@ func TestBoolean(t *testing.T) { func TestFunction(t *testing.T) { tests := []struct { - input Symbol + input Object expectedStr string - expectedSymbol SymbolType + expectedSymbol ObjectType }{ { &Function{ "add", - &Scope{}, }, "add", - FunctionSymbol, + FunctionObject, }, } From 8ec4c1adcc5e462d03c2be9d03700d35a5a9a49b Mon Sep 17 00:00:00 2001 From: boohyunsik Date: Wed, 6 Mar 2019 01:57:13 +0900 Subject: [PATCH 5/5] Impl ResolveCallExpression and tcs --- symbol/resolver.go | 41 ++++++++--- symbol/resolver_test.go | 150 +++++++++++++++++++++++++++------------- symbol/symbol.go | 2 +- 3 files changed, 134 insertions(+), 59 deletions(-) diff --git a/symbol/resolver.go b/symbol/resolver.go index c93d57dd..3728bbf6 100644 --- a/symbol/resolver.go +++ b/symbol/resolver.go @@ -41,7 +41,7 @@ type Resolver struct { defs map[*ast.Identifier]Object // fns manager function its own type - fns map[*ast.FunctionLiteral]ObjectType + fns map[string]*ast.FunctionLiteral } // Define scope error @@ -103,17 +103,17 @@ func NewResolver() *Resolver { scope: s, types: make(map[ast.Expression]ObjectType), defs: make(map[*ast.Identifier]Object), - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: make(map[string]*ast.FunctionLiteral), } } // typeof returns the type of expression exp, -// or return InvalidSymbol if not found +// or return InvalidObject if not found func (r *Resolver) typeOf(exp ast.Expression) ObjectType { if st, ok := r.types[exp]; ok { return st } - return InvalidSymbol + return InvalidObject } // objectOf returns the object denoted by the specified identifier @@ -150,7 +150,7 @@ func ResolveContract(c *ast.Contract) error { obj := &Function{Name: name} r.scope.store[name] = obj - r.fns[f] = FunctionObject + r.fns[f.Name.Name] = f r.defs[f.Name] = obj } @@ -306,7 +306,7 @@ func ResolveIdentifier(identifier *ast.Identifier, r *Resolver) (ObjectType, err return obj.Type(), nil } - return InvalidSymbol, NoExistError{ + return InvalidObject, NoExistError{ identifier: identifier, } } @@ -328,15 +328,36 @@ func ResolveExpression(exp ast.Expression, r *Resolver) (ObjectType, error) { case *ast.StringLiteral: return StringObject, nil } - return InvalidSymbol, nil + return InvalidObject, nil } func ResolveCallExpression(exp *ast.CallExpression, r *Resolver) (ObjectType, error) { - return InvalidSymbol, nil + // check function is defined or not + if _, ok := r.fns[exp.Function.(*ast.Identifier).Name]; !ok { + return InvalidObject, NoExistError{ + identifier: exp.Function.(*ast.Identifier), + } + } + + // check function parameters is right + f := r.fns[exp.Function.(*ast.Identifier).Name] + for i, p := range f.Parameters { + t1 := objTypeMap[p.Type] + t2 := r.typeOf(exp.Arguments[i]) + if t1 != t2 { + //if objTypeMap[p.Type] != r.typeOf(p) { + return InvalidObject, TypeError{ + target: objTypeMap[p.Type], + object: r.typeOf(p), + } + } + } + + return InvalidObject, nil } func ResolvePrefixExpression(exp ast.Expression, r *Resolver) (ObjectType, error) { - return InvalidSymbol, nil + return InvalidObject, nil } func ResolveInfixExpression(exp *ast.InfixExpression, r *Resolver) (ObjectType, error) { @@ -348,5 +369,5 @@ func ResolveInfixExpression(exp *ast.InfixExpression, r *Resolver) (ObjectType, return ot, err } - return InvalidSymbol, nil + return InvalidObject, nil } diff --git a/symbol/resolver_test.go b/symbol/resolver_test.go index 7c909a95..675e1728 100644 --- a/symbol/resolver_test.go +++ b/symbol/resolver_test.go @@ -144,7 +144,7 @@ func TestResolveFunction(t *testing.T) { }, types: map[ast.Expression]ObjectType{}, defs: map[*ast.Identifier]Object{}, - fns: map[*ast.FunctionLiteral]ObjectType{}, + fns: map[string]*ast.FunctionLiteral{}, }, err: nil, }, @@ -179,7 +179,7 @@ func TestResolveFunction(t *testing.T) { vars[1].fl.Parameters[0].Identifier: &Integer{Name: &ast.Identifier{Name: "a"}}, vars[1].fl.Parameters[1].Identifier: &Integer{Name: &ast.Identifier{Name: "b"}}, }, - fns: map[*ast.FunctionLiteral]ObjectType{}, + fns: map[string]*ast.FunctionLiteral{}, }, }, } @@ -270,7 +270,7 @@ func TestResolveFunctionParameter(t *testing.T) { defs: map[*ast.Identifier]Object{ vars[0].pls[0].Identifier: &Integer{Name: &ast.Identifier{Name: "a"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, err: nil, }, @@ -297,7 +297,7 @@ func TestResolveFunctionParameter(t *testing.T) { vars[1].pls[1].Identifier: &Boolean{Name: &ast.Identifier{Name: "b"}}, vars[1].pls[2].Identifier: &String{Name: &ast.Identifier{Name: "c"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, err: nil, }, @@ -544,7 +544,7 @@ func TestResolveBlockStatement(t *testing.T) { &vars[0].bs.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, &vars[0].bs.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, err: nil, }, @@ -696,7 +696,7 @@ func TestResolveIfStatement(t *testing.T) { defs: map[*ast.Identifier]Object{ &vars[0].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, }, { @@ -728,7 +728,7 @@ func TestResolveIfStatement(t *testing.T) { &vars[1].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, &vars[1].Consequence.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, }, { @@ -770,7 +770,7 @@ func TestResolveIfStatement(t *testing.T) { &vars[2].Consequence.Statements[1].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "b"}}, &vars[2].Consequence.Statements[0].(*ast.AssignStatement).Variable: &Integer{Name: &ast.Identifier{Name: "a"}}, }, - fns: make(map[*ast.FunctionLiteral]ObjectType), + fns: map[string]*ast.FunctionLiteral{}, }, }, } @@ -794,72 +794,126 @@ func TestResolveIfStatement(t *testing.T) { } func TestResolveCallExpression(t *testing.T) { - //vars := []* -} - -/* -func TestResolveIdentifier() { - tests := []struct { - setupResolverFn - idf ast.Identifier - ds ast.DataStructure - expected *Resolver - err error + vars := []struct { + fl *ast.FunctionLiteral + ce *ast.CallExpression }{ { - setupResolverFn: defaultResolver, - idf: ast.Identifier{ - Name: "a", - }, - ds: ast.IntType, - expected: &Resolver{ - scope: &Scope{ - store: map[string]Object{ - "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + // case 1 + fl: &ast.FunctionLiteral{ + Name: &ast.Identifier{Name: "add"}, + Parameters: []*ast.ParameterLiteral{ + { + Identifier: &ast.Identifier{Name: "a"}, + Type: ast.IntType, + }, + { + Identifier: &ast.Identifier{Name: "b"}, + Type: ast.IntType, }, }, - types: make(map[ast.Expression]ObjectType), - defs: make(map[*ast.Identifier]Object), - fns: make(map[*ast.FunctionLiteral]ObjectType), + Body: nil, + ReturnType: ast.IntType, + }, + ce: &ast.CallExpression{ + Function: &ast.Identifier{ + Name: "add", + }, + Arguments: []ast.Expression{ + &ast.Identifier{Name: "a"}, + &ast.Identifier{Name: "b"}, + }, }, - err: nil, }, + } + + tests := []struct { + setupResolverFn + input *ast.CallExpression + expected *Resolver + expectedType ObjectType + err error + }{ { + // test case 1 + // function add(int a, int b) + // ... + // add(a, b) setupResolverFn: func() *Resolver { return &Resolver{ scope: &Scope{ store: map[string]Object{ - "a": &Boolean{Name: &ast.Identifier{Name: "a"}}, + "add": &Function{Name: "add"}, }, }, + types: map[ast.Expression]ObjectType{ + &ast.Identifier{Name: "a"}: IntegerObject, + &ast.Identifier{Name: "b"}: IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + vars[0].fl.Name: &Function{Name: "add"}, + }, + fns: map[string]*ast.FunctionLiteral{ + "add": vars[0].fl, + }, } }, - idf: ast.Identifier{ - Name: "a", - }, - ds: ast.IntType, - expected: nil, - err: DupError{ - object: &Boolean{ - Name: &ast.Identifier{ - Name: "a", + input: vars[0].ce, + expected: &Resolver{ + scope: &Scope{ + store: map[string]Object{ + "add": &Function{Name: "add"}, + }, + child: []*Scope{ + { + store: map[string]Object{ + "a": &Integer{Name: &ast.Identifier{Name: "a"}}, + "b": &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + child: []*Scope{ + { + store: map[string]Object{}, + child: []*Scope{}, + }, + }, + }, }, }, + types: map[ast.Expression]ObjectType{ + vars[0].ce.Arguments[0].(*ast.Identifier): IntegerObject, + vars[0].ce.Arguments[1].(*ast.Identifier): IntegerObject, + }, + defs: map[*ast.Identifier]Object{ + vars[0].ce.Arguments[0].(*ast.Identifier): &Integer{Name: &ast.Identifier{Name: "a"}}, + vars[0].ce.Arguments[1].(*ast.Identifier): &Integer{Name: &ast.Identifier{Name: "b"}}, + }, + fns: map[string]*ast.FunctionLiteral{ + "add": vars[0].fl, + }, }, + expectedType: IntegerObject, + err: nil, }, } for i, test := range tests { r := test.setupResolverFn() - _, err := ResolveIdentifier(test.idf, test.ds, r) + if test.expected != nil { + postMakeScope(test.expected.scope) + } + objType, err := ResolveCallExpression(test.input, r) + if err == nil && objType != test.expectedType { + t.Fatalf("[test %d] - TestResolveCallExpression failed.(wrong obj type).\n"+ + "expected: %v\ngot:%v", i, test.expectedType, objType) + } if err == nil && !reflect.DeepEqual(r, test.expected) { - t.Fatalf("test [%d] - TestScopingIdentifier failed.\nexpected=%v\ngot=%v", - i, test.expected, r) + t.Fatalf("[test %d] - TestResolveCallExpression failed.(wrong resolver).\n"+ + "expected: %v\ngot:%v", i, test.expected, r) } if err != nil && !reflect.DeepEqual(err, test.err) { - t.Fatalf("test [%d] - TestScopingIdentifier failed (err case).\nexpected=%v\ngot=%v", - i, test.err, err) + t.Fatalf("[test %d] - TestResolveCallExpression failed.(wrong err).\n"+ + "expected: %v\ngot:%v", i, test.err, err) } + } } -*/ diff --git a/symbol/symbol.go b/symbol/symbol.go index b26b8f64..b668704f 100644 --- a/symbol/symbol.go +++ b/symbol/symbol.go @@ -30,7 +30,7 @@ const ( StringObject = "STRING" FunctionObject = "FUNCTION" VoidObject = "VOID" - InvalidSymbol = "INVALID" + InvalidObject = "INVALID" ) type Object interface {