That should be literally every feature

This commit is contained in:
Sasha Koshka 2023-12-02 21:45:15 -05:00
parent ea502dc2bb
commit 3f88513dc1
6 changed files with 205 additions and 63 deletions

View File

@ -1,5 +1,7 @@
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"
@ -27,24 +29,93 @@ func (this *generator) generateAssignmentSource (
) {
source, err := this.generateExpression(from)
if err != nil { return nil, err }
ty := analyzer.ReduceToBase(to)
toBase := analyzer.ReduceToBase(to)
switch /*ty := */ty.(type) {
switch toBase := toBase.(type) {
// conversion from any type to interface
case *entity.TypeInterface:
// TODO: assign to interface
// 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.
// methods defined on interface or pointer types cannot pass
// through an interface!
// create destination interface
irToType, err := this.generateType(to)
if err != nil { return nil, err }
destination := this.blockManager.newAllocaFront(irToType)
toDataField := this.getInterfaceData(destination, irToType)
fromType := from.Type()
switch fromBase := analyzer.ReduceToBase(fromType).(type) {
// conversion from interface to interface
case *entity.TypeInterface:
// re-assign data
irFromType, err := this.generateType(fromType)
if err != nil { return nil, err }
fromDataField := this.getInterfaceData(source, irFromType)
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), fromDataField)
this.blockManager.NewStore(fromData, toDataField)
// re-assign behaviors
for _, name := range toBase.BehaviorOrder {
fromBehaviorField := this.getInterfaceBehavior (
fromBase, source,
irFromType, name)
toBehaviorField := this.getInterfaceBehavior (
toBase, source,
irToType, name)
fromBehavior := this.blockManager.NewLoad(new(llvm.TypePointer), fromBehaviorField)
this.blockManager.NewStore(fromBehavior, toBehaviorField)
}
// conversion from pointer to interface
case *entity.TypePointer:
// assign data
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), source)
this.blockManager.NewStore(fromData, toDataField)
fromReferenced, ok := fromBase.Referenced.(*entity.TypeNamed)
if !ok {
return nil, errors.New(fmt.Sprint(
"can't assign", fromBase, "to interface"))
}
// assign behaviors
for _, name := range toBase.BehaviorOrder {
toBehaviorField := this.getInterfaceBehavior (
toBase, source,
irToType, name)
fromBehavior, err := this.method(fromReferenced.Name, name)
if err != nil { return nil, err }
this.blockManager.NewStore(fromBehavior, toBehaviorField)
}
// conversion from other type to interface
default:
// assign data
this.blockManager.NewStore(source, toDataField)
fromType, ok := fromType.(*entity.TypeNamed)
if !ok {
return nil, errors.New(fmt.Sprint(
"can't assign", fromBase, "to interface"))
}
// assign behaviors
for _, name := range toBase.BehaviorOrder {
toBehaviorField := this.getInterfaceBehavior (
toBase, source,
irToType, name)
fromBehavior, err := this.method(fromType.Name, name)
if err != nil { return nil, err }
this.blockManager.NewStore(fromBehavior, toBehaviorField)
}
}
// conversion from array to slice
case *entity.TypeSlice:
/*if fromType, ok := analyzer.ReduceToBase(from.Type()).(*entity.TypeArray); ok {
// TODO: convert array to slice
}*/
switch fromBase := analyzer.ReduceToBase(from.Type()).(type) {
case *entity.TypeArray:
return this.generateSlice(to, source, 0, int64(fromBase.Length))
}
}
return source, nil
}

View File

@ -3,8 +3,8 @@ package generator
import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *generator) generateSlice (ty *entity.TypeSlice, data llvm.Value, start, end int64) (llvm.Value, error) {
irType, err := this.generateTypeSlice(ty)
func (this *generator) generateSlice (ty entity.Type, data llvm.Value, start, end int64) (llvm.Value, error) {
irType, err := this.generateType(ty)
if err != nil { return nil, err }
slice := this.blockManager.newAllocaFront(irType)
@ -31,3 +31,42 @@ func (this *generator) generateSlice (ty *entity.TypeSlice, data llvm.Value, sta
endField)
return slice, nil
}
func (this *generator) getInterfaceData (iface llvm.Value, irType llvm.Type) llvm.Value {
return this.blockManager.NewGetElementPtr (
irType, iface,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, 0))
}
func (this *generator) getInterfaceBehavior (
ty *entity.TypeInterface,
iface llvm.Value,
irType llvm.Type,
name string,
) llvm.Value {
offset := this.getInterfaceBehaviorIndex(ty, name)
if offset > -1 {
return this.blockManager.NewGetElementPtr (
irType, iface,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, int64(offset + 1)))
} else {
return nil
}
}
func (this *generator) getInterfaceBehaviorSignature (ty *entity.TypeInterface, name string) (llvm.Type, error) {
return this.generateTypeFunction(ty.BehaviorMap[name])
}
func (this *generator) getInterfaceBehaviorIndex (ty *entity.TypeInterface, name string) int {
offset := -1
for index, nm := range ty.BehaviorOrder {
if nm == name {
offset = index
break
}
}
return offset
}

