Generator now has module support

This commit is contained in:
Sasha Koshka 2024-02-19 21:00:20 -05:00
parent 2f2e762d02
commit 306b029951
8 changed files with 210 additions and 69 deletions

View File

@ -84,7 +84,7 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
sourceDataLoc := this.blockManager.NewLoad(new(llvm.TypePointer), source)
this.blockManager.NewStore(sourceDataLoc, destDataFieldLoc)
fromReferenced, ok := sourceTypeBase.Referenced.(*entity.TypeNamed)
sourceType, ok := sourceTypeBase.Referenced.(*entity.TypeNamed)
if !ok {
return nil, errors.New(fmt.Sprint(
"can't assign", sourceTypeBase, "to interface"))
@ -95,7 +95,12 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
destTypeBase, irDestLoc,
irDestType, name)
fromBehavior, err := this.method(fromReferenced.Name, name)
key := entity.Key {
Unit: sourceType.Unit(),
Name: sourceType.Name,
Method: name,
}
fromBehavior, err := this.method(key)
if err != nil { return nil, err }
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
}
@ -118,7 +123,12 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
destTypeBase, irDestLoc,
irDestType, name)
fromBehavior, err := this.method(sourceType.Name, name)
key := entity.Key {
Unit: sourceType.Unit(),
Name: sourceType.Name,
Method: name,
}
fromBehavior, err := this.method(key)
if err != nil { return nil, err }
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
}

View File

