186 lines
6.0 KiB
Go
186 lines
6.0 KiB
Go
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) generateVariable (variable *entity.Variable) (llvm.Value, error) {
|
|
value := this.blockManager.variable(variable.Declaration)
|
|
irType, err := this.generateType(variable.Type())
|
|
if err != nil { return nil, err }
|
|
return this.blockManager.NewLoad(irType, value), nil
|
|
}
|
|
|
|
func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) {
|
|
function, err := this.function(call.Name)
|
|
if err != nil { return nil, err }
|
|
|
|
args := make([]llvm.Value, len(call.Arguments))
|
|
for index, argument := range call.Arguments {
|
|
irArgument, err := this.generateExpression(argument)
|
|
if err != nil { return nil, err }
|
|
args[index] = irArgument
|
|
}
|
|
return this.blockManager.NewCall(function, function.Signature, args...), nil
|
|
}
|
|
|
|
func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value, error) {
|
|
source, err := this.generateExpression(call.Source)
|
|
if err != nil { return nil, err }
|
|
|
|
// build list of args
|
|
args := make([]llvm.Value, len(call.Arguments) + 1)
|
|
for index, argument := range call.Arguments {
|
|
irArgument, err := this.generateExpression(argument)
|
|
if err != nil { return nil, err }
|
|
args[index + 1] = irArgument
|
|
}
|
|
|
|
// check for methods on named type
|
|
if sourceType, ok := call.Source.Type().(*entity.TypeNamed); ok {
|
|
method, err := this.method(sourceType.Name, call.Name)
|
|
if err != nil { return nil, err }
|
|
|
|
args[0] = source
|
|
return this.blockManager.NewCall(method, method.Signature, args...), nil
|
|
}
|
|
|
|
// 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)
|
|
if err != nil { return nil, err }
|
|
|
|
args[0] = this.blockManager.NewLoad(source.Type(), source)
|
|
return this.blockManager.NewCall(method, method.Signature, args...), nil
|
|
}
|
|
}
|
|
|
|
// check for interface behaviors
|
|
if sourceType := getInterface(call.Source.Type()); sourceType != nil {
|
|
// find offset of the behavior's function pointer
|
|
offset := -1
|
|
for index, name := range sourceType.BehaviorOrder {
|
|
if name == call.Name {
|
|
offset = index
|
|
break
|
|
}
|
|
}
|
|
|
|
if offset > -1 {
|
|
// get the value pointer
|
|
irValue := this.blockManager.NewGetElementPtr (
|
|
source.Type(), source,
|
|
llvm.NewConstInt(llvm.I32, 0),
|
|
llvm.NewConstInt(llvm.I32, 0))
|
|
// get the behavior pointer
|
|
irBehavior := this.blockManager.NewGetElementPtr (
|
|
source.Type(), source,
|
|
llvm.NewConstInt(llvm.I32, 0),
|
|
llvm.NewConstInt(llvm.I32, int64(offset + 1)))
|
|
// get the behavior's signature
|
|
signature, err := this.generateTypeFunction (
|
|
sourceType.BehaviorMap[call.Name])
|
|
if err != nil { return nil, err }
|
|
|
|
args[0] = this.blockManager.NewLoad(irValue.Type(), irValue)
|
|
return this.blockManager.NewCall (
|
|
irBehavior,
|
|
signature.(*llvm.TypeFunction),
|
|
args...), nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New(fmt.Sprintln("no method", call.Name, "for", call.Source))
|
|
}
|
|
|
|
func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
|
|
var last llvm.Value
|
|
var err error
|
|
for _, step := range block.Steps {
|
|
last, err = this.generateStatement(step)
|
|
if err != nil { return nil, err }
|
|
}
|
|
return last, nil
|
|
}
|
|
|
|
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) 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)
|
|
}
|
|
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
|
|
|
|
default:
|
|
return nil, errors.New(fmt.Sprintln("array can't be used as ", ty))
|
|
}
|
|
}
|
|
|
|
func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) {
|
|
return llvm.NewConstBool(bool(*literal.Value)), nil
|
|
}
|