Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/cases_generator/plexer.py
12 views
1
import lexer as lx
2
Token = lx.Token
3
4
5
class PLexer:
6
def __init__(self, src: str, filename: str):
7
self.src = src
8
self.filename = filename
9
self.tokens = list(lx.tokenize(self.src, filename=filename))
10
self.pos = 0
11
12
def getpos(self) -> int:
13
# Current position
14
return self.pos
15
16
def eof(self) -> bool:
17
# Are we at EOF?
18
return self.pos >= len(self.tokens)
19
20
def setpos(self, pos: int) -> None:
21
# Reset position
22
assert 0 <= pos <= len(self.tokens), (pos, len(self.tokens))
23
self.pos = pos
24
25
def backup(self) -> None:
26
# Back up position by 1
27
assert self.pos > 0
28
self.pos -= 1
29
30
def next(self, raw: bool = False) -> Token | None:
31
# Return next token and advance position; None if at EOF
32
# TODO: Return synthetic EOF token instead of None?
33
while self.pos < len(self.tokens):
34
tok = self.tokens[self.pos]
35
self.pos += 1
36
if raw or tok.kind != "COMMENT":
37
return tok
38
return None
39
40
def peek(self, raw: bool = False) -> Token | None:
41
# Return next token without advancing position
42
tok = self.next(raw=raw)
43
self.backup()
44
return tok
45
46
def maybe(self, kind: str, raw: bool = False) -> Token | None:
47
# Return next token without advancing position if kind matches
48
tok = self.peek(raw=raw)
49
if tok and tok.kind == kind:
50
return tok
51
return None
52
53
def expect(self, kind: str) -> Token | None:
54
# Return next token and advance position if kind matches
55
tkn = self.next()
56
if tkn is not None:
57
if tkn.kind == kind:
58
return tkn
59
self.backup()
60
return None
61
62
def require(self, kind: str) -> Token:
63
# Return next token and advance position, requiring kind to match
64
tkn = self.next()
65
if tkn is not None and tkn.kind == kind:
66
return tkn
67
raise self.make_syntax_error(f"Expected {kind!r} but got {tkn and tkn.text!r}", tkn)
68
69
def extract_line(self, lineno: int) -> str:
70
# Return source line `lineno` (1-based)
71
lines = self.src.splitlines()
72
if lineno > len(lines):
73
return ""
74
return lines[lineno - 1]
75
76
def make_syntax_error(self, message: str, tkn: Token|None = None) -> SyntaxError:
77
# Construct a SyntaxError instance from message and token
78
if tkn is None:
79
tkn = self.peek()
80
if tkn is None:
81
tkn = self.tokens[-1]
82
return lx.make_syntax_error(message,
83
self.filename, tkn.line, tkn.column, self.extract_line(tkn.line))
84
85
86
if __name__ == "__main__":
87
import sys
88
if sys.argv[1:]:
89
filename = sys.argv[1]
90
if filename == "-c" and sys.argv[2:]:
91
src = sys.argv[2]
92
filename = "<string>"
93
else:
94
with open(filename) as f:
95
src = f.read()
96
else:
97
filename = "<default>"
98
src = "if (x) { x.foo; // comment\n}"
99
p = PLexer(src, filename)
100
while not p.eof():
101
tok = p.next(raw=True)
102
assert tok
103
left = repr(tok)
104
right = lx.to_text([tok]).rstrip()
105
print(f"{left:40.40} {right}")
106
107