teloce-py 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.
- teloce/__init__.py +17 -0
- teloce/__main__.py +13 -0
- teloce/ast/__init__.py +87 -0
- teloce/ast/components.py +77 -0
- teloce/ast/elements.py +84 -0
- teloce/ast/expressions.py +151 -0
- teloce/ast/nodes.py +271 -0
- teloce/ast/visitors.py +246 -0
- teloce/build/__init__.py +37 -0
- teloce/build/assets.py +128 -0
- teloce/build/builder.py +292 -0
- teloce/build/bundler.py +153 -0
- teloce/build/manifest.py +89 -0
- teloce/build/writer.py +86 -0
- teloce/cli/__init__.py +25 -0
- teloce/cli/benchmark.py +62 -0
- teloce/cli/build.py +81 -0
- teloce/cli/create.py +281 -0
- teloce/cli/debug.py +72 -0
- teloce/cli/dev.py +101 -0
- teloce/cli/doctor.py +99 -0
- teloce/cli/lint.py +148 -0
- teloce/cli/main.py +223 -0
- teloce/cli/server.py +176 -0
- teloce/cli/watch.py +98 -0
- teloce/compiler/__init__.py +31 -0
- teloce/compiler/compiler.py +266 -0
- teloce/compiler/diagnostics.py +129 -0
- teloce/compiler/generator.py +518 -0
- teloce/compiler/lexer.py +515 -0
- teloce/compiler/optimizer.py +115 -0
- teloce/compiler/parser.py +329 -0
- teloce/compiler/source_map.py +79 -0
- teloce/compiler/transformer.py +71 -0
- teloce/components/__init__.py +17 -0
- teloce/components/dependency_graph.py +177 -0
- teloce/components/imports.py +118 -0
- teloce/components/registry.py +108 -0
- teloce/components/resolver.py +107 -0
- teloce/css/__init__.py +20 -0
- teloce/css/generator.py +160 -0
- teloce/css/hashing.py +73 -0
- teloce/css/modules.py +30 -0
- teloce/css/parser.py +395 -0
- teloce/css/scoped.py +179 -0
- teloce/debug/__init__.py +18 -0
- teloce/debug/errors.py +195 -0
- teloce/debug/formatter.py +185 -0
- teloce/debug/suggestions.py +230 -0
- teloce/directives/__init__.py +27 -0
- teloce/directives/base.py +135 -0
- teloce/directives/bind.py +502 -0
- teloce/directives/events.py +126 -0
- teloce/directives/for_.py +95 -0
- teloce/directives/if_.py +105 -0
- teloce/directives/model.py +52 -0
- teloce/directives/registry.py +69 -0
- teloce/directives/show.py +62 -0
- teloce/expressions/__init__.py +41 -0
- teloce/expressions/ast.py +118 -0
- teloce/expressions/generator.py +132 -0
- teloce/expressions/lexer.py +530 -0
- teloce/expressions/parser.py +488 -0
- teloce/filters/__init__.py +22 -0
- teloce/filters/arrays.py +112 -0
- teloce/filters/dates.py +142 -0
- teloce/filters/numbers.py +98 -0
- teloce/filters/objects.py +89 -0
- teloce/filters/registry.py +98 -0
- teloce/filters/strings.py +171 -0
- teloce/javascript/__init__.py +21 -0
- teloce/javascript/dom.py +140 -0
- teloce/javascript/exports.py +49 -0
- teloce/javascript/generator.py +250 -0
- teloce/javascript/helpers.py +218 -0
- teloce/javascript/imports.py +95 -0
- teloce/javascript/module.py +63 -0
- teloce/plugins/__init__.py +21 -0
- teloce/plugins/api.py +120 -0
- teloce/plugins/directives.py +42 -0
- teloce/plugins/filters.py +41 -0
- teloce/plugins/hooks.py +81 -0
- teloce/plugins/registry.py +113 -0
- teloce/project/__init__.py +17 -0
- teloce/project/configuration.py +142 -0
- teloce/project/discovery.py +160 -0
- teloce/project/paths.py +122 -0
- teloce/project/scanner.py +126 -0
- teloce/router/__init__.py +13 -0
- teloce/router/compiler.py +161 -0
- teloce/router/generator.py +166 -0
- teloce/runtime/__init__.py +8 -0
- teloce/runtime/component.js +21 -0
- teloce/runtime/computed.js +2 -0
- teloce/runtime/dom.js +119 -0
- teloce/runtime/effects.js +2 -0
- teloce/runtime/events.js +4 -0
- teloce/runtime/lifecycle.js +3 -0
- teloce/runtime/props.js +7 -0
- teloce/runtime/reactivity.js +16 -0
- teloce/runtime/runtime.js +11 -0
- teloce/runtime/scheduler.js +40 -0
- teloce/runtime/signals.js +131 -0
- teloce/runtime/slots.js +4 -0
- teloce/runtime/standalone.js +221 -0
- teloce/sfc/__init__.py +25 -0
- teloce/sfc/component.py +95 -0
- teloce/sfc/parser.py +289 -0
- teloce/sfc/script.py +1009 -0
- teloce/sfc/sections.py +46 -0
- teloce/sfc/style.py +582 -0
- teloce/sfc/template.py +66 -0
- teloce/template/__init__.py +29 -0
- teloce/template/components.py +122 -0
- teloce/template/conditions.py +75 -0
- teloce/template/directives.py +111 -0
- teloce/template/expressions.py +398 -0
- teloce/template/interpolation.py +74 -0
- teloce/template/lexer.py +421 -0
- teloce/template/loops.py +92 -0
- teloce/template/parser.py +101 -0
- teloce/template/slots.py +66 -0
- teloce/version.py +6 -0
- teloce/watcher/__init__.py +13 -0
- teloce/watcher/watcher.py +174 -0
- teloce_py-0.1.0.dist-info/METADATA +192 -0
- teloce_py-0.1.0.dist-info/RECORD +131 -0
- teloce_py-0.1.0.dist-info/WHEEL +5 -0
- teloce_py-0.1.0.dist-info/entry_points.txt +2 -0
- teloce_py-0.1.0.dist-info/licenses/LICENSE +22 -0
- teloce_py-0.1.0.dist-info/top_level.txt +1 -0
teloce/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Teloce-Py - A Python compiler for Teloce .vel Single File Components
|
|
3
|
+
|
|
4
|
+
Teloce-Py transforms .vel files into self-contained vanilla JavaScript
|
|
5
|
+
without requiring Node.js, npm, or a CDN.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from teloce.version import __version__
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"__version__",
|
|
12
|
+
"compile",
|
|
13
|
+
"compile_file",
|
|
14
|
+
"compile_project",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
from teloce.compiler.compiler import compile, compile_file, compile_project
|
teloce/__main__.py
ADDED
teloce/ast/__init__.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST (Abstract Syntax Tree) package.
|
|
3
|
+
|
|
4
|
+
Defines AST nodes for Teloce templates.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from teloce.ast.nodes import (
|
|
8
|
+
ASTNode,
|
|
9
|
+
NodeType,
|
|
10
|
+
ElementNode,
|
|
11
|
+
TextNode,
|
|
12
|
+
InterpolationNode,
|
|
13
|
+
ForNode,
|
|
14
|
+
IfNode,
|
|
15
|
+
ElseNode,
|
|
16
|
+
EventNode,
|
|
17
|
+
BindingNode,
|
|
18
|
+
ComponentNode,
|
|
19
|
+
SlotNode,
|
|
20
|
+
FragmentNode,
|
|
21
|
+
AttributeNode,
|
|
22
|
+
CommentNode,
|
|
23
|
+
DoctypeNode,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from teloce.ast.expressions import (
|
|
27
|
+
ExpressionNode,
|
|
28
|
+
IdentifierNode,
|
|
29
|
+
LiteralNode,
|
|
30
|
+
BinaryNode,
|
|
31
|
+
UnaryNode,
|
|
32
|
+
CallNode,
|
|
33
|
+
MemberNode,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
from teloce.ast.elements import (
|
|
37
|
+
ElementFactory,
|
|
38
|
+
VoidElement,
|
|
39
|
+
ContainerElement,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from teloce.ast.components import (
|
|
43
|
+
ComponentReference,
|
|
44
|
+
ComponentSlot,
|
|
45
|
+
ComponentProp,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
from teloce.ast.visitors import (
|
|
49
|
+
ASTVisitor,
|
|
50
|
+
ASTTransformer,
|
|
51
|
+
ASTPrinter,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"ASTNode",
|
|
56
|
+
"NodeType",
|
|
57
|
+
"ElementNode",
|
|
58
|
+
"TextNode",
|
|
59
|
+
"InterpolationNode",
|
|
60
|
+
"ForNode",
|
|
61
|
+
"IfNode",
|
|
62
|
+
"ElseNode",
|
|
63
|
+
"EventNode",
|
|
64
|
+
"BindingNode",
|
|
65
|
+
"ComponentNode",
|
|
66
|
+
"SlotNode",
|
|
67
|
+
"FragmentNode",
|
|
68
|
+
"AttributeNode",
|
|
69
|
+
"CommentNode",
|
|
70
|
+
"DoctypeNode",
|
|
71
|
+
"ExpressionNode",
|
|
72
|
+
"IdentifierNode",
|
|
73
|
+
"LiteralNode",
|
|
74
|
+
"BinaryNode",
|
|
75
|
+
"UnaryNode",
|
|
76
|
+
"CallNode",
|
|
77
|
+
"MemberNode",
|
|
78
|
+
"ElementFactory",
|
|
79
|
+
"VoidElement",
|
|
80
|
+
"ContainerElement",
|
|
81
|
+
"ComponentReference",
|
|
82
|
+
"ComponentSlot",
|
|
83
|
+
"ComponentProp",
|
|
84
|
+
"ASTVisitor",
|
|
85
|
+
"ASTTransformer",
|
|
86
|
+
"ASTPrinter",
|
|
87
|
+
]
|
teloce/ast/components.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Component AST nodes.
|
|
3
|
+
|
|
4
|
+
Defines nodes for custom components and component-related structures.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import List, Optional, Dict, Any
|
|
9
|
+
|
|
10
|
+
from teloce.ast.nodes import ASTNode, NodeType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(init=False)
|
|
14
|
+
class ComponentReference(ASTNode):
|
|
15
|
+
"""
|
|
16
|
+
Reference to a custom component.
|
|
17
|
+
"""
|
|
18
|
+
name: str
|
|
19
|
+
props: Dict[str, str] = field(default_factory=dict)
|
|
20
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
21
|
+
slots: Dict[str, List[ASTNode]] = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
def __init__(self, name: str, props: Dict[str, str] = None,
|
|
24
|
+
children: List[ASTNode] = None,
|
|
25
|
+
slots: Dict[str, List[ASTNode]] = None,
|
|
26
|
+
line: int = 0, column: int = 0):
|
|
27
|
+
super().__init__(NodeType.COMPONENT, line, column)
|
|
28
|
+
self.name = name
|
|
29
|
+
self.props = props or {}
|
|
30
|
+
self.children = children or []
|
|
31
|
+
self.slots = slots or {}
|
|
32
|
+
|
|
33
|
+
def accept(self, visitor):
|
|
34
|
+
return visitor.visit_component(self)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(init=False)
|
|
38
|
+
class ComponentSlot(ASTNode):
|
|
39
|
+
"""
|
|
40
|
+
Slot definition inside a component.
|
|
41
|
+
"""
|
|
42
|
+
name: str = "default"
|
|
43
|
+
fallback: List[ASTNode] = field(default_factory=list)
|
|
44
|
+
|
|
45
|
+
def __init__(self, name: str = "default", fallback: List[ASTNode] = None,
|
|
46
|
+
line: int = 0, column: int = 0):
|
|
47
|
+
super().__init__(NodeType.SLOT, line, column)
|
|
48
|
+
self.name = name
|
|
49
|
+
self.fallback = fallback or []
|
|
50
|
+
|
|
51
|
+
def accept(self, visitor):
|
|
52
|
+
return visitor.visit_slot(self)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(init=False)
|
|
56
|
+
class ComponentProp(ASTNode):
|
|
57
|
+
"""
|
|
58
|
+
Prop definition for a component.
|
|
59
|
+
"""
|
|
60
|
+
name: str
|
|
61
|
+
type: Optional[str] = None
|
|
62
|
+
required: bool = False
|
|
63
|
+
default: Any = None
|
|
64
|
+
validator: Optional[str] = None
|
|
65
|
+
|
|
66
|
+
def __init__(self, name: str, type: str = None, required: bool = False,
|
|
67
|
+
default: Any = None, validator: str = None,
|
|
68
|
+
line: int = 0, column: int = 0):
|
|
69
|
+
super().__init__(NodeType.ATTRIBUTE, line, column)
|
|
70
|
+
self.name = name
|
|
71
|
+
self.type = type
|
|
72
|
+
self.required = required
|
|
73
|
+
self.default = default
|
|
74
|
+
self.validator = validator
|
|
75
|
+
|
|
76
|
+
def accept(self, visitor):
|
|
77
|
+
return visitor.visit_attribute(self)
|
teloce/ast/elements.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Element factory and utilities.
|
|
3
|
+
|
|
4
|
+
Provides utilities for creating and working with element nodes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional, Set
|
|
8
|
+
|
|
9
|
+
from teloce.ast.nodes import ElementNode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# HTML void elements (self-closing)
|
|
13
|
+
VOID_ELEMENTS = {
|
|
14
|
+
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
15
|
+
'link', 'meta', 'param', 'source', 'track', 'wbr',
|
|
16
|
+
'command', 'keygen', 'menuitem'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Container elements (can have children)
|
|
20
|
+
CONTAINER_ELEMENTS = {
|
|
21
|
+
'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
22
|
+
'a', 'button', 'form', 'input', 'label', 'select', 'textarea',
|
|
23
|
+
'ul', 'ol', 'li', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
|
|
24
|
+
'section', 'article', 'header', 'footer', 'nav', 'main', 'aside',
|
|
25
|
+
'figure', 'figcaption', 'blockquote', 'pre', 'code', 'em', 'strong',
|
|
26
|
+
'i', 'b', 'u', 's', 'small', 'mark', 'del', 'ins', 'sub', 'sup',
|
|
27
|
+
'iframe', 'video', 'audio', 'canvas', 'svg', 'template'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ElementFactory:
|
|
32
|
+
"""
|
|
33
|
+
Factory for creating element nodes.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def is_void(tag: str) -> bool:
|
|
38
|
+
"""Check if a tag is a void element."""
|
|
39
|
+
return tag.lower() in VOID_ELEMENTS
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def is_container(tag: str) -> bool:
|
|
43
|
+
"""Check if a tag is a container element."""
|
|
44
|
+
return tag.lower() in CONTAINER_ELEMENTS or tag.lower() not in VOID_ELEMENTS
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def create(tag: str, attributes: dict = None,
|
|
48
|
+
children: List = None, line: int = 0, column: int = 0) -> ElementNode:
|
|
49
|
+
"""Create an element node."""
|
|
50
|
+
return ElementNode(tag, attributes, children=children or [], line=line, column=column)
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def create_void(tag: str, attributes: dict = None,
|
|
54
|
+
line: int = 0, column: int = 0) -> ElementNode:
|
|
55
|
+
"""Create a void (self-closing) element."""
|
|
56
|
+
return ElementNode(tag, attributes, children=[], line=line, column=column)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class VoidElement:
|
|
60
|
+
"""
|
|
61
|
+
Represents a void (self-closing) HTML element.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def is_void(tag: str) -> bool:
|
|
66
|
+
return tag.lower() in VOID_ELEMENTS
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def get_all() -> Set[str]:
|
|
70
|
+
return VOID_ELEMENTS.copy()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ContainerElement:
|
|
74
|
+
"""
|
|
75
|
+
Represents a container HTML element that can have children.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def is_container(tag: str) -> bool:
|
|
80
|
+
return tag.lower() in CONTAINER_ELEMENTS or tag.lower() not in VOID_ELEMENTS
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def get_all() -> Set[str]:
|
|
84
|
+
return CONTAINER_ELEMENTS.copy()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Expression AST nodes.
|
|
3
|
+
|
|
4
|
+
Defines AST nodes for expressions used in templates.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import List, Optional, Any
|
|
9
|
+
from enum import Enum, auto
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExpressionType(Enum):
|
|
13
|
+
"""Types of expression nodes."""
|
|
14
|
+
IDENTIFIER = auto()
|
|
15
|
+
LITERAL = auto()
|
|
16
|
+
BINARY = auto()
|
|
17
|
+
UNARY = auto()
|
|
18
|
+
CALL = auto()
|
|
19
|
+
MEMBER = auto()
|
|
20
|
+
ARRAY = auto()
|
|
21
|
+
OBJECT = auto()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ExpressionNode:
|
|
26
|
+
"""Base class for expression nodes."""
|
|
27
|
+
type: ExpressionType
|
|
28
|
+
line: int = 0
|
|
29
|
+
column: int = 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(init=False)
|
|
33
|
+
class IdentifierNode(ExpressionNode):
|
|
34
|
+
"""Identifier expression node."""
|
|
35
|
+
name: str
|
|
36
|
+
|
|
37
|
+
def __init__(self, name: str, line: int = 0, column: int = 0):
|
|
38
|
+
super().__init__(ExpressionType.IDENTIFIER, line, column)
|
|
39
|
+
self.name = name
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"Identifier({self.name})"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(init=False)
|
|
46
|
+
class LiteralNode(ExpressionNode):
|
|
47
|
+
"""Literal value expression node."""
|
|
48
|
+
value: Any
|
|
49
|
+
|
|
50
|
+
def __init__(self, value: Any, line: int = 0, column: int = 0):
|
|
51
|
+
super().__init__(ExpressionType.LITERAL, line, column)
|
|
52
|
+
self.value = value
|
|
53
|
+
|
|
54
|
+
def __repr__(self) -> str:
|
|
55
|
+
return f"Literal({repr(self.value)})"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(init=False)
|
|
59
|
+
class BinaryNode(ExpressionNode):
|
|
60
|
+
"""Binary operation expression node."""
|
|
61
|
+
left: ExpressionNode
|
|
62
|
+
operator: str
|
|
63
|
+
right: ExpressionNode
|
|
64
|
+
|
|
65
|
+
def __init__(self, left: ExpressionNode, operator: str,
|
|
66
|
+
right: ExpressionNode, line: int = 0, column: int = 0):
|
|
67
|
+
super().__init__(ExpressionType.BINARY, line, column)
|
|
68
|
+
self.left = left
|
|
69
|
+
self.operator = operator
|
|
70
|
+
self.right = right
|
|
71
|
+
|
|
72
|
+
def __repr__(self) -> str:
|
|
73
|
+
return f"Binary({self.left} {self.operator} {self.right})"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(init=False)
|
|
77
|
+
class UnaryNode(ExpressionNode):
|
|
78
|
+
"""Unary operation expression node."""
|
|
79
|
+
operator: str
|
|
80
|
+
operand: ExpressionNode
|
|
81
|
+
|
|
82
|
+
def __init__(self, operator: str, operand: ExpressionNode,
|
|
83
|
+
line: int = 0, column: int = 0):
|
|
84
|
+
super().__init__(ExpressionType.UNARY, line, column)
|
|
85
|
+
self.operator = operator
|
|
86
|
+
self.operand = operand
|
|
87
|
+
|
|
88
|
+
def __repr__(self) -> str:
|
|
89
|
+
return f"Unary({self.operator}{self.operand})"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(init=False)
|
|
93
|
+
class CallNode(ExpressionNode):
|
|
94
|
+
"""Function call expression node."""
|
|
95
|
+
callee: ExpressionNode
|
|
96
|
+
arguments: List[ExpressionNode]
|
|
97
|
+
|
|
98
|
+
def __init__(self, callee: ExpressionNode, arguments: List[ExpressionNode] = None,
|
|
99
|
+
line: int = 0, column: int = 0):
|
|
100
|
+
super().__init__(ExpressionType.CALL, line, column)
|
|
101
|
+
self.callee = callee
|
|
102
|
+
self.arguments = arguments or []
|
|
103
|
+
|
|
104
|
+
def __repr__(self) -> str:
|
|
105
|
+
return f"Call({self.callee}, {self.arguments})"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(init=False)
|
|
109
|
+
class MemberNode(ExpressionNode):
|
|
110
|
+
"""Member access expression node."""
|
|
111
|
+
object: ExpressionNode
|
|
112
|
+
property: str
|
|
113
|
+
computed: bool = False
|
|
114
|
+
|
|
115
|
+
def __init__(self, object: ExpressionNode, property: str,
|
|
116
|
+
computed: bool = False, line: int = 0, column: int = 0):
|
|
117
|
+
super().__init__(ExpressionType.MEMBER, line, column)
|
|
118
|
+
self.object = object
|
|
119
|
+
self.property = property
|
|
120
|
+
self.computed = computed
|
|
121
|
+
|
|
122
|
+
def __repr__(self) -> str:
|
|
123
|
+
return f"Member({self.object}.{self.property})"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(init=False)
|
|
127
|
+
class ArrayNode(ExpressionNode):
|
|
128
|
+
"""Array literal expression node."""
|
|
129
|
+
elements: List[ExpressionNode]
|
|
130
|
+
|
|
131
|
+
def __init__(self, elements: List[ExpressionNode] = None,
|
|
132
|
+
line: int = 0, column: int = 0):
|
|
133
|
+
super().__init__(ExpressionType.ARRAY, line, column)
|
|
134
|
+
self.elements = elements or []
|
|
135
|
+
|
|
136
|
+
def __repr__(self) -> str:
|
|
137
|
+
return f"Array({self.elements})"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass(init=False)
|
|
141
|
+
class ObjectNode(ExpressionNode):
|
|
142
|
+
"""Object literal expression node."""
|
|
143
|
+
properties: List[tuple]
|
|
144
|
+
|
|
145
|
+
def __init__(self, properties: List[tuple] = None,
|
|
146
|
+
line: int = 0, column: int = 0):
|
|
147
|
+
super().__init__(ExpressionType.OBJECT, line, column)
|
|
148
|
+
self.properties = properties or []
|
|
149
|
+
|
|
150
|
+
def __repr__(self) -> str:
|
|
151
|
+
return f"Object({self.properties})"
|
teloce/ast/nodes.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST node definitions for Teloce templates.
|
|
3
|
+
|
|
4
|
+
Defines all node types used in the Abstract Syntax Tree.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import Enum, auto
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import List, Optional, Any, Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NodeType(Enum):
|
|
13
|
+
"""Types of AST nodes."""
|
|
14
|
+
ELEMENT = auto()
|
|
15
|
+
TEXT = auto()
|
|
16
|
+
INTERPOLATION = auto()
|
|
17
|
+
FOR = auto()
|
|
18
|
+
IF = auto()
|
|
19
|
+
ELSE = auto()
|
|
20
|
+
EVENT = auto()
|
|
21
|
+
BINDING = auto()
|
|
22
|
+
COMPONENT = auto()
|
|
23
|
+
SLOT = auto()
|
|
24
|
+
FRAGMENT = auto()
|
|
25
|
+
ATTRIBUTE = auto()
|
|
26
|
+
COMMENT = auto()
|
|
27
|
+
DOCTYPE = auto()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ASTNode:
|
|
32
|
+
"""Base class for all AST nodes."""
|
|
33
|
+
type: NodeType
|
|
34
|
+
line: int = 0
|
|
35
|
+
column: int = 0
|
|
36
|
+
|
|
37
|
+
def accept(self, visitor):
|
|
38
|
+
"""Accept a visitor."""
|
|
39
|
+
method_name = f"visit_{self.type.name.lower()}"
|
|
40
|
+
method = getattr(visitor, method_name, None)
|
|
41
|
+
if method:
|
|
42
|
+
return method(self)
|
|
43
|
+
return visitor.visit_default(self)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(init=False)
|
|
47
|
+
class ElementNode(ASTNode):
|
|
48
|
+
"""HTML element node."""
|
|
49
|
+
tag: str
|
|
50
|
+
attributes: Dict[str, str] = field(default_factory=dict)
|
|
51
|
+
events: List['EventNode'] = field(default_factory=list)
|
|
52
|
+
bindings: List['BindingNode'] = field(default_factory=list)
|
|
53
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
def __init__(self, tag: str, attributes: Dict[str, str] = None,
|
|
56
|
+
events: List['EventNode'] = None,
|
|
57
|
+
bindings: List['BindingNode'] = None,
|
|
58
|
+
children: List[ASTNode] = None,
|
|
59
|
+
line: int = 0, column: int = 0):
|
|
60
|
+
super().__init__(NodeType.ELEMENT, line, column)
|
|
61
|
+
self.tag = tag
|
|
62
|
+
self.attributes = attributes or {}
|
|
63
|
+
self.events = events or []
|
|
64
|
+
self.bindings = bindings or []
|
|
65
|
+
self.children = children or []
|
|
66
|
+
|
|
67
|
+
def accept(self, visitor):
|
|
68
|
+
return visitor.visit_element(self)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(init=False)
|
|
72
|
+
class TextNode(ASTNode):
|
|
73
|
+
"""Plain text node."""
|
|
74
|
+
value: str
|
|
75
|
+
|
|
76
|
+
def __init__(self, value: str, line: int = 0, column: int = 0):
|
|
77
|
+
super().__init__(NodeType.TEXT, line, column)
|
|
78
|
+
self.value = value
|
|
79
|
+
|
|
80
|
+
def accept(self, visitor):
|
|
81
|
+
return visitor.visit_text(self)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(init=False)
|
|
85
|
+
class InterpolationNode(ASTNode):
|
|
86
|
+
"""{{ expression }} interpolation node."""
|
|
87
|
+
expression: str
|
|
88
|
+
|
|
89
|
+
def __init__(self, expression: str, line: int = 0, column: int = 0):
|
|
90
|
+
super().__init__(NodeType.INTERPOLATION, line, column)
|
|
91
|
+
self.expression = expression
|
|
92
|
+
|
|
93
|
+
def accept(self, visitor):
|
|
94
|
+
return visitor.visit_interpolation(self)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(init=False)
|
|
98
|
+
class ForNode(ASTNode):
|
|
99
|
+
"""For loop directive node."""
|
|
100
|
+
item: str
|
|
101
|
+
collection: str
|
|
102
|
+
key: str = ""
|
|
103
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
104
|
+
|
|
105
|
+
def __init__(self, item: str, collection: str, key: str = "",
|
|
106
|
+
children: List[ASTNode] = None,
|
|
107
|
+
line: int = 0, column: int = 0):
|
|
108
|
+
super().__init__(NodeType.FOR, line, column)
|
|
109
|
+
self.item = item
|
|
110
|
+
self.collection = collection
|
|
111
|
+
self.key = key
|
|
112
|
+
self.children = children or []
|
|
113
|
+
|
|
114
|
+
def accept(self, visitor):
|
|
115
|
+
return visitor.visit_for(self)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(init=False)
|
|
119
|
+
class IfNode(ASTNode):
|
|
120
|
+
"""If directive node."""
|
|
121
|
+
condition: str
|
|
122
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
123
|
+
else_children: List[ASTNode] = field(default_factory=list)
|
|
124
|
+
|
|
125
|
+
def __init__(self, condition: str, children: List[ASTNode] = None,
|
|
126
|
+
else_children: List[ASTNode] = None,
|
|
127
|
+
line: int = 0, column: int = 0):
|
|
128
|
+
super().__init__(NodeType.IF, line, column)
|
|
129
|
+
self.condition = condition
|
|
130
|
+
self.children = children or []
|
|
131
|
+
self.else_children = else_children or []
|
|
132
|
+
|
|
133
|
+
def accept(self, visitor):
|
|
134
|
+
return visitor.visit_if(self)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass(init=False)
|
|
138
|
+
class ElseNode(ASTNode):
|
|
139
|
+
"""Else directive node."""
|
|
140
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
141
|
+
|
|
142
|
+
def __init__(self, children: List[ASTNode] = None,
|
|
143
|
+
line: int = 0, column: int = 0):
|
|
144
|
+
super().__init__(NodeType.ELSE, line, column)
|
|
145
|
+
self.children = children or []
|
|
146
|
+
|
|
147
|
+
def accept(self, visitor):
|
|
148
|
+
return visitor.visit_else(self)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(init=False)
|
|
152
|
+
class EventNode(ASTNode):
|
|
153
|
+
"""@click event binding node."""
|
|
154
|
+
name: str
|
|
155
|
+
handler: str
|
|
156
|
+
|
|
157
|
+
def __init__(self, name: str, handler: str, line: int = 0, column: int = 0):
|
|
158
|
+
super().__init__(NodeType.EVENT, line, column)
|
|
159
|
+
self.name = name
|
|
160
|
+
self.handler = handler
|
|
161
|
+
|
|
162
|
+
def accept(self, visitor):
|
|
163
|
+
return visitor.visit_event(self)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass(init=False)
|
|
167
|
+
class BindingNode(ASTNode):
|
|
168
|
+
""":model binding node."""
|
|
169
|
+
name: str
|
|
170
|
+
value: str
|
|
171
|
+
|
|
172
|
+
def __init__(self, name: str, value: str, line: int = 0, column: int = 0):
|
|
173
|
+
super().__init__(NodeType.BINDING, line, column)
|
|
174
|
+
self.name = name
|
|
175
|
+
self.value = value
|
|
176
|
+
|
|
177
|
+
def accept(self, visitor):
|
|
178
|
+
return visitor.visit_binding(self)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass(init=False)
|
|
182
|
+
class ComponentNode(ASTNode):
|
|
183
|
+
"""Custom component node."""
|
|
184
|
+
name: str
|
|
185
|
+
props: Dict[str, str] = field(default_factory=dict)
|
|
186
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
187
|
+
slots: Dict[str, List[ASTNode]] = field(default_factory=dict)
|
|
188
|
+
|
|
189
|
+
def __init__(self, name: str, props: Dict[str, str] = None,
|
|
190
|
+
children: List[ASTNode] = None,
|
|
191
|
+
slots: Dict[str, List[ASTNode]] = None,
|
|
192
|
+
line: int = 0, column: int = 0):
|
|
193
|
+
super().__init__(NodeType.COMPONENT, line, column)
|
|
194
|
+
self.name = name
|
|
195
|
+
self.props = props or {}
|
|
196
|
+
self.children = children or []
|
|
197
|
+
self.slots = slots or {}
|
|
198
|
+
|
|
199
|
+
def accept(self, visitor):
|
|
200
|
+
return visitor.visit_component(self)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass(init=False)
|
|
204
|
+
class SlotNode(ASTNode):
|
|
205
|
+
"""Slot node."""
|
|
206
|
+
name: str = "default"
|
|
207
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
208
|
+
|
|
209
|
+
def __init__(self, name: str = "default", children: List[ASTNode] = None,
|
|
210
|
+
line: int = 0, column: int = 0):
|
|
211
|
+
super().__init__(NodeType.SLOT, line, column)
|
|
212
|
+
self.name = name
|
|
213
|
+
self.children = children or []
|
|
214
|
+
|
|
215
|
+
def accept(self, visitor):
|
|
216
|
+
return visitor.visit_slot(self)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass(init=False)
|
|
220
|
+
class FragmentNode(ASTNode):
|
|
221
|
+
"""Fragment node (multiple root elements)."""
|
|
222
|
+
children: List[ASTNode] = field(default_factory=list)
|
|
223
|
+
|
|
224
|
+
def __init__(self, children: List[ASTNode] = None,
|
|
225
|
+
line: int = 0, column: int = 0):
|
|
226
|
+
super().__init__(NodeType.FRAGMENT, line, column)
|
|
227
|
+
self.children = children or []
|
|
228
|
+
|
|
229
|
+
def accept(self, visitor):
|
|
230
|
+
return visitor.visit_fragment(self)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass(init=False)
|
|
234
|
+
class AttributeNode(ASTNode):
|
|
235
|
+
"""HTML attribute node."""
|
|
236
|
+
name: str
|
|
237
|
+
value: str
|
|
238
|
+
|
|
239
|
+
def __init__(self, name: str, value: str, line: int = 0, column: int = 0):
|
|
240
|
+
super().__init__(NodeType.ATTRIBUTE, line, column)
|
|
241
|
+
self.name = name
|
|
242
|
+
self.value = value
|
|
243
|
+
|
|
244
|
+
def accept(self, visitor):
|
|
245
|
+
return visitor.visit_attribute(self)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass(init=False)
|
|
249
|
+
class CommentNode(ASTNode):
|
|
250
|
+
"""HTML comment node."""
|
|
251
|
+
value: str
|
|
252
|
+
|
|
253
|
+
def __init__(self, value: str, line: int = 0, column: int = 0):
|
|
254
|
+
super().__init__(NodeType.COMMENT, line, column)
|
|
255
|
+
self.value = value
|
|
256
|
+
|
|
257
|
+
def accept(self, visitor):
|
|
258
|
+
return visitor.visit_comment(self)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@dataclass(init=False)
|
|
262
|
+
class DoctypeNode(ASTNode):
|
|
263
|
+
"""DOCTYPE node."""
|
|
264
|
+
value: str
|
|
265
|
+
|
|
266
|
+
def __init__(self, value: str, line: int = 0, column: int = 0):
|
|
267
|
+
super().__init__(NodeType.DOCTYPE, line, column)
|
|
268
|
+
self.value = value
|
|
269
|
+
|
|
270
|
+
def accept(self, visitor):
|
|
271
|
+
return visitor.visit_doctype(self)
|