diff --git a/analyzer/assignment.go b/analyzer/assignment.go index 2c18123..34ceba0 100644 --- a/analyzer/assignment.go +++ b/analyzer/assignment.go @@ -483,6 +483,14 @@ func isInteger (ty entity.Type) bool { } } +// isPointer returns whether or not the specified type is a pointer. +func isPointer (ty entity.Type) bool { + switch ReduceToBase(ty).(type) { + case *entity.TypePointer: return true + default: return false + } +} + // isOrdered returns whether or not the values of the specified type can be // ordered. func isOrdered (ty entity.Type) bool { @@ -515,6 +523,15 @@ func IsUnsigned (ty entity.Type) bool { } } + +// integerWidth returns the width of the type, if it is an integer. +func integerWidth (ty entity.Type) (int, bool) { + switch ty := ReduceToBase(ty).(type) { + case *entity.TypeInt: return ty.Width, true + default: return 0, false + } +} + // inRange returns whether the specified value can fit within the given integer // type. func inRange (ty entity.Type, value int64) bool { diff --git a/analyzer/expression-multiplex.go b/analyzer/expression-multiplex.go index c5ef8be..c346f28 100644 --- a/analyzer/expression-multiplex.go +++ b/analyzer/expression-multiplex.go @@ -46,6 +46,8 @@ func (this *Tree) analyzeExpression ( return this.analyzeIfElse(into, mode, expression) case *entity.Match: return this.analyzeMatch(into, mode, expression) + case *entity.Switch: + return this.analyzeSwitch(into, mode, expression) case *entity.Loop: return this.analyzeLoop(into, mode, expression) case *entity.For: diff --git a/analyzer/expression.go b/analyzer/expression.go index a617053..fb671e2 100644 --- a/analyzer/expression.go +++ b/analyzer/expression.go @@ -803,6 +803,87 @@ func (this *Tree) assembleMatchMap (match *entity.Match) (*entity.Match, error) return match, nil } +func (this *Tree) analyzeSwitch ( + into entity.Type, + mode strictness, + switc *entity.Switch, +) ( + entity.Expression, + error, +) { + value, err := this.analyzeExpression(nil, strict, switc.Value) + if err != nil { return nil, err } + switc.Value = value + width, integerOk := integerWidth(value.Type()) + if !integerOk { + return nil, errors.Errorf ( + value.Position(), "cannot switch on type %v", + value.Type()) + } + + switc.CaseOrder = make([]int64, len(switc.Cases)) + switc.CaseMap = make(map[int64] *entity.SwitchCase) + for index, cas := range switc.Cases { + + this.pushScope(cas) + key, err := this.analyzeExpression ( + value.Type(), strict, + cas.Key) + if err != nil { this.popScope(); return nil, err } + cas.Key = key + expression, err := this.analyzeExpression ( + into, mode, + cas.Expression) + if err != nil { this.popScope(); return nil, err } + cas.Expression = expression + this.popScope() + + var keyInt int64 + switch key := key.(type) { + case *entity.LiteralInt: + keyInt = int64(key.Value) + case *entity.LiteralString: + switch { + case width >= 32: + keyInt = int64(key.ValueUTF32[0]) + case width >= 16: + keyInt = int64(key.ValueUTF16[0]) + default: + keyInt = int64(key.ValueUTF8[0]) + } + default: + return nil, errors.Errorf ( + key.Position(), "%v cannot represent a constant integer", + key) + } + + switc.Cases[index] = cas + switc.CaseMap[keyInt] = cas + switc.CaseOrder[index] = keyInt + } + + if switc.Default != nil { + this.pushScope(switc.Default) + expression, err := this.analyzeExpression ( + into, mode, + switc.Default.Expression) + this.popScope() + + if err != nil { return nil, err } + switc.Default.Expression = expression + } + + if into != nil && switc.Default != nil { + return nil, errors.Errorf ( + switc.Position(), + "switch must have a default case") + } + + switc.Ty = into + + return switc, nil +} + func (this *Tree) analyzeLoop ( into entity.Type, mode strictness, diff --git a/entity/expression.go b/entity/expression.go index 226efac..a5fe8e3 100644 --- a/entity/expression.go +++ b/entity/expression.go @@ -459,7 +459,7 @@ type Switch struct { // Semantics Ty Type CaseOrder []int64 - CaseMap map[uint64] *MatchCase + CaseMap map[int64] *SwitchCase } func (*Switch) expression(){} func (this *Switch) Position () errors.Position { return this.Pos }