This should have been way more commits
This commit is contained in:
parent
3a751666ba
commit
aa107072d8
49
generator/assignment.go
Normal file
49
generator/assignment.go
Normal file
@ -0,0 +1,49 @@
|
||||
package generator
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) {
|
||||
source, err := this.generateAssignmentSource (
|
||||
assignment.Value,
|
||||
assignment.Location.Type())
|
||||
if err != nil { return nil, err }
|
||||
destination, err := this.generateExpressionLoc(assignment.Location)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(source, destination)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// generateAssignmentSource performs type coercions if necessary, mainly
|
||||
// interface assignment. this should be called instead of generateExpression
|
||||
// when the type to assign to is known.
|
||||
func (this *generator) generateAssignmentSource (
|
||||
from entity.Expression,
|
||||
to entity.Type,
|
||||
) (
|
||||
llvm.Value,
|
||||
error,
|
||||
) {
|
||||
source, err := this.generateExpression(from)
|
||||
if err != nil { return nil, err }
|
||||
ty := analyzer.ReduceToBase(to)
|
||||
|
||||
switch ty := ty.(type) {
|
||||
// case *entity.TypeInterface:
|
||||
// TODO
|
||||
// sourceTy := analyzer.ReduceToBase(from.Type())
|
||||
// irSourceTy, err := this,generateType(sourceTy)
|
||||
// if err != nil { return nil, err }
|
||||
//
|
||||
// if
|
||||
//
|
||||
// irType, err := this.generateType(to)
|
||||
// if err != nil { return nil, err }
|
||||
// pointer := this.
|
||||
|
||||
default:
|
||||
ty = ty
|
||||
return source, nil
|
||||
}
|
||||
}
|
@ -1,13 +1,23 @@
|
||||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
type loopEntry struct {
|
||||
value llvm.Value
|
||||
stub *llvm.Block
|
||||
wantLocation bool
|
||||
}
|
||||
|
||||
type blockManager struct {
|
||||
*llvm.Block
|
||||
generator *generator
|
||||
function *llvm.Function
|
||||
declarations map[*entity.Declaration] llvm.Value
|
||||
|
||||
loops []*loopEntry
|
||||
}
|
||||
|
||||
func (this *generator) pushBlockManager (function *llvm.Function) *blockManager {
|
||||
@ -30,25 +40,56 @@ func (this *generator) popBlockManager () {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *blockManager) newBlock () *llvm.Block {
|
||||
if this.Block != nil && len(this.Block.Instructions) == 0 { return nil }
|
||||
|
||||
previous := this.Block
|
||||
this.Block = this.function.NewBlock("")
|
||||
return previous
|
||||
func (this *blockManager) pushLoop (wantLocation bool) *loopEntry {
|
||||
entry := &loopEntry { wantLocation: wantLocation }
|
||||
this.loops = append(this.loops, entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
func (this *blockManager) variable (declaration *entity.Declaration) llvm.Value {
|
||||
return this.declarations[declaration]
|
||||
func (this *blockManager) popLoop () {
|
||||
this.loops = this.loops[:len(this.loops) - 1]
|
||||
}
|
||||
|
||||
func (this *blockManager) topLoop () *loopEntry {
|
||||
return this.loops[len(this.loops) - 1]
|
||||
}
|
||||
|
||||
func (this *blockManager) newAllocaFront (element llvm.Type) llvm.Value {
|
||||
alloca := llvm.NewAlloca(element)
|
||||
|
||||
firstBlock := this.function.Blocks[0]
|
||||
if firstBlock.Terminated() {
|
||||
lastIndex := len(firstBlock.Instructions) - 1
|
||||
firstBlock.Instructions = append (
|
||||
firstBlock.Instructions,
|
||||
firstBlock.Instructions[lastIndex])
|
||||
firstBlock.Instructions[lastIndex] = alloca
|
||||
} else {
|
||||
firstBlock.AddInstruction(alloca)
|
||||
}
|
||||
return alloca
|
||||
}
|
||||
|
||||
func (this *blockManager) newBlock () *llvm.Block {
|
||||
this.Block = this.function.NewBlock()
|
||||
return this.Block
|
||||
}
|
||||
|
||||
func (this *blockManager) variable (declaration *entity.Declaration) (llvm.Value, error) {
|
||||
value, ok := this.declarations[declaration]
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("declaration of %v does not exist", declaration.Name))
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (this *blockManager) addDeclaration (declaration *entity.Declaration, initial llvm.Value) error {
|
||||
ty, err := this.generator.generateType(declaration.Type())
|
||||
if err != nil { return err }
|
||||
location := this.Block.NewAlloca(ty)
|
||||
location := this.newAllocaFront(ty)
|
||||
this.declarations[declaration] = location
|
||||
if initial != nil {
|
||||
this.Block.NewStore(initial, location)
|
||||
this.NewStore(initial, location)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,51 +4,66 @@ import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
func (this *generator) valueToLocation (value llvm.Value) llvm.Value {
|
||||
pointer := this.blockManager.newAllocaFront(value.Type())
|
||||
this.blockManager.NewStore(value, pointer)
|
||||
return pointer
|
||||
}
|
||||
|
||||
func (this *generator) locationToValue (pointer llvm.Value, ty entity.Type) (llvm.Value, error) {
|
||||
irType, err := this.generateType(ty)
|
||||
if err != nil { return nil, err }
|
||||
return this.blockManager.NewLoad(irType, pointer), nil
|
||||
}
|
||||
|
||||
// generateExpression generates an expression and returns a register
|
||||
// representing the result.
|
||||
func (this *generator) generateExpression (expression entity.Expression) (llvm.Value, error) {
|
||||
switch expression.(type) {
|
||||
// we get an address from these, so we need to load the value
|
||||
switch expression := expression.(type) {
|
||||
case *entity.Dereference,
|
||||
*entity.MemberAccess,
|
||||
*entity.Slice,
|
||||
*entity.Subscript,
|
||||
*entity.LiteralArray,
|
||||
*entity.LiteralStruct:
|
||||
|
||||
pointer, err := this.generateExpressionLoc(expression)
|
||||
if err != nil { return nil, err }
|
||||
return this.locationToValue(pointer, expression.Type())
|
||||
|
||||
// we get a value from these, so we can return them as-is
|
||||
case *entity.Variable:
|
||||
return this.generateVariable(expression.(*entity.Variable))
|
||||
return this.generateVariable(expression)
|
||||
case *entity.Call:
|
||||
return this.generateCall(expression.(*entity.Call))
|
||||
return this.generateCall(expression)
|
||||
case *entity.MethodCall:
|
||||
return this.generateMethodCall(expression.(*entity.MethodCall))
|
||||
// case *entity.Subscript:
|
||||
// return this.generateSubscript(expression.(*entity.Subscript))
|
||||
// case *entity.Slice:
|
||||
// return this.generateSlice(expression.(*entity.Slice))
|
||||
// case *entity.Dereference:
|
||||
// return this.generateDereference(expression.(*entity.Dereference))
|
||||
// case *entity.Reference:
|
||||
// return this.generateReference(expression.(*entity.Reference))
|
||||
return this.generateMethodCall(expression)
|
||||
case *entity.Reference:
|
||||
return this.generateReference(expression)
|
||||
// case *entity.ValueCast:
|
||||
// return this.generateValueCast(expression.(*entity.ValueCast))
|
||||
// return this.generateValueCast(expression)
|
||||
// case *entity.BitCast:
|
||||
// return this.generateBitCast(expression.(*entity.BitCast))
|
||||
// return this.generateBitCast(expression)
|
||||
// case *entity.Operation:
|
||||
// return this.generateOperation(expression.(*entity.Operation))
|
||||
// return this.generateOperation(expression)
|
||||
case *entity.Block:
|
||||
return this.generateBlock(expression.(*entity.Block))
|
||||
// case *entity.MemberAccess:
|
||||
// return this.generateMemberAccess(expression.(*entity.MemberAccess))
|
||||
// case *entity.IfElse:
|
||||
// return this.generateIfElse(expression.(*entity.IfElse))
|
||||
// case *entity.Loop:
|
||||
// return this.generateLoop(expression.(*entity.Loop))
|
||||
// case *entity.Break:
|
||||
// return this.generateBreak(expression.(*entity.Break))
|
||||
// case *entity.Return:
|
||||
// return this.generateReturn(expression.(*entity.Return))
|
||||
return this.generateBlock(expression)
|
||||
case *entity.IfElse:
|
||||
return this.generateIfElse(expression, false)
|
||||
case *entity.Loop:
|
||||
return this.generateLoop(expression, false)
|
||||
case *entity.Break:
|
||||
return this.generateBreak(expression)
|
||||
case *entity.Return:
|
||||
return this.generateReturn(expression)
|
||||
|
||||
case *entity.LiteralInt:
|
||||
return this.generateLiteralInt(expression.(*entity.LiteralInt))
|
||||
return this.generateLiteralInt(expression)
|
||||
case *entity.LiteralFloat:
|
||||
return this.generateLiteralFloat(expression.(*entity.LiteralFloat))
|
||||
case *entity.LiteralArray:
|
||||
return this.generateLiteralArray(expression.(*entity.LiteralArray))
|
||||
// case *entity.LiteralStruct:
|
||||
// return this.generateLiteralStruct(expression.(*entity.LiteralStruct))
|
||||
return this.generateLiteralFloat(expression)
|
||||
case *entity.LiteralBoolean:
|
||||
return this.generateLiteralBoolean(expression.(*entity.LiteralBoolean))
|
||||
return this.generateLiteralBoolean(expression)
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: generator doesnt know about expression ",
|
||||
@ -57,9 +72,9 @@ func (this *generator) generateExpression (expression entity.Expression) (llvm.V
|
||||
}
|
||||
|
||||
func (this *generator) generateStatement (statement entity.Statement) (llvm.Value, error) {
|
||||
/*if assignment, ok := statement.(*entity.Assignment); ok {
|
||||
if assignment, ok := statement.(*entity.Assignment); ok {
|
||||
return this.generateAssignment(assignment)
|
||||
} else*/ if expression, ok := statement.(entity.Expression); ok {
|
||||
} else if expression, ok := statement.(entity.Expression); ok {
|
||||
return this.generateExpression(expression)
|
||||
} else {
|
||||
panic(fmt.Sprint (
|
||||
@ -67,3 +82,70 @@ func (this *generator) generateStatement (statement entity.Statement) (llvm.Valu
|
||||
statement))
|
||||
}
|
||||
}
|
||||
|
||||
// generateExpressionLoc generates an expression and returns a register
|
||||
// representing the address of the result. This function avoids adding alloca
|
||||
// instructions whenever possible, trying to refer to the original data.
|
||||
func (this *generator) generateExpressionLoc (expression entity.Expression) (llvm.Value, error) {
|
||||
switch expression := expression.(type) {
|
||||
|
||||
// we get a value from these, so we need to store the value and then
|
||||
// return an address to that
|
||||
case *entity.Call,
|
||||
*entity.MethodCall,
|
||||
*entity.Reference,
|
||||
*entity.ValueCast,
|
||||
*entity.BitCast,
|
||||
*entity.Operation,
|
||||
*entity.LiteralInt,
|
||||
*entity.LiteralFloat,
|
||||
*entity.LiteralBoolean:
|
||||
|
||||
value, err := this.generateExpression(expression)
|
||||
if err != nil { return nil, err }
|
||||
return this.valueToLocation(value), nil
|
||||
|
||||
// we get an address from these, so we can return them as-is
|
||||
case *entity.Variable:
|
||||
return this.generateVariableLoc(expression)
|
||||
case *entity.Slice:
|
||||
return this.generateSliceLoc(expression)
|
||||
case *entity.Subscript:
|
||||
return this.generateSubscriptLoc(expression)
|
||||
case *entity.Dereference:
|
||||
return this.generateDereferenceLoc(expression)
|
||||
case *entity.MemberAccess:
|
||||
return this.generateMemberAccessLoc(expression)
|
||||
case *entity.Block:
|
||||
return this.generateBlockLoc(expression)
|
||||
case *entity.IfElse:
|
||||
return this.generateIfElse(expression, true)
|
||||
case *entity.Loop:
|
||||
return this.generateLoop(expression, true)
|
||||
case *entity.Break:
|
||||
return this.generateBreak(expression)
|
||||
case *entity.Return:
|
||||
return this.generateReturn(expression)
|
||||
case *entity.LiteralArray:
|
||||
return this.generateLiteralArrayLoc(expression)
|
||||
case *entity.LiteralStruct:
|
||||
return this.generateLiteralStructLoc(expression)
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: generator doesnt know about location expression ",
|
||||
expression))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateStatementLoc (statement entity.Statement) (llvm.Value, error) {
|
||||
if assignment, ok := statement.(*entity.Assignment); ok {
|
||||
return this.generateAssignment(assignment)
|
||||
} else if expression, ok := statement.(entity.Expression); ok {
|
||||
return this.generateExpressionLoc(expression)
|
||||
} else {
|
||||
panic(fmt.Sprint (
|
||||
"BUG: generator doesnt know about statement ",
|
||||
statement))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,13 @@ import "fmt"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateVariable (variable *entity.Variable) (llvm.Value, error) {
|
||||
value := this.blockManager.variable(variable.Declaration)
|
||||
location, err := this.generateVariableLoc(variable)
|
||||
if err != nil { return nil, err }
|
||||
irType, err := this.generateType(variable.Type())
|
||||
if err != nil { return nil, err }
|
||||
return this.blockManager.NewLoad(irType, value), nil
|
||||
return this.blockManager.NewLoad(irType, location), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) {
|
||||
@ -19,8 +19,11 @@ func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) {
|
||||
|
||||
args := make([]llvm.Value, len(call.Arguments))
|
||||
for index, argument := range call.Arguments {
|
||||
irArgument, err := this.generateExpression(argument)
|
||||
irArgument, err := this.generateAssignmentSource (
|
||||
argument,
|
||||
call.Function.Signature.Arguments[index].Type())
|
||||
if err != nil { return nil, err }
|
||||
|
||||
args[index] = irArgument
|
||||
}
|
||||
return this.blockManager.NewCall(function, function.Signature, args...), nil
|
||||
@ -30,10 +33,16 @@ func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value,
|
||||
source, err := this.generateExpression(call.Source)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
var signature *entity.Signature
|
||||
if call.Method != nil { signature = call.Method.Signature }
|
||||
if call.Behavior != nil { signature = call.Behavior }
|
||||
|
||||
// build list of args
|
||||
args := make([]llvm.Value, len(call.Arguments) + 1)
|
||||
for index, argument := range call.Arguments {
|
||||
irArgument, err := this.generateExpression(argument)
|
||||
irArgument, err := this.generateAssignmentSource (
|
||||
argument,
|
||||
signature.Arguments[index].Type())
|
||||
if err != nil { return nil, err }
|
||||
args[index + 1] = irArgument
|
||||
}
|
||||
@ -96,6 +105,10 @@ func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value,
|
||||
return nil, errors.New(fmt.Sprintln("no method", call.Name, "for", call.Source))
|
||||
}
|
||||
|
||||
func (this *generator) generateReference (reference *entity.Reference) (llvm.Value, error) {
|
||||
return this.generateExpressionLoc(reference.Value)
|
||||
}
|
||||
|
||||
func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
|
||||
var last llvm.Value
|
||||
var err error
|
||||
@ -106,80 +119,91 @@ func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
|
||||
return last, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) {
|
||||
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
|
||||
func (this *generator) generateIfElse (ifelse *entity.IfElse, loc bool) (llvm.Value, error) {
|
||||
condition, err := this.generateExpression(ifelse.Condition)
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil
|
||||
}
|
||||
previous := this.blockManager.Block
|
||||
|
||||
func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) {
|
||||
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
|
||||
var trueBlock, falseBlock *llvm.Block
|
||||
var tru, fals llvm.Value
|
||||
|
||||
trueBlock = this.blockManager.newBlock()
|
||||
if loc {
|
||||
tru, err = this.generateExpressionLoc(ifelse.True)
|
||||
} else {
|
||||
tru, err = this.generateExpression(ifelse.True)
|
||||
}
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralArray (literal *entity.LiteralArray) (llvm.Value, error) {
|
||||
ty := analyzer.ReduceToBase(literal.Type())
|
||||
|
||||
makeData := func (element entity.Type) (llvm.Value, error) {
|
||||
irArrayType, err := this.generateType(&entity.TypeArray {
|
||||
Element: element,
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
array := this.blockManager.NewAlloca(irArrayType)
|
||||
for index, element := range literal.Elements {
|
||||
irElement, err := this.generateExpression(element)
|
||||
if err != nil { return nil, err }
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore(irElement, elementPointer)
|
||||
if ifelse.False != nil {
|
||||
falseBlock = this.blockManager.newBlock()
|
||||
if loc {
|
||||
fals, err = this.generateExpressionLoc(ifelse.False)
|
||||
} else {
|
||||
fals, err = this.generateExpression(ifelse.False)
|
||||
}
|
||||
return array, nil
|
||||
}
|
||||
|
||||
switch ty.(type) {
|
||||
case *entity.TypeArray:
|
||||
ty := ty.(*entity.TypeArray)
|
||||
return makeData(ty.Element)
|
||||
|
||||
case *entity.TypeSlice:
|
||||
ty := ty.(*entity.TypeSlice)
|
||||
array, err := makeData(ty.Element)
|
||||
if err != nil { return nil, err }
|
||||
irType, err := this.generateTypeSlice(ty)
|
||||
if err != nil { return nil, err }
|
||||
slice := this.blockManager.NewAlloca(irType)
|
||||
data := this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
this.blockManager.NewStore(array, data)
|
||||
length := this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 1))
|
||||
sizeType, err := this.typedef("Size")
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt (
|
||||
sizeType.(*llvm.TypeInt),
|
||||
int64(len(literal.Elements))),
|
||||
length)
|
||||
return slice, nil
|
||||
|
||||
case *entity.TypePointer:
|
||||
ty := ty.(*entity.TypePointer)
|
||||
array, err := makeData(ty.Referenced)
|
||||
if err != nil { return nil, err }
|
||||
return array, nil
|
||||
trueIncoming := &llvm.Incoming {
|
||||
X: tru,
|
||||
Predecessor: trueBlock,
|
||||
}
|
||||
falseIncoming := &llvm.Incoming {
|
||||
X: fals,
|
||||
Predecessor: falseBlock,
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("array can't be used as ", ty))
|
||||
previous.NewCondBr(condition, trueBlock, falseBlock)
|
||||
this.blockManager.newBlock()
|
||||
return this.blockManager.NewPhi(trueIncoming, falseIncoming), nil
|
||||
} else {
|
||||
exitBlock := this.blockManager.newBlock()
|
||||
falseBlock.NewBr(exitBlock)
|
||||
previous.NewCondBr(condition, trueBlock, falseBlock)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) {
|
||||
return llvm.NewConstBool(bool(*literal.Value)), nil
|
||||
func (this *generator) generateLoop (loop *entity.Loop, loc bool) (llvm.Value, error) {
|
||||
previous := this.blockManager.Block
|
||||
body := this.blockManager.newBlock()
|
||||
previous.NewBr(body)
|
||||
|
||||
loopEntry := this.blockManager.pushLoop(loc)
|
||||
defer this.blockManager.popLoop()
|
||||
|
||||
_, err := this.generateExpression(loop.Body)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
this.blockManager.NewBr(body)
|
||||
exit := this.blockManager.newBlock()
|
||||
|
||||
if loopEntry.stub != nil {
|
||||
loopEntry.stub.NewBr(exit)
|
||||
}
|
||||
return loopEntry.value, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
|
||||
loopEntry := this.blockManager.topLoop()
|
||||
if brk.Value != nil {
|
||||
value, err := this.generateExpression(brk.Value)
|
||||
if err != nil { return nil, err }
|
||||
loopEntry.value = value
|
||||
}
|
||||
|
||||
loopEntry.stub = this.blockManager.Block
|
||||
this.blockManager.newBlock()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateReturn (ret *entity.Return) (llvm.Value, error) {
|
||||
if ret.Value == nil {
|
||||
this.blockManager.NewRet(nil)
|
||||
} else {
|
||||
value, err := this.generateExpression(ret.Value)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewRet(value)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -8,10 +8,15 @@ testString (test,
|
||||
`,
|
||||
`
|
||||
[puts string:*Byte]:I32
|
||||
[main argc:I32 argv:**Byte]:I32 = {
|
||||
[puts [. argv]]
|
||||
0
|
||||
}
|
||||
[main] = loop [puts (* 104 111 109 101 10 115 109 101 101 116 0)]
|
||||
|
||||
`)
|
||||
}
|
||||
// [write 1 (* 72 101 108 108 111 114 108 100 0) 7]
|
||||
|
||||
// [puts string:*Byte]:I32
|
||||
// [main argc:I32 argv:**Byte] = {
|
||||
// loop {
|
||||
// [puts (* 100 100 0)]
|
||||
// }
|
||||
// }
|
||||
|
106
generator/literal.go
Normal file
106
generator/literal.go
Normal file
@ -0,0 +1,106 @@
|
||||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) {
|
||||
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) {
|
||||
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (llvm.Value, error) {
|
||||
ty := analyzer.ReduceToBase(literal.Type())
|
||||
|
||||
makeData := func (elementType entity.Type) (llvm.Value, error) {
|
||||
irArrayType, err := this.generateType(&entity.TypeArray {
|
||||
Element: elementType,
|
||||
Length: len(literal.Elements),
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
array := this.blockManager.newAllocaFront(irArrayType)
|
||||
for index, element := range literal.Elements {
|
||||
irElement, err := this.generateAssignmentSource (
|
||||
element,
|
||||
elementType)
|
||||
if err != nil { return nil, err }
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore(irElement, elementPointer)
|
||||
}
|
||||
return array, nil
|
||||
}
|
||||
|
||||
switch ty := ty.(type) {
|
||||
case *entity.TypeArray:
|
||||
return makeData(ty.Element)
|
||||
|
||||
case *entity.TypeSlice:
|
||||
array, err := makeData(ty.Element)
|
||||
if err != nil { return nil, err }
|
||||
irType, err := this.generateTypeSlice(ty)
|
||||
if err != nil { return nil, err }
|
||||
slice := this.blockManager.newAllocaFront(irType)
|
||||
data := this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
this.blockManager.NewStore(array, data)
|
||||
length := this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 1))
|
||||
sizeType, err := this.typedef("Size")
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt (
|
||||
sizeType.(*llvm.TypeInt),
|
||||
int64(len(literal.Elements))),
|
||||
length)
|
||||
return slice, nil
|
||||
|
||||
case *entity.TypePointer:
|
||||
array, err := makeData(ty.Referenced)
|
||||
if err != nil { return nil, err }
|
||||
return this.valueToLocation(array), nil
|
||||
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("array can't be used as ", ty))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct) (llvm.Value, error) {
|
||||
ty := analyzer.ReduceToBase(literal.Type())
|
||||
irType, err := this.generateType(ty)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
structure := this.blockManager.newAllocaFront(irType)
|
||||
|
||||
for index, member := range literal.Members {
|
||||
irMember, err := this.generateExpression(member.Value)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irType, structure,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore(irMember, elementPointer)
|
||||
}
|
||||
|
||||
return structure, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) {
|
||||
return llvm.NewConstBool(bool(*literal.Value)), nil
|
||||
}
|
190
generator/location-expression.go
Normal file
190
generator/location-expression.go
Normal file
@ -0,0 +1,190 @@
|
||||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateVariableLoc (variable *entity.Variable) (llvm.Value, error) {
|
||||
return this.blockManager.variable(variable.Declaration)
|
||||
}
|
||||
|
||||
func (this *generator) generateSliceLoc (slice *entity.Slice) (llvm.Value, error) {
|
||||
var err error
|
||||
var start, end, dataAddress llvm.Value
|
||||
var sizeType *llvm.TypeInt; {
|
||||
ty, err := this.typedef("Index")
|
||||
if err != nil { return nil, err }
|
||||
sizeType = ty.(*llvm.TypeInt)
|
||||
}
|
||||
|
||||
if slice.Start == nil {
|
||||
start = llvm.NewConstInt(sizeType, 0)
|
||||
} else {
|
||||
start, err = this.generateExpression(slice.Start)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
if slice.End != nil {
|
||||
end, err = this.generateExpression(slice.End)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
|
||||
sourceType := analyzer.ReduceToBase(slice.Slice.Type())
|
||||
switch sourceType := sourceType.(type) {
|
||||
case *entity.TypeSlice:
|
||||
source, err := this.generateExpression(slice.Slice)
|
||||
if err != nil { return nil, err }
|
||||
irSourceType, err := this.generateType(sourceType)
|
||||
if err != nil { return nil, err }
|
||||
dataAddress = this.blockManager.NewGetElementPtr (
|
||||
irSourceType,
|
||||
source,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
if end == nil {
|
||||
endAddr := this.blockManager.NewGetElementPtr (
|
||||
irSourceType,
|
||||
source,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 2))
|
||||
end = this.blockManager.NewLoad(sizeType, endAddr)
|
||||
}
|
||||
|
||||
case *entity.TypeArray:
|
||||
dataAddress, err = this.generateExpressionLoc(slice.Slice)
|
||||
if err != nil { return nil, err }
|
||||
if end == nil {
|
||||
end = llvm.NewConstInt(sizeType, int64(sourceType.Length))
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: generator can't slice expression ",
|
||||
slice.Slice))
|
||||
}
|
||||
|
||||
|
||||
destinationType, err := this.generateType(slice.Type())
|
||||
if err != nil { return nil, err }
|
||||
newSlice := this.blockManager.newAllocaFront(destinationType)
|
||||
|
||||
// create new slice descriptor
|
||||
dataAddressFieldAddress := this.blockManager.NewGetElementPtr (
|
||||
destinationType,
|
||||
newSlice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
startFieldAddress := this.blockManager.NewGetElementPtr (
|
||||
destinationType,
|
||||
newSlice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 1))
|
||||
endFieldAddress := this.blockManager.NewGetElementPtr (
|
||||
destinationType,
|
||||
newSlice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 2))
|
||||
this.blockManager.NewStore(dataAddressFieldAddress, dataAddress)
|
||||
this.blockManager.NewStore(startFieldAddress, start)
|
||||
this.blockManager.NewStore(endFieldAddress, end)
|
||||
|
||||
return newSlice, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateSubscriptLoc (subscript *entity.Subscript) (llvm.Value, error) {
|
||||
var sizeType *llvm.TypeInt; {
|
||||
ty, err := this.typedef("Index")
|
||||
if err != nil { return nil, err }
|
||||
sizeType = ty.(*llvm.TypeInt)
|
||||
}
|
||||
|
||||
source, err := this.generateExpressionLoc(subscript.Slice)
|
||||
if err != nil { return nil, err }
|
||||
offset, err := this.generateExpression(subscript.Offset)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
var elementType entity.Type
|
||||
var dataAddress llvm.Value
|
||||
|
||||
sourceType := analyzer.ReduceToBase(subscript.Type())
|
||||
switch sourceType := sourceType.(type) {
|
||||
case *entity.TypeSlice:
|
||||
irSourceType, err := this.generateType(sourceType)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// add index to slice start
|
||||
startFieldAddress := this.blockManager.NewGetElementPtr (
|
||||
irSourceType,
|
||||
source,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 1))
|
||||
start := this.blockManager.NewLoad(sizeType, startFieldAddress)
|
||||
offset = this.blockManager.NewAdd(start, offset)
|
||||
|
||||
// get slice element
|
||||
elementType = sourceType.Element
|
||||
dataAddress = this.blockManager.NewGetElementPtr (
|
||||
irSourceType,
|
||||
source,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
|
||||
case *entity.TypeArray:
|
||||
elementType = sourceType.Element
|
||||
dataAddress = source
|
||||
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: generator can't subscript expression ",
|
||||
subscript.Slice))
|
||||
}
|
||||
|
||||
irElementType, err := this.generateType(elementType)
|
||||
if err != nil { return nil, err }
|
||||
return this.blockManager.NewGetElementPtr (
|
||||
irElementType,
|
||||
dataAddress,
|
||||
offset), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateDereferenceLoc (dereference *entity.Dereference) (llvm.Value, error) {
|
||||
return this.generateExpression(dereference.Pointer)
|
||||
}
|
||||
|
||||
func (this *generator) generateMemberAccessLoc (access *entity.MemberAccess) (llvm.Value, error) {
|
||||
source, err := this.generateExpressionLoc(access.Source)
|
||||
if err != nil { return nil, err }
|
||||
sourceType := analyzer.ReduceToBase(access.Source.Type()).(*entity.TypeStruct)
|
||||
irSourceType, err := this.generateType(sourceType)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
offset := -1
|
||||
for index, name := range sourceType.MemberOrder {
|
||||
if name == access.Member {
|
||||
offset = index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return this.blockManager.NewGetElementPtr (
|
||||
irSourceType,
|
||||
source,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(offset))), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateBlockLoc (block *entity.Block) (llvm.Value, error) {
|
||||
var last llvm.Value
|
||||
var err error
|
||||
for index, step := range block.Steps {
|
||||
// prefer generating a normal statement to avoid allocations
|
||||
if index == len(block.Steps) - 1 {
|
||||
last, err = this.generateStatementLoc(step)
|
||||
} else {
|
||||
last, err = this.generateStatement(step)
|
||||
}
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
return last, nil
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ func testReader (test *testing.T, correct string, inputs ...io.Reader) {
|
||||
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)
|
||||
test.Error("parser returned error:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -24,19 +24,19 @@ func testReader (test *testing.T, correct string, inputs ...io.Reader) {
|
||||
tree := analyzer.Tree { }
|
||||
err := tree.Analyze(ast)
|
||||
if err != nil {
|
||||
test.Error("analyzer returned error: ", err)
|
||||
test.Error("analyzer returned error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
module, err := NativeTarget().Generate(tree)
|
||||
if err != nil {
|
||||
test.Error("generator returned error: ", err)
|
||||
test.Error("generator returned error:", err)
|
||||
return
|
||||
}
|
||||
output := new(strings.Builder)
|
||||
_, err = module.WriteTo(output)
|
||||
if err != nil {
|
||||
test.Error("generator returned error: ", err)
|
||||
test.Error("generator returned error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -5,23 +5,6 @@ import "errors"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
// type pointer struct { TypeName string }
|
||||
// func (this *pointer) Name () string { return this.TypeName }
|
||||
// func (this *pointer) SetName (name string) { this.TypeName = name }
|
||||
// func (*pointer) LLString () string { return "ptr" }
|
||||
// func (this *pointer) String () string {
|
||||
// if this.TypeName == "" {
|
||||
// return this.LLString()
|
||||
// } else {
|
||||
// return "%" + this.TypeName
|
||||
// }
|
||||
// }
|
||||
// func (*pointer) Equal (ty llvm.Type) bool {
|
||||
// _, isGenericPointer := ty.(*pointer)
|
||||
// _, isTypedPointer := ty.(*types.PointerType)
|
||||
// return isGenericPointer || isTypedPointer
|
||||
// }
|
||||
|
||||
func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) {
|
||||
return this.typedef(ty.Name)
|
||||
}
|
||||
@ -31,12 +14,13 @@ func (this *generator) generateTypePointer (ty *entity.TypePointer) (llvm.Type,
|
||||
}
|
||||
|
||||
func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (llvm.Type, error) {
|
||||
indexType, err := this.typedef("Index")
|
||||
if err != nil { return nil, err }
|
||||
return &llvm.TypeStruct {
|
||||
Fields: []llvm.Type {
|
||||
/* data */ llvm.Pointer,
|
||||
/* len */ &llvm.TypeInt {
|
||||
BitSize: this.target.WordSize,
|
||||
},
|
||||
/* data */ llvm.Pointer,
|
||||
/* start */ indexType,
|
||||
/* end */ indexType,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -4,18 +4,20 @@ 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 { ptr, i64 }
|
||||
`%Index = type i64
|
||||
%String = type { ptr, %Index, %Index }
|
||||
%Pegasus = type { ptr, ptr, ptr, ptr, ptr }
|
||||
%Point = type { i64, i64 }
|
||||
%Quadrangle = type [4 x %Point]
|
||||
%Rectangle = type { %Point, %Point }
|
||||
%String = type { ptr, i64 }
|
||||
%Bool = type i8
|
||||
%Byte = type i8
|
||||
%Rune = type i32
|
||||
%AllInts = type { %Bool, %Byte, %Index, %Rune, i64, i64, i8, i16, i32, i64, i8, i16, i32, i64 }
|
||||
%AllFloats = type { float, double }
|
||||
%Path = type { ptr, %Index, %Index }
|
||||
%Quadrangle = type [4 x %Point]
|
||||
%AllTypes = type { %String, %Pegasus, %Rectangle, %AllInts, %AllFloats, %Path, %Quadrangle }
|
||||
declare %AllTypes @x()
|
||||
`,
|
||||
`
|
||||
Point: (x:Int y:Int)
|
||||
@ -47,5 +49,15 @@ AllFloats: (
|
||||
f64:F64)
|
||||
Path: *:Point
|
||||
Quadrangle: 4:Point
|
||||
AllTypes: (
|
||||
string:String
|
||||
pegasus:Pegasus
|
||||
rectangle:Rectangle
|
||||
ints:AllInts
|
||||
floats:AllFloats
|
||||
path:Path
|
||||
quadrangle:Quadrangle
|
||||
)
|
||||
[x]:AllTypes
|
||||
`)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user