@ -14,14 +14,11 @@ func (this *generator) generateDeclarationLoc (declaration *entity.Declaration)
}
func (this *generator) generateSliceLoc (slice *entity.Slice) (llvm.Value, error) {
var err error
var start, end, dataAddress llvm.Value
var elementType llvm.Type
var sizeType *llvm.TypeInt; {
ty, err := this.typedef("Index")
if err != nil { return nil, err }
sizeType = ty.(*llvm.TypeInt)
}
sizeType, err := this.generateTypeIndex()
if err != nil { return nil, err }
if slice.Start != nil {
start, err = this.generateExpressionVal(slice.Start)

View File

@ -7,7 +7,10 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
func (this *generator) generateCallVal (call *entity.Call) (llvm.Value, error) {
function, err := this.function(call.Name)
function, err := this.function(entity.Key {
Unit: call.Unit,
Name: call.Name,
})
if err != nil { return nil, err }
args := make([]llvm.Value, len(call.Arguments))
@ -41,7 +44,11 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
// check for methods on named type
if sourceType, ok := call.Source.Type().(*entity.TypeNamed); ok {
method, err := this.method(sourceType.Name, call.Name)
method, err := this.method(entity.Key {
Unit: sourceType.Unit(),
Name: sourceType.Name,
Method: call.Name,
})
if err != errNotFound {
source, err := this.generateExpressionLoc(call.Source)
if err != nil { return nil, err }
@ -53,7 +60,11 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
// check for methods on pointer to named type
if pointerType, ok := call.Source.Type().(*entity.TypePointer); ok {
if sourceType, ok := pointerType.Referenced.(*entity.TypeNamed); ok {
method, err := this.method(sourceType.Name, call.Name)
method, err := this.method(entity.Key {
Unit: sourceType.Unit(),
Name: sourceType.Name,
Method: call.Name,
})
if err != errNotFound {
source, err := this.generateExpressionVal(call.Source)
if err != nil { return nil, err }
@ -93,11 +104,8 @@ func (this *generator) generateReferenceVal (reference *entity.Reference) (llvm.
}
func (this *generator) generateLengthVal (length *entity.Length) (llvm.Value, error) {
var sizeType *llvm.TypeInt; {
ty, err := this.typedef("Index")
if err != nil { return nil, err }
sizeType = ty.(*llvm.TypeInt)
}
sizeType, err := this.generateTypeIndex()
if err != nil { return nil, err }
source, err := this.generateExpressionLoc(length.Slice)
if err != nil { return nil, err }

View File

@ -9,6 +9,11 @@ func (this *generator) generateFunction (
*llvm.Function,
error,
) {
key := entity.Key {
Unit: function.Unit,
Name: function.Signature.Name,
}
params := make([]*llvm.Parameter, len(function.Signature.Arguments))
ret, err := this.generateType(function.Signature.Return)
@ -21,7 +26,7 @@ func (this *generator) generateFunction (
}
var name string; if function.LinkName == "" {
name = function.Signature.Name
name = key.LinkName()
} else {
name = function.LinkName
}
@ -44,7 +49,7 @@ func (this *generator) generateFunction (
}
}
this.functions[function.Signature.Name] = irFunc
this.functions[key] = irFunc
return irFunc, nil
}
@ -54,6 +59,12 @@ func (this *generator) generateMethod (
*llvm.Function,
error,
) {
key := entity.Key {
Unit: method.Unit,
Name: method.TypeName,
Method: method.Signature.Name,
}
params := make([]*llvm.Parameter, len(method.Signature.Arguments) + 1)
ret, err := this.generateType(method.Signature.Return)
@ -67,7 +78,7 @@ func (this *generator) generateMethod (
params[0] = llvm.NewParameter("this", new(llvm.TypePointer))
var name string; if method.LinkName == "" {
name = method.TypeName + "." + method.Signature.Name
name = key.LinkName()
} else {
name = method.LinkName
}
@ -90,6 +101,6 @@ func (this *generator) generateMethod (
}
}
this.functions[method.TypeName + "." + method.Signature.Name] = irFunc
this.functions[key] = irFunc
return irFunc, nil
}

View File

@ -3,6 +3,7 @@ package generator
import "sort"
import "errors"
import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
var errNotFound = errors.New("entity not found")
@ -22,7 +23,8 @@ type generator struct {
tree analyzer.Tree
module *llvm.Module
functions map[string] *llvm.Function
types map[entity.Key] llvm.Type
functions map[entity.Key] *llvm.Function
managerStack []*blockManager
blockManager *blockManager
@ -36,25 +38,28 @@ func (this Target) Generate (tree analyzer.Tree) (*llvm.Module, error) {
module: new(llvm.Module),
target: this,
tree: tree,
functions: make(map[string] *llvm.Function),
types: make(map[entity.Key] llvm.Type),
functions: make(map[entity.Key] *llvm.Function),
}).generate()
}
func (this *generator) generate () (*llvm.Module, error) {
// generate functions
functions := sortMapKeys(this.tree.Functions)
for _, functionName := range functions {
_, err := this.function(functionName)
functions := sortKeyedMapKeys(this.tree.Functions)
for _, key := range functions {
_, err := this.function(key)
if err != nil { return nil, err }
}
// generate methods
types := sortMapKeys(this.tree.Types)
for _, typeName := range types {
ty := this.tree.Types[typeName]
types := sortKeyedMapKeys(this.tree.Types)
for _, key := range types {
ty := this.tree.Types[key]
methods := sortMapKeys(ty.Methods)
for _, methodName := range methods {
_, err := this.method(typeName, methodName)
methodKey := key
methodKey.Method = methodName
_, err := this.method(methodKey)
if err != nil { return nil, err }
}
}
@ -62,39 +67,37 @@ func (this *generator) generate () (*llvm.Module, error) {
return this.module, nil
}
func (this *generator) typedef (typeName string) (llvm.Type, error) {
for _, ty := range this.module.Types {
if ty.Name() == typeName {
return ty, nil
}
}
def, exists := this.tree.Types[typeName]
func (this *generator) typedef (key entity.Key) (llvm.Type, error) {
ty, exists := this.types[key]
if exists { return ty, nil }
def, exists := this.tree.Types[key]
if !exists {
return nil, errNotFound
}
return this.generateTypedef(def)
}
func (this *generator) method (typeName string, name string) (*llvm.Function, error) {
method, exists := this.functions[typeName + "." + name]
func (this *generator) method (key entity.Key) (*llvm.Function, error) {
method, exists := this.functions[key]
if exists { return method, nil }
ty, exists := this.tree.Types[typeName]
ty, exists := this.tree.Types[key.StripMethod()]
if !exists {
return nil, errNotFound
}
if method, exists := ty.Methods[name]; exists {
if method, exists := ty.Methods[key.Method]; exists {
return this.generateMethod(method)
}
return nil, errNotFound
}
func (this *generator) function (name string) (*llvm.Function, error) {
function, exists := this.functions[name]
func (this *generator) function (key entity.Key) (*llvm.Function, error) {
function, exists := this.functions[key]
if exists { return function, nil }
if function, exists := this.tree.Functions[name]; exists {
if function, exists := this.tree.Functions[key]; exists {
return this.generateFunction(function)
}
return nil, errNotFound
@ -110,3 +113,36 @@ func sortMapKeys[T any] (unsorted map[string] T) []string {
sort.Strings(keys)
return keys
}
type keySlice []entity.Key
func (keys keySlice) Len () int {
return len(keys)
}
func (keys keySlice) Less (lefti, righti int) bool {
left := keys[lefti]
right := keys[righti]
for index, leftByte := range left.Unit {
if right.Unit[index] <= leftByte {
return false
}
}
return left.Name < right.Name && left.Method < right.Method
}
func (keys keySlice) Swap (lefti, righti int) {
keys[lefti], keys[righti] = keys[righti], keys[lefti]
}
func sortKeyedMapKeys[T any] (unsorted map[entity.Key] T) keySlice {
keys := make(keySlice, len(unsorted))
index := 0
for key := range unsorted {
keys[index] = key
index ++
}
sort.Sort(keys)
return keys
}

View File

@ -1,33 +1,78 @@
package generator
import "io"
import "testing"
import "strings"
import "github.com/google/uuid"
import "git.tebibyte.media/sashakoshka/fspl/lexer"
import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
import "git.tebibyte.media/sashakoshka/fspl/testcommon"
import "git.tebibyte.media/sashakoshka/fspl/parser/fspl"
func testString (test *testing.T, correct string, input string) {
ast := fsplParser.Tree { }
lx, err := lexer.LexReader("input.fspl", strings.NewReader(input))
if err != nil {
test.Error("lexer returned error:", err)
return
}
err = ast.Parse(fsplParser.UnitInfo { }, lx)
if err != nil && err != io.EOF{
test.Error("parser returned error:", err)
return
}
address := entity.Address("main.fspl")
ast, ok := treeOf(test, address, input, false)
if !ok { return }
tree := analyzer.Tree { }
err = tree.Analyze(ast)
err := tree.Analyze(address.UUID(), nil, ast)
if err != nil {
test.Error("analyzer returned error:", err)
return
}
testSemanticTree(test, correct, input, tree)
}
func testUnits (
test *testing.T,
correct string,
main string,
dependencies ...string,
) {
if len(dependencies) % 2 != 0 {
panic("dependencies len must be even. forget an address?")
}
tree := analyzer.Tree { }
nicknames := make(map[string] uuid.UUID)
// dependencies
for index := 0; index < len(dependencies) - 1; index += 2 {
address := entity.Address(dependencies[index])
source := dependencies[index + 1]
ast, ok := treeOf(test, address, source, true)
if !ok { return }
err := tree.Analyze(address.UUID(), nicknames, ast)
if err != nil {
test.Error("analyzer returned error:\n" + errors.Format(err))
return
}
unit := address.UUID()
nickname, ok := address.Nickname()
if !ok {
test.Errorf("could not generate nickname for %v", address)
return
}
nicknames[nickname] = unit
}
// main
address := entity.Address("main.fspl")
ast, ok := treeOf(test, address, main, false)
if !ok { return }
err := tree.Analyze(address.UUID(), nicknames, ast)
if err != nil {
test.Error("analyzer returned error:\n" + errors.Format(err))
return
}
testSemanticTree(test, correct, "", tree)
}
func testSemanticTree (test *testing.T, correct string, input string, tree analyzer.Tree) {
module, err := (&Target {
WordSize: 64,
Arch: "x86_64",
@ -47,8 +92,32 @@ func testString (test *testing.T, correct string, input string) {
if got != correct {
test.Logf("results do not match")
testcommon.Compare(test, correct, got)
test.Log("SOURCE FSPL CODE:")
test.Log("\033[32m" + input + "\033[0m")
if input != "" {
test.Log("SOURCE FSPL CODE:")
test.Log("\033[32m" + input + "\033[0m")
}
test.Fail()
}
}
func treeOf (test *testing.T, address entity.Address, input string, skim bool) (fsplParser.Tree, bool) {
ast := fsplParser.Tree { }
lx, err := lexer.LexReader (
string(address),
strings.NewReader(input))
if err != nil {
test.Error("lexer returned error:\n" + errors.Format(err))
return ast, false
}
if skim {
err = ast.Skim(lx)
} else {
err = ast.Parse(lx)
}
if err != nil {
test.Error("parser returned error:\n" + errors.Format(err))
return ast, false
}
return ast, true
}

View File

@ -12,7 +12,12 @@ func (this *generator) generateTypedef (
) {
irType, err := this.generateType(def.Type)
if err != nil { return nil, err }
return this.module.NewType(def.Name, irType), nil
irTypeDef := this.module.NewType(def.Name, irType)
this.types[entity.Key {
Unit: def.Unit,
Name: def.Name,
}] = irTypeDef
return irTypeDef, nil
}
func (this *generator) generateType (ty entity.Type) (llvm.Type, error) {
@ -41,9 +46,3 @@ func (this *generator) generateType (ty entity.Type) (llvm.Type, error) {
panic(fmt.Sprintln("generator doesn't know about type", ty))
}
}
func (this *generator) generateTypeIndex () (*llvm.TypeInt, error) {
sizeTypeAny, err := this.typedef("Index")
if err != nil { return nil, err }
return sizeTypeAny.(*llvm.TypeInt), nil
}

View File

@ -6,8 +6,19 @@ import "strings"
import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *generator) generateTypeIndex () (*llvm.TypeInt, error) {
ty, err := this.typedef(entity.Key {
Name: "Index",
})
if err != nil { return nil, err }
return ty.(*llvm.TypeInt), nil
}
func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) {
underlying, err := this.typedef(ty.Name)
underlying, err := this.typedef(entity.Key {
Unit: ty.Type.Unit(),
Name: ty.Name,
})
if err != nil { return nil, err }
return &llvm.TypeDefined { Source: underlying }, nil
}
@ -17,7 +28,7 @@ func (this *generator) generateTypePointer (ty *entity.TypePointer) (llvm.Type,
}
func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (llvm.Type, error) {
indexType, err := this.typedef("Index")
indexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
return &llvm.TypeStruct {
Fields: []llvm.Type {