Add type generator
This commit is contained in:
parent
e501577555
commit
3a1d9e20c2
|
@ -1,11 +1,34 @@
|
|||
package generator
|
||||
|
||||
import "io"
|
||||
import "fmt"
|
||||
import "sort"
|
||||
import "errors"
|
||||
import "github.com/llir/llvm/ir"
|
||||
import "github.com/llir/llvm/ir/types"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
// Target contains information about the machine the code is being written for.
|
||||
type Target struct {
|
||||
// WordSize is the size of the machine word. This determines the width
|
||||
// of the Word type.
|
||||
WordSize uint64
|
||||
|
||||
// Arch specifies the machine architecture
|
||||
Arch string
|
||||
}
|
||||
|
||||
// NativeTarget returns a target describing the current system.
|
||||
func NativeTarget () Target {
|
||||
// FIXME
|
||||
return Target {
|
||||
WordSize: 64,
|
||||
Arch: "x86",
|
||||
}
|
||||
}
|
||||
|
||||
type generator struct {
|
||||
target Target
|
||||
tree analyzer.Tree
|
||||
output io.Writer
|
||||
module ir.Module
|
||||
|
@ -14,26 +37,41 @@ type generator struct {
|
|||
// Generate takes in a semantic tree and writes corresponding LLVM IR to the
|
||||
// given io.Writer. It returns an error in case there is something wrong with
|
||||
// the semantic tree that prevents the code generation process from occurring.
|
||||
func Generate (tree analyzer.Tree, output io.Writer) error {
|
||||
func (this Target) Generate (tree analyzer.Tree, output io.Writer) error {
|
||||
return (&generator {
|
||||
target: this,
|
||||
tree: tree,
|
||||
output: output,
|
||||
}).generate()
|
||||
}
|
||||
|
||||
func (this *generator) generate () error {
|
||||
for name, ty := range this.tree.Types {
|
||||
|
||||
typedefs := sortMapKeys(this.tree.Types)
|
||||
for _, key := range typedefs {
|
||||
_, err := this.typedef(key)
|
||||
if err != nil { return err }
|
||||
}
|
||||
|
||||
// TODO functions
|
||||
|
||||
// TODO methods
|
||||
|
||||
_, err := this.module.WriteTo(this.output)
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *generator) ty (typeName string) types.Type {
|
||||
func (this *generator) typedef (typeName string) (types.Type, error) {
|
||||
for _, ty := range this.module.TypeDefs {
|
||||
if ty.Name() == typeName {
|
||||
return ty
|
||||
return ty, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
def, exists := this.tree.Types[typeName]
|
||||
if !exists {
|
||||
return nil,
|
||||
errors.New(fmt.Sprintln("type", typeName, "not found"))
|
||||
}
|
||||
return this.generateTypedef(def)
|
||||
}
|
||||
|
||||
func (this *generator) method (typeName string, name string) *ir.Func {
|
||||
|
@ -53,3 +91,14 @@ func (this *generator) function (name string) *ir.Func {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortMapKeys[T any] (unsorted map[string] T) []string {
|
||||
keys := make([]string, len(unsorted))
|
||||
index := 0
|
||||
for key := range unsorted {
|
||||
keys[index] = key
|
||||
index ++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package generator
|
||||
|
||||
import "io"
|
||||
import "fmt"
|
||||
import "testing"
|
||||
import "strings"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/parser"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func testString (test *testing.T, correct string, input string) {
|
||||
testReader(test, correct, strings.NewReader(input))
|
||||
}
|
||||
|
||||
func testReader (test *testing.T, correct string, inputs ...io.Reader) {
|
||||
ast := parser.Tree { }
|
||||
for index, stream := range inputs {
|
||||
err := ast.Parse(fmt.Sprintf("stream%d.fspl", index), stream)
|
||||
if err != nil && err != io.EOF{
|
||||
test.Error("parser returned error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tree := analyzer.Tree { }
|
||||
err := tree.Analyze(ast)
|
||||
if err != nil {
|
||||
test.Error("analyzer returned error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
output := new(strings.Builder)
|
||||
err = NativeTarget().Generate(tree, output)
|
||||
if err != nil {
|
||||
test.Error("generator returned error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
got := output.String()
|
||||
if got != correct {
|
||||
test.Logf("results do not match")
|
||||
test.Logf("got:\n%v", got)
|
||||
test.Logf("correct:\n%v", correct)
|
||||
test.Fail()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "github.com/llir/llvm/ir/types"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
func (this *generator) generateTypedef (
|
||||
def *entity.Typedef,
|
||||
) (
|
||||
types.Type,
|
||||
error,
|
||||
) {
|
||||
irType, err := this.generateType(def.Type)
|
||||
if err != nil { return nil, err }
|
||||
return this.module.NewTypeDef(def.Name, irType), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateType (ty entity.Type) (types.Type, error) {
|
||||
switch ty.(type) {
|
||||
case nil:
|
||||
return types.Void, nil
|
||||
case *entity.TypeNamed:
|
||||
return this.generateTypeNamed(ty.(*entity.TypeNamed))
|
||||
case *entity.TypePointer:
|
||||
return this.generateTypePointer(ty.(*entity.TypePointer))
|
||||
case *entity.TypeSlice:
|
||||
return this.generateTypeSlice(ty.(*entity.TypeSlice))
|
||||
case *entity.TypeArray:
|
||||
return this.generateTypeArray(ty.(*entity.TypeArray))
|
||||
case *entity.TypeStruct:
|
||||
return this.generateTypeStruct(ty.(*entity.TypeStruct))
|
||||
case *entity.TypeInterface:
|
||||
return this.generateTypeInerface(ty.(*entity.TypeInterface))
|
||||
case *entity.TypeInt:
|
||||
return this.generateTypeInt(ty.(*entity.TypeInt))
|
||||
case *entity.TypeFloat:
|
||||
return this.generateTypeFloat(ty.(*entity.TypeFloat))
|
||||
case *entity.TypeWord:
|
||||
return this.generateTypeWord(ty.(*entity.TypeWord))
|
||||
default:
|
||||
panic(fmt.Sprintln("generator doesn't know about type", ty))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "errors"
|
||||
import "github.com/llir/llvm/ir/types"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (types.Type, error) {
|
||||
return this.typedef(ty.Name)
|
||||
}
|
||||
|
||||
func (this *generator) generateTypePointer (ty *entity.TypePointer) (types.Type, error) {
|
||||
return types.NewPointer(&types.VoidType { }), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (types.Type, error) {
|
||||
return types.NewStruct (
|
||||
/* data */ types.NewPointer(types.Void),
|
||||
/* len */ types.NewInt(this.target.WordSize)), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeArray (ty *entity.TypeArray) (types.Type, error) {
|
||||
element, err := this.generateType(ty.Element)
|
||||
if err != nil { return nil, err }
|
||||
return types.NewArray(uint64(ty.Length), element), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeStruct (ty *entity.TypeStruct) (types.Type, error) {
|
||||
irStruct := &types.StructType {
|
||||
Fields: make([]types.Type, len(ty.Members)),
|
||||
}
|
||||
for index, member := range ty.Members {
|
||||
field, err := this.generateType(member.Type())
|
||||
if err != nil { return nil, err }
|
||||
irStruct.Fields[index] = field
|
||||
}
|
||||
return irStruct, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeInerface (ty *entity.TypeInterface) (types.Type, error) {
|
||||
irStruct := &types.StructType {
|
||||
Fields: make([]types.Type, len(ty.Behaviors) + 1),
|
||||
}
|
||||
// object pointer
|
||||
irStruct.Fields[0] = types.NewPointer(types.Void)
|
||||
// behaviors
|
||||
for index, behavior := range ty.Behaviors {
|
||||
field, err := this.generateTypeFunction(behavior)
|
||||
if err != nil { return nil, err }
|
||||
irStruct.Fields[index + 1] = field
|
||||
}
|
||||
return irStruct, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeInt (ty *entity.TypeInt) (types.Type, error) {
|
||||
return types.NewInt(uint64(ty.Width)), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeFloat (ty *entity.TypeFloat) (types.Type, error) {
|
||||
var kind types.FloatKind; switch ty.Width {
|
||||
case 16: kind = types.FloatKindHalf
|
||||
case 32: kind = types.FloatKindFloat
|
||||
case 64: kind = types.FloatKindDouble
|
||||
case 80:
|
||||
if this.target.Arch == "x86" {
|
||||
kind = types.FloatKindX86_FP80
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintln (
|
||||
ty, "not available on",
|
||||
this.target.Arch))
|
||||
}
|
||||
case 128:
|
||||
if this.target.Arch == "PowerPC" {
|
||||
kind = types.FloatKindPPC_FP128
|
||||
} else {
|
||||
kind = types.FloatKindFP128
|
||||
}
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln (ty, "has invalid width"))
|
||||
}
|
||||
return &types.FloatType { Kind: kind }, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeWord (ty *entity.TypeWord) (types.Type, error) {
|
||||
return types.NewInt(this.target.WordSize), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeFunction (
|
||||
signature *entity.Signature,
|
||||
) (
|
||||
types.Type,
|
||||
error,
|
||||
) {
|
||||
irFunc := &types.FuncType {
|
||||
Params: make([]types.Type, len(signature.Arguments)),
|
||||
}
|
||||
ret, err := this.generateType(signature.Return)
|
||||
if err != nil { return nil, err }
|
||||
irFunc.RetType = ret
|
||||
for index, argument := range signature.Arguments {
|
||||
param, err := this.generateType(argument.Type())
|
||||
if err != nil { return nil, err }
|
||||
irFunc.Params[index] = param
|
||||
}
|
||||
return irFunc, nil
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package generator
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestType (test *testing.T) {
|
||||
testString (test,
|
||||
`%AllFloats = type { float, double }
|
||||
%Bool = type i8
|
||||
%Byte = type i8
|
||||
%Index = type i64
|
||||
%Rune = type i32
|
||||
%AllInts = type { %Bool, %Byte, %Index, %Rune, i64, i64, i8, i16, i32, i64, i8, i16, i32, i64 }
|
||||
%Path = type { void*, i64 }
|
||||
%Pegasus = type { void*, void ({ void*, i64 }), void ({ void*, i64 }), void (i64), %Bool () }
|
||||
%Point = type { i64, i64 }
|
||||
%Quadrangle = type [4 x %Point]
|
||||
%Rectangle = type { %Point, %Point }
|
||||
%String = type { void*, i64 }
|
||||
`,
|
||||
`
|
||||
Point: (x:Int y:Int)
|
||||
Pegasus: (
|
||||
[clear clouds:*:Point]
|
||||
[fly rings:*:Point]
|
||||
[fall distance:Int]
|
||||
[complete]:Bool)
|
||||
Rectangle: (
|
||||
min:Point
|
||||
max:Point)
|
||||
AllInts: (
|
||||
bool:Bool
|
||||
byte:Byte
|
||||
index:Index
|
||||
rune:Rune
|
||||
word:Int
|
||||
uword:UInt
|
||||
i8:I8
|
||||
i16:I16
|
||||
i32:I32
|
||||
i64:I64
|
||||
u8:U8
|
||||
u16:U16
|
||||
u32:U32
|
||||
u64:U64
|
||||
)
|
||||
AllFloats: (
|
||||
f32:F32
|
||||
f64:F64)
|
||||
Path: *:Point
|
||||
Quadrangle: 4:Point
|
||||
`)
|
||||
}
|
Loading…
Reference in New Issue