Untested analysis of constant declarations

This commit is contained in:
Sasha Koshka 2024-04-11 21:50:05 -04:00
parent d88a34d638
commit 0fd35343c1
6 changed files with 118 additions and 24 deletions

View File

@ -381,7 +381,17 @@ func (this *Tree) isLocationExpression (expression entity.Expression) error {
case *entity.MemberAccess:
return this.isLocationExpression(expression.Source)
default:
return errors.Errorf(pos, "cannot assign to %s", expression.Description())
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

@ -73,7 +73,7 @@ Weekday: String
// name?
func TestErrConstantStringUnspecified (test *testing.T) {
testStringErr (test,
"cannot fill in constant values for non-numeric type Weekday", 5, 11
"cannot fill in constant values for non-numeric type Weekday", 5, 11,
`
Weekday: String
| sunday

View File

@ -363,6 +363,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
@ -1015,13 +1016,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

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

@ -33,22 +33,81 @@ func (this *Tree) analyzeTypedef (
}
}
// 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.Name)
}
// 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)
// assemble constant map
definition, err = this.assembleTypedefConstantMap(definition)
if err != nil { return nil, err }
// analyze constants
fillerValue := 0
for index, constant := range definition.Constants {
constant, err := this.analyzeConstantDeclaration(constant, definition.Type)
if err != nil { return nil, err }
if constant.Value == nil {
if !isNumeric(definition.Type) {
return nil, errors.Errorf (
pos, "cannot fill in constant values for non-numeric type %v",
definition.Type)
}
constant.Value = &entity.LiteralInt {
Pos: pos,
Value: fillerValue,
Ty: definition.Type,
}
fillerValue ++
}
definition.ConstantMap[constant.Name] = constant
definition.ConstantOrder[index] = constant.Name
definition.Constants[index] = constant
}
return definition, err
}
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 }
// only allow values that can be used as constants
err = this.isConstant(value)
if err != nil { return nil, err }
}
// TODO after analysis, check if constant
return constant, nil
}
func (this *Tree) analyzeType (
ty entity.Type,
acceptIncomplete bool,
@ -303,3 +362,24 @@ 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,
) (
*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 nil, 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 typedef, nil
}

View File

@ -31,9 +31,10 @@ type Typedef struct {
Constants []*ConstantDeclaration
// Semantics
Unt uuid.UUID
Methods map[string] *Method
ConstantsMap map[string] *ConstantDeclaration
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 }