Added named blocks
This commit is contained in:
parent
15ec8dce54
commit
4b6d86ad8e
|
@ -167,6 +167,15 @@ type
|
|||
# patch. Used for break statements
|
||||
breakJumps: seq[int]
|
||||
|
||||
NamedBlock = ref object
|
||||
## A "named block object", similar
|
||||
## to a loop object. Used to emit
|
||||
## appropriate jump offsets
|
||||
start: int
|
||||
depth: int
|
||||
breakJumps: seq[int]
|
||||
name: string
|
||||
|
||||
Compiler* = ref object
|
||||
## A wrapper around the Peon compiler's state
|
||||
|
||||
|
@ -191,6 +200,8 @@ type
|
|||
# The current loop being compiled (used to
|
||||
# keep track of where to jump)
|
||||
currentLoop: Loop
|
||||
# Stack of named blocks
|
||||
namedBlocks: seq[NamedBlock]
|
||||
# Are we in REPL mode? If so, Pop instructions
|
||||
# for expression statements at the top level are
|
||||
# swapped for a special instruction that prints
|
||||
|
@ -1616,6 +1627,9 @@ proc patchBreaks(self: Compiler) =
|
|||
## the loop is fully compiled
|
||||
for brk in self.currentLoop.breakJumps:
|
||||
self.patchJump(brk)
|
||||
for blk in self.namedBlocks:
|
||||
for brk in blk.breakJumps:
|
||||
self.patchJump(brk)
|
||||
|
||||
|
||||
proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) =
|
||||
|
@ -2412,19 +2426,45 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
|||
proc continueStmt(self: Compiler, node: ContinueStmt) =
|
||||
## Compiles continue statements. A continue statement
|
||||
## jumps to the next iteration in a loop
|
||||
if self.currentLoop.start > 16777215:
|
||||
self.error("too much code to jump over in continue statement")
|
||||
self.emitByte(Jump, node.token.line)
|
||||
self.emitBytes(self.currentLoop.start.toTriple(), node.token.line)
|
||||
if node.label.isNil():
|
||||
if self.currentLoop.start > 16777215:
|
||||
self.error("too much code to jump over in continue statement")
|
||||
self.emitByte(Jump, node.token.line)
|
||||
self.emitBytes(self.currentLoop.start.toTriple(), node.token.line)
|
||||
else:
|
||||
var blocks: seq[NamedBlock] = @[]
|
||||
var found: bool = false
|
||||
for blk in reversed(self.namedBlocks):
|
||||
blocks.add(blk)
|
||||
if blk.name == node.label.token.lexeme:
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
self.error(&"unknown block name '{node.label.token.lexeme}'", node.label)
|
||||
self.emitByte(Jump, node.token.line)
|
||||
self.emitBytes(blocks[^1].start.toTriple(), node.token.line)
|
||||
|
||||
|
||||
proc breakStmt(self: Compiler, node: BreakStmt) =
|
||||
## Compiles break statements. A break statement
|
||||
## jumps to the end of the loop
|
||||
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
||||
if self.currentLoop.depth > self.depth:
|
||||
# Breaking out of a loop closes its scope
|
||||
self.endScope()
|
||||
if node.label.isNil():
|
||||
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
||||
if self.currentLoop.depth > self.depth:
|
||||
# Breaking out of a loop closes its scope
|
||||
self.endScope()
|
||||
else:
|
||||
var blocks: seq[NamedBlock] = @[]
|
||||
var found: bool = false
|
||||
for blk in reversed(self.namedBlocks):
|
||||
blocks.add(blk)
|
||||
if blk.name == node.label.token.lexeme:
|
||||
for blk in blocks:
|
||||
blk.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
self.error(&"unknown block name '{node.label.token.lexeme}'", node.label)
|
||||
|
||||
|
||||
proc importStmt(self: Compiler, node: ImportStmt) =
|
||||
|
@ -2465,6 +2505,23 @@ proc exportStmt(self: Compiler, node: ExportStmt) =
|
|||
discard
|
||||
|
||||
|
||||
proc namedBlock(self: Compiler, node: NamedBlockStmt) =
|
||||
## Compiles named blocks
|
||||
self.beginScope()
|
||||
var last: Declaration
|
||||
for decl in node.code:
|
||||
if not last.isNil():
|
||||
case last.kind:
|
||||
of NodeKind.breakStmt, NodeKind.continueStmt:
|
||||
self.warning(UnreachableCode, &"code after '{last.token.lexeme}' statement is unreachable", nil, last)
|
||||
else:
|
||||
discard
|
||||
self.declaration(decl)
|
||||
last = decl
|
||||
self.patchBreaks()
|
||||
self.endScope()
|
||||
|
||||
|
||||
proc statement(self: Compiler, node: Statement) =
|
||||
## Compiles all statements
|
||||
case node.kind:
|
||||
|
@ -2480,6 +2537,14 @@ proc statement(self: Compiler, node: Statement) =
|
|||
self.printRepl(kind, expression)
|
||||
else:
|
||||
self.emitByte(Pop, node.token.line)
|
||||
of NodeKind.namedBlockStmt:
|
||||
self.namedBlocks.add(NamedBlock(start: self.chunk.code.len(),
|
||||
depth: self.depth,
|
||||
breakJumps: @[],
|
||||
name: NamedBlockStmt(node).name.token.lexeme))
|
||||
self.namedBlock(NamedBlockStmt(node))
|
||||
#self.patchBreaks()
|
||||
discard self.namedBlocks.pop()
|
||||
of NodeKind.ifStmt:
|
||||
self.ifStmt(IfStmt(node))
|
||||
of NodeKind.assertStmt:
|
||||
|
|
|
@ -41,6 +41,7 @@ type
|
|||
whileStmt,
|
||||
forEachStmt,
|
||||
blockStmt,
|
||||
namedBlockStmt,
|
||||
raiseStmt,
|
||||
assertStmt,
|
||||
tryStmt,
|
||||
|
@ -205,6 +206,9 @@ type
|
|||
BlockStmt* = ref object of Statement
|
||||
code*: seq[Declaration]
|
||||
|
||||
NamedBlockStmt* = ref object of BlockStmt
|
||||
name*: IdentExpr
|
||||
|
||||
ForStmt* = ref object of Statement
|
||||
discard # Unused
|
||||
|
||||
|
@ -230,8 +234,10 @@ type
|
|||
expression*: Expression
|
||||
|
||||
BreakStmt* = ref object of Statement
|
||||
label*: IdentExpr
|
||||
|
||||
ContinueStmt* = ref object of Statement
|
||||
label*: IdentExpr
|
||||
|
||||
ReturnStmt* = ref object of Statement
|
||||
value*: Expression
|
||||
|
@ -543,6 +549,13 @@ proc newBlockStmt*(code: seq[Declaration], token: Token): BlockStmt =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newNamedBlockStmt*(code: seq[Declaration], name: IdentExpr, token: Token): NamedBlockStmt =
|
||||
result = NamedBlockStmt(kind: namedBlockStmt)
|
||||
result.code = code
|
||||
result.token = token
|
||||
result.name = name
|
||||
|
||||
|
||||
proc newWhileStmt*(condition: Expression, body: Statement,
|
||||
token: Token): WhileStmt =
|
||||
result = WhileStmt(kind: whileStmt)
|
||||
|
@ -560,14 +573,16 @@ proc newForEachStmt*(identifier: IdentExpr, expression: Expression,
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newBreakStmt*(token: Token): BreakStmt =
|
||||
proc newBreakStmt*(token: Token, label: IdentExpr = nil): BreakStmt =
|
||||
result = BreakStmt(kind: breakStmt)
|
||||
result.token = token
|
||||
result.label = label
|
||||
|
||||
|
||||
proc newContinueStmt*(token: Token): ContinueStmt =
|
||||
proc newContinueStmt*(token: Token, label: IdentExpr = nil): ContinueStmt =
|
||||
result = ContinueStmt(kind: continueStmt)
|
||||
result.token = token
|
||||
result.label = label
|
||||
|
||||
|
||||
proc newReturnStmt*(value: Expression, token: Token): ReturnStmt =
|
||||
|
|
|
@ -620,13 +620,39 @@ proc blockStmt(self: Parser): Statement =
|
|||
self.endScope()
|
||||
|
||||
|
||||
proc namedBlockStmt(self: Parser): Statement =
|
||||
## Parses named block statement
|
||||
self.beginScope()
|
||||
let tok = self.peek(-1)
|
||||
var code: seq[Declaration] = @[]
|
||||
self.expect(Identifier, "expecting block name after 'block'")
|
||||
var name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
name.file = self.file
|
||||
let enclosingLoop = self.currentLoop
|
||||
self.currentLoop = Loop
|
||||
self.expect(LeftBrace, "expecting '{' after 'block'")
|
||||
while not self.check(RightBrace) and not self.done():
|
||||
code.add(self.declaration())
|
||||
if code[^1].isNil():
|
||||
code.delete(code.high())
|
||||
self.expect(RightBrace, "expecting '}'")
|
||||
result = newNamedBlockStmt(code, name, tok)
|
||||
result.file = self.file
|
||||
self.endScope()
|
||||
self.currentLoop = enclosingLoop
|
||||
|
||||
|
||||
proc breakStmt(self: Parser): Statement =
|
||||
## Parses break statements
|
||||
let tok = self.peek(-1)
|
||||
var label: IdentExpr
|
||||
if self.currentLoop != Loop:
|
||||
self.error("'break' cannot be used outside loops")
|
||||
if self.match(Identifier):
|
||||
label = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
label.file = self.file
|
||||
endOfLine("missing semicolon after 'break'")
|
||||
result = newBreakStmt(tok)
|
||||
result = newBreakStmt(tok, label)
|
||||
result.file = self.file
|
||||
|
||||
|
||||
|
@ -643,10 +669,14 @@ proc deferStmt(self: Parser): Statement =
|
|||
proc continueStmt(self: Parser): Statement =
|
||||
## Parses continue statements
|
||||
let tok = self.peek(-1)
|
||||
var label: IdentExpr
|
||||
if self.currentLoop != Loop:
|
||||
self.error("'continue' cannot be used outside loops")
|
||||
if self.match(Identifier):
|
||||
label = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
label.file = self.file
|
||||
endOfLine("missing semicolon after 'continue'")
|
||||
result = newContinueStmt(tok)
|
||||
result = newContinueStmt(tok, label)
|
||||
result.file = self.file
|
||||
|
||||
|
||||
|
@ -1225,6 +1255,9 @@ proc statement(self: Parser): Statement =
|
|||
of Try:
|
||||
discard self.step()
|
||||
result = self.tryStmt()
|
||||
of Block:
|
||||
discard self.step()
|
||||
result = self.namedBlockStmt()
|
||||
else:
|
||||
result = self.expressionStatement()
|
||||
result.file = self.file
|
||||
|
|
|
@ -55,6 +55,7 @@ proc fillSymbolTable*(tokenizer: Lexer) =
|
|||
tokenizer.symbols.addKeyword("false", False)
|
||||
tokenizer.symbols.addKeyword("ref", TokenType.Ref)
|
||||
tokenizer.symbols.addKeyword("ptr", TokenType.Ptr)
|
||||
tokenizer.symbols.addKeyword("block", TokenType.Block)
|
||||
for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":", "==", "!=",
|
||||
">=", "<=", "+=", "-=", "/=", "*=", "**=", "!", "%", "&", "|", "^",
|
||||
">>", "<<"]:
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import std;
|
||||
|
||||
|
||||
block outer {
|
||||
var x = 1;
|
||||
print(x == 1);
|
||||
block inner {
|
||||
var x = 2;
|
||||
print(x == 2);
|
||||
break outer;
|
||||
print("nope");
|
||||
}
|
||||
}
|
||||
var x = 3;
|
||||
print(x == 3);
|
||||
|
||||
|
||||
var count = 0;
|
||||
block loop {
|
||||
if count < 5 {
|
||||
count = count + 1;
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
print(count == 5);
|
Loading…
Reference in New Issue