Merge pull request 'analyzer-fix-bitcast' (#50) from analyzer-fix-bitcast into main

Reviewed-on: #50
This commit is contained in:
Sasha Koshka 2024-02-29 00:34:41 +00:00
commit 2a0233e18d
5 changed files with 146 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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

View File

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