summaryrefslogtreecommitdiffstats
path: root/Tools/cases_generator/cwriter.py
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2023-12-07 12:49:40 (GMT)
committerGitHub <noreply@github.com>2023-12-07 12:49:40 (GMT)
commitb449415b2f1b41e1c44cb453428657fdf6ff1d36 (patch)
treea31422e8d6a348baf55421d40dce503d1265b8d5 /Tools/cases_generator/cwriter.py
parent3d712a9f4c9f366edbe16b804ec4f6ae50b0a59f (diff)
downloadcpython-b449415b2f1b41e1c44cb453428657fdf6ff1d36.zip
cpython-b449415b2f1b41e1c44cb453428657fdf6ff1d36.tar.gz
cpython-b449415b2f1b41e1c44cb453428657fdf6ff1d36.tar.bz2
GH-111485: Separate out parsing, analysis and code-gen phases of tier 1 code generator (GH-112299)
Diffstat (limited to 'Tools/cases_generator/cwriter.py')
-rw-r--r--Tools/cases_generator/cwriter.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/Tools/cases_generator/cwriter.py b/Tools/cases_generator/cwriter.py
new file mode 100644
index 0000000..0b7edd0
--- /dev/null
+++ b/Tools/cases_generator/cwriter.py
@@ -0,0 +1,111 @@
+from lexer import Token
+from typing import TextIO
+
+
+class CWriter:
+ "A writer that understands tokens and how to format C code"
+
+ last_token: Token | None
+
+ def __init__(self, out: TextIO, indent: int, line_directives: bool):
+ self.out = out
+ self.base_column = indent * 4
+ self.indents = [i * 4 for i in range(indent + 1)]
+ self.line_directives = line_directives
+ self.last_token = None
+ self.newline = True
+
+ def set_position(self, tkn: Token) -> None:
+ if self.last_token is not None:
+ if self.last_token.line < tkn.line:
+ self.out.write("\n")
+ if self.line_directives:
+ self.out.write(f'#line {tkn.line} "{tkn.filename}"\n')
+ self.out.write(" " * self.indents[-1])
+ else:
+ gap = tkn.column - self.last_token.end_column
+ self.out.write(" " * gap)
+ elif self.newline:
+ self.out.write(" " * self.indents[-1])
+ self.last_token = tkn
+ self.newline = False
+
+ def emit_at(self, txt: str, where: Token) -> None:
+ self.set_position(where)
+ self.out.write(txt)
+
+ def maybe_dedent(self, txt: str) -> None:
+ parens = txt.count("(") - txt.count(")")
+ if parens < 0:
+ self.indents.pop()
+ elif "}" in txt or is_label(txt):
+ self.indents.pop()
+
+ def maybe_indent(self, txt: str) -> None:
+ parens = txt.count("(") - txt.count(")")
+ if parens > 0 and self.last_token:
+ offset = self.last_token.end_column - 1
+ if offset <= self.indents[-1] or offset > 40:
+ offset = self.indents[-1] + 4
+ self.indents.append(offset)
+ elif "{" in txt or is_label(txt):
+ self.indents.append(self.indents[-1] + 4)
+
+ def emit_text(self, txt: str) -> None:
+ self.out.write(txt)
+
+ def emit_multiline_comment(self, tkn: Token) -> None:
+ self.set_position(tkn)
+ lines = tkn.text.splitlines(True)
+ first = True
+ for line in lines:
+ text = line.lstrip()
+ if first:
+ spaces = 0
+ else:
+ spaces = self.indents[-1]
+ if text.startswith("*"):
+ spaces += 1
+ else:
+ spaces += 3
+ first = False
+ self.out.write(" " * spaces)
+ self.out.write(text)
+
+ def emit_token(self, tkn: Token) -> None:
+ if tkn.kind == "COMMENT" and "\n" in tkn.text:
+ return self.emit_multiline_comment(tkn)
+ self.maybe_dedent(tkn.text)
+ self.set_position(tkn)
+ self.emit_text(tkn.text)
+ self.maybe_indent(tkn.text)
+
+ def emit_str(self, txt: str) -> None:
+ self.maybe_dedent(txt)
+ if self.newline and txt:
+ if txt[0] != "\n":
+ self.out.write(" " * self.indents[-1])
+ self.newline = False
+ self.emit_text(txt)
+ if txt.endswith("\n"):
+ self.newline = True
+ self.maybe_indent(txt)
+ self.last_token = None
+
+ def emit(self, txt: str | Token) -> None:
+ if isinstance(txt, Token):
+ self.emit_token(txt)
+ elif isinstance(txt, str):
+ self.emit_str(txt)
+ else:
+ assert False
+
+ def start_line(self) -> None:
+ if not self.newline:
+ self.out.write("\n")
+ self.newline = True
+ self.last_token = None
+
+
+def is_label(txt: str) -> bool:
+ return not txt.startswith("//") and txt.endswith(":")