Compare commits
40 Commits
main
...
implement-
Author | SHA1 | Date | |
---|---|---|---|
0c8a9d995b | |||
86ad94f17d | |||
9ffed9bd94 | |||
ec7bd32184 | |||
298b819b46 | |||
1d1908cd5d | |||
c13c4caacb | |||
cc5173dd52 | |||
f4480fe6b1 | |||
f05efb749d | |||
8248135255 | |||
e104da363c | |||
65af9d7f3c | |||
b5f5fa4598 | |||
33768c6d84 | |||
293662e474 | |||
334a45e06f | |||
373db2ae81 | |||
034d9661b3 | |||
7bad3d5672 | |||
3485fa1820 | |||
b822d57757 | |||
0a54232e29 | |||
94272bff03 | |||
6ba8b5465b | |||
0fd35343c1 | |||
d88a34d638 | |||
fc930fa3d4 | |||
40d500b705 | |||
6313992c10 | |||
e12e83921b | |||
2346c34cce | |||
d8f897d023 | |||
386c39126d | |||
7d3074ea2f | |||
d568d5efef | |||
7d8378f7d8 | |||
7ea24b8b31 | |||
172b2fc8db | |||
c7191ff678 |
@ -369,70 +369,29 @@ func (this *Tree) areStructurallyEquivalent (left, right entity.Type) bool {
|
||||
// isLocationExpression returns whether or not an expression is a valid location
|
||||
// expression.
|
||||
func (this *Tree) isLocationExpression (expression entity.Expression) error {
|
||||
// TODO: have some Name() method of Expression so we can use it in a
|
||||
// default case here. this will prevent crashes when new features are
|
||||
// added.
|
||||
cannot := func (pos errors.Position, kind string) error {
|
||||
return errors.Errorf(pos, "cannot assign to %s", kind)
|
||||
}
|
||||
switch expression := expression.(type) {
|
||||
case *entity.Variable:
|
||||
return nil
|
||||
case *entity.Declaration:
|
||||
return nil
|
||||
case *entity.Call:
|
||||
return cannot(expression.Position(), "function call")
|
||||
case *entity.MethodCall:
|
||||
return cannot(expression.Position(), "method call")
|
||||
case *entity.Subscript:
|
||||
return this.isLocationExpression(expression.Slice)
|
||||
case *entity.Slice:
|
||||
return cannot(expression.Position(), "slice operation")
|
||||
case *entity.Dereference:
|
||||
return this.isLocationExpression(expression.Pointer)
|
||||
case *entity.Reference:
|
||||
return cannot(expression.Position(), "reference operation")
|
||||
case *entity.ValueCast:
|
||||
return cannot(expression.Position(), "value cast")
|
||||
case *entity.BitCast:
|
||||
return cannot(expression.Position(), "bit cast")
|
||||
case *entity.Operation:
|
||||
return cannot(expression.Position(), fmt.Sprintf (
|
||||
"cannot assign to %v operation",
|
||||
expression.Operator))
|
||||
case *entity.Block:
|
||||
return cannot(expression.Position(), "block")
|
||||
case *entity.MemberAccess:
|
||||
return this.isLocationExpression (
|
||||
expression.Source)
|
||||
case *entity.IfElse:
|
||||
return cannot(expression.Position(), "if/else")
|
||||
case *entity.Match:
|
||||
return cannot(expression.Position(), "match")
|
||||
case *entity.Loop:
|
||||
return cannot(expression.Position(), "loop")
|
||||
case *entity.Break:
|
||||
return cannot(expression.Position(), "break statement")
|
||||
case *entity.Return:
|
||||
return cannot(expression.Position(), "return statement")
|
||||
case *entity.LiteralInt:
|
||||
return cannot(expression.Position(), "integer literal")
|
||||
case *entity.LiteralFloat:
|
||||
return cannot(expression.Position(), "float literal")
|
||||
case *entity.LiteralString:
|
||||
return cannot(expression.Position(), "string literal")
|
||||
case *entity.LiteralArray:
|
||||
return cannot(expression.Position(), "array literal")
|
||||
case *entity.LiteralStruct:
|
||||
return cannot(expression.Position(), "struct literal")
|
||||
case *entity.LiteralBoolean:
|
||||
return cannot(expression.Position(), "boolean literal")
|
||||
case *entity.LiteralNil:
|
||||
return cannot(expression.Position(), "nil literal")
|
||||
return this.isLocationExpression(expression.Source)
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: analyzer doesnt know about expression",
|
||||
expression))
|
||||
return errors.Errorf(expression.Position(), "cannot assign to %s", expression.Description())
|
||||
}
|
||||
}
|
||||
|
||||
// isConstant returns whether or not an expression can be evaluated at compile
|
||||
// time.
|
||||
func (this *Tree) isConstant (expression entity.Expression) error {
|
||||
if expression.IsConstant() {
|
||||
return nil
|
||||
} else {
|
||||
return errors.Errorf(expression.Position(), "can not use %s as constant", expression.Description())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,3 +321,15 @@ B:(& [g]:Int)
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestAssignmentErrConstant (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"cannot assign to constant", 5, 2,
|
||||
`
|
||||
T: Int
|
||||
| x = 0
|
||||
[main] = {
|
||||
T.x = 5
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
90
analyzer/constant_test.go
Normal file
90
analyzer/constant_test.go
Normal file
@ -0,0 +1,90 @@
|
||||
package analyzer
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestConstantValueSpecified (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
Weekday: Int
|
||||
| sunday = 1
|
||||
| monday = 2
|
||||
| tuesday = 3
|
||||
| wednesday = 4
|
||||
| thursday = 5
|
||||
| friday = 6
|
||||
| saturday = 7
|
||||
|
||||
[print s:String]
|
||||
[printWeekday w:Weekday] = [print switch w
|
||||
| Weekday.sunday 'sunday'
|
||||
| Weekday.monday 'monday'
|
||||
| Weekday.tuesday 'tuesday'
|
||||
| Weekday.wednesday 'wednesday'
|
||||
| Weekday.thursday 'thursday'
|
||||
| Weekday.friday 'friday'
|
||||
| Weekday.saturday 'saturday'
|
||||
* 'unknown']
|
||||
[f] = [printWeekday Weekday.monday]
|
||||
`)}
|
||||
|
||||
func TestConstantValueUnspecified (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
Weekday: Int
|
||||
| sunday
|
||||
| monday
|
||||
| tuesday
|
||||
| wednesday
|
||||
| thursday
|
||||
| friday
|
||||
| saturday
|
||||
|
||||
[print s:String]
|
||||
[printWeekday w:Weekday] = [print switch w
|
||||
| Weekday.sunday 'sunday'
|
||||
| Weekday.monday 'monday'
|
||||
| Weekday.tuesday 'tuesday'
|
||||
| Weekday.wednesday 'wednesday'
|
||||
| Weekday.thursday 'thursday'
|
||||
| Weekday.friday 'friday'
|
||||
| Weekday.saturday 'saturday'
|
||||
* 'unknown']
|
||||
[f] = [printWeekday Weekday.monday]
|
||||
`)}
|
||||
|
||||
func TestConstantString (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
Weekday: String
|
||||
| sunday = 'sunday'
|
||||
| monday = 'monday'
|
||||
| tuesday = 'tuesday'
|
||||
| wednesday = 'wednesday'
|
||||
| thursday = 'thursday'
|
||||
| friday = 'friday'
|
||||
| saturday = 'saturday'
|
||||
|
||||
[print s:String]
|
||||
[printWeekday w:Weekday] = [print [~String w]]
|
||||
[f] = [printWeekday Weekday.monday]
|
||||
`)}
|
||||
|
||||
// TODO: consider filling values for string constants by using the constant
|
||||
// name?
|
||||
func TestErrConstantStringUnspecified (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"cannot fill in constant value for non-numeric type Weekday", 3, 1,
|
||||
`
|
||||
Weekday: String
|
||||
| sunday
|
||||
| monday
|
||||
| tuesday
|
||||
| wednesday
|
||||
| thursday
|
||||
| friday
|
||||
| saturday
|
||||
|
||||
[print s:String]
|
||||
[printWeekday w:Weekday] = [print [~String w]]
|
||||
[f] = [printWeekday Weekday.monday]
|
||||
`)}
|
@ -144,7 +144,7 @@ testStringErr (test,
|
||||
|
||||
func TestSwitchErrNotConstant (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"y cannot represent a constant integer", 7, 4,
|
||||
"y is not constant", 7, 4,
|
||||
`
|
||||
[f x:Int]:Int = {
|
||||
y:Int = 1
|
||||
|
@ -14,6 +14,8 @@ func (this *Tree) analyzeExpression (
|
||||
switch expression := expression.(type) {
|
||||
case *entity.Assignment:
|
||||
return this.analyzeAssignment(expression)
|
||||
case *entity.Constant:
|
||||
return this.analyzeConstant(into, mode, expression)
|
||||
case *entity.Variable:
|
||||
return this.analyzeVariable(into, mode, expression)
|
||||
case *entity.Declaration:
|
||||
|
@ -1,6 +1,7 @@
|
||||
package analyzer
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/fspl/fspl/eval"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
import "git.tebibyte.media/fspl/fspl/parser/fspl"
|
||||
@ -33,6 +34,54 @@ func (this *Tree) analyzeAssignment (
|
||||
return assignment, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeConstant (
|
||||
into entity.Type,
|
||||
mode strictness,
|
||||
constant *entity.Constant,
|
||||
) (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
// get typedef
|
||||
unit, err := this.resolveNickname(constant.Position(), constant.UnitNickname)
|
||||
if err != nil { return nil, err }
|
||||
typedef, err := this.analyzeTypedef(constant.Position(), entity.Key {
|
||||
Unit: unit,
|
||||
Type: constant.TypeName,
|
||||
}, false) // TODO perhaps we should accept incomplete ones?
|
||||
if err != nil { return nil, err }
|
||||
constant.Unit = typedef.Unit()
|
||||
constant.Ty = &entity.TypeNamed {
|
||||
Pos: constant.Position(),
|
||||
UnitNickname: constant.UnitNickname,
|
||||
Name: typedef.Name,
|
||||
Type: typedef.Type,
|
||||
Acc: typedef.Access(),
|
||||
Unt: typedef.Unit(),
|
||||
}
|
||||
|
||||
// check access permissions
|
||||
if typedef.Acc == entity.AccessPrivate && typedef.Unit() != this.unit {
|
||||
return nil, errors.Errorf (
|
||||
constant.Position(), "type %v::%v is private",
|
||||
constant.UnitNickname, constant.TypeName)
|
||||
}
|
||||
|
||||
// get declaration
|
||||
declaration, ok := typedef.ConstantMap[constant.Name]
|
||||
if !ok {
|
||||
return nil, errors.Errorf (
|
||||
constant.Position(), "no constant %v",
|
||||
constant)
|
||||
}
|
||||
constant.Declaration = declaration
|
||||
|
||||
err = this.canAssign(constant.Position(), into, mode, constant.Type())
|
||||
if err != nil { return nil, err }
|
||||
|
||||
return constant, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeVariable (
|
||||
into entity.Type,
|
||||
mode strictness,
|
||||
@ -102,8 +151,8 @@ func (this *Tree) analyzeCall (
|
||||
unit, err := this.resolveNickname(call.Position(), call.UnitNickname)
|
||||
if err != nil { return nil, err }
|
||||
function, err := this.analyzeFunction(call.Position(), entity.Key {
|
||||
Unit: unit,
|
||||
Name: call.Name,
|
||||
Unit: unit,
|
||||
Function: call.Name,
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
call.Function = function
|
||||
@ -363,6 +412,7 @@ func (this *Tree) analyzeReference (
|
||||
if err != nil { return nil, err }
|
||||
err = this.isLocationExpression(reference.Value)
|
||||
if err != nil { return nil, err }
|
||||
// TODO: only allow constants to be referenced as immutable data
|
||||
reference.Value = value
|
||||
reference.Ty = referenced.Referenced
|
||||
|
||||
@ -848,6 +898,9 @@ func (this *Tree) analyzeSwitch (
|
||||
if err != nil { this.popScope(); return nil, err }
|
||||
cas.Expression = expression
|
||||
this.popScope()
|
||||
|
||||
key, err = eval.EvaluateConstant(key)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
var keyInt int64
|
||||
switch key := key.(type) {
|
||||
@ -1015,13 +1068,15 @@ func (this *Tree) analyzeReturn (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
ret.Declaration, _ = this.topDeclaration()
|
||||
declaration, _ := this.topDeclaration()
|
||||
var ty entity.Type
|
||||
switch ret.Declaration.(type) {
|
||||
switch declaration := declaration.(type) {
|
||||
case *entity.Function:
|
||||
ty = ret.Declaration.(*entity.Function).Signature.Return
|
||||
ret.Declaration = declaration
|
||||
ty = declaration.Signature.Return
|
||||
case *entity.Method:
|
||||
ty = ret.Declaration.(*entity.Method).Signature.Return
|
||||
ret.Declaration = declaration
|
||||
ty = declaration.Signature.Return
|
||||
}
|
||||
|
||||
if ty != nil && ret.Value == nil {
|
||||
|
@ -20,7 +20,7 @@ func (this *Tree) analyzeFunction (
|
||||
// error if function is missing
|
||||
function, exists := this.rawFunctions[key]
|
||||
if !exists {
|
||||
return nil, errors.Errorf(pos, "no function named %s", key.Name)
|
||||
return nil, errors.Errorf(pos, "no function named %s", key.Function)
|
||||
}
|
||||
|
||||
// set unit
|
||||
|
@ -14,12 +14,12 @@ func (this *Tree) analyzeMethod (
|
||||
error,
|
||||
) {
|
||||
// get parent typedef
|
||||
typeKey := key.StripMethod()
|
||||
typeKey := key.OnlyType()
|
||||
owner, err := this.analyzeTypedef(pos, typeKey, false)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// return if exists already
|
||||
if method, exists := owner.Methods[key.Method]; exists {
|
||||
if method, exists := owner.Methods[key.Function]; exists {
|
||||
return method, nil
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ func (this *Tree) analyzeMethod (
|
||||
Referenced: &entity.TypeNamed {
|
||||
Pos: method.Position(),
|
||||
Unt: key.Unit,
|
||||
Name: key.Name,
|
||||
Name: key.Type,
|
||||
Type: owner.Type,
|
||||
},
|
||||
},
|
||||
@ -75,7 +75,7 @@ func (this *Tree) analyzeMethod (
|
||||
// add incomplete method to complete methods because there is enough
|
||||
// information for it to be complete from the point of view of other
|
||||
// parts of the code
|
||||
owner.Methods[key.Method] = method
|
||||
owner.Methods[key.Function] = method
|
||||
|
||||
// analyze method body
|
||||
if method.Body != nil {
|
||||
@ -93,10 +93,10 @@ func (this *Tree) methodExists (pos errors.Position, key entity.Key) (bool, erro
|
||||
if existsInRaw { return true, nil }
|
||||
|
||||
// check parent typedef
|
||||
typeKey := key.StripMethod()
|
||||
typeKey := key.OnlyType()
|
||||
owner, err := this.analyzeTypedef(pos, typeKey, false)
|
||||
if err != nil { return false, err }
|
||||
_, existsInType := owner.Methods[key.Method]
|
||||
_, existsInType := owner.Methods[key.Function]
|
||||
return existsInType, nil
|
||||
}
|
||||
|
||||
@ -126,9 +126,9 @@ func (this *Tree) analyzeMethodOrBehaviorInternal (
|
||||
case *entity.TypeNamed:
|
||||
ty := ty.(*entity.TypeNamed)
|
||||
key := entity.Key {
|
||||
Unit: ty.Type.Unit(),
|
||||
Name: ty.Name,
|
||||
Method: name,
|
||||
Unit: ty.Type.Unit(),
|
||||
Type: ty.Name,
|
||||
Function: name,
|
||||
}
|
||||
exists, err := this.methodExists(pos, key)
|
||||
if err != nil { return nil, err }
|
||||
|
@ -203,3 +203,36 @@ Type: (. world:Int)
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestConstantNameErrMissing (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"no constant Weekday.palday", 13, 21,
|
||||
`
|
||||
Weekday: String
|
||||
| sunday = 'sunday'
|
||||
| monday = 'monday'
|
||||
| tuesday = 'tuesday'
|
||||
| wednesday = 'wednesday'
|
||||
| thursday = 'thursday'
|
||||
| friday = 'friday'
|
||||
| saturday = 'saturday'
|
||||
|
||||
[print s:String]
|
||||
[printWeekday w:Weekday] = [print [~String w]]
|
||||
[f] = [printWeekday Weekday.palday]
|
||||
`)}
|
||||
|
||||
func TestConstantUniqueErr (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"tuesday already defined at stream0.fspl:5:1", 8, 1,
|
||||
`
|
||||
Weekday: String
|
||||
| sunday = 'sunday'
|
||||
| monday = 'monday'
|
||||
| tuesday = 'tuesday'
|
||||
| wednesday = 'wednesday'
|
||||
| thursday = 'thursday'
|
||||
| tuesday = 'tuesday'
|
||||
| friday = 'friday'
|
||||
| saturday = 'saturday'
|
||||
`)}
|
||||
|
@ -7,7 +7,7 @@ import "git.tebibyte.media/fspl/fspl/entity"
|
||||
// entity causes the analysis of another.
|
||||
type scopeContextManager []scopeContext
|
||||
|
||||
func (this *scopeContextManager) pushScopeContext (declaration entity.TopLevel) {
|
||||
func (this *scopeContextManager) pushScopeContext (declaration any) {
|
||||
*this = append(*this, scopeContext { declaration: declaration })
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func (this *scopeContextManager) topLoop () (entity.Breakable, bool) {
|
||||
return (*this)[len(*this) - 1].topLoop()
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) topDeclaration () (entity.TopLevel, bool) {
|
||||
func (this *scopeContextManager) topDeclaration () (any, bool) {
|
||||
if len(*this) < 1 { return nil, false }
|
||||
return (*this)[len(*this) - 1].declaration, true
|
||||
}
|
||||
@ -72,7 +72,7 @@ func (this *scopeContextManager) assertPopulated () {
|
||||
type scopeContext struct {
|
||||
scopes []entity.Scoped
|
||||
loops []entity.Breakable
|
||||
declaration entity.TopLevel
|
||||
declaration any // TODO rename to "origin" or smth
|
||||
}
|
||||
|
||||
func (this *scopeContext) pushLoop (loop entity.Breakable) {
|
||||
|
@ -6,6 +6,7 @@ import "github.com/google/uuid"
|
||||
import "git.tebibyte.media/fspl/fspl/lexer"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
import "git.tebibyte.media/fspl/fspl/testcommon"
|
||||
import "git.tebibyte.media/fspl/fspl/parser/fspl"
|
||||
|
||||
func testStringErr (
|
||||
@ -25,7 +26,7 @@ func testStringErr (
|
||||
test.Error("analyzer did not return error")
|
||||
return
|
||||
}
|
||||
compareErr(test, address, string(address), errMessage, errRow, errStart, err)
|
||||
testcommon.CompareErr(test, string(address), errMessage, errRow, errStart, err)
|
||||
}
|
||||
|
||||
func testString (test *testing.T, input string) {
|
||||
@ -110,7 +111,7 @@ func testUnitsErr (
|
||||
|
||||
err := tree.Analyze(address.UUID(), nicknames, ast)
|
||||
if err != nil {
|
||||
compareErr(test, address, errFile, errMessage, errRow, errStart, err)
|
||||
testcommon.CompareErr(test, errFile, errMessage, errRow, errStart, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -129,7 +130,7 @@ func testUnitsErr (
|
||||
if !ok { return }
|
||||
err := tree.Analyze(address.UUID(), nicknames, ast)
|
||||
if err != nil {
|
||||
compareErr(test, address, errFile, errMessage, errRow, errStart, err)
|
||||
testcommon.CompareErr(test, errFile, errMessage, errRow, errStart, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -157,43 +158,3 @@ func treeOf (test *testing.T, address entity.Address, input string, skim bool) (
|
||||
|
||||
return ast, true
|
||||
}
|
||||
|
||||
func compareErr (
|
||||
test *testing.T,
|
||||
address entity.Address,
|
||||
errFile string,
|
||||
errMessage string,
|
||||
errRow int,
|
||||
errStart int,
|
||||
err error,
|
||||
) bool {
|
||||
got := err.(errors.Error)
|
||||
gotMessage := got.Error()
|
||||
gotFile := got.Position().File
|
||||
gotRow := got.Position().Row + 1
|
||||
gotStart := got.Position().Start + 1
|
||||
|
||||
correct :=
|
||||
gotFile == errFile &&
|
||||
gotMessage == errMessage &&
|
||||
gotRow == errRow &&
|
||||
gotStart == errStart
|
||||
if correct { return true }
|
||||
|
||||
test.Log("errors do not match:")
|
||||
if gotMessage != errMessage {
|
||||
test.Log("- messages do not match:")
|
||||
test.Logf(" [%s]", gotMessage)
|
||||
test.Logf(" [%s]", errMessage)
|
||||
}
|
||||
if gotRow != errRow {
|
||||
test.Logf("- rows do not match: (%d, %d)", gotRow, errRow)
|
||||
}
|
||||
if gotStart != errStart {
|
||||
test.Logf("- columns do not match: (%d, %d)", gotStart, errStart)
|
||||
}
|
||||
test.Log("got:\n" + errors.Format(got))
|
||||
test.Logf("correct:\n%v:%v: %v", errRow, errStart, errMessage)
|
||||
test.Fail()
|
||||
return false
|
||||
}
|
||||
|
@ -39,13 +39,18 @@ func (this *Tree) Analyze (
|
||||
return this.analyzeDeclarations()
|
||||
}
|
||||
|
||||
// Unit returns the root unit in this tree.
|
||||
func (this *Tree) Unit () uuid.UUID {
|
||||
return this.unit
|
||||
}
|
||||
|
||||
func (this *Tree) assembleRawMaps () error {
|
||||
for _, declaration := range this.ast.Declarations {
|
||||
switch declaration := declaration.(type) {
|
||||
case *entity.Typedef:
|
||||
key := entity.Key {
|
||||
Unit: this.unit,
|
||||
Name: declaration.Name,
|
||||
Type: declaration.Name,
|
||||
}
|
||||
err := this.topLevelNameAvailable(declaration.Position(), key)
|
||||
if err != nil { return err }
|
||||
@ -54,8 +59,8 @@ func (this *Tree) assembleRawMaps () error {
|
||||
|
||||
case *entity.Function:
|
||||
key := entity.Key {
|
||||
Unit: this.unit,
|
||||
Name: declaration.Signature.Name,
|
||||
Unit: this.unit,
|
||||
Function: declaration.Signature.Name,
|
||||
}
|
||||
err := this.topLevelNameAvailable(declaration.Position(), key)
|
||||
if err != nil { return err }
|
||||
@ -63,9 +68,9 @@ func (this *Tree) assembleRawMaps () error {
|
||||
|
||||
case *entity.Method:
|
||||
key := entity.Key {
|
||||
Unit: this.unit,
|
||||
Name: declaration.TypeName,
|
||||
Method: declaration.Signature.Name,
|
||||
Unit: this.unit,
|
||||
Type: declaration.TypeName,
|
||||
Function: declaration.Signature.Name,
|
||||
}
|
||||
err := this.topLevelNameAvailable(declaration.Position(), key)
|
||||
if err != nil { return err }
|
||||
@ -76,35 +81,35 @@ func (this *Tree) assembleRawMaps () error {
|
||||
}
|
||||
|
||||
func (this *Tree) topLevelNameAvailable (currentPos errors.Position, key entity.Key) error {
|
||||
if fsplParser.IsReserved(key.Name) {
|
||||
if fsplParser.IsReserved(key.Type) {
|
||||
return errors.Errorf (
|
||||
currentPos, "cannot shadow reserved identifier %s",
|
||||
key.Name)
|
||||
key.Type)
|
||||
}
|
||||
if _, isPrimitive := primitiveTypes[key.Name]; isPrimitive {
|
||||
if _, isPrimitive := primitiveTypes[key.Type]; isPrimitive {
|
||||
return errors.Errorf (
|
||||
currentPos, "cannot shadow primitive %s",
|
||||
key.Name)
|
||||
key.Type)
|
||||
}
|
||||
if _, isBuiltin := builtinTypes[key.Name]; isBuiltin {
|
||||
if _, isBuiltin := builtinTypes[key.Type]; isBuiltin {
|
||||
return errors.Errorf (
|
||||
currentPos, "cannot shadow builtin %s",
|
||||
key.Name)
|
||||
key.Type)
|
||||
}
|
||||
if ty, isType := this.rawTypes[key]; isType {
|
||||
return errors.Errorf (
|
||||
currentPos, "%s already declared at %v",
|
||||
key.Name, ty.Position())
|
||||
key.Type, ty.Position())
|
||||
}
|
||||
if function, isFunction := this.rawFunctions[key]; isFunction {
|
||||
return errors.Errorf (
|
||||
currentPos, "%s already declared at %v",
|
||||
key.Name, function.Position())
|
||||
key.Function, function.Position())
|
||||
}
|
||||
if method, isMethod := this.rawMethods[key]; isMethod {
|
||||
return errors.Errorf (
|
||||
currentPos, "%s.%s already declared at %v",
|
||||
key.Name, key.Method, method.Position())
|
||||
key.Type, key.Function, method.Position())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -123,9 +128,9 @@ func (this *Tree) analyzeDeclarations () error {
|
||||
_, err := this.analyzeMethod (
|
||||
rawMethod.Position(),
|
||||
entity.Key {
|
||||
Unit: this.unit,
|
||||
Name: rawMethod.TypeName,
|
||||
Method: rawMethod.Signature.Name,
|
||||
Unit: this.unit,
|
||||
Type: rawMethod.TypeName,
|
||||
Function: rawMethod.Signature.Name,
|
||||
})
|
||||
if err != nil { return err }
|
||||
}
|
||||
@ -157,7 +162,7 @@ func (this *Tree) ensure () {
|
||||
|
||||
for name, ty := range builtinTypes {
|
||||
// builtin types have a zero UUID in their key
|
||||
this.Types[entity.Key { Name: name }] = &entity.Typedef {
|
||||
this.Types[entity.Key { Type: name }] = &entity.Typedef {
|
||||
Acc: entity.AccessPublic,
|
||||
Name: name,
|
||||
Type: ty,
|
||||
|
124
analyzer/type.go
124
analyzer/type.go
@ -1,6 +1,7 @@
|
||||
package analyzer
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/fspl/fspl/eval"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
|
||||
@ -13,7 +14,7 @@ func (this *Tree) analyzeTypedef (
|
||||
error,
|
||||
) {
|
||||
// return a builtin if it exists (search types with zero uuid)
|
||||
if definition, exists := this.Types[entity.Key { Name: key.Name }]; exists {
|
||||
if definition, exists := this.Types[entity.Key { Type: key.Type }]; exists {
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
@ -29,24 +30,95 @@ func (this *Tree) analyzeTypedef (
|
||||
} else {
|
||||
return nil, errors.Errorf (
|
||||
pos, "type %s cannot be used in this context",
|
||||
key.Name)
|
||||
key.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// analyze if it still needs to be analyzed
|
||||
if definition, exists := this.rawTypes[key]; exists {
|
||||
// update unit
|
||||
definition.Unt = key.Unit
|
||||
|
||||
// analyze
|
||||
var err error
|
||||
definition.Type, err = this.analyzeTypeInternal (
|
||||
definition.Type, definition, false)
|
||||
return definition, err
|
||||
// check if it is even real
|
||||
definition, exists := this.rawTypes[key]
|
||||
if !exists {
|
||||
return nil, errors.Errorf(pos, "no type named %s", key.Type)
|
||||
}
|
||||
|
||||
// update unit
|
||||
definition.Unt = key.Unit
|
||||
|
||||
// if we couldn't get the type, error
|
||||
return nil, errors.Errorf(pos, "no type named %s", key.Name)
|
||||
// analyze
|
||||
var err error
|
||||
definition.Type, err = this.analyzeTypeInternal (
|
||||
definition.Type, definition, false)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// analyze constants
|
||||
err = this.analyzeConstantDeclarations(definition)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeConstantDeclarations (
|
||||
definition *entity.Typedef,
|
||||
) (
|
||||
error,
|
||||
) {
|
||||
// assemble constant map
|
||||
err := this.assembleTypedefConstantMap(definition)
|
||||
if err != nil { return err }
|
||||
|
||||
// analyze constants
|
||||
fillerValue := 0
|
||||
for index, constant := range definition.Constants {
|
||||
constant, err := this.analyzeConstantDeclaration(constant, definition.Type)
|
||||
if err != nil { return err }
|
||||
if constant.Value == nil {
|
||||
if !isNumeric(definition.Type) {
|
||||
return errors.Errorf (
|
||||
constant.Position(),
|
||||
"cannot fill in constant value for non-numeric type %v",
|
||||
definition.Name)
|
||||
}
|
||||
constant.Value = &entity.LiteralInt {
|
||||
Pos: constant.Position(),
|
||||
Value: fillerValue,
|
||||
Ty: definition.Type,
|
||||
}
|
||||
fillerValue ++
|
||||
}
|
||||
|
||||
definition.ConstantMap[constant.Name] = constant
|
||||
definition.ConstantOrder[index] = constant.Name
|
||||
definition.Constants[index] = constant
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeConstantDeclaration (
|
||||
constant *entity.ConstantDeclaration,
|
||||
into entity.Type,
|
||||
) (
|
||||
*entity.ConstantDeclaration,
|
||||
error,
|
||||
) {
|
||||
constant.Ty = into
|
||||
|
||||
// analyze the value, if given
|
||||
if constant.Value != nil {
|
||||
// create new scope context for this constant
|
||||
this.pushScopeContext(constant)
|
||||
this.pushScope(constant)
|
||||
defer this.popScopeContext()
|
||||
defer this.popScope()
|
||||
|
||||
// analyze value
|
||||
value, err := this.analyzeExpression(into, strict, constant.Value)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// evaluate constant expression to literal value
|
||||
value, err = eval.EvaluateConstant(value)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
|
||||
return constant, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeType (
|
||||
@ -76,7 +148,7 @@ func (this *Tree) analyzeTypeInternal (
|
||||
rootKey := func () entity.Key {
|
||||
return entity.Key {
|
||||
Unit: this.unit,
|
||||
Name: root.Name,
|
||||
Type: root.Name,
|
||||
}
|
||||
}
|
||||
updateIncompleteInfo := func () {
|
||||
@ -110,7 +182,7 @@ func (this *Tree) analyzeTypeInternal (
|
||||
// analyze the typedef
|
||||
def, err := this.analyzeTypedef(ty.Position(), entity.Key {
|
||||
Unit: unit,
|
||||
Name: ty.Name,
|
||||
Type: ty.Name,
|
||||
}, acceptIncomplete)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
@ -303,3 +375,23 @@ func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signatur
|
||||
behavior.Return, err = this.analyzeType(behavior.Return, false)
|
||||
return behavior, err
|
||||
}
|
||||
|
||||
func (this *Tree) assembleTypedefConstantMap (
|
||||
typedef *entity.Typedef,
|
||||
) (
|
||||
error,
|
||||
) {
|
||||
typedef.ConstantMap = make(map[string] *entity.ConstantDeclaration)
|
||||
typedef.ConstantOrder = make([]string, len(typedef.Constants))
|
||||
for index, constant := range typedef.Constants {
|
||||
if previous, exists := typedef.ConstantMap[constant.Name]; exists {
|
||||
return errors.Errorf (
|
||||
constant.Position(), "%s already defined at %v",
|
||||
constant.Name, previous.Position())
|
||||
}
|
||||
typedef.ConstantMap [constant.Name] = constant
|
||||
typedef.ConstantOrder[index] = constant.Name
|
||||
typedef.Constants [index] = constant
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ are:
|
||||
- Private access: Disallows other modules from accessing a top-level entity.
|
||||
This mode is the default when one isn't supplied.
|
||||
### Type definition
|
||||
Type definitions bind a type to a global type identifier.
|
||||
Type definitions bind a type to a global type identifier. They may list named
|
||||
constant values of their type accessible via constant access.
|
||||
### Function
|
||||
Functions bind a global identifier and argument list to an expression which is
|
||||
evaluated each time the function is called. If no expression is specified, the
|
||||
@ -303,6 +304,11 @@ the function or method will cease.
|
||||
Assignment allows assigning the result of one expression to one or more location
|
||||
expressions. The assignment expression itself has no value and may not be
|
||||
assigned to anything.
|
||||
### Constant access
|
||||
Constant access allows accessing an enumerated constant defined as part of a
|
||||
type. It can be assigned to a type matching the constant declaration's type.
|
||||
Since it contains inherent type information, it may be directly assigned to an
|
||||
interface.
|
||||
|
||||
# Syntax entities
|
||||
|
||||
@ -314,7 +320,7 @@ this without hand-writing a parser.
|
||||
```
|
||||
<file> -> (<typedef> | <function> | <method>)*
|
||||
<access> -> "+" | "#" | "-"
|
||||
<typedef> -> [<access>] <typeIdentifier> ":" <type>
|
||||
<typedef> -> [<access>] <typeIdentifier> ":" <type> <constantDeclaration>*
|
||||
<function> -> [<access>] <signature> ["=" <expression>]
|
||||
<method> -> [<access>] <typeIdentifier> "." <function>
|
||||
|
||||
@ -324,7 +330,7 @@ this without hand-writing a parser.
|
||||
| <arrayType>
|
||||
| <structType>
|
||||
| <interfaceType>
|
||||
<namedType> -> <typeIdentifier>
|
||||
<namedType> -> [<identifier> "::"] <typeIdentifier>
|
||||
<pointerType> -> "*" <type>
|
||||
<sliceType> -> "*" ":" <type>
|
||||
<arrayType> -> <intLiteral> ":" <type>
|
||||
@ -356,9 +362,10 @@ this without hand-writing a parser.
|
||||
| <break>
|
||||
| <return>
|
||||
| <assignment>
|
||||
| <constant>
|
||||
<variable> -> <identifier>
|
||||
<declaration> -> <identifier> ":" <type>
|
||||
<call> -> "[" <expression>+ "]"
|
||||
<call> -> [<identifier> "::"] "[" <expression>+ "]"
|
||||
<subscript> -> "[" "." <expression> <expression> "]"
|
||||
<slice> -> "[" "\" <expression> <expression>? "/" <expression>? "]"
|
||||
<length> -> "[" "#" <expression> "]"
|
||||
@ -380,6 +387,7 @@ this without hand-writing a parser.
|
||||
<break> -> "[" "break" [<expression>] "]"
|
||||
<return> -> "[" "return" [<expression>] "]"
|
||||
<assignment> -> <expression> "=" <expression>
|
||||
<constant> -> <namedType> "." <identifier>
|
||||
|
||||
<intLiteral> -> /-?[1-9][0-9]*/
|
||||
| /-?0[0-7]*/
|
||||
@ -391,15 +399,16 @@ this without hand-writing a parser.
|
||||
<structLiteral> -> "(." <member>* ")"
|
||||
<booleanLiteral> -> "true" | "false"
|
||||
|
||||
<member> -> <identifier> ":" <expression>
|
||||
<matchCase> -> "|" <declaration> <expression>
|
||||
<switchCase> -> "|" <expression> <expression>
|
||||
<defaultCase> -> "*" <expression>
|
||||
<signature> -> "[" <identifier> <declaration>* "]" [":" <type>]
|
||||
<identifier> -> /[a-z][A-Za-z]*/
|
||||
<typeIdentifier> -> /[A-Z][A-Za-z]*/
|
||||
<operator> -> "+" | "++" | "-" | "--" | "*" | "/" | "%"
|
||||
| "!!" | "||" | "&&" | "^^"
|
||||
| "!" | "|" | "&" | "^" | "<<" | ">>"
|
||||
| "<" | ">" | "<=" | ">=" | "="
|
||||
<member> -> <identifier> ":" <expression>
|
||||
<constantDeclaration> -> "|" <identifier> ["=" <expression>]
|
||||
<matchCase> -> "|" <declaration> <expression>
|
||||
<switchCase> -> "|" <expression> <expression>
|
||||
<defaultCase> -> "*" <expression>
|
||||
<signature> -> "[" <identifier> <declaration>* "]" [":" <type>]
|
||||
<identifier> -> /[a-z][A-Za-z]*/
|
||||
<typeIdentifier> -> /[A-Z][A-Za-z]*/
|
||||
<operator> -> "+" | "++" | "-" | "--" | "*" | "/" | "%"
|
||||
| "!!" | "||" | "&&" | "^^"
|
||||
| "!" | "|" | "&" | "^" | "<<" | ">>"
|
||||
| "<" | ">" | "<=" | ">=" | "="
|
||||
```
|
||||
|
@ -22,6 +22,13 @@ type Expression interface {
|
||||
// to conform to. The value of this does not depend on semantic
|
||||
// information fields.
|
||||
HasExplicitType () bool
|
||||
|
||||
// Constant returns true if the expression's value can be computed at
|
||||
// compile time.
|
||||
IsConstant () bool
|
||||
|
||||
// Description returns a human-readable description of the expression.
|
||||
Description () string
|
||||
|
||||
expression ()
|
||||
}
|
||||
@ -43,6 +50,8 @@ func (*Variable) expression(){}
|
||||
func (this *Variable) Position () errors.Position { return this.Pos }
|
||||
func (this *Variable) Type () Type { return this.Declaration.Type() }
|
||||
func (this *Variable) HasExplicitType () bool { return true }
|
||||
func (this *Variable) IsConstant () bool { return false }
|
||||
func (this *Variable) Description () string { return "variable" }
|
||||
func (this *Variable) String () string {
|
||||
return this.Name
|
||||
}
|
||||
@ -62,6 +71,8 @@ func (*Declaration) expression(){}
|
||||
func (this *Declaration) Position () errors.Position { return this.Pos }
|
||||
func (this *Declaration) Type () Type { return this.Ty }
|
||||
func (this *Declaration) HasExplicitType () bool { return true }
|
||||
func (this *Declaration) IsConstant () bool { return false }
|
||||
func (this *Declaration) Description () string { return "declaration" }
|
||||
func (this *Declaration) String () string {
|
||||
return fmt.Sprint(this.Name, ":", this.Ty)
|
||||
}
|
||||
@ -88,6 +99,8 @@ func (*Call) expression(){}
|
||||
func (this *Call) Position () errors.Position { return this.Pos }
|
||||
func (this *Call) Type () Type { return this.Function.Signature.Return }
|
||||
func (this *Call) HasExplicitType () bool { return true }
|
||||
func (this *Call) IsConstant () bool { return false }
|
||||
func (this *Call) Description () string { return "function call" }
|
||||
func (this *Call) String () string {
|
||||
out := ""
|
||||
if this.UnitNickname != "" {
|
||||
@ -129,6 +142,8 @@ func (this *MethodCall) Type () Type {
|
||||
}
|
||||
}
|
||||
func (this *MethodCall) HasExplicitType () bool { return true }
|
||||
func (this *MethodCall) IsConstant () bool { return false }
|
||||
func (this *MethodCall) Description () string { return "method call" }
|
||||
func (this *MethodCall) String () string {
|
||||
out := fmt.Sprint(this.Source, ".[", this.Name)
|
||||
for _, argument := range this.Arguments {
|
||||
@ -156,6 +171,8 @@ func (*Subscript) expression(){}
|
||||
func (this *Subscript) Position () errors.Position { return this.Pos }
|
||||
func (this *Subscript) Type () Type { return this.Ty }
|
||||
func (this *Subscript) HasExplicitType () bool { return true }
|
||||
func (this *Subscript) IsConstant () bool { return false }
|
||||
func (this *Subscript) Description () string { return "subscript operation" }
|
||||
func (this *Subscript) String () string {
|
||||
return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]")
|
||||
}
|
||||
@ -176,6 +193,12 @@ func (*Slice) expression(){}
|
||||
func (this *Slice) Position () errors.Position { return this.Pos }
|
||||
func (this *Slice) Type () Type { return this.Slice.Type() }
|
||||
func (this *Slice) HasExplicitType () bool { return true }
|
||||
func (this *Slice) IsConstant () bool {
|
||||
return this.Slice.IsConstant() &&
|
||||
this.Start.IsConstant() &&
|
||||
this.End.IsConstant()
|
||||
}
|
||||
func (this *Slice) Description () string { return "slice operation" }
|
||||
func (this *Slice) String () string {
|
||||
out := fmt.Sprint("[\\", this.Slice, " ")
|
||||
if this.Start != nil {
|
||||
@ -203,6 +226,8 @@ func (*Length) expression(){}
|
||||
func (this *Length) Position () errors.Position { return this.Pos }
|
||||
func (this *Length) Type () Type { return this.Ty }
|
||||
func (this *Length) HasExplicitType () bool { return true }
|
||||
func (this *Length) IsConstant () bool { return this.Slice.IsConstant() }
|
||||
func (this *Length) Description () string { return "length operation" }
|
||||
func (this *Length) String () string {
|
||||
return fmt.Sprint("[#", this.Slice, "]")
|
||||
}
|
||||
@ -225,6 +250,8 @@ func (*Dereference) expression(){}
|
||||
func (this *Dereference) Position () errors.Position { return this.Pos }
|
||||
func (this *Dereference) Type () Type { return this.Ty }
|
||||
func (this *Dereference) HasExplicitType () bool { return true }
|
||||
func (this *Dereference) IsConstant () bool { return false }
|
||||
func (this *Dereference) Description () string { return "dereference operation" }
|
||||
func (this *Dereference) String () string {
|
||||
return fmt.Sprint("[.", this.Pointer, "]")
|
||||
}
|
||||
@ -249,6 +276,8 @@ func (*Reference) expression(){}
|
||||
func (this *Reference) Position () errors.Position { return this.Pos }
|
||||
func (this *Reference) Type () Type { return this.Ty }
|
||||
func (this *Reference) HasExplicitType () bool { return true }
|
||||
func (this *Reference) IsConstant () bool { return false }
|
||||
func (this *Reference) Description () string { return "reference operation" }
|
||||
func (this *Reference) String () string {
|
||||
return fmt.Sprint("[@", this.Value, "]")
|
||||
}
|
||||
@ -266,6 +295,8 @@ func (*ValueCast) expression(){}
|
||||
func (this *ValueCast) Position () errors.Position { return this.Pos }
|
||||
func (this *ValueCast) Type () Type { return this.Ty }
|
||||
func (this *ValueCast) HasExplicitType () bool { return true }
|
||||
func (this *ValueCast) IsConstant () bool { return this.Value.IsConstant() }
|
||||
func (this *ValueCast) Description () string { return "value cast" }
|
||||
func (this *ValueCast) String () string {
|
||||
return fmt.Sprint("[~ ", this.Ty, this.Value, "]")
|
||||
}
|
||||
@ -284,6 +315,8 @@ func (*BitCast) expression(){}
|
||||
func (this *BitCast) Position () errors.Position { return this.Pos }
|
||||
func (this *BitCast) Type () Type { return this.Ty }
|
||||
func (this *BitCast) HasExplicitType () bool { return true }
|
||||
func (this *BitCast) IsConstant () bool { return this.Value.IsConstant() }
|
||||
func (this *BitCast) Description () string { return "bit cast" }
|
||||
func (this *BitCast) String () string {
|
||||
return fmt.Sprint("[~~ ", this.Ty, this.Value, "]")
|
||||
}
|
||||
@ -308,6 +341,15 @@ func (this *Operation) Type () Type { return this.Ty }
|
||||
func (this *Operation) HasExplicitType () bool {
|
||||
return this.Operator.ResultsInBoolean()
|
||||
}
|
||||
func (this *Operation) IsConstant () bool {
|
||||
for _, argument := range this.Arguments {
|
||||
if !argument.IsConstant() { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *Operation) Description () string {
|
||||
return fmt.Sprintf("%v operation", this.Operator)
|
||||
}
|
||||
func (this *Operation) String () string {
|
||||
out := fmt.Sprint("[", this.Operator)
|
||||
for _, argument := range this.Arguments {
|
||||
@ -338,6 +380,11 @@ func (this *Block) HasExplicitType () bool {
|
||||
if len(this.Steps) == 0 { return false }
|
||||
return this.Steps[len(this.Steps) - 1].HasExplicitType()
|
||||
}
|
||||
func (this *Block) IsConstant () bool {
|
||||
if len(this.Steps) == 0 { return false }
|
||||
return this.Steps[len(this.Steps) - 1].IsConstant()
|
||||
}
|
||||
func (this *Block) Description () string { return "block" }
|
||||
func (this *Block) String () string {
|
||||
out := "{"
|
||||
for index, step := range this.Steps {
|
||||
@ -367,6 +414,8 @@ func (*MemberAccess) expression(){}
|
||||
func (this *MemberAccess) Position () errors.Position { return this.Pos }
|
||||
func (this *MemberAccess) Type () Type { return this.Ty }
|
||||
func (this *MemberAccess) HasExplicitType () bool { return true }
|
||||
func (this *MemberAccess) IsConstant () bool { return this.Source.IsConstant() }
|
||||
func (this *MemberAccess) Description () string { return "member access" }
|
||||
func (this *MemberAccess) String () string {
|
||||
return fmt.Sprint(this.Source, ".", this.Member)
|
||||
}
|
||||
@ -393,6 +442,12 @@ func (this *IfElse) Type () Type { return this.Ty }
|
||||
func (this *IfElse) HasExplicitType () bool {
|
||||
return this.True.HasExplicitType()
|
||||
}
|
||||
func (this *IfElse) IsConstant () bool {
|
||||
return this.Condition.IsConstant() &&
|
||||
this.True.IsConstant() &&
|
||||
this.False.IsConstant()
|
||||
}
|
||||
func (this *IfElse) Description () string { return "if/else" }
|
||||
func (this *IfElse) String () string {
|
||||
out := fmt.Sprint("if ", this.Condition, " then ", this.True)
|
||||
if this.False != nil {
|
||||
@ -432,6 +487,15 @@ func (this *Match) HasExplicitType () bool {
|
||||
return this.Cases[0].HasExplicitType()
|
||||
}
|
||||
}
|
||||
func (this *Match) IsConstant () bool {
|
||||
if !this.Value.IsConstant() { return false }
|
||||
if !this.Default.IsConstant() { return false }
|
||||
for _, cas := range this.Cases {
|
||||
if !cas.IsConstant () { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *Match) Description () string { return "match" }
|
||||
func (this *Match) String () string {
|
||||
out := fmt.Sprint("match ", this.Value)
|
||||
for _, cas := range this.Cases {
|
||||
@ -471,6 +535,15 @@ func (this *Switch) HasExplicitType () bool {
|
||||
return this.Cases[0].HasExplicitType()
|
||||
}
|
||||
}
|
||||
func (this *Switch) IsConstant () bool {
|
||||
if !this.Value.IsConstant() { return false }
|
||||
if !this.Default.IsConstant() { return false }
|
||||
for _, cas := range this.Cases {
|
||||
if !cas.IsConstant () { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *Switch) Description () string { return "switch" }
|
||||
func (this *Switch) String () string {
|
||||
out := fmt.Sprint("switch ", this.Value)
|
||||
for _, cas := range this.Cases {
|
||||
@ -514,6 +587,8 @@ func (this *Loop) HasExplicitType () bool {
|
||||
// loop
|
||||
return false
|
||||
}
|
||||
func (this *Loop) IsConstant () bool { return false }
|
||||
func (this *Loop) Description () string { return "loop" }
|
||||
func (this *Loop) String () string {
|
||||
return fmt.Sprint("loop ", this.Body)
|
||||
}
|
||||
@ -547,6 +622,8 @@ func (this *For) HasExplicitType () bool {
|
||||
// loop
|
||||
return false
|
||||
}
|
||||
func (this *For) IsConstant () bool { return false /* TODO this may be decidable */ }
|
||||
func (this *For) Description () string { return "for loop" }
|
||||
func (this *For) String () string {
|
||||
out := "for"
|
||||
if this.Index != nil {
|
||||
@ -571,6 +648,8 @@ func (*Break) expression(){}
|
||||
func (this *Break) Position () errors.Position { return this.Pos }
|
||||
func (this *Break) Type () Type { return nil }
|
||||
func (this *Break) HasExplicitType () bool { return false }
|
||||
func (this *Break) IsConstant () bool { return false }
|
||||
func (this *Break) Description () string { return "break" }
|
||||
func (this *Break) String () string {
|
||||
if this.Value == nil {
|
||||
return "[break]"
|
||||
@ -597,6 +676,8 @@ func (*Return) expression(){}
|
||||
func (this *Return) Position () errors.Position { return this.Pos }
|
||||
func (this *Return) Type () Type { return nil }
|
||||
func (this *Return) HasExplicitType () bool { return false }
|
||||
func (this *Return) IsConstant () bool { return false }
|
||||
func (this *Return) Description () string { return "return" }
|
||||
func (this *Return) String () string {
|
||||
if this.Value == nil {
|
||||
return "[return]"
|
||||
@ -619,6 +700,40 @@ func (*Assignment) expression(){}
|
||||
func (this *Assignment) Position () errors.Position { return this.Pos }
|
||||
func (this *Assignment) Type () Type { return nil }
|
||||
func (this *Assignment) HasExplicitType () bool { return false }
|
||||
func (this *Assignment) IsConstant () bool { return false }
|
||||
func (this *Assignment) Description () string { return "assignment" }
|
||||
func (this *Assignment) String () string {
|
||||
return fmt.Sprint(this.Location, "=", this.Value)
|
||||
}
|
||||
|
||||
var _ Expression = &Constant { }
|
||||
// Constant access allows accessing an enumerated constant defined as part of a
|
||||
// type. It can be assigned to a type matching the constant declaration's type.
|
||||
// Since it contains inherent type information, it may be directly assigned to
|
||||
// an interface.
|
||||
type Constant struct {
|
||||
// Syntax
|
||||
Pos errors.Position
|
||||
UnitNickname string
|
||||
TypeName string
|
||||
Name string
|
||||
|
||||
// Semantics
|
||||
Ty Type
|
||||
Unit uuid.UUID
|
||||
Declaration *ConstantDeclaration
|
||||
}
|
||||
func (*Constant) expression(){}
|
||||
func (this *Constant) Position () errors.Position { return this.Pos }
|
||||
func (this *Constant) Type () Type { return this.Ty }
|
||||
func (this *Constant) HasExplicitType () bool { return true }
|
||||
func (this *Constant) IsConstant () bool { return true }
|
||||
func (this *Constant) Description () string { return "constant" }
|
||||
func (this *Constant) String () string {
|
||||
output := ""
|
||||
if this.UnitNickname != "" {
|
||||
output += fmt.Sprint(this.UnitNickname, "::")
|
||||
}
|
||||
output += fmt.Sprint(this.TypeName, ".", this.Name)
|
||||
return output
|
||||
}
|
||||
|
@ -28,16 +28,45 @@ func (hash Hash) Number () uint64 {
|
||||
|
||||
// Key globally indexes top level entities in contexts where modules matter.
|
||||
type Key struct {
|
||||
Unit uuid.UUID
|
||||
Name string
|
||||
Method string
|
||||
Unit uuid.UUID
|
||||
|
||||
// TypeName is the name of the type
|
||||
Type string
|
||||
|
||||
// Constant is the name of the constant
|
||||
Constant string
|
||||
|
||||
// Function is the name of the method or function
|
||||
Function string
|
||||
}
|
||||
|
||||
func (key Key) String () string {
|
||||
out := fmt.Sprintf("%v::%v", key.Unit, key.Name)
|
||||
if key.Method != "" {
|
||||
out = fmt.Sprintf("%s.%s", out, key.Method)
|
||||
return fmt.Sprintf("%v::%s", key.Unit, key.name())
|
||||
}
|
||||
|
||||
// LinkName returns the name that the entity it refers to will be given when
|
||||
// compiled.
|
||||
func (key Key) LinkName () string {
|
||||
data := [16]byte(key.Unit)
|
||||
unit := base64.StdEncoding.EncodeToString(data[:])
|
||||
return fmt.Sprintf("%v::%s", unit, key.name())
|
||||
}
|
||||
|
||||
func (key Key) name () string {
|
||||
out := ""
|
||||
switch {
|
||||
case key.Constant != "": out = key.Constant
|
||||
case key.Function != "": out = fmt.Sprintf("[%s]", key.Function)
|
||||
}
|
||||
|
||||
if key.Type != "" {
|
||||
if out == "" {
|
||||
out = key.Type
|
||||
} else {
|
||||
out = fmt.Sprintf("%s.%s", key.Type, out)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@ -47,24 +76,10 @@ func (key Key) Hash () Hash {
|
||||
return NewHash([]byte("Key:" + key.String()))
|
||||
}
|
||||
|
||||
// LinkName returns the name that the entity it refers to will be given when
|
||||
// compiled.
|
||||
func (key Key) LinkName () string {
|
||||
data := [16]byte(key.Unit)
|
||||
out := fmt.Sprintf(
|
||||
"%s::%s",
|
||||
base64.StdEncoding.EncodeToString(data[:]),
|
||||
key.Name)
|
||||
if key.Method != "" {
|
||||
out = fmt.Sprintf("%s.%s", out, key.Method)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// StripMethod returns a copy of the key that refers to a type instead of a
|
||||
// method.
|
||||
func (key Key) StripMethod () Key {
|
||||
key.Method = ""
|
||||
// OnlyType returns a copy of the key that refers to a type instead of a method
|
||||
// or constant.
|
||||
func (key Key) OnlyType () Key {
|
||||
key.Constant = ""
|
||||
key.Function = ""
|
||||
return key
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ func (*LiteralInt) expression(){}
|
||||
func (this *LiteralInt) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralInt) Type () Type { return this.Ty }
|
||||
func (this *LiteralInt) HasExplicitType () bool { return false }
|
||||
func (this *LiteralInt) IsConstant () bool { return true }
|
||||
func (this *LiteralInt) Description () string { return "integer literal" }
|
||||
func (this *LiteralInt) String () string {
|
||||
return fmt.Sprint(this.Value)
|
||||
}
|
||||
@ -42,6 +44,8 @@ func (*LiteralFloat) expression(){}
|
||||
func (this *LiteralFloat) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralFloat) Type () Type { return this.Ty }
|
||||
func (this *LiteralFloat) HasExplicitType () bool { return false }
|
||||
func (this *LiteralFloat) Description () string { return "floating point literal" }
|
||||
func (this *LiteralFloat) IsConstant () bool { return true }
|
||||
func (this *LiteralFloat) String () string {
|
||||
return fmt.Sprint(this.Value)
|
||||
}
|
||||
@ -76,6 +80,8 @@ func (*LiteralString) expression(){}
|
||||
func (this *LiteralString) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralString) Type () Type { return this.Ty }
|
||||
func (this *LiteralString) HasExplicitType () bool { return false }
|
||||
func (this *LiteralString) Description () string { return "string literal" }
|
||||
func (this *LiteralString) IsConstant () bool { return true }
|
||||
func (this *LiteralString) String () string {
|
||||
return Quote(this.ValueUTF8)
|
||||
}
|
||||
@ -100,6 +106,13 @@ func (*LiteralArray) expression(){}
|
||||
func (this *LiteralArray) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralArray) Type () Type { return this.Ty }
|
||||
func (this *LiteralArray) HasExplicitType () bool { return false }
|
||||
func (this *LiteralArray) Description () string { return "array literal" }
|
||||
func (this *LiteralArray) IsConstant () bool {
|
||||
for _, element := range this.Elements {
|
||||
if !element.IsConstant() { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *LiteralArray) String () string {
|
||||
out := "("
|
||||
for index, element := range this.Elements {
|
||||
@ -131,6 +144,13 @@ func (*LiteralStruct) expression(){}
|
||||
func (this *LiteralStruct) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralStruct) Type () Type { return this.Ty }
|
||||
func (this *LiteralStruct) HasExplicitType () bool { return false }
|
||||
func (this *LiteralStruct) Description () string { return "struct literal" }
|
||||
func (this *LiteralStruct) IsConstant () bool {
|
||||
for _, member := range this.Members {
|
||||
if !member.Value.IsConstant() { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *LiteralStruct) String () string {
|
||||
out := "(."
|
||||
for _, member := range this.Members {
|
||||
@ -156,6 +176,8 @@ func (*LiteralBoolean) expression(){}
|
||||
func (this *LiteralBoolean) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralBoolean) Type () Type { return this.Ty }
|
||||
func (this *LiteralBoolean) HasExplicitType () bool { return false }
|
||||
func (this *LiteralBoolean) Description () string { return "boolean literal" }
|
||||
func (this *LiteralBoolean) IsConstant () bool { return true }
|
||||
func (this *LiteralBoolean) String () string {
|
||||
if this.Value {
|
||||
return "true"
|
||||
@ -177,4 +199,6 @@ func (*LiteralNil) expression(){}
|
||||
func (this *LiteralNil) Position () errors.Position { return this.Pos }
|
||||
func (this *LiteralNil) Type () Type { return this.Ty }
|
||||
func (this *LiteralNil) HasExplicitType () bool { return false }
|
||||
func (this *LiteralNil) Description () string { return "nil" }
|
||||
func (this *LiteralNil) IsConstant () bool { return true }
|
||||
func (this *LiteralNil) String () string { return "nil" }
|
||||
|
@ -252,3 +252,20 @@ func Quote (in string) string {
|
||||
}
|
||||
return out + "'"
|
||||
}
|
||||
|
||||
// ConstantDeclaration declares an enumerated constant as part of a Typedef.
|
||||
type ConstantDeclaration struct {
|
||||
// Syntax
|
||||
Pos errors.Position
|
||||
Name string
|
||||
Value Expression
|
||||
|
||||
// Semantics
|
||||
Ty Type
|
||||
Scope
|
||||
}
|
||||
func (this *ConstantDeclaration) Position () errors.Position { return this.Pos }
|
||||
func (this *ConstantDeclaration) Type () Type { return this.Ty }
|
||||
func (this *ConstantDeclaration) String () string {
|
||||
return fmt.Sprint("| ", this.Name, " = ", this.Value)
|
||||
}
|
||||
|
@ -24,14 +24,17 @@ var _ TopLevel = &Typedef { }
|
||||
// Typedef binds a type to a global identifier.
|
||||
type Typedef struct {
|
||||
// Syntax
|
||||
Pos errors.Position
|
||||
Acc Access
|
||||
Name string
|
||||
Type Type
|
||||
Pos errors.Position
|
||||
Acc Access
|
||||
Name string
|
||||
Type Type
|
||||
Constants []*ConstantDeclaration
|
||||
|
||||
// Semantics
|
||||
Unt uuid.UUID
|
||||
Methods map[string] *Method
|
||||
Unt uuid.UUID
|
||||
Methods map[string] *Method
|
||||
ConstantOrder []string
|
||||
ConstantMap map[string] *ConstantDeclaration
|
||||
}
|
||||
func (*Typedef) topLevel(){}
|
||||
func (this *Typedef) Position () errors.Position { return this.Pos }
|
||||
@ -41,6 +44,9 @@ func (this *Typedef) String () string {
|
||||
output := ""
|
||||
output += fmt.Sprint(this.Acc, " ")
|
||||
output += fmt.Sprint(this.Name, ": ", this.Type)
|
||||
for _, constant := range this.Constants {
|
||||
output += fmt.Sprint("\n", constant)
|
||||
}
|
||||
if this.Methods != nil {
|
||||
for _, method := range this.Methods {
|
||||
output += fmt.Sprint("\n", method)
|
||||
|
@ -53,7 +53,7 @@ func (this *TypeNamed) String () string {
|
||||
func (this *TypeNamed) Hash () Hash {
|
||||
return Key {
|
||||
Unit: this.Type.Unit(),
|
||||
Name: this.Name,
|
||||
Type: this.Name,
|
||||
}.Hash()
|
||||
}
|
||||
func (this *TypeNamed) Equals (ty Type) bool {
|
||||
|
@ -1,55 +1,6 @@
|
||||
package errors
|
||||
|
||||
import "testing"
|
||||
import "strings"
|
||||
|
||||
func TestError (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:7: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^`,
|
||||
Errorf (
|
||||
Position {
|
||||
File: "example.fspl",
|
||||
Line: "lorem ipsum dolor",
|
||||
Row: 10,
|
||||
Start: 6,
|
||||
End: 11,
|
||||
},
|
||||
"some error"))
|
||||
}
|
||||
|
||||
func TestErrorTab (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:8: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^`,
|
||||
Errorf (
|
||||
Position {
|
||||
File: "example.fspl",
|
||||
Line: "\tlorem\tipsum\tdolor",
|
||||
Row: 10,
|
||||
Start: 7,
|
||||
End: 12,
|
||||
},
|
||||
"some error"))
|
||||
}
|
||||
|
||||
func TestErrorTabInBetween (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:8: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^^^^^`,
|
||||
Errorf (
|
||||
Position {
|
||||
File: "example.fspl",
|
||||
Line: "\tlorem\tipsum\tdolor",
|
||||
Row: 10,
|
||||
Start: 7,
|
||||
End: 14,
|
||||
},
|
||||
"some error"))
|
||||
}
|
||||
|
||||
func TestGetXInTabbedString (test *testing.T) {
|
||||
getXCase := func (line string, column, correct int) {
|
||||
@ -69,7 +20,6 @@ func TestGetXInTabbedString (test *testing.T) {
|
||||
getXCase("x\tyyyy\tzy", 7, 16)
|
||||
}
|
||||
|
||||
|
||||
func TestFormatTabs (test *testing.T) {
|
||||
fmtCase := func (line string, correct string) {
|
||||
got := formatTabs(line)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package errors
|
||||
package errorstest
|
||||
|
||||
import "testing"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/testcommon"
|
||||
|
||||
func testString (test *testing.T, correct string, got string) {
|
||||
@ -11,6 +12,6 @@ func testString (test *testing.T, correct string, got string) {
|
||||
testcommon.Compare(test, correct, got)
|
||||
}
|
||||
|
||||
func testError (test *testing.T, correct string, err Error) {
|
||||
testString(test, correct, Format(err))
|
||||
func testError (test *testing.T, correct string, err errors.Error) {
|
||||
testString(test, correct, errors.Format(err))
|
||||
}
|
52
errors/test/errors_test.go
Normal file
52
errors/test/errors_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package errorstest
|
||||
|
||||
import "testing"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
|
||||
func TestError (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:7: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^`,
|
||||
errors.Errorf (
|
||||
errors.Position {
|
||||
File: "example.fspl",
|
||||
Line: "lorem ipsum dolor",
|
||||
Row: 10,
|
||||
Start: 6,
|
||||
End: 11,
|
||||
},
|
||||
"some error"))
|
||||
}
|
||||
|
||||
func TestErrorTab (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:8: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^`,
|
||||
errors.Errorf (
|
||||
errors.Position {
|
||||
File: "example.fspl",
|
||||
Line: "\tlorem\tipsum\tdolor",
|
||||
Row: 10,
|
||||
Start: 7,
|
||||
End: 12,
|
||||
},
|
||||
"some error"))
|
||||
}
|
||||
|
||||
func TestErrorTabInBetween (test *testing.T) {
|
||||
testError (test,
|
||||
`example.fspl:11:8: some error
|
||||
11 | lorem ipsum dolor
|
||||
^^^^^^^^^`,
|
||||
errors.Errorf (
|
||||
errors.Position {
|
||||
File: "example.fspl",
|
||||
Line: "\tlorem\tipsum\tdolor",
|
||||
Row: 10,
|
||||
Start: 7,
|
||||
End: 14,
|
||||
},
|
||||
"some error"))
|
||||
}
|
2
eval/doc.go
Normal file
2
eval/doc.go
Normal file
@ -0,0 +1,2 @@
|
||||
// Package eval provides a way to evaluate FSPL expressions.
|
||||
package eval
|
106
eval/eval.go
Normal file
106
eval/eval.go
Normal file
@ -0,0 +1,106 @@
|
||||
package eval
|
||||
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
|
||||
type evaluator struct { }
|
||||
|
||||
func (this *evaluator) assertConstant (expression entity.Expression) error {
|
||||
if expression.IsConstant() {
|
||||
return nil
|
||||
} else {
|
||||
return errors.Errorf(expression.Position(), "%v is not constant", expression)
|
||||
}
|
||||
}
|
||||
|
||||
// EvaluateConstant evaluates a constant expression. On success, it always
|
||||
// returns a literal made of other literals. It is very important that the input
|
||||
// expression has come from the semantic analyzer.
|
||||
func EvaluateConstant (expression entity.Expression) (entity.Expression, error) {
|
||||
// evaluate
|
||||
this := new(evaluator)
|
||||
expression, err := this.evaluateConstant(expression)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// sanity check
|
||||
if !expression.IsConstant() {
|
||||
panic("BUG: evaluation result is not constant")
|
||||
}
|
||||
|
||||
return expression, nil
|
||||
}
|
||||
|
||||
func (this *evaluator) evaluateConstant (expression entity.Expression) (entity.Expression, error) {
|
||||
// TODO expand this to include all constant expressions
|
||||
|
||||
switch expression := expression.(type) {
|
||||
case *entity.LiteralInt:
|
||||
return expression, nil
|
||||
case *entity.LiteralFloat:
|
||||
return expression, nil
|
||||
case *entity.LiteralString:
|
||||
return expression, nil
|
||||
case *entity.LiteralArray:
|
||||
return this.evaluateConstantLiteralArray(expression)
|
||||
case *entity.LiteralStruct:
|
||||
return this.evaluateConstantLiteralStruct(expression)
|
||||
case *entity.LiteralBoolean:
|
||||
return expression, nil
|
||||
case *entity.LiteralNil:
|
||||
return expression, nil
|
||||
|
||||
case *entity.Constant:
|
||||
return expression.Declaration.Value, nil
|
||||
|
||||
default:
|
||||
err := this.assertConstant(expression)
|
||||
if err != nil { return nil, err }
|
||||
return nil, errors.Errorf (
|
||||
expression.Position(),
|
||||
"%v expressions are currently unsupported by the " +
|
||||
"constant expression evaluator", expression.Description())
|
||||
}
|
||||
}
|
||||
|
||||
func (this *evaluator) evaluateConstantLiteralArray (array *entity.LiteralArray) (entity.Expression, error) {
|
||||
result := &entity.LiteralArray {
|
||||
Pos: array.Position(),
|
||||
Elements: make([]entity.Expression, len(array.Elements)),
|
||||
Ty: array.Type(),
|
||||
}
|
||||
|
||||
for index, element := range array.Elements {
|
||||
elementResult, err := this.evaluateConstant(element)
|
||||
if err != nil { return nil, err }
|
||||
result.Elements[index] = elementResult
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *evaluator) evaluateConstantLiteralStruct (struc *entity.LiteralStruct) (entity.Expression, error) {
|
||||
result := &entity.LiteralStruct {
|
||||
Pos: struc.Position(),
|
||||
Members: make([]*entity.Member, len(struc.Members)),
|
||||
MemberOrder: make([]string, len(struc.MemberOrder)),
|
||||
MemberMap: make(map[string] *entity.Member),
|
||||
Ty: struc.Type(),
|
||||
}
|
||||
|
||||
for index, member := range struc.Members {
|
||||
memberResult := &entity.Member {
|
||||
Pos: member.Position(),
|
||||
Name: member.Name,
|
||||
}
|
||||
|
||||
memberValueResult, err := this.evaluateConstant(member.Value)
|
||||
if err != nil { return nil, err }
|
||||
memberResult.Value = memberValueResult
|
||||
|
||||
result.Members[index] = memberResult
|
||||
result.MemberOrder[index] = memberResult.Name
|
||||
result.MemberMap[memberResult.Name] = memberResult
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
92
eval/test/common_test.go
Normal file
92
eval/test/common_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
package evaltest
|
||||
|
||||
import "strings"
|
||||
import "testing"
|
||||
import "git.tebibyte.media/fspl/fspl/eval"
|
||||
import "git.tebibyte.media/fspl/fspl/lexer"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
import "git.tebibyte.media/fspl/fspl/analyzer"
|
||||
import "git.tebibyte.media/fspl/fspl/testcommon"
|
||||
import "git.tebibyte.media/fspl/fspl/parser/fspl"
|
||||
|
||||
func testString (test *testing.T, correct, input string) {
|
||||
address := entity.Address("main.fspl")
|
||||
ast := treeOf(test, address, input, false)
|
||||
|
||||
tree := analyzer.Tree { }
|
||||
err := tree.Analyze(address.UUID(), nil, ast)
|
||||
if err != nil {
|
||||
test.Fatal("analyzer returned error:\n" + errors.Format(err))
|
||||
return
|
||||
}
|
||||
|
||||
expression := getMain(test, address, tree)
|
||||
output, err := eval.EvaluateConstant(expression)
|
||||
if err != nil {
|
||||
test.Fatal("evaluator returned error:\n" + errors.Format(err))
|
||||
}
|
||||
|
||||
got := output.String()
|
||||
if got != correct {
|
||||
test.Logf("results do not match")
|
||||
testcommon.Compare(test, correct, got)
|
||||
test.Log("SOURCE FSPL CODE:")
|
||||
test.Log("\033[32m" + input + "\033[0m")
|
||||
test.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func testStringErr (
|
||||
test *testing.T,
|
||||
errFile string,
|
||||
errMessage string,
|
||||
errRow int,
|
||||
errStart int,
|
||||
input string,
|
||||
) {
|
||||
address := entity.Address("main.fspl")
|
||||
ast := treeOf(test, address, input, false)
|
||||
|
||||
tree := analyzer.Tree { }
|
||||
err := tree.Analyze(address.UUID(), nil, ast)
|
||||
if err != nil {
|
||||
test.Fatal("analyzer returned error:\n" + errors.Format(err))
|
||||
}
|
||||
|
||||
expression := getMain(test, address, tree)
|
||||
_, err = eval.EvaluateConstant(expression)
|
||||
testcommon.CompareErr(test, errFile, errMessage, errRow, errStart, err)
|
||||
}
|
||||
|
||||
func treeOf (test *testing.T, address entity.Address, input string, skim bool) fsplParser.Tree {
|
||||
ast := fsplParser.Tree { }
|
||||
lx, err := lexer.LexReader (
|
||||
string(address),
|
||||
strings.NewReader(input))
|
||||
if err != nil {
|
||||
test.Fatal("lexer returned error:\n" + errors.Format(err))
|
||||
}
|
||||
if skim {
|
||||
err = ast.Skim(lx)
|
||||
} else {
|
||||
err = ast.Parse(lx)
|
||||
}
|
||||
if err != nil {
|
||||
test.Fatal("parser returned error:\n" + errors.Format(err))
|
||||
}
|
||||
|
||||
return ast
|
||||
}
|
||||
|
||||
func getMain (test *testing.T, address entity.Address, tree analyzer.Tree) (entity.Expression) {
|
||||
mainKey := entity.Key {
|
||||
Unit: address.UUID(),
|
||||
Name: "main",
|
||||
}
|
||||
main, ok := tree.Functions[mainKey]
|
||||
if !ok {
|
||||
test.Fatal("ast does not have a main function")
|
||||
}
|
||||
return main.Body
|
||||
}
|
64
eval/test/eval_test.go
Normal file
64
eval/test/eval_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package evaltest
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestEvalLiteralInt (test *testing.T) {
|
||||
testString(test,
|
||||
`-234`,
|
||||
`
|
||||
[main]:Int = -234
|
||||
`)}
|
||||
|
||||
func TestEvalLiteralFloat (test *testing.T) {
|
||||
testString(test,
|
||||
`854.32`,
|
||||
`
|
||||
[main]:F64 = 854.32
|
||||
`)}
|
||||
|
||||
func TestEvalLiteralString (test *testing.T) {
|
||||
testString(test,
|
||||
`'hello'`,
|
||||
`
|
||||
[main]:String = 'hello'
|
||||
`)}
|
||||
|
||||
func TestEvalArray (test *testing.T) {
|
||||
testString(test,
|
||||
`((2 3 1) (8 9 -23) (432 89 1))`,
|
||||
`
|
||||
[main]:3:3:Int = (
|
||||
( 2 3 1)
|
||||
( 8 9 -23)
|
||||
(432 89 1))
|
||||
`)}
|
||||
|
||||
func TestEvalStruct (test *testing.T) {
|
||||
testString(test,
|
||||
`(. a:489 b:5.283 c:'a string' d:(2 3 1 293) e:(. x:32 y:92) f:true g:nil)`,
|
||||
`
|
||||
T:(. a:Int b:F64 c:String d:*:Int e:(. x:Int y:Int) f:Bool g:*Int)
|
||||
[main]:T = (.
|
||||
a: 489
|
||||
b: 5.283
|
||||
c: 'a string'
|
||||
d: (2 3 1 293)
|
||||
e: (. x: 32 y: 92)
|
||||
f: true
|
||||
g: nil)
|
||||
`)}
|
||||
|
||||
func TestEvalLiteralBoolean (test *testing.T) {
|
||||
testString(test,
|
||||
`true`,
|
||||
`
|
||||
[main]:Bool = true
|
||||
`)}
|
||||
|
||||
func TestEvalLiteralNil (test *testing.T) {
|
||||
testString(test,
|
||||
`nil`,
|
||||
`
|
||||
[main]:*Int = nil
|
||||
`)}
|
||||
|
@ -96,9 +96,9 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
||||
destTypeBase, irDestLoc,
|
||||
irDestType, name)
|
||||
key := entity.Key {
|
||||
Unit: sourceType.Unit(),
|
||||
Name: sourceType.Name,
|
||||
Method: name,
|
||||
Unit: sourceType.Unit(),
|
||||
Type: sourceType.Name,
|
||||
Function: name,
|
||||
}
|
||||
fromBehavior, err := this.method(key)
|
||||
if err != nil { return nil, err }
|
||||
@ -124,9 +124,9 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
||||
destTypeBase, irDestLoc,
|
||||
irDestType, name)
|
||||
key := entity.Key {
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Name: sourceType.Name,
|
||||
Method: name,
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Type: sourceType.Name,
|
||||
Function: name,
|
||||
}
|
||||
fromBehavior, err := this.method(key)
|
||||
if err != nil { return nil, err }
|
||||
|
@ -5,7 +5,7 @@ import "testing"
|
||||
func TestValueCastSlicePointer (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
%2 = alloca ptr
|
||||
@ -26,7 +26,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
func TestValueCastIntegerIneffectual (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 5, ptr %1
|
||||
@ -49,7 +49,7 @@ func TestValueCastStringIneffectual (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
%2 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
@ -70,7 +70,7 @@ func TestBitCastStringIneffectual (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
%2 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
|
52
generator/constant.go
Normal file
52
generator/constant.go
Normal file
@ -0,0 +1,52 @@
|
||||
package generator
|
||||
|
||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
|
||||
func (this *generator) generateConstantDeclaration (
|
||||
ty *entity.Typedef,
|
||||
constantName string,
|
||||
) (
|
||||
*llvm.Global,
|
||||
error,
|
||||
) {
|
||||
key := entity.Key {
|
||||
Unit: ty.Unit(),
|
||||
Type: ty.Name,
|
||||
Constant: constantName,
|
||||
}
|
||||
constant := ty.ConstantMap[constantName]
|
||||
|
||||
irType, err := this.typedef(key.OnlyType())
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// TODO: have two maps, constantsVal and constantsLoc. if the constant
|
||||
// is multivalue, generate a constant global and put it in constantsLoc.
|
||||
// if not, put the value in constantsVal. have generator.constant try
|
||||
// both. have single generateConstant method that converts if necessary
|
||||
// by either loading or allocating a copy on the stack.
|
||||
//
|
||||
// Have a ConstantSupport method of Type that returns either:
|
||||
// - ConstantSupportLoc - multivalue, stored
|
||||
// - ConstantSupportVal - singlevalue, not stored
|
||||
// - ConstantSupportNone - cannot be used as a constant
|
||||
//
|
||||
// Pointers and anything with a pointer inside it must return
|
||||
// ConstantSupportNone. Aggregate types (arrays, structs) must return
|
||||
// ConstantSupportLoc if supported at all. Single value types must
|
||||
// return ConstantSupportNone if supported at all. This should be
|
||||
// checked recursively.
|
||||
|
||||
irGlobal := this.module.NewGlobal(key.LinkName(), irType)
|
||||
irGlobal.Constant = true
|
||||
|
||||
if ty.Unit() == this.tree.Unit() {
|
||||
// in root unit, so generate data
|
||||
initial, err := this.generateEvaluated(constant.Value)
|
||||
if err != nil { return nil, err }
|
||||
irGlobal.Initial = initial
|
||||
}
|
||||
|
||||
this.constants[key] = irGlobal
|
||||
return irGlobal, nil
|
||||
}
|
151
generator/constant_test.go
Normal file
151
generator/constant_test.go
Normal file
@ -0,0 +1,151 @@
|
||||
package generator
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestConstantValueSpecified (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::E" = type i64
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.one = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 1
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.three = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 3
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.two = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 2
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = load %"0zNZN147MN2wzMAQ6NS2dQ==::E", %"0zNZN147MN2wzMAQ6NS2dQ==::E" @"0zNZN147MN2wzMAQ6NS2dQ==::E.two"
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[plus4]"(%"0zNZN147MN2wzMAQ6NS2dQ==::E" %1)
|
||||
ret void
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[plus4]"(%"0zNZN147MN2wzMAQ6NS2dQ==::E" %e) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::E"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::E" %e, ptr %1
|
||||
%2 = load %"0zNZN147MN2wzMAQ6NS2dQ==::E", ptr %1
|
||||
switch %"0zNZN147MN2wzMAQ6NS2dQ==::E" %2, label %8 [
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 1, label %5
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 2, label %6
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 3, label %7
|
||||
]
|
||||
3:
|
||||
%4 = phi i64 [ 5, %5 ], [ 6, %6 ], [ 7, %7 ], [ -1, %8 ]
|
||||
ret i64 %4
|
||||
5:
|
||||
br label %3
|
||||
6:
|
||||
br label %3
|
||||
7:
|
||||
br label %3
|
||||
8:
|
||||
br label %3
|
||||
}
|
||||
`,
|
||||
`
|
||||
E: Int
|
||||
| one = 1
|
||||
| two = 2
|
||||
| three = 3
|
||||
|
||||
[plus4 e:E]:Int = switch e
|
||||
| E.one 5
|
||||
| E.two 6
|
||||
| E.three 7
|
||||
* -1
|
||||
[f] = [plus4 E.two]
|
||||
`)}
|
||||
|
||||
func TestConstantValueUnspecified (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::E" = type i64
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.one = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 0
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.three = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 2
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::E.two = constant %"0zNZN147MN2wzMAQ6NS2dQ==::E" i64 1
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = load %"0zNZN147MN2wzMAQ6NS2dQ==::E", %"0zNZN147MN2wzMAQ6NS2dQ==::E" @"0zNZN147MN2wzMAQ6NS2dQ==::E.two"
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[plus4]"(%"0zNZN147MN2wzMAQ6NS2dQ==::E" %1)
|
||||
ret void
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[plus4]"(%"0zNZN147MN2wzMAQ6NS2dQ==::E" %e) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::E"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::E" %e, ptr %1
|
||||
%2 = load %"0zNZN147MN2wzMAQ6NS2dQ==::E", ptr %1
|
||||
switch %"0zNZN147MN2wzMAQ6NS2dQ==::E" %2, label %8 [
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 0, label %5
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 1, label %6
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::E" 2, label %7
|
||||
]
|
||||
3:
|
||||
%4 = phi i64 [ 4, %5 ], [ 5, %6 ], [ 6, %7 ], [ -1, %8 ]
|
||||
ret i64 %4
|
||||
5:
|
||||
br label %3
|
||||
6:
|
||||
br label %3
|
||||
7:
|
||||
br label %3
|
||||
8:
|
||||
br label %3
|
||||
}
|
||||
`,
|
||||
`
|
||||
E: Int
|
||||
| one
|
||||
| two
|
||||
| three
|
||||
|
||||
[plus4 e:E]:Int = switch e
|
||||
| E.one 4
|
||||
| E.two 5
|
||||
| E.three 6
|
||||
* -1
|
||||
[f] = [plus4 E.two]
|
||||
`)}
|
||||
|
||||
func TestConstantValueStruct (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" = type { i64, i64 }
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Cardinal.east = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" { i64, i64 } { i64 1, i64 zeroinitializer }
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Cardinal.north = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" { i64, i64 } { i64 zeroinitializer, i64 -1 }
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Cardinal.south = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" { i64, i64 } { i64 zeroinitializer, i64 1 }
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Cardinal.west = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" { i64, i64 } { i64 -1, i64 zeroinitializer }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal"
|
||||
%2 = load %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal", %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" @"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal.west"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::Cardinal" %2, ptr %1
|
||||
ret void
|
||||
}
|
||||
`,
|
||||
`
|
||||
Cardinal: (.x:Int y:Int)
|
||||
| north = (. y: -1)
|
||||
| south = (. y: 1)
|
||||
| west = (.x: -1 )
|
||||
| east = (.x: 1 )
|
||||
[f] = {
|
||||
c:Cardinal = Cardinal.west
|
||||
}
|
||||
`)}
|
||||
|
||||
func TestConstantValueArray (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Order" = type [3 x i64]
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Order.backward = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Order" [3 x i64] [i64 2, i64 1, i64 0]
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Order.forward = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Order" [3 x i64] [i64 0, i64 1, i64 2]
|
||||
0zNZN147MN2wzMAQ6NS2dQ==::Order.none = constant %"0zNZN147MN2wzMAQ6NS2dQ==::Order" [3 x i64] [i64 0, i64 zeroinitializer, i64 zeroinitializer]
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Order"
|
||||
%2 = load %"0zNZN147MN2wzMAQ6NS2dQ==::Order", %"0zNZN147MN2wzMAQ6NS2dQ==::Order" @"0zNZN147MN2wzMAQ6NS2dQ==::Order.forward"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::Order" %2, ptr %1
|
||||
ret void
|
||||
}
|
||||
`,
|
||||
`
|
||||
Order: 3:Int
|
||||
| none = (0)
|
||||
| forward = (0 1 2)
|
||||
| backward = (2 1 0)
|
||||
[f] = {
|
||||
o:Order = Order.forward
|
||||
}
|
||||
`)}
|
@ -4,7 +4,7 @@ import "testing"
|
||||
|
||||
func TestBranchAssign (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
br i1 true, label %2, label %4
|
||||
@ -32,7 +32,7 @@ testString (test,
|
||||
|
||||
func TestBranchAssignNoFalse (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
br i1 true, label %2, label %3
|
||||
@ -55,7 +55,7 @@ testString (test,
|
||||
|
||||
func TestBranchResult (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
br i1 true, label %2, label %5
|
||||
@ -78,7 +78,7 @@ testString (test,
|
||||
|
||||
func TestLoopSimple (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
br label %1
|
||||
1:
|
||||
@ -98,7 +98,7 @@ testString (test,
|
||||
|
||||
func TestLoopResult (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
br label %2
|
||||
@ -121,7 +121,7 @@ testString (test,
|
||||
|
||||
func TestLoopBranchResult (test *testing.T) {
|
||||
testString (test,
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 6, ptr %1
|
||||
@ -162,7 +162,7 @@ testString (test,
|
||||
func TestMatch (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[matchToInt]"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
@ -204,7 +204,7 @@ func TestMatchReturn (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::isInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::[isInt]"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
@ -236,7 +236,7 @@ func TestMatchReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::isInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::[isInt]"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
@ -276,7 +276,7 @@ testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[matchToInt]"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
@ -309,7 +309,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::
|
||||
%18 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %18
|
||||
%19 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %19)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %19)
|
||||
br label %10
|
||||
20:
|
||||
%21 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||
@ -326,10 +326,10 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::
|
||||
%27 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %27
|
||||
%28 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %28)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %28)
|
||||
br label %10
|
||||
}
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str)
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str)
|
||||
`,
|
||||
`
|
||||
U: (| Int F64 UInt)
|
||||
@ -345,7 +345,7 @@ testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[matchToInt]"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
@ -380,7 +380,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::
|
||||
%20 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %20
|
||||
%21 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %21)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %21)
|
||||
br label %12
|
||||
22:
|
||||
%23 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||
@ -397,7 +397,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::
|
||||
%29 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %29
|
||||
%30 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %30)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %30)
|
||||
br label %12
|
||||
31:
|
||||
%32 = getelementptr [3 x i8], ptr %9, i32 0, i32 0
|
||||
@ -411,10 +411,10 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::
|
||||
%36 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %36
|
||||
%37 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %37)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %37)
|
||||
br label %12
|
||||
}
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str)
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str)
|
||||
`,
|
||||
`
|
||||
U: (| Int F64 UInt)
|
||||
@ -430,7 +430,7 @@ U: (| Int F64 UInt)
|
||||
func TestSwitchReturn (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::[is5]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
@ -455,7 +455,7 @@ define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x
|
||||
func TestSwitchReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::[is5]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
@ -482,7 +482,7 @@ define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x
|
||||
func TestIfElseReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::[is5]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
@ -507,7 +507,7 @@ define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x
|
||||
|
||||
func TestIfElseBreakValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::f"(i64 %x) {
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
@ -545,7 +545,7 @@ testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
0:
|
||||
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||
@ -566,7 +566,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %
|
||||
%14 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %13
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %14, ptr %3
|
||||
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %3
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||
%17 = add %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %2
|
||||
@ -574,8 +574,8 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %
|
||||
18:
|
||||
ret void
|
||||
}
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %x)
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::g"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %x)
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
0:
|
||||
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||
@ -597,7 +597,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::g"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %
|
||||
%14 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %13
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %14, ptr %3
|
||||
%15 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", ptr %3
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %15)
|
||||
%16 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %2
|
||||
%17 = add %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %16, 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %17, ptr %2
|
||||
@ -617,7 +617,7 @@ testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" @"0zNZN147MN2wzMAQ6NS2dQ==::f"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) {
|
||||
0:
|
||||
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %str, ptr %1
|
||||
|
28
generator/evaluated-multiplex.go
Normal file
28
generator/evaluated-multiplex.go
Normal file
@ -0,0 +1,28 @@
|
||||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
|
||||
func (this *generator) generateEvaluated (constant entity.Expression) (llvm.Const, error) {
|
||||
switch constant := constant.(type) {
|
||||
case *entity.LiteralInt:
|
||||
return this.generateEvaluatedInt(constant)
|
||||
case *entity.LiteralFloat:
|
||||
return this.generateEvaluatedFloat(constant)
|
||||
case *entity.LiteralString:
|
||||
return this.generateEvaluatedString(constant)
|
||||
case *entity.LiteralArray:
|
||||
return this.generateEvaluatedArray(constant)
|
||||
case *entity.LiteralStruct:
|
||||
return this.generateEvaluatedStruct(constant)
|
||||
case *entity.LiteralBoolean:
|
||||
return this.generateEvaluatedBoolean(constant)
|
||||
case *entity.LiteralNil:
|
||||
return this.generateEvaluatedNil(constant)
|
||||
default:
|
||||
panic(fmt.Sprintf (
|
||||
"BUG: generator doesnt know about constant %v, ty: %T",
|
||||
constant, constant))
|
||||
}
|
||||
}
|
99
generator/evaluated.go
Normal file
99
generator/evaluated.go
Normal file
@ -0,0 +1,99 @@
|
||||
package generator
|
||||
|
||||
import "fmt"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||
import "git.tebibyte.media/fspl/fspl/entity"
|
||||
import "git.tebibyte.media/fspl/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateEvaluatedInt (literal *entity.LiteralInt) (llvm.Const, error) {
|
||||
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
|
||||
if err != nil { return nil, err }
|
||||
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
switch base.(type) {
|
||||
case *entity.TypeInt, *entity.TypeWord:
|
||||
return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil
|
||||
case *entity.TypeFloat:
|
||||
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), float64(literal.Value)), nil
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("constant int can't be used as", base))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateEvaluatedFloat (literal *entity.LiteralFloat) (llvm.Const, 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) generateEvaluatedString (literal *entity.LiteralString) (llvm.Const, error) {
|
||||
// TODO
|
||||
panic("BUG: not supported yet")
|
||||
}
|
||||
|
||||
func (this *generator) generateEvaluatedArray (literal *entity.LiteralArray) (llvm.Const, error) {
|
||||
baseAny := analyzer.ReduceToBase(literal.Type())
|
||||
base, ok := baseAny.(*entity.TypeArray)
|
||||
if !ok { return nil, errors.New(fmt.Sprintln("constant array can't be used as", baseAny)) }
|
||||
|
||||
irDestTypeAny, err := this.generateType(base)
|
||||
if err != nil { return nil, err }
|
||||
irDestType := irDestTypeAny.(*llvm.TypeArray)
|
||||
irElementType, err := this.generateType(base.Element)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
irArray := &llvm.ConstArray {
|
||||
Ty: irDestType,
|
||||
Elements: make([]llvm.Const, base.Length),
|
||||
}
|
||||
for index := range irArray.Elements {
|
||||
if index < len(literal.Elements) {
|
||||
irElement, err := this.generateEvaluated(literal.Elements[index])
|
||||
if err != nil { return nil, err }
|
||||
irArray.Elements[index] = irElement
|
||||
} else {
|
||||
irArray.Elements[index] = llvm.NewConstZeroInitializer(irElementType)
|
||||
}
|
||||
}
|
||||
|
||||
return irArray, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateEvaluatedStruct (literal *entity.LiteralStruct) (llvm.Const, error) {
|
||||
baseAny := analyzer.ReduceToBase(literal.Type())
|
||||
base, ok := baseAny.(*entity.TypeStruct)
|
||||
if !ok { return nil, errors.New(fmt.Sprintln("constant struct can't be used as", baseAny)) }
|
||||
|
||||
irDestTypeAny, err := this.generateType(base)
|
||||
if err != nil { return nil, err }
|
||||
irDestType := irDestTypeAny.(*llvm.TypeStruct)
|
||||
|
||||
irStruct := &llvm.ConstStruct {
|
||||
Ty: irDestType,
|
||||
Fields: make([]llvm.Const, len(base.Members)),
|
||||
}
|
||||
for index, member := range base.Members {
|
||||
if pair, ok := literal.MemberMap[member.Name]; ok {
|
||||
irMember, err := this.generateEvaluated(pair.Value)
|
||||
if err != nil { return nil, err }
|
||||
irStruct.Fields[index] = irMember
|
||||
} else {
|
||||
irMemberType, err := this.generateType(member.Type())
|
||||
if err != nil { return nil, err }
|
||||
irStruct.Fields[index] = llvm.NewConstZeroInitializer(irMemberType)
|
||||
}
|
||||
}
|
||||
|
||||
return irStruct, nil
|
||||
}
|
||||
|
||||
func (this *generator) generateEvaluatedBoolean (literal *entity.LiteralBoolean) (llvm.Const, error) {
|
||||
return llvm.NewConstBool(bool(literal.Value)), nil
|
||||
}
|
||||
|
||||
func (this *generator) generateEvaluatedNil (literal *entity.LiteralNil) (llvm.Const, error) {
|
||||
ty, err := this.generateType(literal.Type())
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstZeroInitializer(ty), nil
|
||||
}
|
@ -160,3 +160,11 @@ func (this *generator) generateMemberAccessLoc (access *entity.MemberAccess) (ll
|
||||
access.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateConstantLoc (constant *entity.Constant) (llvm.Value, error) {
|
||||
return this.constant(entity.Key {
|
||||
Unit: constant.Unit,
|
||||
Type: constant.TypeName,
|
||||
Constant: constant.Name,
|
||||
})
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ func (this *generator) generateExpressionAny (expression entity.Expression) (reg
|
||||
*entity.LiteralString,
|
||||
*entity.LiteralStruct,
|
||||
*entity.Variable,
|
||||
*entity.Declaration:
|
||||
*entity.Declaration,
|
||||
*entity.Constant:
|
||||
|
||||
pointer, err := this.generateExpressionLoc(expression)
|
||||
return pointer, true, err
|
||||
@ -132,7 +133,8 @@ func (this *generator) generateExpressionVal (expression entity.Expression) (llv
|
||||
*entity.LiteralString,
|
||||
*entity.LiteralStruct,
|
||||
*entity.Variable,
|
||||
*entity.Declaration:
|
||||
*entity.Declaration,
|
||||
*entity.Constant:
|
||||
|
||||
pointer, err := this.generateExpressionLoc(expression)
|
||||
if err != nil { return nil, err }
|
||||
@ -249,6 +251,8 @@ func (this *generator) generateExpressionLoc (expression entity.Expression) (llv
|
||||
case *entity.For:
|
||||
loc, _, err := this.generateFor(expression, resultModeLoc)
|
||||
return loc, err
|
||||
case *entity.Constant:
|
||||
return this.generateConstantLoc(expression)
|
||||
case *entity.LiteralArray:
|
||||
return this.generateLiteralArrayLoc(expression, nil)
|
||||
case *entity.LiteralString:
|
||||
|
@ -8,8 +8,8 @@ import "git.tebibyte.media/fspl/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateCallVal (call *entity.Call) (llvm.Value, error) {
|
||||
function, err := this.function(entity.Key {
|
||||
Unit: call.Unit,
|
||||
Name: call.Name,
|
||||
Unit: call.Unit,
|
||||
Function: call.Name,
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
|
||||
@ -45,9 +45,9 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
|
||||
// check for methods on named type
|
||||
if sourceType, ok := call.Source.Type().(*entity.TypeNamed); ok {
|
||||
methodKey := entity.Key {
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Name: sourceType.Name,
|
||||
Method: call.Name,
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Type: sourceType.Name,
|
||||
Function: call.Name,
|
||||
}
|
||||
method, err := this.method(methodKey)
|
||||
if _, notFound := err.(errNotFound); !notFound {
|
||||
@ -62,9 +62,9 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
|
||||
if pointerType, ok := call.Source.Type().(*entity.TypePointer); ok {
|
||||
if sourceType, ok := pointerType.Referenced.(*entity.TypeNamed); ok {
|
||||
method, err := this.method(entity.Key {
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Name: sourceType.Name,
|
||||
Method: call.Name,
|
||||
Unit: sourceType.Type.Unit(),
|
||||
Type: sourceType.Name,
|
||||
Function: call.Name,
|
||||
})
|
||||
if _, notFound := err.(errNotFound); !notFound {
|
||||
source, err := this.generateExpressionVal(call.Source)
|
||||
|
@ -10,8 +10,8 @@ func (this *generator) generateFunction (
|
||||
error,
|
||||
) {
|
||||
key := entity.Key {
|
||||
Unit: function.Unit(),
|
||||
Name: function.Signature.Name,
|
||||
Unit: function.Unit(),
|
||||
Function: function.Signature.Name,
|
||||
}
|
||||
|
||||
params := make([]*llvm.Parameter, len(function.Signature.Arguments))
|
||||
@ -60,9 +60,9 @@ func (this *generator) generateMethod (
|
||||
error,
|
||||
) {
|
||||
key := entity.Key {
|
||||
Unit: method.Unit(),
|
||||
Name: method.TypeName,
|
||||
Method: method.Signature.Name,
|
||||
Unit: method.Unit(),
|
||||
Type: method.TypeName,
|
||||
Function: method.Signature.Name,
|
||||
}
|
||||
|
||||
params := make([]*llvm.Parameter, len(method.Signature.Arguments) + 1)
|
||||
|
@ -19,20 +19,20 @@ define void @main() {
|
||||
|
||||
func TestFunctionCall (test *testing.T) {
|
||||
testString (test,
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::f"() {
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::g"(i64 5)
|
||||
%1 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(i64 5)
|
||||
ret i64 %1
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::g"(i64 %x) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
%2 = load i64, ptr %1
|
||||
%3 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::h"(i64 %2)
|
||||
%3 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[h]"(i64 %2)
|
||||
ret i64 %3
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::h"(i64 %x) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[h]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
@ -49,21 +49,21 @@ define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::h"(i64 %x) {
|
||||
|
||||
func TestFunctionCallVoid (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::f"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[f]"() {
|
||||
0:
|
||||
%1 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::g"(i64 5)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::h"()
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::g"(i64 6)
|
||||
%1 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(i64 5)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[h]"()
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(i64 6)
|
||||
ret void
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::g"(i64 %x) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[g]"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
%2 = load i64, ptr %1
|
||||
ret i64 %2
|
||||
}
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::h"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[h]"() {
|
||||
0:
|
||||
ret void
|
||||
}
|
||||
@ -78,14 +78,14 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::h"() {
|
||||
func TestMethod (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Number" = type i64
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Number"
|
||||
store i64 5, ptr %1
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number"(ptr %1)
|
||||
%2 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number"(ptr %this) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -108,17 +108,17 @@ Number.[number]: Int = [.this]
|
||||
func TestMethodPtr (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Number" = type i64
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Number"
|
||||
store i64 5, ptr %1
|
||||
%2 = alloca ptr
|
||||
store ptr %1, ptr %2
|
||||
%3 = load ptr, ptr %2
|
||||
%4 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number"(ptr %3)
|
||||
%4 = call i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]"(ptr %3)
|
||||
ret i64 %4
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number"(ptr %this) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -142,17 +142,17 @@ Number.[number]: Int = [.this]
|
||||
func TestMethodChained (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Number" = type i64
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 5, ptr %1
|
||||
%2 = call %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.add"(ptr %1, i64 8)
|
||||
%2 = call %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[add]"(ptr %1, i64 8)
|
||||
%3 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Number"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %2, ptr %3
|
||||
%4 = call %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.mul"(ptr %3, i64 3)
|
||||
%4 = call %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[mul]"(ptr %3, i64 3)
|
||||
ret %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %4
|
||||
}
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.mul"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[mul]"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -164,7 +164,7 @@ define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.mu
|
||||
%6 = mul %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %4, %5
|
||||
ret %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %6
|
||||
}
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.add"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[add]"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -176,7 +176,7 @@ define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.ad
|
||||
%6 = add %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %4, %5
|
||||
ret %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %6
|
||||
}
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.div"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[div]"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -188,7 +188,7 @@ define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.di
|
||||
%6 = sdiv %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %4, %5
|
||||
ret %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %6
|
||||
}
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.sub"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
define %"0zNZN147MN2wzMAQ6NS2dQ==::Number" @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[sub]"(ptr %this, %"0zNZN147MN2wzMAQ6NS2dQ==::Number" %x) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -239,10 +239,10 @@ define void @main() {
|
||||
store ptr %3, ptr %10
|
||||
%11 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %2, i32 0, i32 1
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 6, ptr %11
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::Greeter.greet"(ptr %1)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::Greeter.[greet]"(ptr %1)
|
||||
ret void
|
||||
}
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::Greeter.greet"(ptr %this) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::Greeter.[greet]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
|
@ -30,8 +30,9 @@ type generator struct {
|
||||
tree analyzer.Tree
|
||||
module *llvm.Module
|
||||
|
||||
types map[entity.Key] llvm.Type
|
||||
functions map[entity.Key] *llvm.Function
|
||||
types map[entity.Key] llvm.Type
|
||||
functions map[entity.Key] *llvm.Function
|
||||
constants map[entity.Key] *llvm.Global
|
||||
|
||||
managerStack []*blockManager
|
||||
blockManager *blockManager
|
||||
@ -47,25 +48,39 @@ func (this Target) Generate (tree analyzer.Tree) (*llvm.Module, error) {
|
||||
tree: tree,
|
||||
types: make(map[entity.Key] llvm.Type),
|
||||
functions: make(map[entity.Key] *llvm.Function),
|
||||
constants: make(map[entity.Key] *llvm.Global),
|
||||
}).generate()
|
||||
}
|
||||
|
||||
func (this *generator) generate () (*llvm.Module, error) {
|
||||
// generate functions
|
||||
functions := sortKeyedMapKeys(this.tree.Functions)
|
||||
types := sortKeyedMapKeys(this.tree.Types)
|
||||
|
||||
// generate constants
|
||||
for _, key := range types {
|
||||
ty := this.tree.Types[key]
|
||||
constants := sortMapKeys(ty.ConstantMap)
|
||||
for _, constantName := range constants {
|
||||
constantKey := key
|
||||
constantKey.Constant = constantName
|
||||
_, err := this.constant(constantKey)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
}
|
||||
|
||||
// generate functions
|
||||
for _, key := range functions {
|
||||
_, err := this.function(key)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
|
||||
// generate methods
|
||||
types := sortKeyedMapKeys(this.tree.Types)
|
||||
for _, key := range types {
|
||||
ty := this.tree.Types[key]
|
||||
methods := sortMapKeys(ty.Methods)
|
||||
for _, methodName := range methods {
|
||||
methodKey := key
|
||||
methodKey.Method = methodName
|
||||
methodKey.Function = methodName
|
||||
_, err := this.method(methodKey)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
@ -85,20 +100,35 @@ func (this *generator) typedef (key entity.Key) (llvm.Type, error) {
|
||||
return this.generateTypedef(def)
|
||||
}
|
||||
|
||||
func (this *generator) method (key entity.Key) (*llvm.Function, error) {
|
||||
method, exists := this.functions[key]
|
||||
if exists { return method, nil }
|
||||
func (this *generator) constant (key entity.Key) (*llvm.Global, error) {
|
||||
constant, exists := this.constants[key]
|
||||
if exists { return constant, nil }
|
||||
|
||||
ty, exists := this.tree.Types[key.StripMethod()]
|
||||
ty, exists := this.tree.Types[key.OnlyType()]
|
||||
if !exists {
|
||||
return nil, errNotFound("owner of method " + key.String())
|
||||
}
|
||||
|
||||
if method, exists := ty.Methods[key.Method]; exists {
|
||||
if _, exists := ty.ConstantMap[key.Constant]; exists {
|
||||
return this.generateConstantDeclaration(ty, key.Constant)
|
||||
}
|
||||
return nil, errNotFound("constant " + key.String())
|
||||
}
|
||||
|
||||
func (this *generator) method (key entity.Key) (*llvm.Function, error) {
|
||||
method, exists := this.functions[key]
|
||||
if exists { return method, nil }
|
||||
|
||||
ty, exists := this.tree.Types[key.OnlyType()]
|
||||
if !exists {
|
||||
return nil, errNotFound("owner of method " + key.String())
|
||||
}
|
||||
|
||||
if method, exists := ty.Methods[key.Function]; exists {
|
||||
return this.generateMethod(method)
|
||||
}
|
||||
return nil, errNotFound("method " + key.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) function (key entity.Key) (*llvm.Function, error) {
|
||||
function, exists := this.functions[key]
|
||||
|
@ -6,14 +6,14 @@ func TestInterfaceBasic (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Doer" = type { ptr, ptr }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::T" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Doer"
|
||||
%2 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Doer", ptr %1, i32 0, i32 0
|
||||
%3 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::T"
|
||||
store ptr %3, ptr %2
|
||||
%4 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Doer", ptr %1, i32 0, i32 1
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::T.do", ptr %4
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::T.[do]", ptr %4
|
||||
%5 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Doer", ptr %1, i32 0, i32 1
|
||||
%6 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Doer", ptr %1, i32 0, i32 0
|
||||
%7 = load ptr, ptr %6
|
||||
@ -21,7 +21,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
call void %8(ptr %7)
|
||||
ret void
|
||||
}
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::T.do"(ptr %this) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::T.[do]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -44,7 +44,7 @@ func TestInterfaceIntegerReturn (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::Number" = type i64
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Numbered" = type { ptr, ptr }
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Number"
|
||||
store i64 5, ptr %1
|
||||
@ -52,7 +52,7 @@ define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
%3 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Numbered", ptr %2, i32 0, i32 0
|
||||
store ptr %1, ptr %3
|
||||
%4 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Numbered", ptr %2, i32 0, i32 1
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number", ptr %4
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]", ptr %4
|
||||
%5 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Numbered", ptr %2, i32 0, i32 1
|
||||
%6 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Numbered", ptr %2, i32 0, i32 0
|
||||
%7 = load ptr, ptr %6
|
||||
@ -60,7 +60,7 @@ define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
%9 = call i64 %8(ptr %7)
|
||||
ret i64 %9
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.number"(ptr %this) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Number.[number]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -96,12 +96,12 @@ define i64 @main() {
|
||||
%3 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Writer", ptr %2, i32 0, i32 0
|
||||
store ptr %1, ptr %3
|
||||
%4 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Writer", ptr %2, i32 0, i32 1
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::File.write", ptr %4
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::File.[write]", ptr %4
|
||||
%5 = load %"0zNZN147MN2wzMAQ6NS2dQ==::Writer", ptr %2
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"0zNZN147MN2wzMAQ6NS2dQ==::Writer" %5)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[sayHello]"(%"0zNZN147MN2wzMAQ6NS2dQ==::Writer" %5)
|
||||
ret i64 0
|
||||
}
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"0zNZN147MN2wzMAQ6NS2dQ==::Writer" %writer) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[sayHello]"(%"0zNZN147MN2wzMAQ6NS2dQ==::Writer" %writer) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Writer"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::Writer" %writer, ptr %1
|
||||
@ -153,7 +153,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"0zNZN147MN2wzMAQ6NS2dQ==::Wr
|
||||
%28 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %27(ptr %26, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %23)
|
||||
ret void
|
||||
}
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::File.write"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer) {
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::File.[write]"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
@ -165,10 +165,10 @@ define %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::File.write
|
||||
%6 = load ptr, ptr %5
|
||||
%7 = getelementptr { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }, ptr %2, i32 0, i32 1
|
||||
%8 = load %"AAAAAAAAAAAAAAAAAAAAAA==::Index", ptr %7
|
||||
%9 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::write"(%"0zNZN147MN2wzMAQ6NS2dQ==::File" %4, ptr %6, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %8)
|
||||
%9 = call %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::[write]"(%"0zNZN147MN2wzMAQ6NS2dQ==::File" %4, ptr %6, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %8)
|
||||
ret %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %9
|
||||
}
|
||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::write"(%"0zNZN147MN2wzMAQ6NS2dQ==::File" %fd, ptr %buffer, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %count)
|
||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::[write]"(%"0zNZN147MN2wzMAQ6NS2dQ==::File" %fd, ptr %buffer, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" %count)
|
||||
`,
|
||||
`
|
||||
[write fd:File buffer:*Byte count:Index]: Index
|
||||
@ -202,10 +202,10 @@ define void @main() {
|
||||
store i32 0, ptr %4
|
||||
store ptr %4, ptr %3
|
||||
%5 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Writer", ptr %2, i32 0, i32 1
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::File.write", ptr %5
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::File.[write]", ptr %5
|
||||
ret void
|
||||
}
|
||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::File.write"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer)
|
||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"0zNZN147MN2wzMAQ6NS2dQ==::File.[write]"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer)
|
||||
`,
|
||||
`
|
||||
Writer: (& [write buffer:*:Byte]: Index)
|
||||
@ -230,10 +230,10 @@ define void @main() {
|
||||
%3 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::Impl"
|
||||
store ptr %3, ptr %2
|
||||
%4 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::Face", ptr %1, i32 0, i32 1
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::Impl.x", ptr %4
|
||||
store ptr @"0zNZN147MN2wzMAQ6NS2dQ==::Impl.[x]", ptr %4
|
||||
ret void
|
||||
}
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Impl.x"(ptr %this) {
|
||||
define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::Impl.[x]"(ptr %this) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
|
@ -7,24 +7,11 @@ import "git.tebibyte.media/fspl/fspl/entity"
|
||||
import "git.tebibyte.media/fspl/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 }
|
||||
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
switch base.(type) {
|
||||
case *entity.TypeInt, *entity.TypeWord:
|
||||
return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil
|
||||
case *entity.TypeFloat:
|
||||
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), float64(literal.Value)), nil
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("int can't be used as", base))
|
||||
}
|
||||
return this.generateEvaluated(literal)
|
||||
}
|
||||
|
||||
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
|
||||
return this.generateEvaluated(literal)
|
||||
}
|
||||
|
||||
// generateLiteralArrayLoc generates an array literal. irDestLoc specifies the
|
||||
@ -269,11 +256,9 @@ func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct,
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) {
|
||||
return llvm.NewConstBool(bool(literal.Value)), nil
|
||||
return this.generateEvaluated(literal)
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralNil (literal *entity.LiteralNil) (llvm.Value, error) {
|
||||
ty, err := this.generateType(literal.Type())
|
||||
if err != nil { return nil, err }
|
||||
return llvm.NewConstZeroInitializer(ty), nil
|
||||
return this.generateEvaluated(literal)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ func TestPrintDigit (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::printDigit"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %digit) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[printDigit]"(%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %digit) {
|
||||
0:
|
||||
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::Byte"
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::Byte" %digit, ptr %1
|
||||
@ -33,7 +33,7 @@ declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @write(i32 %file, ptr %buffer, %"AAAA
|
||||
|
||||
func TestSignedUnsignedDivision (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
%2 = sdiv i64 20, -5
|
||||
@ -71,7 +71,7 @@ testString (test,
|
||||
|
||||
func TestFloatMath (test *testing.T) {
|
||||
testString (test,
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca double
|
||||
%2 = fadd double 2.0, 3.0
|
||||
@ -95,7 +95,7 @@ func TestCompare (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 32, ptr %1
|
||||
@ -131,7 +131,7 @@ func TestSlice (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %string) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[print]"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %string) {
|
||||
0:
|
||||
%1 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||
store %"AAAAAAAAAAAAAAAAAAAAAA==::String" %string, ptr %1
|
||||
|
@ -5,7 +5,7 @@ import "testing"
|
||||
func TestUnitTwo (test *testing.T) {
|
||||
testUnits (test,
|
||||
`%"E80Pxr3rNdulEDOmHs9Hsg==::X" = type i64
|
||||
define %"E80Pxr3rNdulEDOmHs9Hsg==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define %"E80Pxr3rNdulEDOmHs9Hsg==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
ret i64 5
|
||||
}
|
||||
@ -19,7 +19,7 @@ define %"E80Pxr3rNdulEDOmHs9Hsg==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
func TestUnitAssignRestricted (test *testing.T) {
|
||||
testUnits (test,
|
||||
`%"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt"
|
||||
%2 = alloca %"s0sfKEEPN2W0H9cNKD+OOg==::RestrictedInt"
|
||||
@ -42,7 +42,7 @@ func TestNestedUnitTypedef (test *testing.T) {
|
||||
testUnits (test,
|
||||
`%"kOsrvvRIOh2nYGTmlfGyKQ==::X" = type i64
|
||||
%"qnTuCId6O/ihTdVz5Ln6WQ==::X" = type %"kOsrvvRIOh2nYGTmlfGyKQ==::X"
|
||||
define %"qnTuCId6O/ihTdVz5Ln6WQ==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define %"qnTuCId6O/ihTdVz5Ln6WQ==::X" @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
ret i64 5
|
||||
}
|
||||
@ -72,12 +72,12 @@ define i32 @main() {
|
||||
%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
|
||||
store ptr @"1cqXRNyHN06gi9QQb4GCsg==::File.[write]", ptr %4
|
||||
%5 = load %"1cqXRNyHN06gi9QQb4GCsg==::Writer", ptr %2
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %5)
|
||||
call void @"0zNZN147MN2wzMAQ6NS2dQ==::[sayHello]"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %5)
|
||||
ret i32 0
|
||||
}
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %writer) {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[sayHello]"(%"1cqXRNyHN06gi9QQb4GCsg==::Writer" %writer) {
|
||||
0:
|
||||
%1 = alloca %"1cqXRNyHN06gi9QQb4GCsg==::Writer"
|
||||
store %"1cqXRNyHN06gi9QQb4GCsg==::Writer" %writer, ptr %1
|
||||
@ -101,7 +101,7 @@ define void @"0zNZN147MN2wzMAQ6NS2dQ==::sayHello"(%"1cqXRNyHN06gi9QQb4GCsg==::Wr
|
||||
%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)
|
||||
declare %"AAAAAAAAAAAAAAAAAAAAAA==::Index" @"1cqXRNyHN06gi9QQb4GCsg==::File.[write]"(ptr %this, { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } %buffer)
|
||||
`,
|
||||
`[sayHello writer:io::Writer] = writer.[write 'hi\n']
|
||||
|
||||
@ -130,10 +130,10 @@ testUnits (test,
|
||||
define void @main() {
|
||||
0:
|
||||
%1 = alloca %"9U3Q1r5tPJSXoVNdBSzCow==::T"
|
||||
call void @"9U3Q1r5tPJSXoVNdBSzCow==::T.method"(ptr %1)
|
||||
call void @"9U3Q1r5tPJSXoVNdBSzCow==::T.[method]"(ptr %1)
|
||||
ret void
|
||||
}
|
||||
declare void @"9U3Q1r5tPJSXoVNdBSzCow==::T.method"(ptr %this)
|
||||
declare void @"9U3Q1r5tPJSXoVNdBSzCow==::T.[method]"(ptr %this)
|
||||
`,
|
||||
|
||||
`[main] 'main' = {
|
||||
@ -152,10 +152,10 @@ testUnits (test,
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
%2 = load ptr, ptr %1
|
||||
call void @"9U3Q1r5tPJSXoVNdBSzCow==::T.method"(ptr %2)
|
||||
call void @"9U3Q1r5tPJSXoVNdBSzCow==::T.[method]"(ptr %2)
|
||||
ret void
|
||||
}
|
||||
declare void @"9U3Q1r5tPJSXoVNdBSzCow==::T.method"(ptr %this)
|
||||
declare void @"9U3Q1r5tPJSXoVNdBSzCow==::T.[method]"(ptr %this)
|
||||
`,
|
||||
|
||||
`[main] 'main' = {
|
||||
|
@ -12,12 +12,13 @@ func (this *generator) generateTypedef (
|
||||
) {
|
||||
key := entity.Key {
|
||||
Unit: def.Unit(),
|
||||
Name: def.Name,
|
||||
Type: def.Name,
|
||||
}
|
||||
irType, err := this.generateType(def.Type)
|
||||
if err != nil { return nil, err }
|
||||
irTypeDef := this.module.NewType(key.LinkName(), irType)
|
||||
this.types[key] = irTypeDef
|
||||
|
||||
return irTypeDef, nil
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import "git.tebibyte.media/fspl/fspl/entity"
|
||||
|
||||
func (this *generator) generateTypeIndex () (*llvm.TypeInt, error) {
|
||||
ty, err := this.typedef(entity.Key {
|
||||
Name: "Index",
|
||||
Type: "Index",
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
return ty.(*llvm.TypeInt), nil
|
||||
@ -17,7 +17,7 @@ func (this *generator) generateTypeIndex () (*llvm.TypeInt, error) {
|
||||
func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) {
|
||||
underlying, err := this.typedef(entity.Key {
|
||||
Unit: ty.Type.Unit(),
|
||||
Name: ty.Name,
|
||||
Type: ty.Name,
|
||||
})
|
||||
if err != nil { return nil, err }
|
||||
return &llvm.TypeDefined { Source: underlying }, nil
|
||||
|
@ -17,7 +17,7 @@ testString (test,
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Path" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Quadrangle" = type [4 x %"0zNZN147MN2wzMAQ6NS2dQ==::Point"]
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::AllTypes" = type { %"AAAAAAAAAAAAAAAAAAAAAA==::String", %"0zNZN147MN2wzMAQ6NS2dQ==::Pegasus", %"0zNZN147MN2wzMAQ6NS2dQ==::Rectangle", %"0zNZN147MN2wzMAQ6NS2dQ==::AllInts", %"0zNZN147MN2wzMAQ6NS2dQ==::AllFloats", %"0zNZN147MN2wzMAQ6NS2dQ==::Path", %"0zNZN147MN2wzMAQ6NS2dQ==::Quadrangle" }
|
||||
declare %"0zNZN147MN2wzMAQ6NS2dQ==::AllTypes" @"0zNZN147MN2wzMAQ6NS2dQ==::x"()
|
||||
declare %"0zNZN147MN2wzMAQ6NS2dQ==::AllTypes" @"0zNZN147MN2wzMAQ6NS2dQ==::[x]"()
|
||||
`,
|
||||
`
|
||||
Point: (.x:Int y:Int)
|
||||
@ -67,7 +67,7 @@ testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type ptr
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::B" = type ptr
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::C" = type ptr
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::A"
|
||||
%2 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::B"
|
||||
@ -90,7 +90,7 @@ testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type ptr
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::B" = type %"0zNZN147MN2wzMAQ6NS2dQ==::A"
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::C" = type %"0zNZN147MN2wzMAQ6NS2dQ==::B"
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::A"
|
||||
%2 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::B"
|
||||
@ -112,7 +112,7 @@ func TestChainedTypedefGetElementPtr (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type { i64 }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::B" = type %"0zNZN147MN2wzMAQ6NS2dQ==::A"
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::B"
|
||||
%2 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::B", ptr %1, i32 0, i32 0
|
||||
@ -139,7 +139,7 @@ testString (test,
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Point" = type { i64, i64 }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Error" = type { ptr, ptr }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::PointOrError" = type { i64, i128 }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
%2 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::SmallU"
|
||||
|
@ -5,7 +5,7 @@ import "testing"
|
||||
func TestAssignmentUnion (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca double
|
||||
store double 0x401ECCCCCCCCCCCD, ptr %1
|
||||
@ -29,7 +29,7 @@ U: (| Int F64)
|
||||
func TestAssignmentUnionToUnion (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::[main]"() {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
%2 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
|
@ -74,7 +74,10 @@ expression, the parser follows this decision tree to determine what to parse:
|
||||
| | 'loop' =Loop
|
||||
| | 'for' =For
|
||||
| | +Colon =Declaration
|
||||
| | +DoubleColon =Call
|
||||
| | +DoubleColon | +TypeIdent =Constant
|
||||
| | +LBracket =Call
|
||||
|
|
||||
| +TypeIdent =Constant
|
||||
|
|
||||
| +LParen X =LiteralArray
|
||||
| | +Dot =LiteralStruct
|
||||
|
@ -7,6 +7,7 @@ import "git.tebibyte.media/fspl/fspl/entity"
|
||||
var descriptionExpression = "expression"
|
||||
var startTokensExpression = []lexer.TokenKind {
|
||||
lexer.Ident,
|
||||
lexer.TypeIdent,
|
||||
lexer.LParen,
|
||||
lexer.LBracket,
|
||||
lexer.LBrace,
|
||||
@ -95,13 +96,14 @@ func (this *treeParser) parseExpressionRoot () (entity.Expression, error) {
|
||||
if err != nil { return nil, err }
|
||||
|
||||
switch this.Kind() {
|
||||
case lexer.Ident: return this.parseExpressionRootIdent()
|
||||
case lexer.LParen: return this.parseExpressionRootLParen()
|
||||
case lexer.LBracket: return this.parseExpressionRootLBracket()
|
||||
case lexer.LBrace: return this.parseBlock()
|
||||
case lexer.Int: return this.parseLiteralInt()
|
||||
case lexer.Float: return this.parseLiteralFloat()
|
||||
case lexer.String: return this.parseLiteralString()
|
||||
case lexer.Ident: return this.parseExpressionRootIdent()
|
||||
case lexer.TypeIdent: return this.parseConstantCore(this.Pos(), "")
|
||||
case lexer.LParen: return this.parseExpressionRootLParen()
|
||||
case lexer.LBracket: return this.parseExpressionRootLBracket()
|
||||
case lexer.LBrace: return this.parseBlock()
|
||||
case lexer.Int: return this.parseLiteralInt()
|
||||
case lexer.Float: return this.parseLiteralFloat()
|
||||
case lexer.String: return this.parseLiteralString()
|
||||
}
|
||||
panic(this.bug())
|
||||
}
|
||||
@ -127,11 +129,19 @@ func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
|
||||
// Colon: declaration
|
||||
return this.parseDeclarationCore(pos, name)
|
||||
case lexer.DoubleColon:
|
||||
// DoubleColon: call
|
||||
err := this.ExpectNext(lexer.LBracket)
|
||||
// DoubleColon: call, constant
|
||||
err := this.ExpectNext(lexer.LBracket, lexer.TypeIdent)
|
||||
if err != nil { return nil, err }
|
||||
this.Next()
|
||||
return this.parseCallCore(pos, name)
|
||||
|
||||
switch this.Kind() {
|
||||
case lexer.TypeIdent:
|
||||
// TypeIdent: constant
|
||||
return this.parseConstantCore(pos, name)
|
||||
case lexer.LBracket:
|
||||
// LBracket: call
|
||||
this.Next()
|
||||
return this.parseCallCore(pos, name)
|
||||
}
|
||||
default:
|
||||
// *: variable
|
||||
return &entity.Variable {
|
||||
@ -140,6 +150,7 @@ func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
panic(this.bug())
|
||||
}
|
||||
|
||||
func (this *treeParser) parseExpressionRootLParen () (entity.Expression, error) {
|
||||
@ -155,7 +166,6 @@ func (this *treeParser) parseExpressionRootLParen () (entity.Expression, error)
|
||||
case lexer.Dot: return this.parseLiteralStructCore(pos)
|
||||
default: return this.parseLiteralArrayCore(pos)
|
||||
}
|
||||
panic(this.bug())
|
||||
}
|
||||
|
||||
func (this *treeParser) parseExpressionRootLBracket () (entity.Expression, error) {
|
||||
@ -717,3 +727,25 @@ func (this *treeParser) parseFor () (*entity.For, error) {
|
||||
|
||||
return loop, nil
|
||||
}
|
||||
|
||||
func (this *treeParser) parseConstantCore (pos errors.Position, unitNickname string) (*entity.Constant, error) {
|
||||
pos = pos.Union(this.Pos())
|
||||
err := this.Expect(lexer.TypeIdent)
|
||||
if err != nil { return nil, err }
|
||||
constant := &entity.Constant {
|
||||
Pos: this.Pos(),
|
||||
UnitNickname: unitNickname,
|
||||
TypeName: this.Value(),
|
||||
}
|
||||
|
||||
err = this.ExpectNext(lexer.Dot)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
err = this.ExpectNext(lexer.Ident)
|
||||
if err != nil { return nil, err }
|
||||
constant.Pos = constant.Pos.Union(this.Pos())
|
||||
constant.Name = this.Value()
|
||||
|
||||
this.Next()
|
||||
return constant, nil
|
||||
}
|
||||
|
@ -11,7 +11,11 @@ testString (test,
|
||||
- Union: (| Int F64 *:U8 (. x:Int y:Int))
|
||||
- Array: 16:16:16:Int
|
||||
- StructArray: 31:24:340920:(. x:Int y:Int)
|
||||
- String: *:U8`,
|
||||
- String: *:U8
|
||||
+ FileDescriptor: I32
|
||||
| stdin = 0
|
||||
| stdout = 1
|
||||
| stderr = 2`,
|
||||
// input
|
||||
`
|
||||
BasicInt: Int
|
||||
@ -29,6 +33,10 @@ StructArray: 31:24:340920:(.
|
||||
x:Int
|
||||
y:Int)
|
||||
String: *:U8
|
||||
+ FileDescriptor: I32
|
||||
| stdin = 0
|
||||
| stdout = 1
|
||||
| stderr = 2
|
||||
`)
|
||||
}
|
||||
|
||||
@ -75,7 +83,9 @@ testString (test,
|
||||
- [for s:String] = for i:Index e:Byte in str if [> i 3] then [break e]
|
||||
- [for s:String] = for e:Byte in s [break e]
|
||||
- [matchToInt u:(| Int F64)]:Int = match u | u:Int u | u:F64 [~ Int u] * 0
|
||||
- [switch x:Int] = switch x | 0 5 | 1 4 | 2 3 * 0`,
|
||||
- [switch x:Int] = switch x | 0 5 | 1 4 | 2 3 * 0
|
||||
- [whatFile fd:FD]:String = switch fd | FD.stdin 'in' | FD.stdout 'out' * '?'
|
||||
- [whatFile2 fd:io::FD]:String = switch fd | io::FD.stdin 'in' | io::FD.stdout 'out' * '?'`,
|
||||
// input
|
||||
`
|
||||
[var] = sdfdf
|
||||
@ -134,6 +144,14 @@ testString (test,
|
||||
| 1 4
|
||||
| 2 3
|
||||
* 0
|
||||
[whatFile fd:FD]:String = switch fd
|
||||
| FD.stdin 'in'
|
||||
| FD.stdout 'out'
|
||||
* '?'
|
||||
[whatFile2 fd:io::FD]:String = switch fd
|
||||
| io::FD.stdin 'in'
|
||||
| io::FD.stdout 'out'
|
||||
* '?'
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,14 @@ func (this *treeParser) parseFunctionCore (pos errors.Position, access entity.Ac
|
||||
return function, nil
|
||||
}
|
||||
|
||||
func (this *treeParser) parseMethodCore (pos errors.Position, access entity.Access, typeName string) (*entity.Method, error) {
|
||||
func (this *treeParser) parseMethodCore (
|
||||
pos errors.Position,
|
||||
access entity.Access,
|
||||
typeName string,
|
||||
) (
|
||||
*entity.Method,
|
||||
error,
|
||||
) {
|
||||
function, err := this.parseFunctionCore(pos, access)
|
||||
if err != nil { return nil, err }
|
||||
return &entity.Method {
|
||||
@ -139,14 +146,52 @@ func (this *treeParser) parseMethodCore (pos errors.Position, access entity.Acce
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *treeParser) parseTypedefCore (pos errors.Position, access entity.Access, typeName string) (*entity.Typedef, error) {
|
||||
func (this *treeParser) parseTypedefCore (
|
||||
pos errors.Position,
|
||||
access entity.Access,
|
||||
typeName string,
|
||||
) (
|
||||
*entity.Typedef,
|
||||
error,
|
||||
) {
|
||||
pos = pos.Union(this.Pos())
|
||||
ty, err := this.parseType()
|
||||
if err != nil { return nil, err }
|
||||
return &entity.Typedef {
|
||||
|
||||
typedef := &entity.Typedef {
|
||||
Pos: pos,
|
||||
Acc: access,
|
||||
Name: typeName,
|
||||
Type: ty,
|
||||
}, nil
|
||||
}
|
||||
|
||||
for this.Is(lexer.Symbol) && this.ValueIs("|") {
|
||||
constant, err := this.parseConstantDeclaration()
|
||||
if err != nil { return nil, err }
|
||||
typedef.Constants = append(typedef.Constants, constant)
|
||||
}
|
||||
|
||||
return typedef, nil
|
||||
}
|
||||
|
||||
func (this *treeParser) parseConstantDeclaration () (*entity.ConstantDeclaration, error) {
|
||||
err := this.ExpectValue(lexer.Symbol, "|")
|
||||
if err != nil { return nil, err }
|
||||
declaration := &entity.ConstantDeclaration {
|
||||
Pos: this.Pos(),
|
||||
}
|
||||
|
||||
err = this.ExpectNext(lexer.Ident)
|
||||
if err != nil { return nil, err }
|
||||
declaration.Name = this.Value()
|
||||
this.Next()
|
||||
|
||||
if this.Is(lexer.Symbol) && this.ValueIs("=") {
|
||||
this.Next()
|
||||
value, err := this.parseExpression()
|
||||
if err != nil { return nil, err }
|
||||
declaration.Value = value
|
||||
}
|
||||
|
||||
return declaration, nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import "testing"
|
||||
import "strings"
|
||||
import "strconv"
|
||||
import "encoding/hex"
|
||||
import "git.tebibyte.media/fspl/fspl/errors"
|
||||
|
||||
// LogColumns logs columns of text side by side, each column 80 characters wide.
|
||||
func LogColumns (test *testing.T, width int, columns ...any) {
|
||||
@ -62,3 +63,44 @@ func CompareHex (test *testing.T, correct, got string) {
|
||||
got = hex.Dump([]byte(got))
|
||||
Compare(test, correct, got)
|
||||
}
|
||||
|
||||
// CompareErr compares a correct error with a result error, and causes the test
|
||||
// to fail and returns false if they differ. Otherwise, it returns true.
|
||||
func CompareErr (
|
||||
test *testing.T,
|
||||
errFile string,
|
||||
errMessage string,
|
||||
errRow int,
|
||||
errStart int,
|
||||
err error,
|
||||
) bool {
|
||||
got := err.(errors.Error)
|
||||
gotMessage := got.Error()
|
||||
gotFile := got.Position().File
|
||||
gotRow := got.Position().Row + 1
|
||||
gotStart := got.Position().Start + 1
|
||||
|
||||
correct :=
|
||||
gotFile == errFile &&
|
||||
gotMessage == errMessage &&
|
||||
gotRow == errRow &&
|
||||
gotStart == errStart
|
||||
if correct { return true }
|
||||
|
||||
test.Log("errors do not match:")
|
||||
if gotMessage != errMessage {
|
||||
test.Log("- messages do not match:")
|
||||
test.Logf(" [%s]", gotMessage)
|
||||
test.Logf(" [%s]", errMessage)
|
||||
}
|
||||
if gotRow != errRow {
|
||||
test.Logf("- rows do not match: (%d, %d)", gotRow, errRow)
|
||||
}
|
||||
if gotStart != errStart {
|
||||
test.Logf("- columns do not match: (%d, %d)", gotStart, errStart)
|
||||
}
|
||||
test.Log("got:\n" + errors.Format(got))
|
||||
test.Logf("correct:\n%v:%v: %v", errRow, errStart, errMessage)
|
||||
test.Fail()
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user