beancode 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
beancode/__init__.py ADDED
@@ -0,0 +1,106 @@
1
+ class BCError(Exception):
2
+ # row, col, bol
3
+ pos: tuple[int, int, int]
4
+
5
+ def __init__(self, msg: str, ctx=None) -> None: # type: ignore
6
+ self.len = 1
7
+ if type(ctx).__name__ == "Token":
8
+ self.pos = ctx.pos # type: ignore
9
+ self.len = len(ctx.get_raw()[0]) # type: ignore
10
+ elif type(ctx) == tuple:
11
+ self.pos = ctx
12
+ else:
13
+ self.pos = (0, 0, 0) # type: ignore
14
+
15
+ s = f"\033[31;1merror: \033[0m\033[2m{msg}\033[0m\n"
16
+ self.msg = s
17
+ super().__init__(s)
18
+
19
+ def print(self, filename: str, file_content: str):
20
+ line = self.pos[0]
21
+ col = self.pos[1]
22
+ bol = self.pos[2]
23
+
24
+ eol = bol
25
+ while eol != len(file_content) and file_content[eol] != "\n":
26
+ eol += 1
27
+
28
+ if self.pos == (0, 0, 0):
29
+ print(self.msg, end="")
30
+ return
31
+
32
+ line_begin = f" \033[31;1m{line}\033[0m | "
33
+ padding = len(str(line) + " | ") + col - 1
34
+ spaces = lambda *_: " " * padding
35
+
36
+ print(f"\033[0m\033[1m{filename}:{line}: ", end="")
37
+ print(self.msg, end="")
38
+
39
+ print(line_begin, end="")
40
+ print(file_content[bol:eol])
41
+
42
+ tildes = f"{spaces()}\033[31;1m{'~' * self.len}\033[0m"
43
+ print(tildes)
44
+ indicator = f"{spaces()}\033[31;1m∟ \033[0m\033[1merror at line {line} column {col}\033[0m"
45
+ print(indicator)
46
+
47
+
48
+ class BCWarning(Exception):
49
+ # row, col, bol
50
+ pos: tuple[int, int, int]
51
+
52
+ def __init__(self, msg: str, ctx=None, data=None) -> None: # type: ignore
53
+ self.len = 1
54
+ self.data = data
55
+ if type(ctx).__name__ == "Token":
56
+ self.pos = ctx.pos # type: ignore
57
+ self.len = len(ctx.get_raw()[0]) # type: ignore
58
+ elif type(ctx) == tuple[int, int, int]:
59
+ self.pos = ctx
60
+ else:
61
+ self.pos = (0, 0, 0) # type: ignore
62
+
63
+ s = f"\033[35;1mwarning: \033[0m\033[2m{msg}\033[0m\n"
64
+ self.msg = s
65
+ super().__init__(s)
66
+
67
+ def print(self, filename: str, file_content: str):
68
+ line = self.pos[0]
69
+ col = self.pos[1]
70
+ bol = self.pos[2]
71
+
72
+ eol = bol
73
+ while eol != len(file_content) and file_content[eol] != "\n":
74
+ eol += 1
75
+
76
+ if self.pos == (0, 0, 0):
77
+ print(self.msg, end="")
78
+ return
79
+
80
+ line_begin = f" \033[35;1m{line}\033[0m | "
81
+ padding = len(str(line) + " | ") + col
82
+ spaces = lambda *_: " " * padding
83
+
84
+ print(f"\033[0m\033[1m{filename}:{line}: ", end="")
85
+ print(self.msg, end="")
86
+
87
+ print(line_begin, end="")
88
+ print(file_content[bol:eol])
89
+
90
+ tildes = f"{spaces()}\033[35;1m{'~' * self.len}\033[0m"
91
+ print(tildes)
92
+ indicator = f"{spaces()}\033[35;1m∟ \033[0m\033[1mwarning at line {line} column {col}\033[0m"
93
+ print(indicator)
94
+
95
+
96
+ def error(msg: str):
97
+ print(f"\033[31;1merror: \033[0m{msg}")
98
+ exit(1)
99
+
100
+
101
+ def panic(msg: str):
102
+ print(f"\033[31;1mpanic! \033[0m{msg}")
103
+ print(
104
+ "\033[31mplease report this error to the developers. A traceback is provided:\033[0m"
105
+ )
106
+ raise Exception("panicked")
beancode/__main__.py ADDED
@@ -0,0 +1,60 @@
1
+ import os
2
+ import sys
3
+ import argparse
4
+
5
+ from .interpreter import Interpreter
6
+ from .lexer import *
7
+ from .parser import Parser
8
+ from . import BCError, BCWarning, error
9
+
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser()
13
+ parser.add_argument("file", type=str)
14
+ parser.add_argument(
15
+ "--debug", action="store_true", help="show debugging information"
16
+ )
17
+ args = parser.parse_args()
18
+
19
+ if not os.path.exists(args.file):
20
+ error(f"file {args.file} does not exist!")
21
+
22
+ with open(args.file, "r+") as f:
23
+ file_content = f.read()
24
+
25
+ lexer = Lexer(file_content)
26
+ toks = lexer.tokenize()
27
+
28
+ if args.debug:
29
+ for tok in toks:
30
+ print(tok)
31
+
32
+ parser = Parser(toks)
33
+
34
+ try:
35
+ program, _ = parser.program()
36
+ except BCError as err:
37
+ err.print(args.file, file_content)
38
+ exit(1)
39
+ except BCWarning as w:
40
+ w.print(args.file, file_content)
41
+ exit(1)
42
+
43
+ if args.debug:
44
+ print("\033[1m----- BEGINNING OF AST -----\033[0m", file=sys.stderr)
45
+ for stmt in program.stmts:
46
+ print(stmt)
47
+ print()
48
+ print("\033[0m\033[1m----- END OF AST -----\033[0m", file=sys.stderr)
49
+
50
+ try:
51
+ i = Interpreter(program.stmts)
52
+ i.toplevel = True
53
+ i.visit_block(None)
54
+ except BCError as err:
55
+ err.print(args.file, file_content)
56
+ exit(1)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ main()
beancode/bean_ast.py ADDED
@@ -0,0 +1,488 @@
1
+ import typing
2
+ from dataclasses import dataclass
3
+ from . import *
4
+
5
+
6
+ @dataclass
7
+ class Expr:
8
+ # location of the token
9
+ pos: tuple[int, int, int] | None
10
+
11
+
12
+ BCPrimitiveType = typing.Literal["integer", "real", "char", "string", "boolean", "null"]
13
+
14
+
15
+ @dataclass
16
+ class BCArrayType:
17
+ inner: BCPrimitiveType
18
+ is_matrix: bool # true: 2d array
19
+ flat_bounds: tuple["Expr", "Expr"] | None = None # begin:end
20
+ matrix_bounds: tuple["Expr", "Expr", "Expr", "Expr"] | None = (
21
+ None # begin:end,begin:end
22
+ )
23
+
24
+ def has_bounds(self) -> bool:
25
+ return self.flat_bounds is not None or self.matrix_bounds is not None
26
+
27
+ def get_flat_bounds(self) -> tuple["Expr", "Expr"]:
28
+ if self.flat_bounds is None:
29
+ raise BCError("tried to access flat bounds on array without flat bounds")
30
+ return self.flat_bounds
31
+
32
+ def get_matrix_bounds(self) -> tuple["Expr", "Expr", "Expr", "Expr"]:
33
+ if self.matrix_bounds is None:
34
+ raise BCError("tried to access matrixbounds on array without matrix bounds")
35
+ return self.matrix_bounds
36
+
37
+
38
+ @dataclass
39
+ class BCArray:
40
+ typ: BCArrayType
41
+ flat: list["BCValue"] | None = None # must be a BCPrimitiveType
42
+ matrix: list[list["BCValue"]] | None = None # must be a BCPrimitiveType
43
+ flat_bounds: tuple[int, int] | None = None
44
+ matrix_bounds: tuple[int, int, int, int] | None = None
45
+
46
+ def __repr__(self) -> str:
47
+ if not self.typ.is_matrix:
48
+ return str(self.flat)
49
+ else:
50
+ return str(self.matrix)
51
+
52
+ def get_flat(self) -> list["BCValue"]:
53
+ if self.flat is None:
54
+ raise BCError("tried to access flat array from a matrix array")
55
+ return self.flat
56
+
57
+ def get_matrix(self) -> list[list["BCValue"]]:
58
+ if self.matrix is None:
59
+ raise BCError("tried to access matrix array from a flat array")
60
+ return self.matrix
61
+
62
+
63
+ BCType = BCArrayType | BCPrimitiveType
64
+
65
+
66
+ @dataclass
67
+ class BCValue:
68
+ kind: BCType
69
+ integer: int | None = None
70
+ real: float | None = None
71
+ char: str | None = None
72
+ string: str | None = None
73
+ boolean: bool | None = None
74
+ array: BCArray | None = None
75
+
76
+ def is_uninitialized(self) -> bool:
77
+ return (
78
+ self.integer is None
79
+ and self.real is None
80
+ and self.char is None
81
+ and self.string is None
82
+ and self.boolean is None
83
+ and self.array is None
84
+ )
85
+
86
+ def is_null(self) -> bool:
87
+ return self.kind == "null"
88
+
89
+ @classmethod
90
+ def empty(cls, kind: BCType) -> "BCValue":
91
+ return cls(
92
+ kind,
93
+ integer=None,
94
+ real=None,
95
+ char=None,
96
+ string=None,
97
+ boolean=None,
98
+ array=None,
99
+ )
100
+
101
+ @classmethod
102
+ def new_integer(cls, i: int) -> "BCValue":
103
+ return cls("integer", integer=i)
104
+
105
+ @classmethod
106
+ def new_real(cls, r: float) -> "BCValue":
107
+ return cls("real", real=r)
108
+
109
+ @classmethod
110
+ def new_boolean(cls, b: bool) -> "BCValue":
111
+ return cls("boolean", boolean=b)
112
+
113
+ @classmethod
114
+ def new_char(cls, c: str) -> "BCValue":
115
+ return cls("char", char=c[0])
116
+
117
+ @classmethod
118
+ def new_string(cls, s: str) -> "BCValue":
119
+ return cls("string", string=s)
120
+
121
+ # arrays later
122
+
123
+ def get_integer(self) -> int:
124
+ if self.kind != "integer":
125
+ raise BCError(f"tried to access integer value from BCValue of {self.kind}")
126
+
127
+ return self.integer # type: ignore
128
+
129
+ def get_real(self) -> float:
130
+ if self.kind != "real":
131
+ raise BCError(f"tried to access real value from BCValue of {self.kind}")
132
+
133
+ return self.real # type: ignore
134
+
135
+ def get_char(self) -> str:
136
+ if self.kind != "char":
137
+ raise BCError(f"tried to access char value from BCValue of {self.kind}")
138
+
139
+ return self.char # type: ignore
140
+
141
+ def get_string(self) -> str:
142
+ if self.kind != "string":
143
+ raise BCError(f"tried to access string value from BCValue of {self.kind}")
144
+
145
+ return self.string # type: ignore
146
+
147
+ def get_boolean(self) -> bool:
148
+ if self.kind != "boolean":
149
+ raise BCError(f"tried to access boolean value from BCValue of {self.kind}")
150
+
151
+ return self.boolean # type: ignore
152
+
153
+ def get_array(self) -> BCArray:
154
+ if not isinstance(self.kind, BCArrayType):
155
+ raise BCError(f"tried to access array value from BCValue of {self.kind}")
156
+
157
+ return self.array # type: ignore
158
+
159
+ def __repr__(self) -> str: # type: ignore
160
+ if isinstance(self.kind, BCArrayType):
161
+ return self.array.__repr__()
162
+
163
+ if self.is_uninitialized():
164
+ return "(null)"
165
+
166
+ match self.kind:
167
+ case "string":
168
+ return self.get_string()
169
+ case "real":
170
+ return str(self.get_real())
171
+ case "integer":
172
+ return str(self.get_integer())
173
+ case "char":
174
+ return str(self.get_char())
175
+ case "boolean":
176
+ return str(self.get_boolean())
177
+ case "null":
178
+ return "(null)"
179
+
180
+
181
+ @dataclass
182
+ class Literal(Expr):
183
+ kind: BCPrimitiveType
184
+ integer: int | None = None
185
+ real: float | None = None
186
+ char: str | None = None
187
+ string: str | None = None
188
+ boolean: bool | None = None
189
+
190
+ def to_bcvalue(self) -> BCValue:
191
+ return BCValue(
192
+ self.kind,
193
+ integer=self.integer,
194
+ real=self.real,
195
+ char=self.char,
196
+ string=self.string,
197
+ boolean=self.boolean,
198
+ array=None,
199
+ )
200
+
201
+
202
+ @dataclass
203
+ class Negation(Expr):
204
+ inner: Expr
205
+
206
+
207
+ @dataclass
208
+ class Not(Expr):
209
+ inner: Expr
210
+
211
+
212
+ @dataclass
213
+ class Grouping(Expr):
214
+ inner: Expr
215
+
216
+
217
+ @dataclass
218
+ class Identifier(Expr):
219
+ ident: str
220
+
221
+
222
+ @dataclass
223
+ class Typecast(Expr):
224
+ typ: BCPrimitiveType
225
+ expr: Expr
226
+
227
+
228
+ @dataclass
229
+ class ArrayLiteral(Expr):
230
+ items: list[Expr]
231
+
232
+
233
+ Operator = typing.Literal[
234
+ "assign",
235
+ "equal",
236
+ "less_than",
237
+ "greater_than",
238
+ "less_than_or_equal",
239
+ "greater_than_or_equal",
240
+ "not_equal",
241
+ "mul",
242
+ "div",
243
+ "add",
244
+ "sub",
245
+ "and",
246
+ "or",
247
+ "not",
248
+ ]
249
+
250
+
251
+ @dataclass
252
+ class BinaryExpr(Expr):
253
+ lhs: Expr
254
+ op: Operator
255
+ rhs: Expr
256
+
257
+
258
+ @dataclass
259
+ class ArrayIndex(Expr):
260
+ ident: Identifier
261
+ idx_outer: Expr
262
+ idx_inner: Expr | None = None
263
+
264
+
265
+ StatementKind = typing.Literal[
266
+ "declare",
267
+ "output",
268
+ "input",
269
+ "constant",
270
+ "assign",
271
+ "if",
272
+ "caseof",
273
+ "while",
274
+ "for",
275
+ "repeatuntil",
276
+ "function",
277
+ "procedure",
278
+ "call",
279
+ "fncall",
280
+ "return",
281
+ "scope",
282
+ "include",
283
+ ]
284
+
285
+
286
+ @dataclass
287
+ class CallStatement:
288
+ pos: tuple[int, int, int]
289
+ ident: str
290
+ args: list[Expr]
291
+
292
+
293
+ @dataclass
294
+ class FunctionCall(Expr):
295
+ ident: str
296
+ args: list[Expr]
297
+
298
+
299
+ @dataclass
300
+ class OutputStatement:
301
+ pos: tuple[int, int, int]
302
+ items: list[Expr]
303
+
304
+
305
+ @dataclass
306
+ class InputStatement:
307
+ pos: tuple[int, int, int]
308
+ ident: Identifier
309
+
310
+
311
+ @dataclass
312
+ class ConstantStatement:
313
+ pos: tuple[int, int, int]
314
+ ident: Identifier
315
+ value: Literal
316
+ export: bool = False
317
+
318
+
319
+ @dataclass
320
+ class DeclareStatement:
321
+ pos: tuple[int, int, int]
322
+ ident: Identifier
323
+ typ: BCType
324
+ export: bool = False
325
+ expr: Expr | None = None
326
+
327
+
328
+ @dataclass
329
+ class AssignStatement:
330
+ pos: tuple[int, int, int]
331
+ ident: Identifier | ArrayIndex
332
+ value: Expr
333
+
334
+
335
+ @dataclass
336
+ class IfStatement:
337
+ pos: tuple[int, int, int]
338
+ cond: Expr
339
+ if_block: list["Statement"]
340
+ else_block: list["Statement"]
341
+
342
+
343
+ @dataclass
344
+ class CaseofBranch:
345
+ pos: tuple[int, int, int]
346
+ expr: Expr
347
+ stmt: "Statement"
348
+
349
+
350
+ @dataclass
351
+ class CaseofStatement:
352
+ pos: tuple[int, int, int]
353
+ expr: Expr
354
+ branches: list[CaseofBranch]
355
+ otherwise: "Statement | None"
356
+
357
+
358
+ @dataclass
359
+ class WhileStatement:
360
+ pos: tuple[int, int, int]
361
+ cond: Expr
362
+ block: list["Statement"]
363
+
364
+
365
+ @dataclass
366
+ class ForStatement:
367
+ pos: tuple[int, int, int]
368
+ counter: Identifier
369
+ block: list["Statement"]
370
+ begin: Expr
371
+ end: Expr
372
+ step: Expr | None
373
+
374
+
375
+ @dataclass
376
+ class RepeatUntilStatement:
377
+ pos: tuple[int, int, int]
378
+ cond: Expr
379
+ block: list["Statement"]
380
+
381
+
382
+ @dataclass
383
+ class FunctionArgument:
384
+ pos: tuple[int, int, int]
385
+ name: str
386
+ typ: BCType
387
+
388
+
389
+ @dataclass
390
+ class ProcedureStatement:
391
+ pos: tuple[int, int, int]
392
+ name: str
393
+ args: list[FunctionArgument]
394
+ block: list["Statement"]
395
+ export: bool = False
396
+
397
+
398
+ @dataclass
399
+ class FunctionStatement:
400
+ pos: tuple[int, int, int]
401
+ name: str
402
+ args: list[FunctionArgument]
403
+ returns: BCType
404
+ block: list["Statement"]
405
+ export: bool = False
406
+
407
+
408
+ @dataclass
409
+ class ReturnStatement:
410
+ pos: tuple[int, int, int]
411
+ expr: Expr | None
412
+
413
+
414
+ @dataclass
415
+ class ScopeStatement:
416
+ pos: tuple[int, int, int]
417
+ block: list["Statement"]
418
+
419
+
420
+ @dataclass
421
+ class IncludeStatement:
422
+ pos: tuple[int, int, int]
423
+ file: str
424
+ ffi: bool
425
+
426
+
427
+ @dataclass
428
+ class Statement:
429
+ kind: StatementKind
430
+ declare: DeclareStatement | None = None
431
+ output: OutputStatement | None = None
432
+ input: InputStatement | None = None
433
+ constant: ConstantStatement | None = None
434
+ assign: AssignStatement | None = None
435
+ if_s: IfStatement | None = None
436
+ caseof: CaseofStatement | None = None
437
+ while_s: WhileStatement | None = None
438
+ for_s: ForStatement | None = None
439
+ repeatuntil: RepeatUntilStatement | None = None
440
+ function: FunctionStatement | None = None
441
+ procedure: ProcedureStatement | None = None
442
+ call: CallStatement | None = None
443
+ fncall: FunctionCall | None = None # Impostor! expr as statement?!
444
+ return_s: ReturnStatement | None = None
445
+ scope: ScopeStatement | None = None
446
+ include: IncludeStatement | None = None
447
+
448
+ def __repr__(self) -> str:
449
+ match self.kind:
450
+ case "declare":
451
+ return self.declare.__repr__()
452
+ case "output":
453
+ return self.output.__repr__()
454
+ case "input":
455
+ return self.input.__repr__()
456
+ case "constant":
457
+ return self.constant.__repr__()
458
+ case "assign":
459
+ return self.assign.__repr__()
460
+ case "if":
461
+ return self.if_s.__repr__()
462
+ case "caseof":
463
+ return self.caseof.__repr__()
464
+ case "while":
465
+ return self.while_s.__repr__()
466
+ case "for":
467
+ return self.for_s.__repr__()
468
+ case "repeatuntil":
469
+ return self.repeatuntil.__repr__()
470
+ case "function":
471
+ return self.function.__repr__()
472
+ case "procedure":
473
+ return self.procedure.__repr__()
474
+ case "call":
475
+ return self.call.__repr__()
476
+ case "return":
477
+ return self.return_s.__repr__()
478
+ case "fncall":
479
+ return self.fncall.__repr__()
480
+ case "scope":
481
+ return self.scope.__repr__()
482
+ case "include":
483
+ return self.include.__repr__()
484
+
485
+
486
+ @dataclass
487
+ class Program:
488
+ stmts: list[Statement]
beancode/bean_ffi.py ADDED
@@ -0,0 +1,68 @@
1
+ from dataclasses import dataclass
2
+ from typing import Callable, TypedDict
3
+ from .bean_ast import BCArrayType, BCPrimitiveType, BCType, BCValue, Literal
4
+
5
+
6
+ def _int_literal(i: int) -> Literal:
7
+ return Literal(None, "integer", integer=i)
8
+
9
+
10
+ def array(inner: BCPrimitiveType, low: int, high: int) -> BCArrayType:
11
+ b = (_int_literal(low), _int_literal(high))
12
+ return BCArrayType(inner, is_matrix=False, flat_bounds=b)
13
+
14
+
15
+ def matrix(
16
+ inner: BCPrimitiveType,
17
+ low_outer: int,
18
+ high_outer: int,
19
+ low_inner: int,
20
+ high_inner: int,
21
+ ) -> BCArrayType:
22
+ b = (
23
+ _int_literal(low_outer),
24
+ _int_literal(high_outer),
25
+ _int_literal(low_inner),
26
+ _int_literal(high_inner),
27
+ )
28
+ return BCArrayType(inner, is_matrix=True, matrix_bounds=b)
29
+
30
+
31
+ BCParamSpec = dict[str, BCType]
32
+ BCArgsList = dict[str, BCValue]
33
+
34
+
35
+ @dataclass
36
+ class BCFunction: # ffi variant of a function
37
+ name: str
38
+ params: BCParamSpec
39
+ returns: BCPrimitiveType
40
+ fn: Callable[[BCArgsList], BCValue]
41
+
42
+
43
+ @dataclass
44
+ class BCProcedure: # ffi variant of a function
45
+ name: str
46
+ params: BCParamSpec # spec of arg names and types
47
+ fn: Callable[[BCArgsList], None]
48
+
49
+
50
+ @dataclass
51
+ class BCDeclare:
52
+ name: str
53
+ typ: BCType | None = None # either typ, value or both must be set
54
+ value: BCValue | None = None
55
+
56
+
57
+ @dataclass
58
+ class BCConstant:
59
+ name: str
60
+ value: BCValue
61
+
62
+
63
+ @dataclass
64
+ class Exports(TypedDict):
65
+ constants: list[BCConstant]
66
+ variables: list[BCDeclare]
67
+ procs: list[BCProcedure]
68
+ funcs: list[BCFunction]