Skip to content

Commit 1d26232

Browse files
Add CONTINUE control flow statement, use curly brackets instead of square brackets in the standard library
1 parent 524f4c1 commit 1d26232

15 files changed

+760
-682
lines changed
61.2 KB
Binary file not shown.

__pycache__/lexer.cpython-314.pyc

11.1 KB
Binary file not shown.

__pycache__/parser.cpython-314.pyc

20.1 KB
Binary file not shown.

asmln.exe

680 Bytes
Binary file not shown.

interpreter.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
SourceLocation,
2929
Statement,
3030
WhileStatement,
31+
ContinueStatement,
3132
)
3233

3334

@@ -66,6 +67,11 @@ def __init__(self, count: int) -> None:
6667
self.count = count
6768

6869

70+
class ContinueSignal(Exception):
71+
def __init__(self) -> None:
72+
super().__init__()
73+
74+
6975
class JumpSignal(Exception):
7076
def __init__(self, target: int) -> None:
7177
super().__init__(target)
@@ -574,6 +580,8 @@ def run(self) -> None:
574580
self._execute_block(program.statements, global_env)
575581
except BreakSignal as bs:
576582
raise ASMRuntimeError(f"BREAK({bs.count}) escaped enclosing loops", rewrite_rule="BREAK")
583+
except ContinueSignal:
584+
raise ASMRuntimeError("CONTINUE used outside loop", rewrite_rule="CONTINUE")
577585
except ASMRuntimeError as error:
578586
if self.logger.entries:
579587
error.step_index = self.logger.entries[-1].step_index
@@ -647,6 +655,9 @@ def _execute_statement(self, statement: Statement, env: Environment) -> None:
647655
if count <= 0:
648656
raise ASMRuntimeError("BREAK count must be > 0", location=statement.location, rewrite_rule="BREAK")
649657
raise BreakSignal(count)
658+
if isinstance(statement, ContinueStatement):
659+
# Signal to the innermost loop to skip to next iteration.
660+
raise ContinueSignal()
650661
if isinstance(statement, GotoStatement):
651662
target = self._evaluate_expression(statement.expression, env)
652663
raise JumpSignal(target)
@@ -672,6 +683,18 @@ def _execute_while(self, statement: WhileStatement, env: Environment) -> None:
672683
bs.count -= 1
673684
raise
674685
return
686+
except ContinueSignal:
687+
# Evaluate condition now to determine if there will be another iteration.
688+
try:
689+
next_cond = self._evaluate_expression(statement.condition, env)
690+
except ASMRuntimeError:
691+
# Propagate evaluation errors
692+
raise
693+
if next_cond != 0:
694+
# proceed to next iteration
695+
continue
696+
# no next iteration -> behave like BREAK(1)
697+
return
675698

676699
def _execute_for(self, statement: ForStatement, env: Environment) -> None:
677700
target: int = self._evaluate_expression(statement.target_expr, env)
@@ -684,6 +707,14 @@ def _execute_for(self, statement: ForStatement, env: Environment) -> None:
684707
bs.count -= 1
685708
raise
686709
return
710+
except ContinueSignal:
711+
# For FOR loops, increment the counter and decide whether to continue
712+
current = env.get(statement.counter)
713+
if current + 1 < target:
714+
env.set(statement.counter, current + 1)
715+
continue
716+
# no further iterations -> behave like BREAK(1)
717+
return
687718
env.set(statement.counter, env.get(statement.counter) + 1)
688719

689720
def _evaluate_expression(self, expression: Expression, env: Environment) -> int:

lexer.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Token:
1919
column: int
2020

2121

