Add rudimentary symbol abstractions

This massages the symbol layer to reflect more closely what we need.

There is a Symbol interface.  It is an interface because it's polymorphic
and we'll need to switch on type tests throughout the code a fair bit.

In addition to the Symbol interface, there are three other interfaces:

* ModuleMember, for any Module member symbols.

* ClassMember, for any Class member symbols.

* Type, to permit polymorphic treatment of Classes and built-in types.

There are concrete symbols for Module, ModuleProperty, ModuleMethod,
Class, ClassProperty, and ClassMethod.  These map directly to the
corresponding AST abstractions and simply permit us to annotate those
AST nodes with some semantic information and, more importantly, to
inject them into the symbol table as we perform binding/typechecking.

Class implements the Type abstraction.

There is also a primitive node with four constant types, AnyType,
BoolType, NumberType, and StringType, each of which is registered in
an export Primitives map, keyed by their name/keyword/token.  These
of course implement the Type abstraction.

Finally, there are ArrayType and MapType symbols, which also implement
Type.  They wrap other types as keys/elements.

I'm peeling off this part from my gigantic pending change, since this is
mostly standalone, and ideally leads to more independent chunks.
This commit is contained in:
joeduffy 2017-01-19 12:02:49 -08:00
parent 4874b2f7c6
commit 5aff453cc1
5 changed files with 204 additions and 7 deletions

View file

@ -0,0 +1,57 @@
// Copyright 2016 Marapongo, Inc. All rights reserved.
package symbols
import (
"github.com/marapongo/mu/pkg/compiler/ast"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/tokens"
)
// Class is a fully bound class symbol.
type Class struct {
Tree *ast.Class
Members map[tokens.Module]ClassMember
}
var _ Symbol = (*Class)(nil)
var _ Type = (*Class)(nil)
var _ ModuleMember = (*Class)(nil)
func (node *Class) symbol() {}
func (node *Class) typesym() {}
func (node *Class) moduleMember() {}
func (node *Class) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *Class) GetTree() diag.Diagable { return node.Tree }
// ClassMember is a marker interface for things that can be module members.
type ClassMember interface {
Symbol
classMember()
}
// ClassProperty is a fully bound module property symbol.
type ClassProperty struct {
Tree *ast.ClassProperty
}
var _ Symbol = (*ClassProperty)(nil)
var _ ClassMember = (*ClassProperty)(nil)
func (node *ClassProperty) symbol() {}
func (node *ClassProperty) classMember() {}
func (node *ClassProperty) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *ClassProperty) GetTree() diag.Diagable { return node.Tree }
// ClassMethod is a fully bound module method symbol.
type ClassMethod struct {
Tree *ast.ClassMethod
}
var _ Symbol = (*ClassMethod)(nil)
var _ ClassMember = (*ClassMethod)(nil)
func (node *ClassMethod) symbol() {}
func (node *ClassMethod) classMember() {}
func (node *ClassMethod) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *ClassMethod) GetTree() diag.Diagable { return node.Tree }

View file

@ -2,7 +2,52 @@
package symbols
import (
"github.com/marapongo/mu/pkg/compiler/ast"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/tokens"
)
// Module is a fully bound module symbol.
type Module struct {
Symbol
Tree *ast.Module
Members map[tokens.Module]ModuleMember
}
var _ Symbol = (*Module)(nil)
func (node *Module) symbol() {}
func (node *Module) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *Module) GetTree() diag.Diagable { return node.Tree }
// ModuleMember is a marker interface for things that can be module members.
type ModuleMember interface {
Symbol
moduleMember()
}
// ModuleProperty is a fully bound module property symbol.
type ModuleProperty struct {
Tree *ast.ModuleProperty
}
var _ Symbol = (*ModuleProperty)(nil)
var _ ModuleMember = (*ModuleProperty)(nil)
func (node *ModuleProperty) symbol() {}
func (node *ModuleProperty) moduleMember() {}
func (node *ModuleProperty) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *ModuleProperty) GetTree() diag.Diagable { return node.Tree }
// ModuleMethod is a fully bound module method symbol.
type ModuleMethod struct {
Tree *ast.ModuleMethod
}
var _ Symbol = (*ModuleMethod)(nil)
var _ ModuleMember = (*ModuleMethod)(nil)
func (node *ModuleMethod) symbol() {}
func (node *ModuleMethod) moduleMember() {}
func (node *ModuleMethod) GetName() tokens.Token { return node.Tree.Name.Ident }
func (node *ModuleMethod) GetTree() diag.Diagable { return node.Tree }

