Merge pull request 'implement-switch-expressions' (#70) from implement-switch-expressions into main
Reviewed-on: #70
This commit is contained in:
commit
6e3125ce62
@ -475,7 +475,7 @@ func isNumeric (ty entity.Type) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isInteger returns whether or not the specified type is an integer.
|
// isInteger returns whether or not the specified type is an integer or word.
|
||||||
func isInteger (ty entity.Type) bool {
|
func isInteger (ty entity.Type) bool {
|
||||||
switch ReduceToBase(ty).(type) {
|
switch ReduceToBase(ty).(type) {
|
||||||
case *entity.TypeInt, *entity.TypeWord: return true
|
case *entity.TypeInt, *entity.TypeWord: return true
|
||||||
@ -483,6 +483,22 @@ func isInteger (ty entity.Type) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isWord returns whether or not the specified type is a word.
|
||||||
|
func isWord (ty entity.Type) bool {
|
||||||
|
switch ReduceToBase(ty).(type) {
|
||||||
|
case *entity.TypeWord: return true
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// isOrdered returns whether or not the values of the specified type can be
|
||||||
// ordered.
|
// ordered.
|
||||||
func isOrdered (ty entity.Type) bool {
|
func isOrdered (ty entity.Type) bool {
|
||||||
@ -515,6 +531,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
|
// inRange returns whether the specified value can fit within the given integer
|
||||||
// type.
|
// type.
|
||||||
func inRange (ty entity.Type, value int64) bool {
|
func inRange (ty entity.Type, value int64) bool {
|
||||||
|
@ -33,6 +33,16 @@ U: (| Int F64)
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatchDefault (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
U: (| Int F64)
|
||||||
|
[isInt u:U]:Bool = match u
|
||||||
|
| u:Int [return true]
|
||||||
|
* false
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMatchUnionUnderComplete (test *testing.T) {
|
func TestMatchUnionUnderComplete (test *testing.T) {
|
||||||
testString (test,
|
testString (test,
|
||||||
`
|
`
|
||||||
@ -90,6 +100,78 @@ U: (| Int F64 UInt)
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSwitch (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
[switch x:Int]:Int = switch x
|
||||||
|
| 0 5
|
||||||
|
| 1 4
|
||||||
|
| 2 3
|
||||||
|
* 0
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchReturn (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
[is5 x:Int]:Bool = {
|
||||||
|
switch x | 5 [return true]
|
||||||
|
false
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchReturnValueUsed (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`
|
||||||
|
[is5 x:Int]:Bool = switch x
|
||||||
|
| 5 [return true]
|
||||||
|
* false
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchErrBadLiteral (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"cannot use array literal as Int", 4, 4,
|
||||||
|
`
|
||||||
|
[switch x:Int]:Int = switch x
|
||||||
|
| 0 5
|
||||||
|
| (1) 4
|
||||||
|
| 2 3
|
||||||
|
* 0
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchErrNotConstant (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"y cannot represent a constant integer", 7, 4,
|
||||||
|
`
|
||||||
|
[switch x:Int]:Int = {
|
||||||
|
y:Int = 1
|
||||||
|
|
||||||
|
switch x
|
||||||
|
| 0 5
|
||||||
|
| y 4
|
||||||
|
| 2 3
|
||||||
|
* 0
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchErrDuplicate (test *testing.T) {
|
||||||
|
testStringErr (test,
|
||||||
|
"65 already listed in match at stream0.fspl:6:2", 7, 4,
|
||||||
|
`
|
||||||
|
[switch x:I8]:Int = switch x
|
||||||
|
| 0 5
|
||||||
|
| 1 4
|
||||||
|
| 2 3
|
||||||
|
| 65 2
|
||||||
|
| 'A' 1
|
||||||
|
* 0
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIfElseReturnValueUsed (test *testing.T) {
|
func TestIfElseReturnValueUsed (test *testing.T) {
|
||||||
testString (test,
|
testString (test,
|
||||||
`
|
`
|
||||||
|
@ -46,6 +46,8 @@ func (this *Tree) analyzeExpression (
|
|||||||
return this.analyzeIfElse(into, mode, expression)
|
return this.analyzeIfElse(into, mode, expression)
|
||||||
case *entity.Match:
|
case *entity.Match:
|
||||||
return this.analyzeMatch(into, mode, expression)
|
return this.analyzeMatch(into, mode, expression)
|
||||||
|
case *entity.Switch:
|
||||||
|
return this.analyzeSwitch(into, mode, expression)
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
return this.analyzeLoop(into, mode, expression)
|
return this.analyzeLoop(into, mode, expression)
|
||||||
case *entity.For:
|
case *entity.For:
|
||||||
|
@ -759,7 +759,21 @@ func (this *Tree) analyzeMatch (
|
|||||||
match.CaseMap[hash] = cas
|
match.CaseMap[hash] = cas
|
||||||
}
|
}
|
||||||
|
|
||||||
if into != nil && len(match.Cases) < len(rawUnion.Allowed) {
|
if match.Default != nil {
|
||||||
|
this.pushScope(match.Default)
|
||||||
|
expression, err := this.analyzeExpression (
|
||||||
|
into, mode,
|
||||||
|
match.Default.Expression)
|
||||||
|
this.popScope()
|
||||||
|
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
match.Default.Expression = expression
|
||||||
|
}
|
||||||
|
|
||||||
|
allCasesCovered :=
|
||||||
|
len(match.Cases) == len(rawUnion.Allowed) ||
|
||||||
|
match.Default != nil
|
||||||
|
if into != nil && !allCasesCovered {
|
||||||
return nil, errors.Errorf (
|
return nil, errors.Errorf (
|
||||||
match.Position(),
|
match.Position(),
|
||||||
"match does not cover all types within %v",
|
"match does not cover all types within %v",
|
||||||
@ -772,7 +786,7 @@ func (this *Tree) analyzeMatch (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Tree) assembleMatchMap (match *entity.Match) (*entity.Match, error) {
|
func (this *Tree) assembleMatchMap (match *entity.Match) (*entity.Match, error) {
|
||||||
match.CaseMap = make(map[entity.Hash] *entity.Case)
|
match.CaseMap = make(map[entity.Hash] *entity.MatchCase)
|
||||||
match.CaseOrder = make([]entity.Hash, len(match.Cases))
|
match.CaseOrder = make([]entity.Hash, len(match.Cases))
|
||||||
for index, cas := range match.Cases {
|
for index, cas := range match.Cases {
|
||||||
hash := cas.Declaration.Ty.Hash()
|
hash := cas.Declaration.Ty.Hash()
|
||||||
@ -789,6 +803,104 @@ func (this *Tree) assembleMatchMap (match *entity.Match) (*entity.Match, error)
|
|||||||
return match, nil
|
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())
|
||||||
|
wordOk := isWord(value.Type())
|
||||||
|
if !integerOk && !wordOk {
|
||||||
|
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:
|
||||||
|
if !integerOk {
|
||||||
|
return nil, errors.Errorf (
|
||||||
|
key.Position(),
|
||||||
|
"cannot use string literal when " +
|
||||||
|
"switching on %v",
|
||||||
|
value.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if previous, exists := switc.CaseMap[keyInt]; exists {
|
||||||
|
return nil, errors.Errorf (
|
||||||
|
cas.Key.Position(),
|
||||||
|
"%v already listed in match at %v",
|
||||||
|
keyInt, previous.Position())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
func (this *Tree) analyzeLoop (
|
||||||
into entity.Type,
|
into entity.Type,
|
||||||
mode strictness,
|
mode strictness,
|
||||||
|
@ -82,6 +82,23 @@ testUnit (test,
|
|||||||
0,
|
0,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
func TestMatchDefaultPrint (test *testing.T) {
|
||||||
|
dependencies := []string {
|
||||||
|
compileDependency(test, "io"),
|
||||||
|
}
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/match-default-print", dependencies,
|
||||||
|
"", "something else\n",
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
|
||||||
|
func TestSwitchExitCode (test *testing.T) {
|
||||||
|
testUnit (test,
|
||||||
|
"/test-data/data/switch-exit-code", nil,
|
||||||
|
"", "",
|
||||||
|
4,
|
||||||
|
)}
|
||||||
|
|
||||||
func TestReturnAssign (test *testing.T) {
|
func TestReturnAssign (test *testing.T) {
|
||||||
dependencies := []string {
|
dependencies := []string {
|
||||||
compileDependency(test, "io"),
|
compileDependency(test, "io"),
|
||||||
|
2
compiler/test-data/data/match-default-print/fspl.mod
Normal file
2
compiler/test-data/data/match-default-print/fspl.mod
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
'4f3d6ccb-c233-4648-abd2-72e3f9a82efd'
|
||||||
|
+ 'io'
|
11
compiler/test-data/data/match-default-print/main.fspl
Normal file
11
compiler/test-data/data/match-default-print/main.fspl
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[main]: I32 'main' = {
|
||||||
|
x:UInt = 7
|
||||||
|
[matchToInt x]
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
U: (| Int F64 UInt)
|
||||||
|
[matchToInt u:U] = match u
|
||||||
|
| u:Int io::[println 'Int']
|
||||||
|
| u:F64 io::[println 'F64']
|
||||||
|
* io::[println 'something else']
|
1
compiler/test-data/data/switch-exit-code/fspl.mod
Normal file
1
compiler/test-data/data/switch-exit-code/fspl.mod
Normal file
@ -0,0 +1 @@
|
|||||||
|
'433ed4ee-be14-4d0f-baef-a13919ec3b6c'
|
10
compiler/test-data/data/switch-exit-code/main.fspl
Normal file
10
compiler/test-data/data/switch-exit-code/main.fspl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[main]: I32 'main' = {
|
||||||
|
x:I32 = 1
|
||||||
|
[switch x]
|
||||||
|
}
|
||||||
|
|
||||||
|
[switch x:I32]:I32 = switch x
|
||||||
|
| 0 5
|
||||||
|
| 1 4
|
||||||
|
| 2 3
|
||||||
|
* 0
|
@ -266,8 +266,15 @@ value. Each case takes the form of a declaration, and an associated expression.
|
|||||||
If the type of the union matches the type of the declaration in the case, the
|
If the type of the union matches the type of the declaration in the case, the
|
||||||
expression is executed and the value of the union is made available to it
|
expression is executed and the value of the union is made available to it
|
||||||
through the declaration. If the value of the match expression is used, all
|
through the declaration. If the value of the match expression is used, all
|
||||||
possible types in the union must be accounted for. It may be assigned to any
|
possible types in the union must be accounted for, or it must have a default
|
||||||
type that satisfies the assignment rules of its first case.
|
case. It may be assigned to any type that satisfies the assignment rules of its
|
||||||
|
first case.
|
||||||
|
### Switch
|
||||||
|
Switch is a control flow branching expression that executes one of several case
|
||||||
|
expressions depending on the value of the input. It accepts any pointer or
|
||||||
|
integer type. If the value of the switch expression is used, a default case must
|
||||||
|
be present. It may be assigned to any type that satisfies the assignment rules
|
||||||
|
of its first case.
|
||||||
### Loop
|
### Loop
|
||||||
Loop is a control flow expression that repeats an expression until a break
|
Loop is a control flow expression that repeats an expression until a break
|
||||||
statement is called from within it. The break statement must be given a value
|
statement is called from within it. The break statement must be given a value
|
||||||
@ -366,7 +373,8 @@ this without hand-writing a parser.
|
|||||||
<ifelse> -> "if" <expression>
|
<ifelse> -> "if" <expression>
|
||||||
"then" <expression>
|
"then" <expression>
|
||||||
["else" <expression>]
|
["else" <expression>]
|
||||||
<match> -> "match" <expression> <case>*
|
<match> -> "match" <expression> <matchCase>* [<defaultCase>]
|
||||||
|
<switch> -> "switch" <expression> <switchCase>* [<defaultCase>]
|
||||||
<loop> -> "loop" <expression>
|
<loop> -> "loop" <expression>
|
||||||
<for> -> "for" <expression> [<expression>] in <expression> <expression>
|
<for> -> "for" <expression> [<expression>] in <expression> <expression>
|
||||||
<break> -> "[" "break" [<expression>] "]"
|
<break> -> "[" "break" [<expression>] "]"
|
||||||
@ -384,7 +392,9 @@ this without hand-writing a parser.
|
|||||||
<booleanLiteral> -> "true" | "false"
|
<booleanLiteral> -> "true" | "false"
|
||||||
|
|
||||||
<member> -> <identifier> ":" <expression>
|
<member> -> <identifier> ":" <expression>
|
||||||
<case> -> "|" <declaration> <expression>
|
<matchCase> -> "|" <declaration> <expression>
|
||||||
|
<switchCase> -> "|" <expression> <expression>
|
||||||
|
<defaultCase> -> "*" <expression>
|
||||||
<signature> -> "[" <identifier> <declaration>* "]" [":" <type>]
|
<signature> -> "[" <identifier> <declaration>* "]" [":" <type>]
|
||||||
<identifier> -> /[a-z][A-Za-z]*/
|
<identifier> -> /[a-z][A-Za-z]*/
|
||||||
<typeIdentifier> -> /[A-Z][A-Za-z]*/
|
<typeIdentifier> -> /[A-Z][A-Za-z]*/
|
||||||
|
@ -414,12 +414,13 @@ type Match struct {
|
|||||||
// Syntax
|
// Syntax
|
||||||
Pos errors.Position
|
Pos errors.Position
|
||||||
Value Expression
|
Value Expression
|
||||||
Cases []*Case
|
Cases []*MatchCase
|
||||||
|
Default *DefaultCase
|
||||||
|
|
||||||
// Semantics
|
// Semantics
|
||||||
Ty Type
|
Ty Type
|
||||||
CaseOrder []Hash
|
CaseOrder []Hash
|
||||||
CaseMap map[Hash] *Case
|
CaseMap map[Hash] *MatchCase
|
||||||
}
|
}
|
||||||
func (*Match) expression(){}
|
func (*Match) expression(){}
|
||||||
func (this *Match) Position () errors.Position { return this.Pos }
|
func (this *Match) Position () errors.Position { return this.Pos }
|
||||||
@ -436,6 +437,48 @@ func (this *Match) String () string {
|
|||||||
for _, cas := range this.Cases {
|
for _, cas := range this.Cases {
|
||||||
out += fmt.Sprint(" ", cas)
|
out += fmt.Sprint(" ", cas)
|
||||||
}
|
}
|
||||||
|
if this.Default != nil {
|
||||||
|
out += fmt.Sprint(" ", this.Default)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Expression = &Switch { }
|
||||||
|
// Switch is a control flow branching expression that executes one of several
|
||||||
|
// case expressions depending on the value of the input. It accepts any pointer
|
||||||
|
// or integer type. If the value of the switch expression is used, a default
|
||||||
|
// case must be present. It may be assigned to any type that satisfies the
|
||||||
|
// assignment rules of its first case.
|
||||||
|
type Switch struct {
|
||||||
|
// Syntax
|
||||||
|
Pos errors.Position
|
||||||
|
Value Expression
|
||||||
|
Cases []*SwitchCase
|
||||||
|
Default *DefaultCase
|
||||||
|
|
||||||
|
// Semantics
|
||||||
|
Ty Type
|
||||||
|
CaseOrder []int64
|
||||||
|
CaseMap map[int64] *SwitchCase
|
||||||
|
}
|
||||||
|
func (*Switch) expression(){}
|
||||||
|
func (this *Switch) Position () errors.Position { return this.Pos }
|
||||||
|
func (this *Switch) Type () Type { return this.Ty }
|
||||||
|
func (this *Switch) HasExplicitType () bool {
|
||||||
|
if len(this.Cases) == 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return this.Cases[0].HasExplicitType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (this *Switch) String () string {
|
||||||
|
out := fmt.Sprint("switch ", this.Value)
|
||||||
|
for _, cas := range this.Cases {
|
||||||
|
out += fmt.Sprint(" ", cas)
|
||||||
|
}
|
||||||
|
if this.Default != nil {
|
||||||
|
out += fmt.Sprint(" ", this.Default)
|
||||||
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,18 +101,41 @@ func (this *Member) String () string {
|
|||||||
return fmt.Sprint(this.Name, ":", this.Value)
|
return fmt.Sprint(this.Name, ":", this.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case represents a match case.
|
// MatchCase represents a case in a match expression.
|
||||||
type Case struct {
|
type MatchCase struct {
|
||||||
Pos errors.Position
|
Pos errors.Position
|
||||||
Declaration *Declaration
|
Declaration *Declaration
|
||||||
Expression
|
Expression
|
||||||
Scope
|
Scope
|
||||||
}
|
}
|
||||||
func (this *Case) Position () errors.Position { return this.Pos }
|
func (this *MatchCase) Position () errors.Position { return this.Pos }
|
||||||
func (this *Case) String () string {
|
func (this *MatchCase) String () string {
|
||||||
return fmt.Sprint("| ", this.Declaration, this.Expression)
|
return fmt.Sprint("| ", this.Declaration, this.Expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SwitchCase represents a case in a switch expression.
|
||||||
|
type SwitchCase struct {
|
||||||
|
Pos errors.Position
|
||||||
|
Key Expression
|
||||||
|
Expression
|
||||||
|
Scope
|
||||||
|
}
|
||||||
|
func (this *SwitchCase) Position () errors.Position { return this.Pos }
|
||||||
|
func (this *SwitchCase) String () string {
|
||||||
|
return fmt.Sprint("| ", this.Key, this.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultCase represents the default case in a switch or match expression.
|
||||||
|
type DefaultCase struct {
|
||||||
|
Pos errors.Position
|
||||||
|
Expression
|
||||||
|
Scope
|
||||||
|
}
|
||||||
|
func (this *DefaultCase) Position () errors.Position { return this.Pos }
|
||||||
|
func (this *DefaultCase) String () string {
|
||||||
|
return fmt.Sprint("* ", this.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
// Operator determines which operation to apply to a value in an operation
|
// Operator determines which operation to apply to a value in an operation
|
||||||
// expression.
|
// expression.
|
||||||
type Operator int; const (
|
type Operator int; const (
|
||||||
|
@ -340,6 +340,145 @@ U: (| Int F64 UInt)
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatchDefault (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64
|
||||||
|
%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" }
|
||||||
|
define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||||
|
0:
|
||||||
|
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||||
|
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||||
|
%2 = alloca i64
|
||||||
|
%3 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
%4 = alloca [3 x i8]
|
||||||
|
%5 = alloca double
|
||||||
|
%6 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
%7 = alloca [3 x i8]
|
||||||
|
%8 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String"
|
||||||
|
%9 = alloca [3 x i8]
|
||||||
|
%10 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 0
|
||||||
|
%11 = load i64, ptr %10
|
||||||
|
switch i64 %11, label %31 [
|
||||||
|
i64 7620767046192759206, label %13
|
||||||
|
i64 9186060094042213285, label %22
|
||||||
|
]
|
||||||
|
12:
|
||||||
|
ret void
|
||||||
|
13:
|
||||||
|
%14 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||||
|
%15 = load i64, ptr %14
|
||||||
|
store i64 %15, ptr %2
|
||||||
|
%16 = getelementptr [3 x i8], ptr %4, i32 0, i32 0
|
||||||
|
store i8 73, ptr %16
|
||||||
|
%17 = getelementptr [3 x i8], ptr %4, i32 0, i32 1
|
||||||
|
store i8 110, ptr %17
|
||||||
|
%18 = getelementptr [3 x i8], ptr %4, i32 0, i32 2
|
||||||
|
store i8 116, ptr %18
|
||||||
|
%19 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 0
|
||||||
|
store ptr %4, ptr %19
|
||||||
|
%20 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %20
|
||||||
|
%21 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3
|
||||||
|
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %21)
|
||||||
|
br label %12
|
||||||
|
22:
|
||||||
|
%23 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||||
|
%24 = load double, ptr %23
|
||||||
|
store double %24, ptr %5
|
||||||
|
%25 = getelementptr [3 x i8], ptr %7, i32 0, i32 0
|
||||||
|
store i8 70, ptr %25
|
||||||
|
%26 = getelementptr [3 x i8], ptr %7, i32 0, i32 1
|
||||||
|
store i8 54, ptr %26
|
||||||
|
%27 = getelementptr [3 x i8], ptr %7, i32 0, i32 2
|
||||||
|
store i8 52, ptr %27
|
||||||
|
%28 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 0
|
||||||
|
store ptr %7, ptr %28
|
||||||
|
%29 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %29
|
||||||
|
%30 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6
|
||||||
|
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %30)
|
||||||
|
br label %12
|
||||||
|
31:
|
||||||
|
%32 = getelementptr [3 x i8], ptr %9, i32 0, i32 0
|
||||||
|
store i8 105, ptr %32
|
||||||
|
%33 = getelementptr [3 x i8], ptr %9, i32 0, i32 1
|
||||||
|
store i8 100, ptr %33
|
||||||
|
%34 = getelementptr [3 x i8], ptr %9, i32 0, i32 2
|
||||||
|
store i8 107, ptr %34
|
||||||
|
%35 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8, i32 0, i32 0
|
||||||
|
store ptr %9, ptr %35
|
||||||
|
%36 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8, i32 0, i32 1
|
||||||
|
store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %36
|
||||||
|
%37 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8
|
||||||
|
call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %37)
|
||||||
|
br label %12
|
||||||
|
}
|
||||||
|
declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str)
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
U: (| Int F64 UInt)
|
||||||
|
[print str:String]
|
||||||
|
[matchToInt u:U] = match u
|
||||||
|
| u:Int [print 'Int']
|
||||||
|
| u:F64 [print 'F64']
|
||||||
|
* [print 'idk']
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestSwitchReturn (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||||
|
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||||
|
0:
|
||||||
|
%1 = alloca i64
|
||||||
|
store i64 %x, ptr %1
|
||||||
|
%2 = load i64, ptr %1
|
||||||
|
switch i64 %2, label %3 [
|
||||||
|
i64 5, label %4
|
||||||
|
]
|
||||||
|
3:
|
||||||
|
ret i1 false
|
||||||
|
4:
|
||||||
|
ret i1 true
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
[is5 x:Int]:Bool = {
|
||||||
|
switch x | 5 [return true]
|
||||||
|
false
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchReturnValueUsed (test *testing.T) {
|
||||||
|
testString (test,
|
||||||
|
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||||
|
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||||
|
0:
|
||||||
|
%1 = alloca i64
|
||||||
|
store i64 %x, ptr %1
|
||||||
|
%2 = load i64, ptr %1
|
||||||
|
switch i64 %2, label %6 [
|
||||||
|
i64 5, label %5
|
||||||
|
]
|
||||||
|
3:
|
||||||
|
%4 = phi i1 [ false, %6 ]
|
||||||
|
ret i1 %4
|
||||||
|
5:
|
||||||
|
ret i1 true
|
||||||
|
6:
|
||||||
|
br label %3
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
[is5 x:Int]:Bool = switch x
|
||||||
|
| 5 [return true]
|
||||||
|
* false
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIfElseReturnValueUsed (test *testing.T) {
|
func TestIfElseReturnValueUsed (test *testing.T) {
|
||||||
testString (test,
|
testString (test,
|
||||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||||
|
@ -95,6 +95,8 @@ func (this *generator) generateExpressionAny (expression entity.Expression) (reg
|
|||||||
return this.generateIfElse(expression, resultModeAny)
|
return this.generateIfElse(expression, resultModeAny)
|
||||||
case *entity.Match:
|
case *entity.Match:
|
||||||
return this.generateMatch(expression, resultModeAny)
|
return this.generateMatch(expression, resultModeAny)
|
||||||
|
case *entity.Switch:
|
||||||
|
return this.generateSwitch(expression, resultModeAny)
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
return this.generateLoop(expression, resultModeAny)
|
return this.generateLoop(expression, resultModeAny)
|
||||||
case *entity.For:
|
case *entity.For:
|
||||||
@ -160,6 +162,9 @@ func (this *generator) generateExpressionVal (expression entity.Expression) (llv
|
|||||||
case *entity.Match:
|
case *entity.Match:
|
||||||
val, _, err := this.generateMatch(expression, resultModeVal)
|
val, _, err := this.generateMatch(expression, resultModeVal)
|
||||||
return val, err
|
return val, err
|
||||||
|
case *entity.Switch:
|
||||||
|
val, _, err := this.generateSwitch(expression, resultModeVal)
|
||||||
|
return val, err
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
val, _, err := this.generateLoop(expression, resultModeVal)
|
val, _, err := this.generateLoop(expression, resultModeVal)
|
||||||
return val, err
|
return val, err
|
||||||
@ -235,6 +240,9 @@ func (this *generator) generateExpressionLoc (expression entity.Expression) (llv
|
|||||||
case *entity.Match:
|
case *entity.Match:
|
||||||
loc, _, err := this.generateMatch(expression, resultModeLoc)
|
loc, _, err := this.generateMatch(expression, resultModeLoc)
|
||||||
return loc, err
|
return loc, err
|
||||||
|
case *entity.Switch:
|
||||||
|
loc, _, err := this.generateSwitch(expression, resultModeLoc)
|
||||||
|
return loc, err
|
||||||
case *entity.Loop:
|
case *entity.Loop:
|
||||||
loc, _, err := this.generateLoop(expression, resultModeLoc)
|
loc, _, err := this.generateLoop(expression, resultModeLoc)
|
||||||
return loc, err
|
return loc, err
|
||||||
|
@ -122,7 +122,6 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llvm.Value, bool, error) {
|
func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llvm.Value, bool, error) {
|
||||||
var loc bool
|
|
||||||
value, err := this.generateExpressionLoc(match.Value)
|
value, err := this.generateExpressionLoc(match.Value)
|
||||||
if err != nil { return nil, false, err }
|
if err != nil { return nil, false, err }
|
||||||
irUnionType, err := this.generateType(match.Value.Type())
|
irUnionType, err := this.generateType(match.Value.Type())
|
||||||
@ -134,6 +133,31 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
|||||||
|
|
||||||
irCases := make([]*llvm.Case, len(match.Cases))
|
irCases := make([]*llvm.Case, len(match.Cases))
|
||||||
irIncomings := []*llvm.Incoming { }
|
irIncomings := []*llvm.Incoming { }
|
||||||
|
loc := false
|
||||||
|
first := true
|
||||||
|
|
||||||
|
generateCaseExpression := func (expression entity.Expression) error {
|
||||||
|
thisMode := mode
|
||||||
|
if !first && mode != resultModeAny {
|
||||||
|
if loc { thisMode = resultModeLoc
|
||||||
|
} else { thisMode = resultModeVal }
|
||||||
|
}
|
||||||
|
result, thisLoc, err := this.generateExpression(expression, thisMode)
|
||||||
|
if err != nil { return err }
|
||||||
|
if first { loc = thisLoc }
|
||||||
|
if !this.blockManager.Terminated() {
|
||||||
|
if expression != nil {
|
||||||
|
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||||
|
X: result,
|
||||||
|
Predecessor: this.blockManager.Block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.blockManager.NewBr(exitBlock)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate type cases
|
||||||
for index, cas := range match.Cases {
|
for index, cas := range match.Cases {
|
||||||
// set up ir case
|
// set up ir case
|
||||||
caseBlock := this.blockManager.newBlock()
|
caseBlock := this.blockManager.newBlock()
|
||||||
@ -151,30 +175,27 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
|||||||
this.blockManager.addDeclaration(cas.Declaration, data)
|
this.blockManager.addDeclaration(cas.Declaration, data)
|
||||||
|
|
||||||
// generate case expression
|
// generate case expression
|
||||||
thisMode := mode
|
err = generateCaseExpression(cas.Expression)
|
||||||
if index != 0 && mode != resultModeAny {
|
|
||||||
if loc { thisMode = resultModeLoc
|
|
||||||
} else { thisMode = resultModeVal }
|
|
||||||
}
|
|
||||||
caseExpression, thisLoc, err := this.generateExpression(cas.Expression, thisMode)
|
|
||||||
if err != nil { return nil, false, err }
|
if err != nil { return nil, false, err }
|
||||||
if index == 0 { loc = thisLoc }
|
|
||||||
if !this.blockManager.Terminated() {
|
|
||||||
if caseExpression != nil {
|
|
||||||
irIncomings = append(irIncomings, &llvm.Incoming {
|
|
||||||
X: caseExpression,
|
|
||||||
Predecessor: this.blockManager.Block,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.blockManager.NewBr(exitBlock)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate default case
|
||||||
|
var defaultBlock llvm.Value
|
||||||
|
if match.Default != nil {
|
||||||
|
defaultBlock = this.blockManager.newBlock()
|
||||||
|
err = generateCaseExpression(match.Default.Expression)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
}
|
}
|
||||||
|
|
||||||
// create switch branch
|
// create switch branch
|
||||||
this.blockManager.Block = previousBlock
|
this.blockManager.Block = previousBlock
|
||||||
hashFieldLoc := this.getUnionTypeFieldLoc(value, irUnionType)
|
hashFieldLoc := this.getUnionTypeFieldLoc(value, irUnionType)
|
||||||
hash := this.blockManager.NewLoad(irHashType, hashFieldLoc)
|
hash := this.blockManager.NewLoad(irHashType, hashFieldLoc)
|
||||||
|
if defaultBlock == nil {
|
||||||
this.blockManager.NewSwitch(hash, exitBlock, irCases...)
|
this.blockManager.NewSwitch(hash, exitBlock, irCases...)
|
||||||
|
} else {
|
||||||
|
this.blockManager.NewSwitch(hash, defaultBlock, irCases...)
|
||||||
|
}
|
||||||
|
|
||||||
// discard/obtain results
|
// discard/obtain results
|
||||||
this.blockManager.Block = exitBlock
|
this.blockManager.Block = exitBlock
|
||||||
@ -183,10 +204,94 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
|||||||
} else {
|
} else {
|
||||||
irType, err := this.generateType(match.Type())
|
irType, err := this.generateType(match.Type())
|
||||||
if err != nil { return nil, false, err }
|
if err != nil { return nil, false, err }
|
||||||
|
if defaultBlock == nil {
|
||||||
irIncomings = append(irIncomings, &llvm.Incoming {
|
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||||
X: llvm.NewConstZeroInitializer(irType),
|
X: llvm.NewConstZeroInitializer(irType),
|
||||||
Predecessor: previousBlock,
|
Predecessor: previousBlock,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
return this.blockManager.NewPhi(irIncomings...), loc, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *generator) generateSwitch (match *entity.Switch, mode resultMode) (llvm.Value, bool, error) {
|
||||||
|
irValue, err := this.generateExpressionVal(match.Value)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
irValueType, err := this.generateType(match.Value.Type())
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
|
||||||
|
previousBlock := this.blockManager.Block
|
||||||
|
exitBlock := this.blockManager.newBlock()
|
||||||
|
|
||||||
|
irCases := make([]*llvm.Case, len(match.Cases))
|
||||||
|
irIncomings := []*llvm.Incoming { }
|
||||||
|
loc := false
|
||||||
|
first := true
|
||||||
|
|
||||||
|
generateCaseExpression := func (expression entity.Expression) error {
|
||||||
|
thisMode := mode
|
||||||
|
if !first && mode != resultModeAny {
|
||||||
|
if loc { thisMode = resultModeLoc
|
||||||
|
} else { thisMode = resultModeVal }
|
||||||
|
}
|
||||||
|
result, thisLoc, err := this.generateExpression(expression, thisMode)
|
||||||
|
if err != nil { return err }
|
||||||
|
if first { loc = thisLoc }
|
||||||
|
if !this.blockManager.Terminated() {
|
||||||
|
if expression != nil {
|
||||||
|
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||||
|
X: result,
|
||||||
|
Predecessor: this.blockManager.Block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.blockManager.NewBr(exitBlock)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate value cases
|
||||||
|
for index, cas := range match.Cases {
|
||||||
|
caseBlock := this.blockManager.newBlock()
|
||||||
|
key := match.CaseOrder[index]
|
||||||
|
irCases[index] = &llvm.Case {
|
||||||
|
X: llvm.NewConstInt (
|
||||||
|
llvm.ReduceToBase(irValueType).(*llvm.TypeInt),
|
||||||
|
key),
|
||||||
|
Target: caseBlock,
|
||||||
|
}
|
||||||
|
err = generateCaseExpression(cas.Expression)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate default case
|
||||||
|
var defaultBlock llvm.Value
|
||||||
|
if match.Default != nil {
|
||||||
|
defaultBlock = this.blockManager.newBlock()
|
||||||
|
err = generateCaseExpression(match.Default.Expression)
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
}
|
||||||
|
|
||||||
|
// create switch branch
|
||||||
|
this.blockManager.Block = previousBlock
|
||||||
|
if defaultBlock == nil {
|
||||||
|
this.blockManager.NewSwitch(irValue, exitBlock, irCases...)
|
||||||
|
} else {
|
||||||
|
this.blockManager.NewSwitch(irValue, defaultBlock, irCases...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// discard/obtain results
|
||||||
|
this.blockManager.Block = exitBlock
|
||||||
|
if mode == resultModeAny {
|
||||||
|
return nil, false, nil
|
||||||
|
} else {
|
||||||
|
irType, err := this.generateType(match.Type())
|
||||||
|
if err != nil { return nil, false, err }
|
||||||
|
if defaultBlock == nil {
|
||||||
|
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||||
|
X: llvm.NewConstZeroInitializer(irType),
|
||||||
|
Predecessor: previousBlock,
|
||||||
|
})
|
||||||
|
}
|
||||||
return this.blockManager.NewPhi(irIncomings...), loc, nil
|
return this.blockManager.NewPhi(irIncomings...), loc, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ expression, the parser follows this decision tree to determine what to parse:
|
|||||||
| | 'nil' =LiteralNil
|
| | 'nil' =LiteralNil
|
||||||
| | 'if' =IfElse
|
| | 'if' =IfElse
|
||||||
| | 'match' =Match
|
| | 'match' =Match
|
||||||
|
| | 'switch' =Switch
|
||||||
| | 'loop' =Loop
|
| | 'loop' =Loop
|
||||||
| | +Colon =Declaration
|
| | +Colon =Declaration
|
||||||
| | +DoubleColon =Call
|
| | +DoubleColon =Call
|
||||||
|
@ -117,6 +117,7 @@ func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
|
|||||||
case "nil": return this.parseLiteralNil()
|
case "nil": return this.parseLiteralNil()
|
||||||
case "if": return this.parseIfElse()
|
case "if": return this.parseIfElse()
|
||||||
case "match": return this.parseMatch()
|
case "match": return this.parseMatch()
|
||||||
|
case "switch": return this.parseSwitch()
|
||||||
case "loop": return this.parseLoop()
|
case "loop": return this.parseLoop()
|
||||||
case "for": return this.parseFor()
|
case "for": return this.parseFor()
|
||||||
default:
|
default:
|
||||||
@ -580,18 +581,24 @@ func (this *treeParser) parseMatch () (*entity.Match, error) {
|
|||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
for this.Is(lexer.Symbol) && this.ValueIs("|") {
|
for this.Is(lexer.Symbol) && this.ValueIs("|") {
|
||||||
cas, err := this.parseCase()
|
cas, err := this.parseMatchCase()
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
match.Cases = append(match.Cases, cas)
|
match.Cases = append(match.Cases, cas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.Is(lexer.Star) {
|
||||||
|
defaul, err := this.parseDefaultCase()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
match.Default = defaul
|
||||||
|
}
|
||||||
|
|
||||||
return match, nil
|
return match, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *treeParser) parseCase () (*entity.Case, error) {
|
func (this *treeParser) parseMatchCase () (*entity.MatchCase, error) {
|
||||||
err := this.ExpectValue(lexer.Symbol, "|")
|
err := this.ExpectValue(lexer.Symbol, "|")
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
cas := &entity.Case {
|
cas := &entity.MatchCase {
|
||||||
Pos: this.Pos(),
|
Pos: this.Pos(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,6 +611,63 @@ func (this *treeParser) parseCase () (*entity.Case, error) {
|
|||||||
return cas, nil
|
return cas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *treeParser) parseSwitch () (*entity.Switch, error) {
|
||||||
|
err := this.ExpectValue(lexer.Ident, "switch")
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
switc := &entity.Switch {
|
||||||
|
Pos: this.Pos(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.ExpectNextDesc(descriptionExpression, startTokensExpression...)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
switc.Value, err = this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
|
for this.Is(lexer.Symbol) && this.ValueIs("|") {
|
||||||
|
cas, err := this.parseSwitchCase()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
switc.Cases = append(switc.Cases, cas)
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.Is(lexer.Star) {
|
||||||
|
defaul, err := this.parseDefaultCase()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
switc.Default = defaul
|
||||||
|
}
|
||||||
|
|
||||||
|
return switc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *treeParser) parseSwitchCase () (*entity.SwitchCase, error) {
|
||||||
|
err := this.ExpectValue(lexer.Symbol, "|")
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
cas := &entity.SwitchCase {
|
||||||
|
Pos: this.Pos(),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Next()
|
||||||
|
cas.Key, err = this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
cas.Expression, err = this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
|
return cas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *treeParser) parseDefaultCase () (*entity.DefaultCase, error) {
|
||||||
|
err := this.Expect(lexer.Star)
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
cas := &entity.DefaultCase {
|
||||||
|
Pos: this.Pos(),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Next()
|
||||||
|
cas.Expression, err = this.parseExpression()
|
||||||
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
|
return cas, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *treeParser) parseLoop () (*entity.Loop, error) {
|
func (this *treeParser) parseLoop () (*entity.Loop, error) {
|
||||||
err := this.ExpectValue(lexer.Ident, "loop")
|
err := this.ExpectValue(lexer.Ident, "loop")
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
|
@ -74,7 +74,8 @@ testString (test,
|
|||||||
- [loop]:Int = {i:Int=0 if [< i 3] then return 1 loop {[print 3] if [> i 3] then break 0 i=[++ i]}}
|
- [loop]:Int = {i:Int=0 if [< i 3] then return 1 loop {[print 3] if [> i 3] then break 0 i=[++ i]}}
|
||||||
- [for s:String] = for i:Index e:Byte in str if [> i 3] then [break e]
|
- [for s:String] = for i:Index e:Byte in str if [> i 3] then [break e]
|
||||||
- [for s:String] = for e:Byte in s [break e]
|
- [for s:String] = for e:Byte in s [break e]
|
||||||
- [matchToInt u:(| Int F64)]:Int = match u | u:Int u | u:F64 [~ Int u]`,
|
- [matchToInt u:(| Int F64)]:Int = match u | u:Int u | u:F64 [~ Int u] * 0
|
||||||
|
- [switch x:Int] = switch x | 0 5 | 1 4 | 2 3 * 0`,
|
||||||
// input
|
// input
|
||||||
`
|
`
|
||||||
[var] = sdfdf
|
[var] = sdfdf
|
||||||
@ -127,6 +128,12 @@ testString (test,
|
|||||||
[matchToInt u:(| Int F64)]:Int = match u
|
[matchToInt u:(| Int F64)]:Int = match u
|
||||||
| u:Int u
|
| u:Int u
|
||||||
| u:F64 [~Int u]
|
| u:F64 [~Int u]
|
||||||
|
* 0
|
||||||
|
[switch x:Int] = switch x
|
||||||
|
| 0 5
|
||||||
|
| 1 4
|
||||||
|
| 2 3
|
||||||
|
* 0
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user