Merge pull request 'generator-multi-unit-tests' (#51) from generator-multi-unit-tests into main

Reviewed-on: #51
This commit is contained in:
Sasha Koshka 2024-03-01 05:00:07 +00:00
commit 8e3b21f807
6 changed files with 212 additions and 9 deletions

View File

@ -124,7 +124,7 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
destTypeBase, irDestLoc,
irDestType, name)
key := entity.Key {
Unit: sourceType.Unit(),
Unit: sourceType.Type.Unit(),
Name: sourceType.Name,
Method: name,
}

View File

@ -86,3 +86,44 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
}
`)
}
// TODO: Perhaps run all generator output in these tests through llc to check the
// integrity of the IR.
// TODO: uncomment and complete once bitcasting is fully supported
// func TestBitCastPointer (test *testing.T) {
// testString (test,
// `
// `,
// `
// Struct: (. x:Int y:Int)
// Array: 4:Int
// [main] = {
// ptr:*Byte
// stc:Struct
// arr:Array
// str:String
// flt:F64
// idx:Index
//
// ; struct
// ptr = [~~*Byte [~~Struct ptr]]
// stc = [~~Struct [~~*Byte stc]]
// ; array
// ptr = [~~*Byte [~~Array ptr]]
// arr = [~~Array [~~*Byte arr]]
// ; slice
// ptr = [~~*Byte [~~String ptr]]
// str = [~~String [~~*Byte str]]
// ; int
// ptr = [~~*Byte [~~Index ptr]]
// idx = [~~Index [~~*Byte idx]]
// ; float
// ptr = [~~*Byte [~~F64 ptr]]
// flt = [~~F64 [~~*Byte flt]]
//
// ; arithmetic
// ptr = [~~*Byte [+ 1 [~~Index ptr]]]
// }
// `)
// }

View File

