-
Notifications
You must be signed in to change notification settings - Fork 0
/
lex.py
148 lines (122 loc) · 3.18 KB
/
lex.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import ply.lex as lex
import sys
import logger
###########################################
# DesSine lexer
# Made by Pierre Bürki and Loïck Jeanneret
# Last updated on 11.01.20
###########################################
reserved_words = (
'while',
'for',
'if',
'else',
'function',
)
tokens = (
# Operators
'ADD_OP',
'MUL_OP',
'MOD_OP',
'EQUAL',
'COMPARATOR',
# Delimiters
'INIT_PREFIX',
#'COMMENT',
'SEMICOLON',
'COMMA',
'PARENTHESIS_OPEN',
'PARENTHESIS_CLOSE',
'BRACKET_OPEN',
'BRACKET_CLOSE',
'newline',
# Terms
'NUMBER',
'HEX_NUMBER',
'IDENTIFIER',
# Builtins
'INIT_FUNCTION',
'BUILTIN_ACTION',
'BUILTIN_FUNCTION',
'BUILTIN_READONLY',
) + tuple(map(lambda s: s.upper(), reserved_words)) # Appends reserved words
t_ADD_OP = r"[+-]"
t_MUL_OP = r"[*/]"
t_MOD_OP = r"%"
t_PARENTHESIS_OPEN = r"\("
t_PARENTHESIS_CLOSE = r"\)"
t_BRACKET_OPEN = r"\{"
t_BRACKET_CLOSE = r"\}"
t_SEMICOLON = r";"
t_COMMA = r","
t_EQUAL = r"="
t_COMPARATOR = r"[!=]=|[><]=?" # Matches ==, !=, <=, >=, <, >
t_INIT_PREFIX = r"\#"
# Functions that can only be called at the start using #
init_functions = ("width", "height", "background")
# builtin methods returning nothing
builtin_actions = ("draw",
"move",
"rotate",
"scale",
"setColor",
"setLineWidth",
"log",)
# builting methods returning something
builtin_functions = ("sin",)
# constants (called readonlys because it may support position in the future)
builtin_readonlys = ("PI",)
# Identifiers may identify the following things:
# - reserved words (for, if, else, while, ...)
# - init functions (width, height, ...)
# - built-in functions, actions, readonlys (sin, draw, pi, ...)
def t_IDENTIFIER(t):
r"[A-Za-z_][\w_]*"
if t.value in reserved_words:
t.type = t.value.upper()
elif t.value in init_functions:
t.type = "INIT_FUNCTION"
elif t.value in builtin_actions:
t.type = "BUILTIN_ACTION"
elif t.value in builtin_functions:
t.type = "BUILTIN_FUNCTION"
elif t.type in builtin_readonlys:
t.type = "BUILTIN_READONLY"
return t
# Hex numbers are used to encode colors
def t_HEX_NUMBER(t):
r"0x[0-9A-Fa-f]+"
t.value = int(t.value, 16)
return t
# Support integer and float numbers
def t_NUMBER(t):
r"\d+(?:\.\d*)?"
if '.' in t.value:
t.value = float(t.value)
else:
t.value = int(t.value)
return t
# We use new lines as instruction separator
def t_newline(t):
r"\n+"
t.lexer.lineno += len(t.value)
return t
# No multiline comment support as of yet
def t_COMMENT(t):
r"//.*\n*"
t.lexer.lineno += t.value.count("\n")
pass
t_ignore = " \t"
def t_error(t):
logger.error("Lexical error", t.lineno, f"illegal character '{t.value[0]}'")
t.lexer.skip(1)
sys.exit()
lex.lex()
if __name__ == "__main__":
with open(sys.argv[1]) as file:
lex.input(file.read())
while True:
tok = lex.token()
if not tok:
break
print(f"line {tok.lineno}: {tok.type}({tok.value})")