Implemented switch statements
This commit is contained in:
parent
d07b455f67
commit
dbf416d45b
@ -9,18 +9,18 @@ fizzbuzz(n) {
|
|||||||
x := 1
|
x := 1
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// TODO: implement switch statement
|
switch {
|
||||||
if x % 15 == 0 {
|
x % 15 == 0 {
|
||||||
print("FizzBuzz", 8)
|
print("FizzBuzz", 8)
|
||||||
} else {
|
}
|
||||||
if x % 5 == 0 {
|
x % 5 == 0 {
|
||||||
print("Buzz", 4)
|
print("Buzz", 4)
|
||||||
} else {
|
}
|
||||||
if x % 3 == 0 {
|
x % 3 == 0 {
|
||||||
print("Fizz", 4)
|
print("Fizz", 4)
|
||||||
} else {
|
}
|
||||||
log.number(x)
|
_ {
|
||||||
}
|
log.number(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,9 +28,9 @@ fizzbuzz(n) {
|
|||||||
|
|
||||||
if x > n {
|
if x > n {
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
print(" ", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(" ", 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,6 @@ func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {
|
|||||||
case *Define:
|
case *Define:
|
||||||
count += node.Expression.Count(buffer, kind, name)
|
count += node.Expression.Count(buffer, kind, name)
|
||||||
|
|
||||||
case *Return:
|
|
||||||
if node.Value != nil {
|
|
||||||
count += node.Value.Count(buffer, kind, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *If:
|
case *If:
|
||||||
count += node.Condition.Count(buffer, kind, name)
|
count += node.Condition.Count(buffer, kind, name)
|
||||||
count += Count(node.Body, buffer, kind, name)
|
count += Count(node.Body, buffer, kind, name)
|
||||||
@ -33,6 +28,20 @@ func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {
|
|||||||
case *Loop:
|
case *Loop:
|
||||||
count += Count(node.Body, buffer, kind, name)
|
count += Count(node.Body, buffer, kind, name)
|
||||||
|
|
||||||
|
case *Return:
|
||||||
|
if node.Value != nil {
|
||||||
|
count += node.Value.Count(buffer, kind, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Switch:
|
||||||
|
for _, c := range node.Cases {
|
||||||
|
if c.Condition != nil {
|
||||||
|
count += c.Condition.Count(buffer, kind, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
count += Count(c.Body, buffer, kind, name)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unknown AST type")
|
panic("unknown AST type")
|
||||||
}
|
}
|
||||||
|
16
src/build/ast/Switch.go
Normal file
16
src/build/ast/Switch.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Switch represents a switch statement.
|
||||||
|
type Switch struct {
|
||||||
|
Cases []Case
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case represents a case inside a switch.
|
||||||
|
type Case struct {
|
||||||
|
Condition *expression.Expression
|
||||||
|
Body AST
|
||||||
|
}
|
@ -51,6 +51,21 @@ func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
|
|||||||
value := expression.Parse(tokens[1:])
|
value := expression.Parse(tokens[1:])
|
||||||
return &Return{Value: value}, nil
|
return &Return{Value: value}, nil
|
||||||
|
|
||||||
|
case token.Switch:
|
||||||
|
blockStart := tokens.IndexKind(token.BlockStart)
|
||||||
|
blockEnd := tokens.LastIndexKind(token.BlockEnd)
|
||||||
|
|
||||||
|
if blockStart == -1 {
|
||||||
|
return nil, errors.New(errors.MissingBlockStart, nil, tokens[0].End())
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockEnd == -1 {
|
||||||
|
return nil, errors.New(errors.MissingBlockEnd, nil, tokens[len(tokens)-1].End())
|
||||||
|
}
|
||||||
|
|
||||||
|
cases, err := parseSwitch(tokens[blockStart+1:blockEnd], source)
|
||||||
|
return &Switch{Cases: cases}, err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: tokens[0].Text(source)}, nil, tokens[0].Position)
|
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: tokens[0].Text(source)}, nil, tokens[0].Position)
|
||||||
}
|
}
|
||||||
|
37
src/build/ast/parseSwitch.go
Normal file
37
src/build/ast/parseSwitch.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseSwitch generates the cases inside a switch statement.
|
||||||
|
func parseSwitch(tokens token.List, source []byte) ([]Case, error) {
|
||||||
|
var cases []Case
|
||||||
|
|
||||||
|
err := EachInstruction(tokens, func(caseTokens token.List) error {
|
||||||
|
blockStart, _, body, err := block(caseTokens, source)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conditionTokens := caseTokens[:blockStart]
|
||||||
|
var condition *expression.Expression
|
||||||
|
|
||||||
|
if len(conditionTokens) == 1 && conditionTokens[0].Kind == token.Identifier && conditionTokens[0].Text(source) == "_" {
|
||||||
|
condition = nil
|
||||||
|
} else {
|
||||||
|
condition = expression.Parse(conditionTokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
cases = append(cases, Case{
|
||||||
|
Condition: condition,
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return cases, err
|
||||||
|
}
|
@ -23,10 +23,6 @@ func (f *Function) CompileASTNode(node ast.Node) error {
|
|||||||
f.Fold(node.Expression)
|
f.Fold(node.Expression)
|
||||||
return f.CompileDefinition(node)
|
return f.CompileDefinition(node)
|
||||||
|
|
||||||
case *ast.Return:
|
|
||||||
f.Fold(node.Value)
|
|
||||||
return f.CompileReturn(node)
|
|
||||||
|
|
||||||
case *ast.If:
|
case *ast.If:
|
||||||
f.Fold(node.Condition)
|
f.Fold(node.Condition)
|
||||||
return f.CompileIf(node)
|
return f.CompileIf(node)
|
||||||
@ -34,6 +30,17 @@ func (f *Function) CompileASTNode(node ast.Node) error {
|
|||||||
case *ast.Loop:
|
case *ast.Loop:
|
||||||
return f.CompileLoop(node)
|
return f.CompileLoop(node)
|
||||||
|
|
||||||
|
case *ast.Return:
|
||||||
|
f.Fold(node.Value)
|
||||||
|
return f.CompileReturn(node)
|
||||||
|
|
||||||
|
case *ast.Switch:
|
||||||
|
for _, c := range node.Cases {
|
||||||
|
f.Fold(c.Condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.CompileSwitch(node)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unknown AST type")
|
panic("unknown AST type")
|
||||||
}
|
}
|
||||||
|
55
src/build/core/CompileSwitch.go
Normal file
55
src/build/core/CompileSwitch.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileSwitch compiles a multi-branch instruction.
|
||||||
|
func (f *Function) CompileSwitch(s *ast.Switch) error {
|
||||||
|
f.count.multiBranch++
|
||||||
|
end := fmt.Sprintf("%s_switch_%d_end", f.UniqueName, f.count.multiBranch)
|
||||||
|
|
||||||
|
for _, branch := range s.Cases {
|
||||||
|
if branch.Condition == nil {
|
||||||
|
f.PushScope(branch.Body, f.File.Bytes)
|
||||||
|
err := f.CompileAST(branch.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.PopScope()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
f.count.branch++
|
||||||
|
|
||||||
|
var (
|
||||||
|
success = fmt.Sprintf("%s_case_%d_true", f.UniqueName, f.count.branch)
|
||||||
|
fail = fmt.Sprintf("%s_case_%d_false", f.UniqueName, f.count.branch)
|
||||||
|
err = f.CompileCondition(branch.Condition, success, fail)
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.AddLabel(success)
|
||||||
|
f.PushScope(branch.Body, f.File.Bytes)
|
||||||
|
err = f.CompileAST(branch.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Jump(asm.JUMP, end)
|
||||||
|
f.PopScope()
|
||||||
|
f.AddLabel(fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.AddLabel(end)
|
||||||
|
return nil
|
||||||
|
}
|
@ -22,9 +22,10 @@ type Function struct {
|
|||||||
|
|
||||||
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
||||||
type counter struct {
|
type counter struct {
|
||||||
assert int
|
assert int
|
||||||
branch int
|
branch int
|
||||||
data int
|
multiBranch int
|
||||||
loop int
|
data int
|
||||||
subBranch int
|
loop int
|
||||||
|
subBranch int
|
||||||
}
|
}
|
||||||
|
@ -69,5 +69,6 @@ const (
|
|||||||
Import // import
|
Import // import
|
||||||
Loop // loop
|
Loop // loop
|
||||||
Return // return
|
Return // return
|
||||||
|
Switch // switch
|
||||||
_keywordsEnd // </keywords>
|
_keywordsEnd // </keywords>
|
||||||
)
|
)
|
||||||
|
@ -151,6 +151,8 @@ func Tokenize(buffer []byte) List {
|
|||||||
kind = Loop
|
kind = Loop
|
||||||
case "return":
|
case "return":
|
||||||
kind = Return
|
kind = Return
|
||||||
|
case "switch":
|
||||||
|
kind = Switch
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens = append(tokens, Token{Kind: kind, Position: position, Length: Length(len(identifier))})
|
tokens = append(tokens, Token{Kind: kind, Position: position, Length: Length(len(identifier))})
|
||||||
|
Loading…
Reference in New Issue
Block a user