22-
KEYWORDS = {"IF", "ELSIF", "ELSE", "WHILE", "FOR", "FUNC", "RETURN", "BREAK", "GOTO", "GOTOPOINT"}
22+
KEYWORDS = {"IF", "ELSIF", "ELSE", "WHILE", "FOR", "FUNC", "RETURN", "BREAK", "GOTO", "GOTOPOINT", "CONTINUE"}
2323
SYMBOLS = {
2424
"(": "LPAREN",
2525
")": "RPAREN",
@@ -55,23 +55,8 @@ def tokenize(self) -> List[Token]:
5555
_advance()
5656
continue
5757
if ch == "^":
58-
if self.index + 1 >= len(self.text):
59-
raise ASMParseError(
60-
f"Invalid line continuation '^' at {self.filename}:{self.line}:{self.column}"
61-
)
62-
next_ch = self.text[self.index + 1]
63-
if next_ch == "\n":
64-
_advance()
65-
_advance()
66-
continue
67-
if next_ch == "\r":
68-
_advance()
69-
if not self._eof and self._peek() == "\n":
70-
_advance()
71-
continue
72-
raise ASMParseError(
73-
f"Invalid line continuation '^' not followed by newline at {self.filename}:{self.line}:{self.column}"
74-
)
58+
self._consume_line_continuation()
59+
continue
7560
if ch == "\n":
7661
tokens.append(Token("NEWLINE", "\n", self.line, self.column))
7762
_advance()
@@ -119,9 +104,16 @@ def _consume_signed_number(self) -> Token:
119104

120105
def _consume_binary_digits(self) -> str:
121106
digits: List[str] = []
122-
while not self._eof and self._peek() in "01":
123-
digits.append(self._peek())
124-
self._advance()
107+
while not self._eof:
108+
ch = self._peek()
109+
if ch in "01":
110+
digits.append(ch)
111+
self._advance()
112+
continue
113+
if ch == "^":
114+
self._consume_line_continuation()
115+
continue
116+
break
125117
return "".join(digits)
126118

127119
def _consume_identifier(self) -> Token:
@@ -131,9 +123,16 @@ def _consume_identifier(self) -> Token:
131123
f"Identifiers must not start with '0' or '1' at {self.filename}:{line}:{col}"
132124
)
133125
chars: List[str] = []
134-
while not self._eof and self._is_identifier_part(self._peek()):
135-
chars.append(self._peek())
136-
self._advance()
126+
while not self._eof:
127+
ch = self._peek()
128+
if self._is_identifier_part(ch):
129+
chars.append(ch)
130+
self._advance()
131+
continue
132+
if ch == "^":
133+
self._consume_line_continuation()
134+
continue
135+
break
137136
value = "".join(chars)
138137
token_type: str = value if value in KEYWORDS else "IDENT"
139138
return Token(token_type, value, line, col)
@@ -142,7 +141,27 @@ def _is_identifier_start(self, ch: str) -> bool:
142141
return (ch == "_") or ("A" <= ch <= "Z") or ("a" <= ch <= "z")
143142

144143
def _is_identifier_part(self, ch: str) -> bool:
145-
return (ch == "_") or ("A" <= ch <= "Z") or ("a" <= ch <= "z") or ("0" <= ch <= "9") or (ch == ".")
144+
return (ch == "_") or ("A" <= ch <= "Z") or ("a" <= ch <= "z") or (ch in "01")
145+
146+
def _consume_line_continuation(self) -> None:
147+
if self.index + 1 >= len(self.text):
148+
raise ASMParseError(
149+
f"Invalid line continuation '^' at {self.filename}:{self.line}:{self.column}"
150+
)
151+
ch1 = self.text[self.index + 1]
152+
if ch1 == "\n":
153+
self._advance()
154+
self._advance()
155+
return
156+
if ch1 == "\r":
157+
self._advance()
158+
self._advance()
159+
if self.index < len(self.text) and self.text[self.index] == "\n":
160+
self._advance()
161+
return
162+
raise ASMParseError(
163+
f"Invalid line continuation '^' not followed by newline at {self.filename}:{self.line}:{self.column}"
164+
)
146165

147166
@property
148167
def _eof(self) -> bool:

0 commit comments

Comments
 (0)