Skip to content

Commit dc7a55a

Browse files
committed
Add handling for #if macros. Work in progress
1 parent e599204 commit dc7a55a

File tree

5 files changed

+84
-9
lines changed

5 files changed

+84
-9
lines changed

Tools/cases_generator/cwriter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def emit_token(self, tkn: Token) -> None:
9999
self.maybe_dedent(tkn.text)
100100
self.set_position(tkn)
101101
self.emit_text(tkn.text)
102-
if tkn.kind == "CMACRO":
102+
if tkn.kind.startswith("CMACRO"):
103103
self.newline = True
104104
self.maybe_indent(tkn.text)
105105

Tools/cases_generator/generators_common.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from typing import Callable, TextIO, Iterator, Iterable
1313
from lexer import Token
1414
from stack import Storage, StackError
15-
from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt
15+
from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt, MacroIfStmt
1616

1717
# Set this to true for voluminous output showing state of stack and locals
1818
PRINT_STACKS = False
@@ -476,6 +476,8 @@ def _emit_stmt(
476476
return self._emit_for(stmt, uop, storage, inst)
477477
elif isinstance(stmt, WhileStmt):
478478
return self._emit_while(stmt, uop, storage, inst)
479+
elif isinstance(stmt, MacroIfStmt):
480+
return self._emit_macro_if(stmt, uop, storage, inst)
479481
else:
480482
raise NotImplementedError("Unexpected statement")
481483

@@ -533,6 +535,37 @@ def _emit_simple(
533535
except StackError as ex:
534536
raise analysis_error(ex.args[0], tkn) #from None
535537

538+
539+
def _emit_macro_if(
540+
self,
541+
stmt: IfStmt,
542+
uop: CodeSection,
543+
storage: Storage,
544+
inst: Instruction | None,
545+
) -> tuple[bool, Token, Storage]:
546+
self.out.emit(stmt.condition)
547+
branch = stmt.else_ is not None
548+
reachable = True
549+
for s in stmt.body:
550+
r, tkn, storage = self._emit_stmt(s, uop, storage, inst)
551+
if tkn is not None:
552+
self.out.emit(tkn)
553+
if not r:
554+
reachable = False
555+
if branch:
556+
else_storage = storage.copy()
557+
self.out.emit(stmt.else_)
558+
for s in stmt.else_body:
559+
r, tkn, else_storage = self._emit_stmt(s, uop, else_storage, inst)
560+
if tkn is not None:
561+
self.out.emit(tkn)
562+
if not r:
563+
reachable = False
564+
storage.merge(else_storage, self.out)
565+
self.out.emit(stmt.endif)
566+
return reachable, None, storage
567+
568+
536569
def _emit_if(
537570
self,
538571
stmt: IfStmt,
@@ -640,7 +673,7 @@ def emit_tokens(
640673
if emit_braces:
641674
self.out.emit(tkn)
642675
except StackError as ex:
643-
raise analysis_error(ex.args[0], last) from None
676+
raise analysis_error(ex.args[0], tkn) from None
644677
return storage
645678

646679
def emit(self, txt: str | Token) -> None:

Tools/cases_generator/lexer.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ def choice(*opts: str) -> str:
8080

8181
# Macros
8282
macro = r"#.*\n"
83-
CMACRO = "CMACRO"
83+
CMACRO_IF = "CMACRO_IF"
84+
CMACRO_ELSE = "CMACRO_ELSE"
85+
CMACRO_ENDIF = "CMACRO_ENDIF"
86+
CMACRO_OTHER = "CMACRO_OTHER"
8487

8588
id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
8689
IDENTIFIER = "IDENTIFIER"
@@ -292,6 +295,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
292295
linestart = -1
293296
for m in matcher.finditer(src):
294297
start, end = m.span()
298+
macro_body = ""
295299
text = m.group(0)
296300
if text in keywords:
297301
kind = keywords[text]
@@ -316,7 +320,15 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
316320
elif text[0] == "'":
317321
kind = CHARACTER
318322
elif text[0] == "#":
319-
kind = CMACRO
323+
macro_body = text[1:].strip()
324+
if macro_body.startswith("if"):
325+
kind = CMACRO_IF
326+
elif macro_body.startswith("else"):
327+
kind = CMACRO_ELSE
328+
elif macro_body.startswith("endif"):
329+
kind = CMACRO_ENDIF
330+
else:
331+
kind = CMACRO_OTHER
320332
elif text[0] == "/" and text[1] in "/*":
321333
kind = COMMENT
322334
else:
@@ -338,7 +350,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
338350
line += newlines
339351
else:
340352
begin = line, start - linestart
341-
if kind == CMACRO:
353+
if macro_body:
342354
linestart = end
343355
line += 1
344356
if kind != "\n":

Tools/cases_generator/parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
ForStmt,
1818
WhileStmt,
1919
BlockStmt,
20+
MacroIfStmt,
2021
)
2122

2223
import pprint

Tools/cases_generator/parsing.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ def tokens(self) -> Iterator[lx.Token]:
171171
class MacroIfStmt(Stmt):
172172
condition: lx.Token
173173
body: list[Stmt]
174+
else_: lx.Token | None
174175
else_body: list[Stmt] | None
176+
endif: lx.Token
175177

176178
def print(self, out:CWriter) -> None:
177179
out.emit(self.condition)
@@ -657,7 +659,15 @@ def stmt(self) -> Stmt:
657659
return self.for_stmt(tkn)
658660
elif tkn := self.expect(lx.WHILE):
659661
return self.while_stmt(tkn)
660-
elif tkn := self.expect(lx.CMACRO):
662+
elif tkn := self.expect(lx.CMACRO_IF):
663+
return self.macro_if(tkn)
664+
elif tkn := self.expect(lx.CMACRO_ELSE):
665+
msg = "Unexpected #else"
666+
raise self.make_syntax_error(msg)
667+
elif tkn := self.expect(lx.CMACRO_ENDIF):
668+
msg = "Unexpected #endif"
669+
raise self.make_syntax_error(msg)
670+
elif tkn := self.expect(lx.CMACRO_OTHER):
661671
return SimpleStmt([tkn])
662672
elif tkn := self.expect(lx.SWITCH):
663673
msg = "switch statements are not supported due to their complex flow control. Sorry."
@@ -678,6 +688,24 @@ def if_stmt(self, if_: lx.Token) -> IfStmt:
678688
else_body = self.block()
679689
return IfStmt(if_, condition, body, else_, else_body)
680690

691+
692+
def macro_if(self, cond: lx.Token) -> IfStmt:
693+
else_ = None
694+
body: list[Stmt] = []
695+
else_body: list[Stmt] | None = None
696+
part = body
697+
while True:
698+
if tkn := self.expect(lx.CMACRO_ENDIF):
699+
return MacroIfStmt(cond, body, else_, else_body, tkn)
700+
elif tkn := self.expect(lx.CMACRO_ELSE):
701+
if part is else_body:
702+
raise self.make_syntax_error("Multiple #else")
703+
else_ = tkn
704+
else_body = []
705+
part = else_body
706+
else:
707+
part.append(self.stmt())
708+
681709
def for_stmt(self, for_: lx.Token) -> ForStmt:
682710
lparen = self.require(lx.LPAREN)
683711
header = [lparen] + self.consume_to(lx.RPAREN)
@@ -693,6 +721,7 @@ def while_stmt(self, while_: lx.Token) -> WhileStmt:
693721

694722
if __name__ == "__main__":
695723
import sys
724+
import pprint
696725

697726
if sys.argv[1:]:
698727
filename = sys.argv[1]
@@ -710,5 +739,5 @@ def while_stmt(self, while_: lx.Token) -> WhileStmt:
710739
filename = "<default>"
711740
src = "if (x) { x.foo; // comment\n}"
712741
parser = Parser(src, filename)
713-
x = parser.definition()
714-
print(x)
742+
while node := parser.definition():
743+
pprint.pprint(node)

0 commit comments

Comments
 (0)