fspl/generator/expression.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
}