View file

@ -9,5 +9,6 @@ import (
// Package is a fully bound package symbol.
type Package struct {
Symbol
Modules map[tokens.Module]*Module
Dependencies map[tokens.Package]*Package
Modules map[tokens.Module]*Module
}

View file

@ -3,12 +3,13 @@
package symbols
import (
"github.com/marapongo/mu/pkg/compiler/ast"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/tokens"
)
// Symbol is the base type for all MuIL symbol types.
type Symbol struct {
Name tokens.Token // the name of this symbol.
Tree ast.Node // the program tree associated with this symbol.
// Symbol is the base interface for all MuIL symbol types.
type Symbol interface {
symbol()
GetName() tokens.Token // the unique name of this symbol.
GetTree() diag.Diagable // the diagnosable tree associated with this symbol.
}

View file

@ -0,0 +1,93 @@
// Copyright 2016 Marapongo, Inc. All rights reserved.
package symbols
import (
"fmt"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/tokens"
)
// Type is a type symbol that can be used for typechecking operations.
type Type interface {
Symbol
typesym()
}
// primitive is an internal representation of a primitive type symbol (any, bool, number, string).
type primitive struct {
Name tokens.Token
}
var _ Symbol = (*primitive)(nil)
var _ Type = (*primitive)(nil)
func (node *primitive) symbol() {}
func (node *primitive) typesym() {}
func (node *primitive) GetName() tokens.Token { return node.Name }
func (node *primitive) GetTree() diag.Diagable { return nil }
// All of the primitive types.
var (
AnyType = &primitive{"any"}
BoolType = &primitive{"bool"}
NumberType = &primitive{"number"}
StringType = &primitive{"string"}
)
// Primitives contains a map of all primitive types, keyed by their token/name.
var Primitives = map[tokens.Token]Type{
AnyType.Name: AnyType,
BoolType.Name: BoolType,
NumberType.Name: NumberType,
StringType.Name: StringType,
}
// Declare some type decorator strings used for parsing and producing array/map types.
const (
TypeDecorsArray = "%v" + TypeDecorsArraySuffix
TypeDecorsArraySuffix = "[]"
TypeDecorsMap = TypeDecorsMapPrefix + "%v" + TypeDecorsMapSeparator + "%v"
TypeDecorsMapPrefix = "map["
TypeDecorsMapSeparator = "]"
)
// ArrayName creates a new array name from an element type.
func ArrayName(elem Type) tokens.Token {
// TODO: consider caching this to avoid creating needless strings.
return tokens.Token(fmt.Sprintf(TypeDecorsArray, elem.GetName()))
}
// MapName creates a new array name from an element type.
func MapName(key Type, elem Type) tokens.Token {
// TODO: consider caching this to avoid creating needless strings.
return tokens.Token(fmt.Sprintf(TypeDecorsMap, key.GetName(), elem.GetName()))
}
// ArrayType is an array whose elements are of some other type.
type ArrayType struct {
Element Type
}
var _ Symbol = (*ArrayType)(nil)
var _ Type = (*ArrayType)(nil)
func (node *ArrayType) symbol() {}
func (node *ArrayType) typesym() {}
func (node *ArrayType) GetName() tokens.Token { return ArrayName(node.Element) }
func (node *ArrayType) GetTree() diag.Diagable { return nil }
// KeyType is an array whose keys and elements are of some other types.
type MapType struct {
Key Type
Element Type
}
var _ Symbol = (*MapType)(nil)
var _ Type = (*MapType)(nil)
func (node *MapType) symbol() {}
func (node *MapType) typesym() {}
func (node *MapType) GetName() tokens.Token { return MapName(node.Key, node.Element) }
func (node *MapType) GetTree() diag.Diagable { return nil }