View File

@ -70,29 +70,13 @@ func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value,
// 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])
irSourceType, err := this.generateType(sourceType)
if err != nil { return nil, err }
irBehavior := this.getInterfaceBehavior(sourceType, source, irSourceType, call.Name)
if irBehavior != nil {
irValue := this.getInterfaceData(source, irSourceType)
signature, err := this.getInterfaceBehaviorSignature(sourceType, call.Name)
if err != nil { return nil, err }
args[0] = this.blockManager.NewLoad(irValue.Type(), irValue)

View File

@ -38,3 +38,42 @@ func (this *generator) generateFunction (
}
return irFunc, nil
}
func (this *generator) generateMethod (
method *entity.Method,
) (
*llvm.Function,
error,
) {
params := make([]*llvm.Parameter, len(method.Signature.Arguments) + 1)
ret, err := this.generateType(method.Signature.Return)
if err != nil { return nil, err }
for index, argument := range method.Signature.Arguments {
paramType, err := this.generateType(argument.Type())
if err != nil { return nil, err }
params[index + 1] = llvm.NewParameter(argument.Name, paramType)
}
ownerType, err := this.typedef(method.TypeName)
if err != nil { return nil, err }
params[0] = llvm.NewParameter("this", ownerType)
irFunc := this.module.NewFunction(method.Signature.Name, ret, params...)
if method.Body != nil {
this.blockManager = this.pushBlockManager(irFunc)
defer this.popBlockManager()
this.blockManager.addDeclarationsInFunction(method)
body, err := this.generateExpression(method.Body)
if err != nil { return nil, err }
if method.Signature.Return == nil {
this.blockManager.NewRet(nil)
} else {
this.blockManager.NewRet(body)
}
}
return irFunc, nil
}

View File

@ -6,17 +6,28 @@ func TestFunction (test *testing.T) {
testString (test,
`
`,
`[puts string:*Byte]:I32
`
[puts string:*Byte]:I32
[read fd:Int buf:*Byte count:Index]:Index
[itoa value:Int buffer:String base:Int]:String = {
if [== base 0] then { base = 10 }
index:Index = 0
loop {
digit:Int = [% value base]
[.buffer index] = [~Byte [+ digit '0']]
value = [/ value base]
index = [++index]
}
buffer
}
[main argc:I32 argv:**Byte] = {
[puts 'allocing buffer']
buffer:*:Byte = bufferData:8:Byte
[puts 'reading into buffer']
[read 1 [~ *Byte buffer] 7]
[puts 'adding a null terminator at offset 7']
[. buffer 7] = 0
[puts 'echoing buffer']
[puts [~ *Byte buffer]]
[itoa 5 string:16:Byte 10]
[.string 15] = 0
[puts [.[@string]]]
}
`)
}

View File

@ -67,20 +67,18 @@ func (this *generator) method (typeName string, name string) (*llvm.Function, er
return function, nil
}
}
return nil, nil
// TODO uncomment when generateMethod is added
// ty, exists := this.tree.Types[typeName]
// if !exists {
// return nil,
// errors.New(fmt.Sprintln("type", typeName, "not found"))
// }
// function, exists := ty.Methods[name]
// if !exists {
// return nil, errors.New(fmt.Sprintln (
// "method", typeName + "." + name, "not found"))
// }
// // return this.generateMethod(function), nil
ty, exists := this.tree.Types[typeName]
if !exists {
return nil,
errors.New(fmt.Sprintln("type", typeName, "not found"))
}
method, exists := ty.Methods[name]
if !exists {
return nil, errors.New(fmt.Sprintln (
"method", typeName + "." + name, "not found"))
}
return this.generateMethod(method)
}
func (this *generator) function (name string) (*llvm.Function, error) {