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..3728bbf6 100644 --- a/symbol/resolver.go +++ b/symbol/resolver.go @@ -17,41 +17,357 @@ 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[string]*ast.FunctionLiteral +} + +// 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[string]*ast.FunctionLiteral), } } // typeof returns the type of expression exp, -// or return InvalidSymbol if not found -func (r *Resolver) typeOf(exp ast.Expression) SymbolType { - return "" +// or return InvalidObject if not found +func (r *Resolver) typeOf(exp ast.Expression) ObjectType { + if st, ok := r.types[exp]; ok { + return st + } + return InvalidObject } // 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 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.Name.Name] = f + r.defs[f.Name] = obj + } + + for _, f := range c.Functions { + if err := ResolveFunction(f, r); err != nil { + return err + } + } return nil } -func (r *Resolver) ResolveContract(c *ast.Contract) error { +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 InvalidObject, 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 InvalidObject, nil +} + +func ResolveCallExpression(exp *ast.CallExpression, r *Resolver) (ObjectType, error) { + // 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 InvalidObject, 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 InvalidObject, nil +} diff --git a/symbol/resolver_test.go b/symbol/resolver_test.go new file mode 100644 index 00000000..675e1728 --- /dev/null +++ b/symbol/resolver_test.go @@ -0,0 +1,919 @@ +/* +// * 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[string]*ast.FunctionLiteral{}, + }, + 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[string]*ast.FunctionLiteral{}, + }, + }, + } + 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: map[string]*ast.FunctionLiteral{}, + }, + 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: map[string]*ast.FunctionLiteral{}, + }, + 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: map[string]*ast.FunctionLiteral{}, + }, + 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: map[string]*ast.FunctionLiteral{}, + }, + }, + { + // 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: map[string]*ast.FunctionLiteral{}, + }, + }, + { + // 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: map[string]*ast.FunctionLiteral{}, + }, + }, + } + + 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 := []struct { + fl *ast.FunctionLiteral + ce *ast.CallExpression + }{ + { + // 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, + }, + }, + Body: nil, + ReturnType: ast.IntType, + }, + ce: &ast.CallExpression{ + Function: &ast.Identifier{ + Name: "add", + }, + Arguments: []ast.Expression{ + &ast.Identifier{Name: "a"}, + &ast.Identifier{Name: "b"}, + }, + }, + }, + } + + 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{ + "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, + }, + } + }, + 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() + 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] - 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] - TestResolveCallExpression failed.(wrong err).\n"+ + "expected: %v\ngot:%v", i, test.err, err) + } + + } +} diff --git a/symbol/scope.go b/symbol/scope.go deleted file mode 100644 index 9c0162dc..00000000 --- a/symbol/scope.go +++ /dev/null @@ -1,108 +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 ( - "bytes" - "fmt" - "sort" -) - -// NewScope() makes a new scope. -func NewScope() *Scope { - s := make(map[string]Symbol) - return &Scope{ - store: s, - inner: make([]*Scope, 0), - outer: nil, - } -} - -// NewEnclosedScope makes a new scope which has outer scope. -func NewEnclosedScope(outer *Scope) *Scope { - scope := NewScope() - scope.outer = outer - return scope -} - -// Scope represent a variable's scope. -type Scope struct { - store map[string]Symbol - - inner []*Scope - outer *Scope -} - -// 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 - } - 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 (s *Scope) SetOuter(outer *Scope) { - s.outer = outer -} - -func (s *Scope) GetOuter() *Scope { - return s.outer -} - -func (s *Scope) AppendInner(in *Scope) { - s.inner = append(s.inner, in) -} - -func (s *Scope) GetInner() []*Scope { - return s.inner -} - -func (s *Scope) String() string { - var out bytes.Buffer - scope := s - - 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())) - } - - scope = scope.outer - } - return out.String() -} diff --git a/symbol/scope_test.go b/symbol/scope_test.go deleted file mode 100644 index 7eee5c98..00000000 --- a/symbol/scope_test.go +++ /dev/null @@ -1,209 +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 ( - "testing" - - "github.com/DE-labtory/koa/ast" -) - -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 TestScopeString(t *testing.T) { - tests := []struct { - scope Scope - expected string - }{ - { - 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"}}, - }, - outer: nil, - }, - }, - "[ Scope ]\nkey:a, symbol:a\nkey:b, symbol:b\n[ Scope ]\nkey:c, symbol:c\n", - }, - { - Scope{ - store: map[string]Symbol{ - "c": &Integer{&ast.Identifier{Name: "c"}}, - "b": &Integer{&ast.Identifier{Name: "b"}}, - "a": &Integer{&ast.Identifier{Name: "a"}}, - }, - outer: &Scope{ - store: map[string]Symbol{ - "d": &String{&ast.Identifier{Name: "d"}}, - }, - }, - }, - "[ 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) - } - } -} diff --git a/symbol/symbol.go b/symbol/symbol.go index 07d6d263..b668704f 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" + InvalidObject = "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, }, }