@ -44,11 +44,12 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
// check for methods on named type
if sourceType, ok := call.Source.Type().(*entity.TypeNamed); ok {
method, err := this.method(entity.Key {
Unit: sourceType.Unit(),
methodKey := entity.Key {
Unit: sourceType.Type.Unit(),
Name: sourceType.Name,
Method: call.Name,
})
}
method, err := this.method(methodKey)
if _, notFound := err.(errNotFound); !notFound {
source, err := this.generateExpressionLoc(call.Source)
if err != nil { return nil, err }
@ -263,18 +264,52 @@ func (this *generator) generateBitCastVal (cast *entity.BitCast) (llvm.Value, er
irToType, err := this.generateType(cast.Type())
if err != nil { return nil, err }
// Attempt to short circuit if types are equal to avoid LLVM bitching at
// us
// attempt to short circuit if types are equal, or else LLVM complains
if llvm.TypesEqual(irFromType, irToType) {
return this.generateExpressionVal(cast.Value)
}
from, err := this.generateExpressionVal(cast.Value)
if err != nil { return nil, err }
// determine how to *bit cast*. because of course its not that simple.
// thanks LLVM!
fromType := analyzer.ReduceToBase(cast.Value.Type())
toType := analyzer.ReduceToBase(cast.Type())
switch toType.(type) {
case *entity.TypeInt, *entity.TypeWord:
switch fromType.(type) {
case *entity.TypePointer:
// cast from pointer to int
return this.blockManager.NewPtrToInt(from, irToType), nil
}
case *entity.TypePointer:
switch fromType.(type) {
case *entity.TypeInt, *entity.TypeWord:
// cast from int to pointer
return this.blockManager.NewIntToPtr(from, irToType), nil
default:
// cast from something else to pointer
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
temporaryInteger := this.blockManager.NewBitCast(from, irIndexType)
return this.blockManager.NewIntToPtr(temporaryInteger, irToType), nil
}
default:
switch fromType.(type) {
case *entity.TypePointer:
// cast from pointer to something else
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
temporaryInteger := this.blockManager.NewPtrToInt(from, irIndexType)
return this.blockManager.NewBitCast(temporaryInteger, irToType), nil
}
}
// default to a normal bit cast
return this.blockManager.NewBitCast(from, irToType), nil
}
func (this *generator) generateOperationVal (operation *entity.Operation) (llvm.Value, error) {
nSameType := func (binary func (x, y llvm.Value) llvm.Value) (llvm.Value, error) {
irX, err := this.generateExpressionVal(operation.Arguments[0])

View File

@ -86,7 +86,7 @@ func (this *generator) method (key entity.Key) (*llvm.Function, error) {
ty, exists := this.tree.Types[key.StripMethod()]
if !exists {
return nil, errNotFound("method " + key.String())
return nil, errNotFound("owner of method " + key.String())
}
if method, exists := ty.Methods[key.Method]; exists {

125
generator/multiunit_test.go Normal file
View File

@ -0,0 +1,125 @@
package generator
import "testing"
func TestUnitTwo (test *testing.T) {
testUnits (test,
`%"E80Pxr3rNdulEDOmHs9Hsg==::X" = type i64
define %"E80Pxr3rNdulEDOmHs9Hsg==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
0:
ret i64 5
}
`,
`[main]:something::X = 5`,
"something.fspl",
`+ X:Int`,
)}
func TestUnitAssignRestricted (test *testing.T) {
testUnits (test,
`%"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt" = type i64
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
0:
%1 = alloca %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt"
%2 = alloca %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt"
%3 = load %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt", ptr %2
store %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt" %3, ptr %1
ret void
}
`,
`[main] = {
x:other::RestrictedInt
y:other::RestrictedInt
x = y
}`,
"other.fspl",
`~ RestrictedInt:Int`,
)}
func TestNestedUnitTypedef (test *testing.T) {
testUnits (test,
`%"kOsrvvRIOh2nYGTmlfGyKQ==::X" = type i64
%"qnTuCId6O/ihTdVz5Ln6WQ==::X" = type %"kOsrvvRIOh2nYGTmlfGyKQ==::X"
define %"qnTuCId6O/ihTdVz5Ln6WQ==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
0:
ret i64 5
}
`,
`[main]:layer1::X = 5`,
"layer0.fspl",
`+ X:Int`,
"layer1.fspl",
`+ X:layer0::X`,
)}
func TestUnitInterfaceSatisfaction (test *testing.T) {
testUnits (test,
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
%"qsUY87+PPGG/YPYMcnGU/g==::FileDescriptor" = type i64
%"1cqXRNyHN06gi9QQb4GCsg==::File" = type %"qsUY87+PPGG/YPYMcnGU/g==::FileDescriptor"
%"1cqXRNyHN06gi9QQb4GCsg==::Writer" = type { ptr, ptr }
%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(%"qsUY87+PPGG/YPYMcnGU/g==::FileDescriptor" %file, ptr %buffer, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %count)
define i32 @main() {
0:
%1 = alloca %"1cqXRNyHN06gi9QQb4GCsg==::File"
store i64 1, ptr %1
%2 = alloca %"1cqXRNyHN06gi9QQb4GCsg==::Writer"
%3 = getelementptr %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %2, i32 0, i32 0
store ptr %1, ptr %3
%4 = getelementptr %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %2, i32 0, i32 1
store ptr @"1cqXRNyHN06gi9QQb4GCsg==::File.write", ptr %4
%5 = load %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %2
call void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %5)
ret i32 0
}
define void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %writer) {
0:
%1 = alloca %"1cqXRNyHN06gi9QQb4GCsg==::Writer"
store %"1cqXRNyHN06gi9QQb4GCsg==::Writer" %writer, ptr %1
%2 = alloca { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
%3 = alloca [3 x %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"]
%4 = getelementptr [3 x %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"], ptr %3, i32 0, i32 0
store i8 104, ptr %4
%5 = getelementptr [3 x %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"], ptr %3, i32 0, i32 1
store i8 105, ptr %5
%6 = getelementptr [3 x %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"], ptr %3, i32 0, i32 2
store i8 10, ptr %6
%7 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %2, i32 0, i32 0
store ptr %3, ptr %7
%8 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %2, i32 0, i32 1
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %8
%9 = load { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %2
%10 = getelementptr %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %1, i32 0, i32 1
%11 = getelementptr %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %1, i32 0, i32 0
%12 = load ptr, ptr %11
%13 = load ptr, ptr %10
%14 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %13(ptr %12, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %9)
ret void
}
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"1cqXRNyHN06gi9QQb4GCsg==::File.write"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer)
`,
`[sayHello writer:io::Writer] = writer.[write 'hi\n']
[main]: I32 'main' = {
stdout:io::File = 1
[sayHello stdout]
0
}`,
"cstdio.fspl",
`+ FileDescriptor: Int
+ [write file:FileDescriptor buffer:*Byte count:Index]: Index 'write'`,
"io.fspl",
`+ Writer: (~ [write buffer:*:Byte]: Index)
+ File: cstdio::FileDescriptor
+ File.[write buffer:*:Byte]:Index =
cstdio::[write
[~cstdio::FileDescriptor [.this]]
[~*Byte buffer] [#buffer]]`,
)}

View File

@ -18,7 +18,7 @@ func testString (test *testing.T, correct string, input string) {
tree := analyzer.Tree { }
err := tree.Analyze(address.UUID(), nil, ast)
if err != nil {
test.Error("analyzer returned error:", err)
test.Error("analyzer returned error:\n" + errors.Format(err))
return
}
@ -40,6 +40,7 @@ func testUnits (
// dependencies
for index := 0; index < len(dependencies) - 1; index += 2 {
address := entity.Address(dependencies[index])
test.Log("analyzing", address, "UUID", address.UUID())
source := dependencies[index + 1]
ast, ok := treeOf(test, address, source, true)
if !ok { return }
@ -61,6 +62,7 @@ func testUnits (
// main
address := entity.Address("main.fspl")
test.Log("analyzing MAIN", address, "UUID", address.UUID())
ast, ok := treeOf(test, address, main, false)
if !ok { return }
err := tree.Analyze(address.UUID(), nicknames, ast)