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