Added switch statements

This commit is contained in:
Mattia Giambirtone 2022-12-07 09:15:29 +01:00
parent af3c7234be
commit f1875736e3
8 changed files with 109 additions and 5 deletions

View File

@ -851,6 +851,8 @@ proc dispatch*(self: var PeonVM) =
# Pops N elements off the call stack # Pops N elements off the call stack
for _ in 0..<int(self.readShort()): for _ in 0..<int(self.readShort()):
discard self.popc() discard self.popc()
of DupTop:
self.push(self.peek())
# Jump opcodes # Jump opcodes
of Jump: of Jump:
# Absolute jump # Absolute jump

View File

@ -2629,6 +2629,26 @@ proc namedBlock(self: Compiler, node: NamedBlockStmt) =
self.endScope() self.endScope()
proc switchStmt(self: Compiler, node: SwitchStmt) =
## Compiles switch statements
self.expression(node.switch)
var ifJump: int = -1
var thenJumps: seq[int] = @[]
for branch in node.branches:
self.emitByte(DupTop, branch.body.token.line)
self.expression(branch.cond)
self.emitByte(Equal, branch.body.token.line)
ifJump = self.emitJump(JumpIfFalsePop, branch.body.token.line)
self.blockStmt(branch.body)
thenJumps.add(self.emitJump(JumpForwards, branch.body.token.line))
self.patchJump(ifJump)
if not node.default.isNil():
self.blockStmt(node.default)
for jump in thenJumps:
self.patchJump(jump)
self.emitByte(OpCode.Pop, node.token.line)
proc statement(self: Compiler, node: Statement) = proc statement(self: Compiler, node: Statement) =
## Compiles all statements ## Compiles all statements
case node.kind: case node.kind:
@ -2644,6 +2664,8 @@ proc statement(self: Compiler, node: Statement) =
self.printRepl(kind, expression) self.printRepl(kind, expression)
else: else:
self.emitByte(Pop, node.token.line) self.emitByte(Pop, node.token.line)
of NodeKind.switchStmt:
self.switchStmt(SwitchStmt(node))
of NodeKind.namedBlockStmt: of NodeKind.namedBlockStmt:
self.namedBlocks.add(NamedBlock(start: self.chunk.code.len(), self.namedBlocks.add(NamedBlock(start: self.chunk.code.len(),
depth: self.depth, depth: self.depth,

View File

@ -77,7 +77,8 @@ type
identExpr, # Identifier identExpr, # Identifier
pragmaExpr, pragmaExpr,
refExpr, refExpr,
ptrExpr ptrExpr,
switchStmt
# Here I would've rather used object variants, and in fact that's what was in # Here I would've rather used object variants, and in fact that's what was in
# place before, but not being able to re-declare a field of the same type in # place before, but not being able to re-declare a field of the same type in
@ -291,6 +292,11 @@ type
Ptr* = ref object of Expression Ptr* = ref object of Expression
value*: Expression value*: Expression
SwitchStmt* = ref object of Statement
switch*: Expression
branches*: seq[tuple[cond: Expression, body: BlockStmt]]
default*: BlockStmt
proc isConst*(self: ASTNode): bool = proc isConst*(self: ASTNode): bool =
@ -337,6 +343,15 @@ proc newPtrExpr*(expression: Expression, token: Token): Ptr =
result.token = token result.token = token
proc newSwitchStmt*(switch: Expression, branches: seq[tuple[cond: Expression, body: BlockStmt]], default: BlockStmt, token: Token): SwitchStmt =
new(result)
result.kind = switchStmt
result.switch = switch
result.branches = branches
result.token = token
result.default = default
proc newIntExpr*(literal: Token): IntExpr = proc newIntExpr*(literal: Token): IntExpr =
result = IntExpr(kind: intExpr) result = IntExpr(kind: intExpr)
result.literal = literal result.literal = literal

View File

@ -186,7 +186,8 @@ type
PopC, # Pop off the call stack onto the operand stack PopC, # Pop off the call stack onto the operand stack
PushC, # Pop off the operand stack onto the call stack PushC, # Pop off the operand stack onto the call stack
SysClock64, # Pushes the output of a monotonic clock on the stack SysClock64, # Pushes the output of a monotonic clock on the stack
LoadTOS # Pushes the top of the call stack onto the operand stack LoadTOS, # Pushes the top of the call stack onto the operand stack
DupTop # Duplicates the top of the operand stack onto the operand stack
# We group instructions by their operation/operand types for easier handling when debugging # We group instructions by their operation/operand types for easier handling when debugging
@ -265,6 +266,7 @@ const simpleInstructions* = {Return, LoadNil,
Float32LessThan, Float32LessThan,
Float32GreaterOrEqual, Float32GreaterOrEqual,
Float32LessOrEqual, Float32LessOrEqual,
DupTop
} }
# Constant instructions are instructions that operate on the bytecode constant table # Constant instructions are instructions that operate on the bytecode constant table

View File

@ -38,7 +38,7 @@ type
Yield, Defer, Try, Except, Yield, Defer, Try, Except,
Finally, Type, Operator, Case, Finally, Type, Operator, Case,
Enum, From, Ptr, Ref, Object, Enum, From, Ptr, Ref, Object,
Export, Block, Template Export, Block, Template, Switch,
# Literal types # Literal types
Integer, Float, String, Identifier, Integer, Float, String, Identifier,

View File

@ -1203,12 +1203,37 @@ proc expressionStatement(self: Parser): Statement =
result.file = self.file result.file = self.file
proc switchStmt(self: Parser): Statement =
## Parses switch statements
let tok = self.peek(-1)
let switch = self.expression()
self.expect(TokenType.LeftBrace, "expecting '{' after switch condition")
var branches: seq[tuple[cond: Expression, body: BlockStmt]] = @[]
var match: Expression
var body: BlockStmt
var default: BlockStmt
while not self.check([TokenType.RightBrace, TokenType.Else]) and not self.done():
self.expect(TokenType.Case, "expecting at least one 'case' label in switch statement")
match = self.expression()
self.expect(TokenType.LeftBrace, "expecting '{' after expression match in switch statement")
body = BlockStmt(self.blockStmt())
branches.add((cond: match, body: body))
if self.match(Else):
self.expect(TokenType.LeftBrace, "expecting '{' after else clause in switch statement")
default = BlockStmt(self.blockStmt())
self.expect(TokenType.RightBrace, "missing closing '}' in switch statement")
result = newSwitchStmt(switch, branches, default, tok)
proc statement(self: Parser): Statement = proc statement(self: Parser): Statement =
## Parses statements ## Parses statements
case self.peek().kind: case self.peek().kind:
of If: of If:
discard self.step() discard self.step()
result = self.ifStmt() result = self.ifStmt()
of Switch:
discard self.step()
result = self.switchStmt()
of Assert: of Assert:
discard self.step() discard self.step()
result = self.assertStmt() result = self.assertStmt()

View File

@ -46,6 +46,9 @@ proc fillSymbolTable*(tokenizer: Lexer) =
tokenizer.symbols.addKeyword("return", TokenType.Return) tokenizer.symbols.addKeyword("return", TokenType.Return)
tokenizer.symbols.addKeyword("object", Object) tokenizer.symbols.addKeyword("object", Object)
tokenizer.symbols.addKeyword("export", Export) tokenizer.symbols.addKeyword("export", Export)
tokenizer.symbols.addKeyword("block", TokenType.Block)
tokenizer.symbols.addKeyword("template", TokenType.Template)
tokenizer.symbols.addKeyword("switch", TokenType.Switch)
# These are more like expressions with a reserved # These are more like expressions with a reserved
# name that produce a value of a builtin type, # name that produce a value of a builtin type,
# but we don't need to care about that until # but we don't need to care about that until
@ -55,8 +58,6 @@ proc fillSymbolTable*(tokenizer: Lexer) =
tokenizer.symbols.addKeyword("false", False) tokenizer.symbols.addKeyword("false", False)
tokenizer.symbols.addKeyword("ref", TokenType.Ref) tokenizer.symbols.addKeyword("ref", TokenType.Ref)
tokenizer.symbols.addKeyword("ptr", TokenType.Ptr) tokenizer.symbols.addKeyword("ptr", TokenType.Ptr)
tokenizer.symbols.addKeyword("block", TokenType.Block)
tokenizer.symbols.addKeyword("template", TokenType.Template)
for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":", "==", "!=", for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":", "==", "!=",
">=", "<=", "+=", "-=", "/=", "*=", "**=", "!", "%", "&", "|", "^", ">=", "<=", "+=", "-=", "/=", "*=", "**=", "!", "%", "&", "|", "^",
">>", "<<"]: ">>", "<<"]:

37
tests/switch.pn Normal file
View File

@ -0,0 +1,37 @@
import std;
switch 2 + 2 {
case 4 {
print(true);
}
case 3 {
print(false);
}
else {
print(false);
}
}
switch 2 + 1 {
case 4 {
print(false);
}
case 3 {
print(true);
}
}
switch 2 + 3 {
case 4 {
print(false);
}
case 3 {
print(false);
}
else {
print(true);
}
}