Added named blocks
This commit is contained in:
parent
15ec8dce54
commit
4b6d86ad8e
|
@ -167,6 +167,15 @@ type
|
||||||
# patch. Used for break statements
|
# patch. Used for break statements
|
||||||
breakJumps: seq[int]
|
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
|
Compiler* = ref object
|
||||||
## A wrapper around the Peon compiler's state
|
## A wrapper around the Peon compiler's state
|
||||||
|
|
||||||
|
@ -191,6 +200,8 @@ type
|
||||||
# The current loop being compiled (used to
|
# The current loop being compiled (used to
|
||||||
# keep track of where to jump)
|
# keep track of where to jump)
|
||||||
currentLoop: Loop
|
currentLoop: Loop
|
||||||
|
# Stack of named blocks
|
||||||
|
namedBlocks: seq[NamedBlock]
|
||||||
# Are we in REPL mode? If so, Pop instructions
|
# Are we in REPL mode? If so, Pop instructions
|
||||||
# for expression statements at the top level are
|
# for expression statements at the top level are
|
||||||
# swapped for a special instruction that prints
|
# swapped for a special instruction that prints
|
||||||
|
@ -1616,6 +1627,9 @@ proc patchBreaks(self: Compiler) =
|
||||||
## the loop is fully compiled
|
## the loop is fully compiled
|
||||||
for brk in self.currentLoop.breakJumps:
|
for brk in self.currentLoop.breakJumps:
|
||||||
self.patchJump(brk)
|
self.patchJump(brk)
|
||||||
|
for blk in self.namedBlocks:
|
||||||
|
for brk in blk.breakJumps:
|
||||||
|
self.patchJump(brk)
|
||||||
|
|
||||||
|
|
||||||
proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) =
|
proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) =
|
||||||
|
@ -2412,19 +2426,45 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
proc continueStmt(self: Compiler, node: ContinueStmt) =
|
proc continueStmt(self: Compiler, node: ContinueStmt) =
|
||||||
## Compiles continue statements. A continue statement
|
## Compiles continue statements. A continue statement
|
||||||
## jumps to the next iteration in a loop
|
## jumps to the next iteration in a loop
|
||||||
if self.currentLoop.start > 16777215:
|
if node.label.isNil():
|
||||||
self.error("too much code to jump over in continue statement")
|
if self.currentLoop.start > 16777215:
|
||||||
self.emitByte(Jump, node.token.line)
|
self.error("too much code to jump over in continue statement")
|
||||||
self.emitBytes(self.currentLoop.start.toTriple(), node.token.line)
|
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) =
|
proc breakStmt(self: Compiler, node: BreakStmt) =
|
||||||
## Compiles break statements. A break statement
|
## Compiles break statements. A break statement
|
||||||
## jumps to the end of the loop
|
## jumps to the end of the loop
|
||||||
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
if node.label.isNil():
|
||||||
if self.currentLoop.depth > self.depth:
|
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
||||||
# Breaking out of a loop closes its scope
|
if self.currentLoop.depth > self.depth:
|
||||||
self.endScope()
|
# 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) =
|
proc importStmt(self: Compiler, node: ImportStmt) =
|
||||||
|
@ -2465,6 +2505,23 @@ proc exportStmt(self: Compiler, node: ExportStmt) =
|
||||||
discard
|
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) =
|
proc statement(self: Compiler, node: Statement) =
|
||||||
## Compiles all statements
|
## Compiles all statements
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
@ -2480,6 +2537,14 @@ 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.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:
|
of NodeKind.ifStmt:
|
||||||
self.ifStmt(IfStmt(node))
|
self.ifStmt(IfStmt(node))
|
||||||
of NodeKind.assertStmt:
|
of NodeKind.assertStmt:
|
||||||
|
|
|
@ -41,6 +41,7 @@ type
|
||||||
whileStmt,
|
whileStmt,
|
||||||
forEachStmt,
|
forEachStmt,
|
||||||
blockStmt,
|
blockStmt,
|
||||||
|
namedBlockStmt,
|
||||||
raiseStmt,
|
raiseStmt,
|
||||||
assertStmt,
|
assertStmt,
|
||||||
tryStmt,
|
tryStmt,
|
||||||
|
@ -205,6 +206,9 @@ type
|
||||||
BlockStmt* = ref object of Statement
|
BlockStmt* = ref object of Statement
|
||||||
code*: seq[Declaration]
|
code*: seq[Declaration]
|
||||||
|
|
||||||
|
NamedBlockStmt* = ref object of BlockStmt
|
||||||
|
name*: IdentExpr
|
||||||
|
|
||||||
ForStmt* = ref object of Statement
|
ForStmt* = ref object of Statement
|
||||||
discard # Unused
|
discard # Unused
|
||||||
|
|
||||||
|
@ -230,8 +234,10 @@ type
|
||||||
expression*: Expression
|
expression*: Expression
|
||||||
|
|
||||||
BreakStmt* = ref object of Statement
|
BreakStmt* = ref object of Statement
|
||||||
|
label*: IdentExpr
|
||||||
|
|
||||||
ContinueStmt* = ref object of Statement
|
ContinueStmt* = ref object of Statement
|
||||||
|
label*: IdentExpr
|
||||||
|
|
||||||
ReturnStmt* = ref object of Statement
|
ReturnStmt* = ref object of Statement
|
||||||
value*: Expression
|
value*: Expression
|
||||||
|
@ -543,6 +549,13 @@ proc newBlockStmt*(code: seq[Declaration], token: Token): BlockStmt =
|
||||||
result.token = token
|
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,
|
proc newWhileStmt*(condition: Expression, body: Statement,
|
||||||
token: Token): WhileStmt =
|
token: Token): WhileStmt =
|
||||||
result = WhileStmt(kind: whileStmt)
|
result = WhileStmt(kind: whileStmt)
|
||||||
|
@ -560,14 +573,16 @@ proc newForEachStmt*(identifier: IdentExpr, expression: Expression,
|
||||||
result.token = token
|
result.token = token
|
||||||
|
|
||||||
|
|
||||||
proc newBreakStmt*(token: Token): BreakStmt =
|
proc newBreakStmt*(token: Token, label: IdentExpr = nil): BreakStmt =
|
||||||
result = BreakStmt(kind: breakStmt)
|
result = BreakStmt(kind: breakStmt)
|
||||||
result.token = token
|
result.token = token
|
||||||
|
result.label = label
|
||||||
|
|
||||||
|
|
||||||
proc newContinueStmt*(token: Token): ContinueStmt =
|
proc newContinueStmt*(token: Token, label: IdentExpr = nil): ContinueStmt =
|
||||||
result = ContinueStmt(kind: continueStmt)
|
result = ContinueStmt(kind: continueStmt)
|
||||||
result.token = token
|
result.token = token
|
||||||
|
result.label = label
|
||||||
|
|
||||||
|
|
||||||
proc newReturnStmt*(value: Expression, token: Token): ReturnStmt =
|
proc newReturnStmt*(value: Expression, token: Token): ReturnStmt =
|
||||||
|
|
|
@ -620,13 +620,39 @@ proc blockStmt(self: Parser): Statement =
|
||||||
self.endScope()
|
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 =
|
proc breakStmt(self: Parser): Statement =
|
||||||
## Parses break statements
|
## Parses break statements
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
|
var label: IdentExpr
|
||||||
if self.currentLoop != Loop:
|
if self.currentLoop != Loop:
|
||||||
self.error("'break' cannot be used outside loops")
|
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'")
|
endOfLine("missing semicolon after 'break'")
|
||||||
result = newBreakStmt(tok)
|
result = newBreakStmt(tok, label)
|
||||||
result.file = self.file
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
|
@ -643,10 +669,14 @@ proc deferStmt(self: Parser): Statement =
|
||||||
proc continueStmt(self: Parser): Statement =
|
proc continueStmt(self: Parser): Statement =
|
||||||
## Parses continue statements
|
## Parses continue statements
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
|
var label: IdentExpr
|
||||||
if self.currentLoop != Loop:
|
if self.currentLoop != Loop:
|
||||||
self.error("'continue' cannot be used outside loops")
|
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'")
|
endOfLine("missing semicolon after 'continue'")
|
||||||
result = newContinueStmt(tok)
|
result = newContinueStmt(tok, label)
|
||||||
result.file = self.file
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
|
@ -1225,6 +1255,9 @@ proc statement(self: Parser): Statement =
|
||||||
of Try:
|
of Try:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = self.tryStmt()
|
result = self.tryStmt()
|
||||||
|
of Block:
|
||||||
|
discard self.step()
|
||||||
|
result = self.namedBlockStmt()
|
||||||
else:
|
else:
|
||||||
result = self.expressionStatement()
|
result = self.expressionStatement()
|
||||||
result.file = self.file
|
result.file = self.file
|
||||||
|
|
|
@ -55,6 +55,7 @@ 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)
|
||||||
for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":", "==", "!=",
|
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