Compare commits

...

40 Commits

Author SHA1 Message Date
0c8a9d995b Added array and struct constant generation 2024-04-22 21:13:08 -04:00
86ad94f17d Updated test for constant generation 2024-04-22 20:24:19 -04:00
9ffed9bd94 The absolute SLOG! 2024-04-22 20:20:49 -04:00
ec7bd32184 Re-named some things relating to constants 2024-04-22 20:03:25 -04:00
298b819b46 Re-add generating constants 2024-04-22 19:56:28 -04:00
1d1908cd5d Im gonna have to rewrite this whole compiler arent i 2024-04-21 12:27:22 -04:00
c13c4caacb Add Unit() method to semantic tree 2024-04-21 12:24:18 -04:00
cc5173dd52 Keys now support constants 2024-04-21 11:31:41 -04:00
f4480fe6b1 Generator no longer generates constants because I assumed untrue things 2024-04-21 11:01:07 -04:00
f05efb749d Generator generates constants 2024-04-21 00:19:33 -04:00
8248135255 Switch statements now evaluate constants 2024-04-20 23:56:50 -04:00
e104da363c Break functionality out of analyzeTypedef 2024-04-20 23:50:53 -04:00
65af9d7f3c Perform constant evaluation during constant declaration analysis 2024-04-20 23:47:12 -04:00
b5f5fa4598 Added eval tests 2024-04-20 23:40:41 -04:00
33768c6d84 Added CompareErr to testcommon 2024-04-20 23:07:38 -04:00
293662e474 Errors no longer directly depends on testcommon 2024-04-20 23:07:16 -04:00
334a45e06f Oops 2024-04-12 00:44:53 -04:00
373db2ae81 Added stub for constant expression evaluator 2024-04-12 00:43:24 -04:00
034d9661b3 Add back switching on constants to the test 2024-04-12 00:12:49 -04:00
7bad3d5672 Update analyzer tests accordingly 2024-04-12 00:09:06 -04:00
3485fa1820 Changed constant declaration syntax to not be ambiguous 2024-04-12 00:07:22 -04:00
b822d57757 Fix an error being ignored when analyzing typedefs 2024-04-11 23:57:47 -04:00
0a54232e29 Remove tests with switching on constants for now 2024-04-11 23:55:40 -04:00
94272bff03 Pass some tests 2024-04-11 23:22:27 -04:00
6ba8b5465b Add untested constant access analysis 2024-04-11 22:30:31 -04:00
0fd35343c1 Untested analysis of constant declarations 2024-04-11 21:50:05 -04:00
d88a34d638 Scope constant declarations 2024-04-11 21:01:55 -04:00
fc930fa3d4 Added a Description() method to Expression 2024-04-11 20:46:59 -04:00
40d500b705 Add IsConstant() to Expression 2024-04-11 20:25:16 -04:00
6313992c10 Added analyzer tests for constants 2024-04-07 23:47:30 -04:00
e12e83921b Parse constant access unit specifiers 2024-04-06 14:01:14 -04:00
2346c34cce OOps 2024-04-06 13:51:11 -04:00
d8f897d023 Add unit specifiers to constant access 2024-04-06 13:49:11 -04:00
386c39126d Parse constant declarations 2024-04-06 13:40:03 -04:00
7d3074ea2f Parse constant access 2024-04-03 13:32:45 -04:00
d568d5efef Add parser tests for constants 2024-04-03 13:32:26 -04:00
7d8378f7d8 Fix FSPL parser readme tree 2024-04-03 12:52:02 -04:00
7ea24b8b31 Add constants to entity 2024-04-03 12:41:31 -04:00
172b2fc8db Added constants to the spec 2024-04-03 11:33:00 -04:00
c7191ff678 Add enumerated constant access to parser readme 2024-04-03 11:26:49 -04:00
53 changed files with 1581 additions and 421 deletions

View File

@ -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())
}
}

View File

@ -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
View 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]
`)}

View File

@ -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

View File

@ -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:

View File

@ -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 {

View File

@ -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

View File

@ -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 }

View File

@ -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'
`)}

View File

@ -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) {

View File

@ -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
}

View File

@ -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,

View File

@ -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
}

View File

@ -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> -> "+" | "++" | "-" | "--" | "*" | "/" | "%"
| "!!" | "||" | "&&" | "^^"
| "!" | "|" | "&" | "^" | "<<" | ">>"
| "<" | ">" | "<=" | ">=" | "="
```

View File

@ -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
}

View File

@ -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
}

View File

@ -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" }

View File

@ -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)
}

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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))
}

View 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
View File

@ -0,0 +1,2 @@
// Package eval provides a way to evaluate FSPL expressions.
package eval

106
eval/eval.go Normal file
View 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
View 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
View 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
`)}

View File

@ -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 }

View File

@ -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
View 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
View 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
}
`)}

View File

@ -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

View 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
View 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
}

View File

@ -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,
})
}

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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' = {

View File

@ -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
}

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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
}

View File

@ -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'
* '?'
`)
}

View File

@ -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
}

View File

@ -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
}