1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (C) 2024 Antmicro 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18# SPDX-License-Identifier: Apache-2.0 19 20from typing import Any, Optional, Iterable, Union, TypeVar, Callable 21from enum import Enum 22from contextlib import contextmanager 23 24from itertools import chain 25from functools import partial 26 27_T = TypeVar('_T') 28 29def intersperse(it: Iterable[_T], s: _T) -> Iterable[_T]: 30 add_s = False 31 while True: 32 try: 33 v = it.__next__() 34 except StopIteration: 35 return 36 if add_s: 37 yield s 38 yield v 39 add_s = True 40 41class CodeCC(Enum): 42 NEST = 0 43 UNNEST = 1 44 COMMENT_BEGIN = 2 45 COMMENT_END = 3 46 COMMENT_MODE_SET_INLINE = 4 47 COMMENT_MODE_RESTORE = 5 48 DOC_BEGIN = 6 49 DOC_END = 7 50 51class AstException(Exception): 52 def __init__(self): 53 super().__init__() 54 55class AstWrongTypeException(AstException): 56 def __init__(self, got: type, expected: type) -> None: 57 super().__init__() 58 self.got = got 59 self.expected = expected 60 61 def __str__(self) -> str: 62 return f'Expected type `{self.expected}`, got `{self.got}`' 63 64def expect_type(obj: Any, ty: type) -> Any: 65 if type(obj) is not ty: 66 raise AstWrongTypeException(type(obj), ty) 67 return Any 68 69class Node: 70 def __init__(self, name: Optional[str] = None, 71 parent: Optional[tuple['Node', str]] = None, 72 previous: Optional['Node'] = None, 73 next: Optional['Node'] = None, 74 indents: bool = True, 75 comment: Optional[str] = None, 76 doc: Optional[str] = None): 77 self.name = name if name is not None else '<anonymous>' 78 self.indents = indents 79 self.m_parent = parent 80 self.m_previous = previous 81 self.m_next = next 82 self.m_comment = comment 83 self.m_doc = doc 84 85 def get_hierarchical_name(self): 86 if self.parent: 87 return self.generate_reference(self.parent) + '.' + self.name 88 return self.name 89 90 @property 91 def parent(self) -> Optional[tuple['Node', str]]: 92 return self.m_parent 93 94 @parent.setter 95 def parent(self, value: tuple['Node', str]): 96 if self.m_parent is not None: 97 raise RuntimeError(f'Node {self.name} of type {type(self)} ' 98 f'has a parent of type {type(self.parent[0])}' 99 'already attached') 100 if self.m_previous is not None: 101 raise RuntimeError('Node can\'t have a predecessor and a parent at the same time') 102 self.m_parent = value 103 104 @parent.deleter 105 def parent(self): 106 self.m_parent = None 107 108 def get_parent(self) -> Optional[tuple['Node', str]]: 109 return self.first().parent 110 111 def children(self) -> Iterable['Node']: 112 return [] 113 114 def tokenize(self, cg: 'CodeGenerator') -> Iterable[Union[str, CodeCC]]: 115 raise RuntimeError('Abstract codegen') 116 117 def m_path_to_root(self) -> list['Node']: 118 n = self 119 path_to_root = [] 120 while n is not None: 121 path_to_root.append(n) 122 n = n.parent[0] 123 124 @property 125 def null(self) -> bool: return False 126 127 def detach(self) -> 'Node': 128 if self.previous is None and self.parent is None: 129 raise RuntimeError('Node is already detached') 130 131 132 if self.parent is not None: 133 setattr(self.parent[0], self.parent[1], NullNode()) 134 135 del(self.previous) 136 del(self.parent) 137 138 return self 139 140 def cut(self) -> 'Node': 141 if self.next is None: 142 return self.detach() 143 tail = self.m_next.detach() 144 self.replace(tail) 145 return self 146 147 @property 148 def previous(self) -> Optional['Node']: 149 return self.m_previous 150 151 @previous.setter 152 def previous(self, node: 'Node') -> None: 153 if self.m_previous is not None: 154 raise RuntimeError(f'Node {self.name} of type {type(self)} ' 155 f'has a predecessor {self.previous.name} ' 156 f'of type {type(self.previous)} already attached') 157 if self.m_parent is not None: 158 raise RuntimeError('Node can\'t have a predecessor and a parent at the same time') 159 self.m_previous = node 160 161 @previous.deleter 162 def previous(self) -> None: 163 if self.m_previous is not None: 164 self.m_previous.m_next = None 165 self.m_previous = None 166 167 @property 168 def next(self) -> Optional['Node']: 169 return self.m_next 170 171 @next.setter 172 def next(self, node: 'Node') -> None: 173 if self.m_next is not None: 174 raise RuntimeError(f'Node {self.name} of type {type(self)} ' 175 f'has a successor {self.m_next.name} ' 176 f'of type {type(self.m_next)} already attached') 177 self.m_next = node 178 179 @next.deleter 180 def next(self) -> None: 181 if self.m_next is not None: 182 self.m_previous = None 183 self.m_next = None 184 185 def first(self) -> 'Node': 186 first = self 187 while True: 188 if first.previous is None: 189 return first 190 first = first.previous 191 192 def last(self) -> 'Node': 193 last = self 194 while True: 195 if last.next is None: 196 return last 197 last = last.next 198 199 def replace(self, node: 'Node') -> None: 200 if not self.previous and not self.parent: 201 raise RuntimeError('No links to replace') 202 if self.previous: 203 prev = self.previous 204 del(self.previous) 205 prev.next = node 206 elif self.parent: 207 (parent, field) = self.parent 208 del(self.parent) 209 node.parent = (parent, field) 210 setattr(parent, field, node) 211 tail = self.next 212 del(self.next) 213 if tail is not None: 214 node.last().append(tail) 215 216 def append(self, node: 'Node', insert: bool = False) -> None: 217 last = node.last() 218 219 if self.next is not None: 220 if not insert: 221 raise RuntimeError('Node is already linked') 222 next = self.next 223 if insert: 224 del(self.next) 225 last.next = next 226 self.next = node 227 node.previous = self 228 229 def then(self, node: 'Node') -> 'Node': 230 self.last().append(node) 231 return self 232 233 def iterate(self) -> Iterable['Node']: 234 node = self 235 while node is not None: 236 next = node.next # Keep the original next in case we replaced the node 237 yield node 238 node = next 239 240 @staticmethod 241 def join(nodes: Optional[Iterable['Node']]) -> 'Node': 242 if nodes is None: return NullNode() 243 244 it = nodes.__iter__() 245 try: 246 node = it.__next__() 247 except StopIteration: 248 return NullNode() 249 250 first = node 251 last = node.last() 252 while True: 253 try: 254 node = it.__next__() 255 except StopIteration: 256 break 257 last.append(node) 258 last = node.last() 259 260 return first 261 262 @staticmethod 263 def or_null(node: Optional[_T]) -> Union[_T, 'NullNode']: 264 if node is None: return NullNode() 265 return node 266 267 def emit_comment_tokens(self, inline: bool = False) -> list[Union[str, CodeCC]]: 268 tokens = [] 269 270 if self.m_doc is not None: 271 tokens += [CodeCC.DOC_BEGIN, self.m_doc, CodeCC.DOC_END] 272 273 if self.m_comment is not None: 274 if inline: tokens.append(CodeCC.COMMENT_MODE_SET_INLINE) 275 tokens += [CodeCC.COMMENT_BEGIN, self.m_comment, CodeCC.COMMENT_END] 276 if inline: tokens.append(CodeCC.COMMENT_MODE_RESTORE) 277 278 return tokens 279 280 def __str__(self) -> str: 281 return CodeGenerator.emit(self) 282 283class NullNode(Node): 284 def __init__(self, **kwargs): 285 super().__init__(**kwargs) 286 287 def iterate(self) -> Iterable['Node']: 288 return [] 289 290 def append(self, node: Node, insert: bool = False) -> None: 291 return self.replace(node) 292 293 @property 294 def null(self) -> bool: return True 295 296class CodeGenerator: 297 def __init__(self): 298 self.m_indent = 0 299 self.m_s = '' 300 self.m_namespace: list['Namespace'] = [] 301 self.m_comment_inline = 0 302 303 def generate_reference(self, node: Node) -> str: 304 # TODO: Take context into account 305 parent = node.get_parent() 306 if parent: 307 return self.generate_reference(parent[0]) + '.' + node.name 308 return node.name 309 310 @contextmanager 311 def enter_namespace(self, namespace: 'Namespace'): 312 self.m_namespace.append(namespace) 313 try: 314 yield 315 finally: 316 self.m_namespace.pop() 317 318 def m_add_indent(self): 319 if len(self.m_s) != 0 and self.m_s[-1] == '\n': 320 self.m_s += ' ' * self.m_indent 321 322 @staticmethod 323 def emit(node: Node, comments: bool = False, docs: bool = True) -> str: 324 cg = CodeGenerator() 325 326 for token in node.tokenize(cg): 327 match token: 328 case str(code): 329 for line in code.splitlines(keepends=True): 330 cg.m_add_indent() 331 cg.m_s += line 332 case CodeCC.NEST: 333 if node.indents: 334 cg.m_indent += 1 335 case CodeCC.UNNEST: 336 if node.indents: 337 cg.m_indent -= 1 338 case CodeCC.COMMENT_MODE_SET_INLINE: 339 cg.m_comment_mode += 1 340 case CodeCC.COMMENT_MODE_RESTORE: 341 cg.m_comment_mode -= 1 342 case CodeCC.COMMENT_BEGIN: 343 if comments: 344 if cg.m_comment_inline: 345 cg.m_s += '/* ' 346 else: 347 cg.m_add_indent() 348 cg.m_s += '// ' 349 case CodeCC.COMMENT_END: 350 if comments: cg.m_s += '\n' if cg.m_comment_inline == 0 else ' */' 351 case CodeCC.DOC_BEGIN: 352 if docs: 353 cg.m_add_indent() 354 cg.m_s += '/// <summary> ' 355 case CodeCC.DOC_END: 356 if docs: cg.m_s += ' </summary>\n' 357 case _ as invalid: 358 raise RuntimeError(f'Invalid code control: {invalid}') 359 360 return cg.m_s; 361 362class HardCode(Node): 363 def __init__(self, code: str, **kwargs) -> None: 364 super().__init__(**kwargs) 365 self.code = code 366 367 def tokenize(self, _: CodeGenerator) -> str: 368 return self.emit_comment_tokens(inline=True) + [self.code] 369 370def TypeMetaClass(name, bases, attrs: dict): 371 ret = type(name, bases, attrs) 372 373 setattr(ret, 'sbyte', ret('sbyte', 8)) 374 setattr(ret, 'byte', ret('byte', 8)) 375 setattr(ret, 'short', ret('short', 16)) 376 setattr(ret, 'ushort', ret('ushort', 16)) 377 setattr(ret, 'int', ret('int', 32)) 378 setattr(ret, 'uint', ret('uint', 32)) 379 setattr(ret, 'long', ret('long', 64)) 380 setattr(ret, 'ulong', ret('ulong', 64)) 381 setattr(ret, 'bool', ret('bool', 8)) 382 setattr(ret, 'string', ret('string')) 383 384 return ret 385 386class Type(Node, metaclass=TypeMetaClass): 387 sbyte: 'Type' 388 byte: 'Type' 389 short: 'Type' 390 ushort: 'Type' 391 int: 'Type' 392 uint: 'Type' 393 long: 'Type' 394 ulong: 'Type' 395 bool: 'Type' 396 string: 'Type' 397 398 @property 399 def width(self) -> Optional[int]: 400 return self.width_ 401 402 @property 403 def is_long(self) -> bool: 404 return self.width > 32 if self.width is not None else False 405 406 @property 407 def is_unsigned(self) -> bool: 408 return self in [Type.byte, Type.ushort, Type.uint, Type.ulong] 409 410 def __init__(self, name: str, width: Optional[int] = None, **kwargs): 411 super().__init__(name=name, **kwargs) 412 self.width_ = width 413 414 def __eq__(self, other) -> bool: 415 return self.name == other.name 416 417 def array(self) -> 'Type': 418 return Type(self.name + '[]') 419 420 def tokenize(self, _: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 421 return self.emit_comment_tokens() + [self.name] 422 423class Expr(Node): 424 def __init__(self, ty: Type, **kwargs): 425 super().__init__(**kwargs) 426 self.type = ty 427 428 def into_stmt(self) -> 'StmtExpr': 429 return StmtExpr(self) 430 431class HardExpr(Expr): 432 def __init__(self, code: str, ty: Type, **kwargs) -> None: 433 super().__init__(ty=ty, **kwargs) 434 self.code = code 435 436 def tokenize(self, _: CodeGenerator) -> str: 437 return self.emit_comment_tokens() + [self.code] 438 439class IntLit(Expr): 440 def __init__(self, value: int, unsigned: bool = False, long: bool = False, 441 fmt: str = 'd', **kwargs): 442 match (unsigned, long): 443 case (False, False): 444 super().__init__(ty=Type.int, **kwargs) 445 case (False, True): 446 super().__init__(ty=Type.long, **kwargs) 447 case (True, False): 448 super().__init__(ty=Type.uint, **kwargs) 449 case (True, True): 450 super().__init__(ty=Type.ulong, **kwargs) 451 452 self.value = value 453 self.fmt = fmt 454 455 def tokenize(self, _: CodeGenerator) -> Iterable[str | CodeCC]: 456 match self.fmt: 457 case 'd': s = str(self.value) 458 case 'h': s = hex(self.value) 459 case _: raise RuntimeError(f'Invalid integer format `{self.fmt}`') 460 461 if self.type in [Type.ulong, Type.uint, Type.ushort, Type.byte]: 462 s += 'U' 463 if self.type in [Type.long, Type.ulong]: 464 s += 'L' 465 466 return self.emit_comment_tokens() + [s] 467 468class StringLit(Expr): 469 def __init__(self, value: str, **kwargs): 470 super().__init__(ty=Type.string, **kwargs) 471 self.value = value 472 473 def tokenize(self, _: CodeGenerator) -> Iterable[str | CodeCC]: 474 return self.emit_comment_tokens(inline=True) + \ 475 ['"' + self.value.replace('"', '\"') + '"'] 476 477class Arg(Node): 478 def __init__(self, value: Any, name: Optional[str] = None, 479 out: bool = False, **kwargs) -> None: 480 super().__init__(name=name, **kwargs) 481 self.named = name is not None 482 self.value = value 483 self.out = out 484 485 def tokenize(self, _: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 486 s = '' 487 if self.named: 488 s += f'{self.name}: ' 489 if self.out: 490 s += f'out ' 491 492 s += str(self.value) 493 494 return self.emit_comment_tokens(inline=True) + [s] 495 496class BoolLit(Expr): 497 def __init__(self, value: bool, **kwargs) -> None: 498 super().__init__(ty=Type.bool, **kwargs) 499 self.m_value = value 500 501 @property 502 def value(self) -> bool: return self.m_value 503 504 def tokenize(self, _: CodeGenerator) -> str: 505 return self.emit_comment_tokens(inline=True) + ['true' if self.value else 'false'] 506 507class Stmt(Node): 508 def __init__(self, **kwargs) -> None: 509 super().__init__(**kwargs) 510 511class StmtExpr(Stmt): 512 def __init__(self, expr: Expr, **kwargs) -> None: 513 super().__init__(**kwargs) 514 self.m_expr = expr 515 expr.parent = (self, 'expr') 516 517 @property 518 def expr(self) -> Expr: return self.expr 519 520 def children(self) -> Iterable[Node]: 521 return [self.m_expr] 522 523 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 524 return chain(self.emit_comment_tokens(), self.m_expr.tokenize(cg), [';\n']) 525 526class VariableRef(Expr): 527 def __init__(self, decl: 'VariableDecl', **kwargs): 528 super().__init__(ty=decl.type, **kwargs) 529 self.decl = decl 530 531 def tokenize(self, cg: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 532 return self.emit_comment_tokens() + [self.decl.name] 533 534def AccessibilityModMetaClass(name, bases, attrs: dict): 535 ret = type(name, bases, attrs) 536 setattr(ret, 'PUBLIC', ret(1)) 537 setattr(ret, 'INTERNAL', ret(2)) 538 setattr(ret, 'PROTECTED', ret(3)) 539 setattr(ret, 'PRIVATE', ret(4)) 540 return ret 541 542class AccessibilityMod(Node, metaclass=AccessibilityModMetaClass): 543 PUBLIC: 'AccessibilityMod' 544 INTERNAL: 'AccessibilityMod' 545 PROTECTED: 'AccessibilityMod' 546 PRIVATE: 'AccessibilityMod' 547 548 def __init__(self, value: int, **kwargs) -> None: 549 super().__init__(**kwargs) 550 self.value = value 551 552 def __eq__(self, value: 'AccessibilityMod') -> bool: 553 return self.value == value.value 554 555 def tokenize(self, _: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 556 match self: 557 case AccessibilityMod.PUBLIC: tokens = ['public'] 558 case AccessibilityMod.INTERNAL: tokens = ['internal'] 559 case AccessibilityMod.PROTECTED: tokens = ['protected'] 560 case AccessibilityMod.PRIVATE: tokens = ['private'] 561 case _: raise RuntimeError('Invalid AccessibilityMod') 562 563 return self.emit_comment_tokens(inline=True) + tokens 564 565class VariableDecl(Stmt): 566 def __init__(self, name: str, ty: Type, init: Optional[Expr] = None, 567 access: Optional[AccessibilityMod] = None, **kwargs): 568 super().__init__(name=name, **kwargs) 569 self.type = ty 570 self.init = init 571 self.access = access 572 573 def ref(self) -> VariableRef: 574 return VariableRef(self) 575 576 def tokenize(self, cg: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 577 return chain( 578 self.emit_comment_tokens(), 579 [str(self.access) + ' '] if self.access is not None else '', 580 self.type.tokenize(cg), 581 [' ' + self.name], 582 *(chain([' = '], self.init.tokenize(cg)) if self.init is not None else []), 583 [';\n'] 584 ) 585 586class ArgDecl(VariableDecl): 587 def __init__(self, name: str, ty: Type, out: bool = False, 588 default: Any = None, **kwargs) -> None: 589 super().__init__(name=name, ty=ty, **kwargs) 590 591 self.type = ty 592 self.out = out 593 self.default = default 594 595 def tokenize(self, _: CodeGenerator) -> Iterable[str | CodeCC]: 596 s = str(self.type) 597 if self.out: 598 s += ' out' 599 s += ' ' + self.name 600 601 if self.default: 602 s += f' = {self.default}' 603 604 return self.emit_comment_tokens(inline=True) + [s] 605 606class InvokableDefinition(Node): 607 def __init__(self, ret_ty: Optional[Type] = None, 608 static: bool = False, override: bool = False, 609 virtual: bool = False, abstract: bool = False, 610 partial: bool = False, 611 access: Optional[AccessibilityMod] = None, **kwargs) -> None: 612 super().__init__(**kwargs) 613 614 self.ret_type = ret_ty 615 self.static = static 616 self.virtual = virtual 617 self.override = override 618 self.abstract = abstract 619 self.partial = partial 620 self.access = access 621 622 @property 623 def p_prefix(self) -> str: 624 prefix = '' 625 if self.partial: prefix += 'partial ' 626 if self.static: prefix += 'static ' 627 if self.override: prefix += 'override ' 628 if self.virtual: prefix += 'virtual ' 629 if self.abstract: prefix += 'abstract ' 630 return prefix 631 632 @staticmethod 633 def p_definition_tail( 634 cg: CodeGenerator, 635 body: Optional[Union[StmtExpr]] 636 ) -> Iterable[Union[str, CodeCC]]: 637 if body is not None: 638 return chain( 639 ['\n{\n', CodeCC.NEST], 640 *map(lambda s: s.tokenize(cg), body.iterate()), 641 [CodeCC.UNNEST, '}\n'] 642 ) 643 return [';\n'] 644 645class MethodDefinition(InvokableDefinition): 646 def __init__(self, name: str, 647 args: Optional[ArgDecl] = None, 648 body: Optional[Union[StmtExpr]] = None, 649 constructor: bool = False, 650 access: Optional[AccessibilityMod] = None, 651 **kwargs) -> None: 652 super().__init__(name=name, access=access, **kwargs) 653 654 self.args = args if args is not None else NullNode() 655 self.body = body 656 self.ctor = constructor 657 658 if args is not None: args.parent = (self, 'args') 659 if body is not None: body.parent = (self, 'body') 660 661 def children(self) -> Iterable[Node]: 662 if self.body is not None: 663 return chain(self.args.iterate(), self.body.iterate()) 664 return self.args.iterate() 665 666 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 667 match (self.ret_type, self.ctor): 668 case (Type() as ret_ty, False): return_ty_prefix = str(ret_ty) + ' ' 669 case (None, False): return_ty_prefix = 'void ' 670 case (None, True): return_ty_prefix = '' 671 case _: raise RuntimeError('Invalid method return type configuration') 672 673 prefix = self.p_prefix + return_ty_prefix 674 675 tail = InvokableDefinition.p_definition_tail(cg, self.body) 676 677 return chain( 678 self.emit_comment_tokens(), 679 [str(self.access) + ' '] if self.access is not None else '', 680 [f'{prefix}{self.name}(' + ', '.join(str(arg) for arg in self.args.iterate()) + ')'], 681 tail 682 ) 683 684class PropertyDefintion(InvokableDefinition): 685 def __init__(self, name: str, get: Union[bool, StmtExpr] = False, 686 set: Union[bool, StmtExpr] = False, 687 access: Optional[AccessibilityMod] = None, **kwargs) -> None: 688 super().__init__(name=name, access=access, **kwargs) 689 690 self.get = get 691 self.set = set 692 693 if isinstance(get, Node): get.parent = (self, 'get') 694 if isinstance(set, Node): set.parent = (self, 'set') 695 696 def children(self) -> Iterable[Node]: 697 return chain( 698 self.get.iterate() if isinstance(self.get, Node) else [], 699 self.set.iterate() if isinstance(self.set, Node) else [] 700 ) 701 702 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 703 code = self.emit_comment_tokens() + \ 704 ([str(self.access) + ' '] if self.access is not None else [' ']) + \ 705 [f'{self.p_prefix}{self.ret_type} {self.name} ' + '{'] 706 707 indent = type(self.set) is not bool or type(self.get) is not bool 708 709 if indent: 710 code[-1] += '\n' 711 code.append(CodeCC.NEST) 712 713 def make_body(accessor: Union[bool, Stmt], name: str): 714 match accessor: 715 case True: 716 if not indent: code[-1] += ' ' 717 code.append(name + ';') 718 if indent: code[-1] += '\n' 719 case Node() as get_body: 720 code.append(name) 721 code.extend(InvokableDefinition.p_definition_tail(cg, get_body)) 722 723 make_body(self.get, 'get') 724 make_body(self.set, 'set') 725 726 if indent: 727 code.append(CodeCC.UNNEST) 728 code.append('}\n') 729 else: 730 code.append(' }\n') 731 732 return code 733 734class Call(Expr): 735 def __init__(self, method: Union[str | MethodDefinition], *arguments: Arg, 736 ret_ty: Optional[Type] = None, 737 object: Optional[Node] = None, 738 breakline: bool = False, **kwargs) -> None: 739 740 match (method, ret_ty): 741 case (str() as name, Type() | None as ty): 742 super().__init__(ty=ret_ty, **kwargs) 743 self.method_name = name 744 case (MethodDefinition() as m, None): 745 super().__init__(ty=m.ret_type, **kwargs) 746 self.method_name = m.name 747 case _: 748 raise RuntimeError('Invalid call method reference') 749 750 self.arguments = Node.join(arguments) 751 self.object = object 752 self.breakline = breakline 753 754 if len(arguments) != 0: self.arguments.parent = (self, 'arguments') 755 756 def children(self) -> Iterable[Node]: 757 return self.arguments.iterate() 758 759 def tokenize(self, cg: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 760 call = [self.method_name + \ 761 '(' + ', '.join(str(arg) for arg in self.arguments.iterate()) + ')'] 762 if self.object is None: return call 763 764 nested = False 765 sep = [] 766 if self.breakline: 767 sep.append('\n') 768 if not self.object is Call or self.object.object is None: 769 nested = True 770 sep.append(CodeCC.NEST) 771 sep.append('.') 772 773 tokens = chain(self.object.tokenize(cg), sep, call, [CodeCC.UNNEST] if nested else []) 774 return chain(self.emit_comment_tokens(not self.breakline), tokens) 775 776class Class(Node): 777 def __init__(self, name: str, 778 fields: Optional[VariableDecl] = None, 779 properties: Optional[PropertyDefintion] = None, 780 methods:Optional[MethodDefinition] = None, 781 classes: Optional['Class'] = None, 782 derives: Optional[list[tuple[Optional[AccessibilityMod], 'Class']]] = None, 783 abstract: bool = False, partial: bool = False, 784 struct: bool = False, 785 access: Optional[AccessibilityMod] = None, **kwargs 786 ) -> None: 787 super().__init__(name=name, **kwargs) 788 789 self.fields = Node.or_null(fields) 790 self.properties = Node.or_null(properties) 791 self.methods = Node.or_null(methods) 792 self.classes = Node.or_null(classes) 793 self.derives = derives if derives is not None else [] 794 self.abstract = abstract 795 self.partial = partial 796 self.struct = struct 797 self.access = access 798 799 self.fields.parent = (self, 'members') 800 self.properties.parent = (self, 'properties') 801 self.methods.parent = (self, 'method') 802 self.classes.parent = (self, 'classes') 803 804 @property 805 def type(self) -> Type: 806 return Type(self.name) 807 808 def children(self) -> Iterable[Node]: 809 return chain( 810 self.fields.iterate(), 811 self.properties.iterate(), 812 self.methods.iterate(), 813 self.classes.iterate() 814 ) 815 816 def tokenize(self, cg: CodeGenerator) -> Iterable[Union[str, CodeCC]]: 817 def with_access(access: AccessibilityMod, node: Node): 818 if access is not None: 819 return chain([str(access), ' '], node.tokenize(cg)) 820 return node.tokenize(cg) 821 822 header = [str(self.access) + ' '] if self.access is not None else [''] 823 if self.abstract: header[-1] += 'partial ' 824 if self.partial: header[-1] += 'partial ' 825 header[-1] += ('struct ' if self.struct else 'class ') + self.name 826 if len(self.derives) != 0: 827 header = chain( 828 header, 829 [' : '], 830 *intersperse((with_access(a, c.type) for a, c in self.derives), [', ']) 831 ) 832 header = chain(header, ['\n{\n']) 833 834 return chain( 835 self.emit_comment_tokens(), 836 header, 837 [CodeCC.NEST], 838 *(f.tokenize(cg) for f in self.fields.iterate()), 839 '\n' if not self.properties.null else '', 840 *(p.tokenize(cg) for p in self.properties.iterate()), 841 '\n' if not self.methods.null else '', 842 *intersperse((m.tokenize(cg) for m in self.methods.iterate()), '\n'), 843 '\n' if not self.classes.null else '', 844 *intersperse((c.tokenize(cg) for c in self.classes.iterate()), '\n'), 845 [CodeCC.UNNEST, '}\n'] 846 ) 847 848class Namespace(Node): 849 def __init__(self, name: str, 850 namespaces: Optional[list['Namespace']] = None, 851 classes: Optional[Class] = None, 852 **kwargs): 853 super().__init__(name=name, **kwargs) 854 855 self.namespaces = Node.join(namespaces) 856 self.classes = Node.or_null(classes) 857 858 if namespaces is not None: self.namespaces.parent = (self, 'namespaces') 859 if classes is not None: self.classes.parent = (self, 'classes') 860 861 def children(self) -> Iterable[Node]: 862 return chain(self.namespaces.iterate(), self.classes.iterate()) 863 864 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 865 fullname = cg.generate_reference(self) 866 with cg.enter_namespace(self): 867 return chain( 868 self.emit_comment_tokens(), 869 [f'namespace {fullname}\n', '{\n', CodeCC.NEST], 870 *intersperse((c.tokenize(cg) for c in self.classes.iterate()), '\n'), 871 '\n' if not self.namespaces.null else '', 872 *intersperse((n.tokenize(cg) for n in self.namespaces.iterate()), '\n'), 873 [CodeCC.UNNEST, '}\n'] 874 ) 875 876class New(Expr): 877 def __init__(self, ty: Type, *args: Arg, **kwargs,) -> None: 878 super().__init__(ty, **kwargs) 879 self.args = Node.join(args) 880 881 if len(args) != 0: self.args.parent = (self, 'args') 882 883 def children(self) -> Iterable[Node]: 884 return self.args.iterate() 885 886 def tokenize(self, _: CodeGenerator) -> Iterable[str | CodeCC]: 887 return self.emit_comment_tokens(inline=True) + \ 888 [f'new {self.type}(' + ', '.join(str(arg) for arg in self.args.iterate()) + ')'] 889 890class NewArray(Expr): 891 def __init__(self, ty: Type, count: int, **kwargs,) -> None: 892 super().__init__(ty, **kwargs) 893 self.count = count 894 895 def tokenize(self, _: CodeGenerator) -> Iterable[str | CodeCC]: 896 return self.emit_comment_tokens(inline=True) + [f'new {self.type}[{self.count}]'] 897 898class Assign(Expr): 899 def __init__(self, lhs: Expr, rhs: Expr, **kwargs): 900 super().__init__(ty=lhs.type, **kwargs) 901 902 self.lhs = lhs 903 self.rhs = rhs 904 905 lhs.parent = (self, 'lhs') 906 rhs.parent = (self, 'rhs') 907 908 def children(self) -> Iterable[Node]: 909 return [self.lhs, self.rhs] 910 911 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 912 return chain( 913 self.lhs.tokenize(cg), 914 self.emit_comment_tokens(inline=True), 915 [' = '], 916 self.rhs.tokenize(cg) 917 ) 918 919class This(Node): 920 def __init__(self, **kwargs): 921 super().__init__(**kwargs) 922 923 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 924 return self.emit_comment_tokens(inline=True) + ['this'] 925 926class Return(Stmt): 927 def __init__(self, expr: 'Expr | None' = None, **kwargs) -> None: 928 super().__init__(**kwargs) 929 self.expr = expr 930 if expr is not None: self.expr.parent = (self, 'expr') 931 932 def children(self) -> Iterable[Node]: 933 return [self.expr] if self.expr is not None else [] 934 935 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 936 tokens = self.emit_comment_tokens() 937 if self.expr is not None: 938 return chain(tokens, ['return '], self.expr.tokenize(cg), [';\n']) 939 else: 940 return tokens + ['return;\n'] 941 942class Throw(Stmt): 943 def __init__(self, expr: 'Expr', **kwargs) -> None: 944 super().__init__(**kwargs) 945 self.expr = expr; 946 947 def children(self) -> Iterable[Node]: 948 return [self.expr] 949 950 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 951 return chain( 952 self.emit_comment_tokens(), 953 ['throw '], 954 self.expr.tokenize(cg), [';\n'] 955 ) 956 957class BinaryOp(Expr): 958 def __init__(self, op: str, lhs: Expr, rhs: Expr, ty: Optional[Type] = None, **kwargs): 959 if ty is None: 960 super().__init__(ty=lhs.type, **kwargs) 961 else: 962 super().__init__(ty=ty, **kwargs) 963 964 self.op = op 965 self.lhs = lhs 966 self.rhs = rhs 967 968 self.lhs.parent = (self, 'lhs') 969 self.rhs.parent = (self, 'rhs') 970 971 def children(self) -> Iterable[Node]: 972 return [self.lhs, self.rhs] 973 974 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 975 return chain( 976 self.lhs.tokenize(cg), 977 self.emit_comment_tokens(inline=True), 978 [f' {self.op} '], 979 self.rhs.tokenize(cg), 980 ) 981 982class If(Stmt): 983 def __init__( 984 self, 985 condition: Expr, 986 then: Stmt, 987 else_: Optional[Stmt] = None, 988 **kwargs 989 ) -> None: 990 super().__init__(**kwargs) 991 self.condition = condition 992 self.then_ = then 993 self.else_ = else_ if else_ is not None else NullNode() 994 995 self.condition.parent = (self, 'condition') 996 self.then_.parent = (self, 'then_') 997 if else_ is not None: self.else_.parent = (self, 'else_') 998 999 def children(self) -> Iterable[Node]: 1000 return chain( 1001 [self.condition], 1002 self.then_.iterate(), 1003 self.else_.iterate() 1004 ) 1005 1006 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 1007 tokens = chain( 1008 ['if('], self.condition.tokenize(cg), [')\n{\n', CodeCC.NEST], 1009 *(stmt.tokenize(cg) for stmt in self.then_.iterate()), 1010 [CodeCC.UNNEST, '}\n'] 1011 ) 1012 1013 if not self.else_.null: 1014 tokens = chain( 1015 self.emit_comment_tokens(), 1016 tokens, 1017 ['else\n{\n', CodeCC.NEST], 1018 *(stmt.tokenize(cg) for stmt in self.else_.iterate()), 1019 [CodeCC.UNNEST, '}\n'] 1020 ) 1021 1022 return tokens 1023 1024class Cast(Expr): 1025 def __init__(self, ty: Type, expr: Expr, **kwargs): 1026 super().__init__(ty, **kwargs) 1027 self.expr = expr 1028 1029 self.expr.parent = (self, 'expr') 1030 1031 def children(self) -> Iterable[Node]: 1032 return [self.expr] 1033 1034 def tokenize(self, cg: CodeGenerator) -> Iterable[str | CodeCC]: 1035 return chain( 1036 self.emit_comment_tokens(inline=True), 1037 [f'({str(self.type)})'], 1038 self.expr.tokenize(cg) 1039 ) 1040