Untested analysis of constant declarations
This commit is contained in:
parent
d88a34d638
commit
0fd35343c1
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
104
analyzer/type.go
104
analyzer/type.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in New Issue