Merge pull request 'analyzer-fix-bitcast' (#50) from analyzer-fix-bitcast into main
Reviewed-on: #50
This commit is contained in:
commit
2a0233e18d
|
@ -89,3 +89,34 @@ IntDerived: Int
|
|||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestBitCastPointer (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
Struct: (. x:Int y:Int)
|
||||
Array: 4:Int
|
||||
[main] = {
|
||||
ptr:*Byte
|
||||
stc:Struct
|
||||
arr:Array
|
||||
str:String
|
||||
idx:Index
|
||||
|
||||
; struct
|
||||
ptr = [~~*Byte [~~Struct ptr]]
|
||||
stc = [~~Struct [~~*Byte stc]]
|
||||
; array
|
||||
ptr = [~~*Byte [~~Array ptr]]
|
||||
arr = [~~Array [~~*Byte arr]]
|
||||
; slice
|
||||
ptr = [~~*Byte [~~String ptr]]
|
||||
str = [~~String [~~*Byte str]]
|
||||
; int
|
||||
ptr = [~~*Byte [~~Index ptr]]
|
||||
idx = [~~Index [~~*Byte idx]]
|
||||
|
||||
; arithmetic
|
||||
ptr = [~~*Byte [+ 1 [~~Index ptr]]]
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
|
|
@ -417,6 +417,14 @@ func (this *Tree) analyzeOperation (
|
|||
err := this.typeRestricted(operation.Position, into)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
intoUntrustworthy := into == nil || mode == force || mode == coerce
|
||||
|
||||
undefined := func (ty entity.Type) (entity.Expression, error) {
|
||||
return nil, errors.Errorf (
|
||||
operation.Position, "operator %v undefined for %v",
|
||||
operation.Operator, entity.FormatType(ty))
|
||||
}
|
||||
|
||||
wrongInto := func () (entity.Expression, error) {
|
||||
return nil, errors.Errorf (
|
||||
operation.Position, "expected %v",
|
||||
|
@ -428,6 +436,28 @@ func (this *Tree) analyzeOperation (
|
|||
operation.Position, "wrong argument count for %v",
|
||||
operation.Operator)
|
||||
}
|
||||
|
||||
determineArgumentType := func () (entity.Type, int, error) {
|
||||
// find the first argument that has explicit type information.
|
||||
boss := -1
|
||||
var argumentType entity.Type
|
||||
for index, argument := range operation.Arguments {
|
||||
if argument.HasExplicitType() {
|
||||
argument, err := this.analyzeExpression(nil, strict, argument)
|
||||
if err != nil { return nil, -1, err }
|
||||
boss = index
|
||||
operation.Arguments[index] = argument
|
||||
argumentType = argument.Type()
|
||||
break
|
||||
}
|
||||
}
|
||||
if argumentType == nil {
|
||||
return nil, -1, errors.Errorf (
|
||||
operation.Position,
|
||||
"operation arguments have ambiguous type")
|
||||
}
|
||||
return argumentType, boss, err
|
||||
}
|
||||
|
||||
nSameType := func (n int, constraint func (entity.Type) bool) (entity.Expression, error) {
|
||||
if n > 0 {
|
||||
|
@ -436,17 +466,29 @@ func (this *Tree) analyzeOperation (
|
|||
if len(operation.Arguments) < 1 { return wrongArgCount() }
|
||||
}
|
||||
n = len(operation.Arguments)
|
||||
if !constraint(into) { return wrongInto() }
|
||||
|
||||
left, err := this.analyzeExpression(into, mode, operation.Arguments[0])
|
||||
if err != nil { return nil, err }
|
||||
operation.Arguments[0] = left
|
||||
operation.Ty = left.Type()
|
||||
boss := -1
|
||||
var argumentType entity.Type
|
||||
if intoUntrustworthy {
|
||||
// determine the type of all arguments (and the return
|
||||
// type) using determineArgumentType
|
||||
var err error
|
||||
argumentType, boss, err = determineArgumentType()
|
||||
if err != nil { return nil, err }
|
||||
if !constraint(argumentType) { return undefined(argumentType) }
|
||||
} else {
|
||||
// into is trustworthy, so we don't need to determine
|
||||
// anything
|
||||
argumentType = into
|
||||
}
|
||||
if !constraint(argumentType) { return undefined(argumentType) }
|
||||
operation.Ty = argumentType
|
||||
|
||||
for index := 1; index < n; index ++ {
|
||||
for index, argument := range operation.Arguments {
|
||||
if index == boss { continue }
|
||||
argument, err := this.analyzeExpression (
|
||||
left.Type(), strict,
|
||||
operation.Arguments[index])
|
||||
operation.Ty, strict,
|
||||
argument)
|
||||
if err != nil { return nil, err }
|
||||
operation.Arguments[index] = argument
|
||||
}
|
||||
|
@ -455,41 +497,21 @@ func (this *Tree) analyzeOperation (
|
|||
}
|
||||
|
||||
comparison := func (argConstraint func (entity.Type) bool) (entity.Expression, error) {
|
||||
if len(operation.Arguments) < 2 { return wrongArgCount() }
|
||||
if !isBoolean(into) { return wrongInto() }
|
||||
operation.Ty = builtinType("Bool")
|
||||
if len(operation.Arguments) < 2 { return wrongArgCount() }
|
||||
if !isBoolean(into) && !intoUntrustworthy { return wrongInto() }
|
||||
operation.Ty = builtinType("Bool")
|
||||
|
||||
// find the first argument that has explicit type information.
|
||||
// TODO: possibly make a method of expressions to check this
|
||||
// without analyzing anything
|
||||
boss := -1
|
||||
var argumentType entity.Type
|
||||
for index, argument := range operation.Arguments {
|
||||
argument, err := this.analyzeExpression(nil, strict, argument)
|
||||
if err == nil {
|
||||
boss = index
|
||||
operation.Arguments[index] = argument
|
||||
argumentType = argument.Type()
|
||||
break
|
||||
}
|
||||
}
|
||||
if argumentType == nil {
|
||||
return nil, errors.Errorf (
|
||||
operation.Position,
|
||||
"operation arguments have ambiguous type")
|
||||
}
|
||||
// determine argument type
|
||||
argumentType, boss, err := determineArgumentType()
|
||||
if err != nil { return nil, err }
|
||||
if !argConstraint(argumentType) { return undefined(argumentType) }
|
||||
|
||||
// analyze all remaining arguments
|
||||
for index, argument := range operation.Arguments {
|
||||
if index == boss { continue }
|
||||
|
||||
argument, err := this.analyzeExpression (
|
||||
argumentType, strict, argument)
|
||||
if err != nil {
|
||||
boss = index
|
||||
operation.Arguments[index] = argument
|
||||
break
|
||||
}
|
||||
if err != nil { return nil, err }
|
||||
operation.Arguments[index] = argument
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import "git.tebibyte.media/fspl/fspl/errors"
|
|||
// Expression is any construct that can be evaluated.
|
||||
type Expression interface {
|
||||
Type () Type
|
||||
HasExplicitType () bool
|
||||
expression ()
|
||||
}
|
||||
|
||||
|
@ -24,6 +25,7 @@ type Variable struct {
|
|||
}
|
||||
func (*Variable) expression(){}
|
||||
func (this *Variable) Type () Type { return this.Declaration.Type() }
|
||||
func (this *Variable) HasExplicitType () bool { return true }
|
||||
func (this *Variable) String () string {
|
||||
return this.Name
|
||||
}
|
||||
|
@ -40,6 +42,7 @@ type Declaration struct {
|
|||
}
|
||||
func (*Declaration) expression(){}
|
||||
func (this *Declaration) Type () Type { return this.Ty }
|
||||
func (this *Declaration) HasExplicitType () bool { return true }
|
||||
func (this *Declaration) String () string {
|
||||
return fmt.Sprint(this.Name, ":", this.Ty)
|
||||
}
|
||||
|
@ -63,6 +66,7 @@ type Call struct {
|
|||
}
|
||||
func (*Call) expression(){}
|
||||
func (this *Call) Type () Type { return this.Function.Signature.Return }
|
||||
func (this *Call) HasExplicitType () bool { return true }
|
||||
func (this *Call) String () string {
|
||||
out := ""
|
||||
if this.UnitNickname != "" {
|
||||
|
@ -101,6 +105,7 @@ func (this *MethodCall) Type () Type {
|
|||
return this.Behavior.Return
|
||||
}
|
||||
}
|
||||
func (this *MethodCall) HasExplicitType () bool { return true }
|
||||
func (this *MethodCall) String () string {
|
||||
out := fmt.Sprint(this.Source, ".[", this.Name)
|
||||
for _, argument := range this.Arguments {
|
||||
|
@ -125,6 +130,7 @@ type Subscript struct {
|
|||
}
|
||||
func (*Subscript) expression(){}
|
||||
func (this *Subscript) Type () Type { return this.Ty }
|
||||
func (this *Subscript) HasExplicitType () bool { return true }
|
||||
func (this *Subscript) String () string {
|
||||
return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]")
|
||||
}
|
||||
|
@ -142,6 +148,7 @@ type Slice struct {
|
|||
}
|
||||
func (*Slice) expression(){}
|
||||
func (this *Slice) Type () Type { return this.Slice.Type() }
|
||||
func (this *Slice) HasExplicitType () bool { return true }
|
||||
func (this *Slice) String () string {
|
||||
out := fmt.Sprint("[\\", this.Slice, " ")
|
||||
if this.Start != nil {
|
||||
|
@ -166,6 +173,7 @@ type Length struct {
|
|||
}
|
||||
func (*Length) expression(){}
|
||||
func (this *Length) Type () Type { return this.Ty }
|
||||
func (this *Length) HasExplicitType () bool { return true }
|
||||
func (this *Length) String () string {
|
||||
return fmt.Sprint("[#", this.Slice, "]")
|
||||
}
|
||||
|
@ -185,6 +193,7 @@ type Dereference struct {
|
|||
}
|
||||
func (*Dereference) expression(){}
|
||||
func (this *Dereference) Type () Type { return this.Ty }
|
||||
func (this *Dereference) HasExplicitType () bool { return true }
|
||||
func (this *Dereference) String () string {
|
||||
return fmt.Sprint("[.", this.Pointer, "]")
|
||||
}
|
||||
|
@ -206,6 +215,7 @@ type Reference struct {
|
|||
}
|
||||
func (*Reference) expression(){}
|
||||
func (this *Reference) Type () Type { return this.Ty }
|
||||
func (this *Reference) HasExplicitType () bool { return true }
|
||||
func (this *Reference) String () string {
|
||||
return fmt.Sprint("[@", this.Value, "]")
|
||||
}
|
||||
|
@ -220,6 +230,7 @@ type ValueCast struct {
|
|||
}
|
||||
func (*ValueCast) expression(){}
|
||||
func (this *ValueCast) Type () Type { return this.Ty }
|
||||
func (this *ValueCast) HasExplicitType () bool { return true }
|
||||
func (this *ValueCast) String () string {
|
||||
return fmt.Sprint("[~ ", this.Ty, this.Value, "]")
|
||||
}
|
||||
|
@ -235,6 +246,7 @@ type BitCast struct {
|
|||
}
|
||||
func (*BitCast) expression(){}
|
||||
func (this *BitCast) Type () Type { return this.Ty }
|
||||
func (this *BitCast) HasExplicitType () bool { return true }
|
||||
func (this *BitCast) String () string {
|
||||
return fmt.Sprint("[~~ ", this.Ty, this.Value, "]")
|
||||
}
|
||||
|
@ -254,6 +266,9 @@ type Operation struct {
|
|||
}
|
||||
func (*Operation) expression(){}
|
||||
func (this *Operation) Type () Type { return this.Ty }
|
||||
func (this *Operation) HasExplicitType () bool {
|
||||
return this.Operator.ResultsInBoolean()
|
||||
}
|
||||
func (this *Operation) String () string {
|
||||
out := fmt.Sprint("[", this.Operator)
|
||||
for _, argument := range this.Arguments {
|
||||
|
@ -278,6 +293,10 @@ type Block struct {
|
|||
}
|
||||
func (*Block) expression(){}
|
||||
func (this *Block) Type () Type { return this.Ty }
|
||||
func (this *Block) HasExplicitType () bool {
|
||||
if len(this.Steps) == 0 { return false }
|
||||
return this.Steps[len(this.Steps) - 1].HasExplicitType()
|
||||
}
|
||||
func (this *Block) String () string {
|
||||
out := "{"
|
||||
for index, step := range this.Steps {
|
||||
|
@ -304,6 +323,7 @@ type MemberAccess struct {
|
|||
}
|
||||
func (*MemberAccess) expression(){}
|
||||
func (this *MemberAccess) Type () Type { return this.Ty }
|
||||
func (this *MemberAccess) HasExplicitType () bool { return true }
|
||||
func (this *MemberAccess) String () string {
|
||||
return fmt.Sprint(this.Source, ".", this.Member)
|
||||
}
|
||||
|
@ -325,6 +345,9 @@ type IfElse struct {
|
|||
}
|
||||
func (*IfElse) expression(){}
|
||||
func (this *IfElse) Type () Type { return this.Ty }
|
||||
func (this *IfElse) HasExplicitType () bool {
|
||||
return this.True.HasExplicitType()
|
||||
}
|
||||
func (this *IfElse) String () string {
|
||||
out := fmt.Sprint("if ", this.Condition, " then ", this.True)
|
||||
if this.False != nil {
|
||||
|
@ -350,6 +373,11 @@ type Loop struct {
|
|||
}
|
||||
func (*Loop) expression(){}
|
||||
func (this *Loop) Type () Type { return this.Ty }
|
||||
func (this *Loop) HasExplicitType () bool {
|
||||
// this is as accurate as possible without doing a full analysis of the
|
||||
// loop
|
||||
return false
|
||||
}
|
||||
func (this *Loop) String () string {
|
||||
return fmt.Sprint("loop ", this.Body)
|
||||
}
|
||||
|
@ -366,6 +394,7 @@ type Break struct {
|
|||
}
|
||||
func (*Break) expression(){}
|
||||
func (this *Break) Type () Type { return nil }
|
||||
func (this *Break) HasExplicitType () bool { return false }
|
||||
func (this *Break) String () string {
|
||||
if this.Value == nil {
|
||||
return "[break]"
|
||||
|
@ -388,6 +417,7 @@ type Return struct {
|
|||
}
|
||||
func (*Return) expression(){}
|
||||
func (this *Return) Type () Type { return nil }
|
||||
func (this *Return) HasExplicitType () bool { return false }
|
||||
func (this *Return) String () string {
|
||||
if this.Value == nil {
|
||||
return "[return]"
|
||||
|
@ -407,6 +437,7 @@ type Assignment struct {
|
|||
}
|
||||
func (*Assignment) expression(){}
|
||||
func (this *Assignment) Type () Type { return nil }
|
||||
func (this *Assignment) HasExplicitType () bool { return false }
|
||||
func (this *Assignment) String () string {
|
||||
return fmt.Sprint(this.Location, "=", this.Value)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ type LiteralInt struct {
|
|||
}
|
||||
func (*LiteralInt) expression(){}
|
||||
func (this *LiteralInt) Type () Type { return this.Ty }
|
||||
func (this *LiteralInt) HasExplicitType () bool { return false }
|
||||
func (this *LiteralInt) String () string {
|
||||
return fmt.Sprint(this.Value)
|
||||
}
|
||||
|
@ -36,6 +37,7 @@ type LiteralFloat struct {
|
|||
}
|
||||
func (*LiteralFloat) expression(){}
|
||||
func (this *LiteralFloat) Type () Type { return this.Ty }
|
||||
func (this *LiteralFloat) HasExplicitType () bool { return false }
|
||||
func (this *LiteralFloat) String () string {
|
||||
return fmt.Sprint(this.Value)
|
||||
}
|
||||
|
@ -67,6 +69,7 @@ type LiteralString struct {
|
|||
}
|
||||
func (*LiteralString) expression(){}
|
||||
func (this *LiteralString) Type () Type { return this.Ty }
|
||||
func (this *LiteralString) HasExplicitType () bool { return false }
|
||||
func (this *LiteralString) String () string {
|
||||
return Quote(this.ValueUTF8)
|
||||
}
|
||||
|
@ -88,6 +91,7 @@ type LiteralArray struct {
|
|||
}
|
||||
func (*LiteralArray) expression(){}
|
||||
func (this *LiteralArray) Type () Type { return this.Ty }
|
||||
func (this *LiteralArray) HasExplicitType () bool { return false }
|
||||
func (this *LiteralArray) String () string {
|
||||
out := "(*"
|
||||
for _, element := range this.Elements {
|
||||
|
@ -115,6 +119,7 @@ type LiteralStruct struct {
|
|||
}
|
||||
func (*LiteralStruct) expression(){}
|
||||
func (this *LiteralStruct) Type () Type { return this.Ty }
|
||||
func (this *LiteralStruct) HasExplicitType () bool { return false }
|
||||
func (this *LiteralStruct) String () string {
|
||||
out := "(."
|
||||
for _, member := range this.Members {
|
||||
|
@ -137,6 +142,7 @@ type LiteralBoolean struct {
|
|||
}
|
||||
func (*LiteralBoolean) expression(){}
|
||||
func (this *LiteralBoolean) Type () Type { return this.Ty }
|
||||
func (this *LiteralBoolean) HasExplicitType () bool { return false }
|
||||
func (this *LiteralBoolean) String () string {
|
||||
if this.Value {
|
||||
return "true"
|
||||
|
@ -154,4 +160,5 @@ type LiteralNil struct {
|
|||
}
|
||||
func (*LiteralNil) expression(){}
|
||||
func (this *LiteralNil) Type () Type { return this.Ty }
|
||||
func (this *LiteralNil) HasExplicitType () bool { return false }
|
||||
func (this *LiteralNil) String () string { return "nil" }
|
||||
|
|
|
@ -95,6 +95,25 @@ type Operator int; const (
|
|||
OperatorEqual
|
||||
)
|
||||
|
||||
// ResultsInBoolean determines whether or not this operation will always result
|
||||
// in a boolean value.
|
||||
func (operator Operator) ResultsInBoolean () bool {
|
||||
switch operator {
|
||||
case OperatorLogicalNot,
|
||||
OperatorLogicalOr,
|
||||
OperatorLogicalAnd,
|
||||
OperatorLogicalXor,
|
||||
OperatorLess,
|
||||
OperatorGreater,
|
||||
OperatorLessEqual,
|
||||
OperatorGreaterEqual,
|
||||
OperatorEqual:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var operatorToString = []string {
|
||||
// Math
|
||||
"+",
|
||||
|
|
Loading…
Reference in New Issue