-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathconstants.py
231 lines (180 loc) · 5.97 KB
/
constants.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# vim: ts=4:et:sw=4:
# ----------------------------------------------------------------------
# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel)
#
# This program is Free Software and is released under the terms of
# the GNU General License
# ----------------------------------------------------------------------
import enum
import os
from enum import StrEnum
from typing import Final, Optional, Union
from .decorator import classproperty
# -------------------------------------------------
# Global constants
# -------------------------------------------------
# Path to main ZX Basic compiler executable
ZXBASIC_ROOT = os.path.abspath(
os.path.join(os.path.abspath(os.path.dirname(os.path.abspath(__file__))), os.path.pardir)
)
# ----------------------------------------------------------------------
# Class enums
# ----------------------------------------------------------------------
@enum.unique
class CLASS(StrEnum):
"""Enums class constants"""
unknown = "unknown" # 0
var = "var" # 1 # scalar variable
array = "array" # 2 # array variable
function = "function" # 3 # function
label = "label" # 4 Labels
const = "const" # 5 # constant literal value e.g. 5 or "AB"
sub = "sub" # 6 # subroutine
type = "type" # 7 # type
@classproperty
def classes(cls):
return cls.unknown, cls.var, cls.array, cls.function, cls.sub, cls.const, cls.label
@classmethod
def is_valid(cls, class_: Union[str, "CLASS"]):
"""Whether the given class is
valid or not.
"""
return class_ in set(CLASS)
@classmethod
def to_string(cls, class_: "CLASS"):
assert cls.is_valid(class_)
return class_.value
class ARRAY:
"""Enums array constants"""
bound_size = 2 # This might change depending on arch, program, etc..
bound_count = 2 # Size of bounds counter
array_type_size = 1 # Size of array type
@enum.unique
class TYPE(enum.IntEnum):
"""Enums primary type constants"""
unknown = 0
byte = 1
ubyte = 2
integer = 3
uinteger = 4
long = 5
ulong = 6
fixed = 7
float = 8
string = 9
boolean = 10
@classmethod
def type_size(cls, type_: "TYPE") -> int:
type_sizes = {
cls.boolean: 1,
cls.byte: 1,
cls.ubyte: 1,
cls.integer: 2,
cls.uinteger: 2,
cls.long: 4,
cls.ulong: 4,
cls.fixed: 4,
cls.float: 5,
cls.string: 2,
cls.unknown: 0,
}
return type_sizes[type_]
@classproperty
def types(cls) -> set["TYPE"]:
return set(TYPE)
@classmethod
def size(cls, type_: "TYPE") -> int:
return cls.type_size(type_)
@classproperty
def integral(cls) -> set["TYPE"]:
return {cls.boolean, cls.byte, cls.ubyte, cls.integer, cls.uinteger, cls.long, cls.ulong}
@classproperty
def signed(cls) -> set["TYPE"]:
return {cls.byte, cls.integer, cls.long, cls.fixed, cls.float}
@classproperty
def unsigned(cls) -> set["TYPE"]:
return {cls.boolean, cls.ubyte, cls.uinteger, cls.ulong}
@classproperty
def decimals(cls) -> set["TYPE"]:
return {cls.fixed, cls.float}
@classproperty
def numbers(cls) -> set["TYPE"]:
return cls.integral | cls.decimals
@classmethod
def is_valid(cls, type_: "TYPE") -> bool:
"""Whether the given type is
valid or not.
"""
return type_ in cls.types
@classmethod
def is_signed(cls, type_: "TYPE") -> bool:
return type_ in cls.signed
@classmethod
def is_unsigned(cls, type_: "TYPE") -> bool:
return type_ in cls.unsigned
@classmethod
def to_signed(cls, type_: "TYPE") -> "TYPE":
"""Return signed type or equivalent"""
if type_ in cls.unsigned:
return {
TYPE.boolean: TYPE.byte,
TYPE.ubyte: TYPE.byte,
TYPE.uinteger: TYPE.integer,
TYPE.ulong: TYPE.long,
}[type_]
if type_ in cls.decimals or type_ in cls.signed:
return type_
return cls.unknown
@staticmethod
def to_string(type_: "TYPE") -> str:
"""Return ID representation (string) of a type"""
return type_.name
@staticmethod
def to_type(typename: str) -> Optional["TYPE"]:
"""Converts a type ID to name. On error returns None"""
for t in TYPE:
if t.name == typename:
return t
return None
@enum.unique
class SCOPE(str, enum.Enum):
"""Enum scopes"""
global_ = "global"
local = "local"
parameter = "parameter"
@staticmethod
def is_valid(scope: Union[str, "SCOPE"]) -> bool:
return scope in set(SCOPE)
@staticmethod
def to_string(scope: "SCOPE") -> str:
assert SCOPE.is_valid(scope)
return scope.value
@enum.unique
class CONVENTION(str, enum.Enum):
unknown = "unknown"
fastcall = "__fastcall__"
stdcall = "__stdcall__"
@staticmethod
def is_valid(convention: Union[str, "CONVENTION"]):
return convention in set(CONVENTION)
@staticmethod
def to_string(convention: "CONVENTION"):
assert CONVENTION.is_valid(convention)
return convention.value
@enum.unique
class LoopType(str, enum.Enum):
DO = "DO"
FOR = "FOR"
WHILE = "WHILE"
# ----------------------------------------------------------------------
# Deprecated suffixes for variable names, such as "a$"
# ----------------------------------------------------------------------
DEPRECATED_SUFFIXES: Final[frozenset[str]] = frozenset(("$", "%", "&"))
# ----------------------------------------------------------------------
# Identifier type
# i8 = Integer, 8 bits
# u8 = Unsigned, 8 bits and so on
# ----------------------------------------------------------------------
ID_TYPES = TYPE.types
# Maps deprecated suffixes to types
SUFFIX_TYPE = {"$": TYPE.string, "%": TYPE.integer, "&": TYPE.long}