shell-lite 0.3.5__tar.gz → 0.4.0__tar.gz

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.
Files changed (29) hide show
  1. shell_lite-0.4.0/PKG-INFO +75 -0
  2. shell_lite-0.4.0/README.md +62 -0
  3. {shell_lite-0.3.5 → shell_lite-0.4.0}/pyproject.toml +2 -2
  4. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/ast_nodes.py +24 -1
  5. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/compiler.py +14 -2
  6. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/interpreter.py +210 -16
  7. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/js_compiler.py +17 -3
  8. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/lexer.py +3 -0
  9. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/main.py +258 -81
  10. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/parser.py +227 -23
  11. shell_lite-0.4.0/shell_lite.egg-info/PKG-INFO +75 -0
  12. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite.egg-info/SOURCES.txt +1 -0
  13. shell_lite-0.4.0/shell_lite.egg-info/requires.txt +1 -0
  14. shell_lite-0.3.5/PKG-INFO +0 -40
  15. shell_lite-0.3.5/README.md +0 -28
  16. shell_lite-0.3.5/shell_lite.egg-info/PKG-INFO +0 -40
  17. {shell_lite-0.3.5 → shell_lite-0.4.0}/LICENSE +0 -0
  18. {shell_lite-0.3.5 → shell_lite-0.4.0}/setup.cfg +0 -0
  19. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/__init__.py +0 -0
  20. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/cli.py +0 -0
  21. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/formatter.py +0 -0
  22. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite/runtime.py +0 -0
  23. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite.egg-info/dependency_links.txt +0 -0
  24. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite.egg-info/entry_points.txt +0 -0
  25. {shell_lite-0.3.5 → shell_lite-0.4.0}/shell_lite.egg-info/top_level.txt +0 -0
  26. {shell_lite-0.3.5 → shell_lite-0.4.0}/tests/test_interpreter.py +0 -0
  27. {shell_lite-0.3.5 → shell_lite-0.4.0}/tests/test_lexer.py +0 -0
  28. {shell_lite-0.3.5 → shell_lite-0.4.0}/tests/test_parser.py +0 -0
  29. {shell_lite-0.3.5 → shell_lite-0.4.0}/tests/test_stdlib.py +0 -0
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.1
2
+ Name: shell-lite
3
+ Version: 0.4.0
4
+ Summary: A lightweight, English-like scripting language.
5
+ Author-email: Shrey Naithani <contact@shelllite.tech>
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: prompt_toolkit>=3.0.0
13
+
14
+ # ShellLite: The English-Like Programming Language
15
+ # By Shrey Naithani
16
+ ShellLite is a modern, interpreted programming language designed to prioritize human readability. It replaces complex syntax with natural English commands, making software development accessible and maintainable.
17
+
18
+ ## Version 0.04.0 (Polaris Update)
19
+
20
+ This release transforms ShellLite from a scripting tool into a comprehensive platform with three major pillars:
21
+ 1. **The Bridge**: Import and use any Python library (pandas, requests, etc.) natively.
22
+ 2. **The Canvas**: Build native desktop GUI applications with declarative syntax.
23
+ 3. **The Universe**: Integrated package management for project dependencies.
24
+
25
+ ## Installation
26
+
27
+ ### Via PyPI (Recommended)
28
+ You can install ShellLite directly from PyPI:
29
+ ```bash
30
+ pip install shell-lite
31
+ ```
32
+
33
+ ### Windows Installer
34
+ Download the latest `shl.exe` from the Releases page.
35
+
36
+ ## Quick Start
37
+
38
+ ### 1. Hello World
39
+ Save this as `hello.shl`:
40
+ ```shell-lite
41
+ say "Hello, World"
42
+ ```
43
+ Run it:
44
+ ```bash
45
+ shl hello.shl
46
+ ```
47
+ ## Project Management
48
+
49
+ Initialize a new project:
50
+ ```bash
51
+ shl init
52
+ ```
53
+
54
+ Install dependencies defined in `shell-lite.toml`:
55
+ ```bash
56
+ shl install
57
+ ```
58
+
59
+ ## Ecosystem & Tools
60
+
61
+ | Tool | Description | Link |
62
+ | :--- | :--- | :--- |
63
+ | **ShellDesk** | The official IDE for ShellLite. | [GitHub](https://github.com/Shrey-N/ShellDesk) |
64
+ | **VS Code Extension** | Syntax highlighting and snippets. | [Marketplace](https://marketplace.visualstudio.com/items?itemName=ShellLite.shelllite-hello) / [OpenVSX](https://open-vsx.org/extension/shelllite/shelllite-hello) |
65
+ | **Website** | Official website source code. | [GitHub](https://github.com/Shrey-N/ShellLite-Website) |
66
+
67
+ ## Documentation
68
+
69
+ Full documentation is available directly in this repository:
70
+ - [**Language Guide**](https://github.com/Shrey-N/ShellLite)
71
+ - [**Examples**](https://github.com/Shrey-N/ShellLite/tree/main/examples)
72
+
73
+ License: MIT
74
+ Made by Shrey Naithani
75
+
@@ -0,0 +1,62 @@
1
+ # ShellLite: The English-Like Programming Language
2
+ # By Shrey Naithani
3
+ ShellLite is a modern, interpreted programming language designed to prioritize human readability. It replaces complex syntax with natural English commands, making software development accessible and maintainable.
4
+
5
+ ## Version 0.04.0 (Polaris Update)
6
+
7
+ This release transforms ShellLite from a scripting tool into a comprehensive platform with three major pillars:
8
+ 1. **The Bridge**: Import and use any Python library (pandas, requests, etc.) natively.
9
+ 2. **The Canvas**: Build native desktop GUI applications with declarative syntax.
10
+ 3. **The Universe**: Integrated package management for project dependencies.
11
+
12
+ ## Installation
13
+
14
+ ### Via PyPI (Recommended)
15
+ You can install ShellLite directly from PyPI:
16
+ ```bash
17
+ pip install shell-lite
18
+ ```
19
+
20
+ ### Windows Installer
21
+ Download the latest `shl.exe` from the Releases page.
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Hello World
26
+ Save this as `hello.shl`:
27
+ ```shell-lite
28
+ say "Hello, World"
29
+ ```
30
+ Run it:
31
+ ```bash
32
+ shl hello.shl
33
+ ```
34
+ ## Project Management
35
+
36
+ Initialize a new project:
37
+ ```bash
38
+ shl init
39
+ ```
40
+
41
+ Install dependencies defined in `shell-lite.toml`:
42
+ ```bash
43
+ shl install
44
+ ```
45
+
46
+ ## Ecosystem & Tools
47
+
48
+ | Tool | Description | Link |
49
+ | :--- | :--- | :--- |
50
+ | **ShellDesk** | The official IDE for ShellLite. | [GitHub](https://github.com/Shrey-N/ShellDesk) |
51
+ | **VS Code Extension** | Syntax highlighting and snippets. | [Marketplace](https://marketplace.visualstudio.com/items?itemName=ShellLite.shelllite-hello) / [OpenVSX](https://open-vsx.org/extension/shelllite/shelllite-hello) |
52
+ | **Website** | Official website source code. | [GitHub](https://github.com/Shrey-N/ShellLite-Website) |
53
+
54
+ ## Documentation
55
+
56
+ Full documentation is available directly in this repository:
57
+ - [**Language Guide**](https://github.com/Shrey-N/ShellLite)
58
+ - [**Examples**](https://github.com/Shrey-N/ShellLite/tree/main/examples)
59
+
60
+ License: MIT
61
+ Made by Shrey Naithani
62
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shell-lite"
7
- version = "0.03.5"
7
+ version = "0.04.0"
8
8
  description = "A lightweight, English-like scripting language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -14,7 +14,7 @@ classifiers = [
14
14
  "Programming Language :: Python :: 3",
15
15
  "Operating System :: OS Independent",
16
16
  ]
17
- dependencies = []
17
+ dependencies = ["prompt_toolkit>=3.0.0"]
18
18
 
19
19
  [project.scripts]
20
20
  shl = "shell_lite.main:main"
@@ -83,7 +83,7 @@ class Return(Node):
83
83
  @dataclass
84
84
  class ClassDef(Node):
85
85
  name: str
86
- properties: List[str]
86
+ properties: List[tuple[str, Optional[Node]]]
87
87
  methods: List[FunctionDef]
88
88
  parent: Optional[str] = None
89
89
  @dataclass
@@ -272,3 +272,26 @@ class FileRead(Node):
272
272
  class DatabaseOp(Node):
273
273
  op: str
274
274
  args: List[Node]
275
+ @dataclass
276
+ class PythonImport(Node):
277
+ module_name: str
278
+ alias: Optional[str]
279
+
280
+ @dataclass
281
+ class App(Node):
282
+ title: str
283
+ width: int
284
+ height: int
285
+ body: List[Node]
286
+
287
+ @dataclass
288
+ class Widget(Node):
289
+ widget_type: str # 'button', 'input', 'heading', 'text'
290
+ label: str
291
+ var_name: Optional[str] = None
292
+ event_handler: Optional[List[Node]] = None # For buttons with 'do:'
293
+
294
+ @dataclass
295
+ class Layout(Node):
296
+ layout_type: str # 'column', 'row'
297
+ body: List[Node]
@@ -360,8 +360,20 @@ class Compiler:
360
360
  parent = node.parent if node.parent else "Instance"
361
361
  code = f"class {node.name}({parent}):\n"
362
362
  self.indentation += 1
363
- args = ["self"] + node.properties
364
- assigns = [f"self.{p} = {p}" for p in node.properties]
363
+ args = ["self"]
364
+ assigns = []
365
+ for prop in node.properties:
366
+ if isinstance(prop, tuple):
367
+ name, default = prop
368
+ if default:
369
+ args.append(f"{name}={self.visit(default)}")
370
+ else:
371
+ args.append(name)
372
+ assigns.append(f"self.{name} = {name}")
373
+ else:
374
+ args.append(prop)
375
+ assigns.append(f"self.{prop} = {prop}")
376
+
365
377
  if not assigns:
366
378
  assigns = ["pass"]
367
379
  code += f"{self.indent()}def __init__({', '.join(args)}):\n"
@@ -2,6 +2,7 @@ from typing import Any, Dict, List, Callable
2
2
  from .ast_nodes import *
3
3
  from .lexer import Token, Lexer
4
4
  from .parser import Parser
5
+ import importlib
5
6
  import operator
6
7
  import re
7
8
  import os
@@ -618,12 +619,31 @@ class Interpreter:
618
619
  raise NameError(f"Class '{node.class_name}' not defined.")
619
620
  class_def = self.classes[node.class_name]
620
621
  all_properties = self._get_class_properties(class_def)
621
- if len(node.args) != len(all_properties):
622
- raise TypeError(f"Structure '{node.class_name}' expects {len(all_properties)} args for properties {all_properties}, got {len(node.args)}")
622
+
623
+ # Check args length
624
+ # First count how many properties are required (no default)
625
+ required_count = 0
626
+ for name, default_val in all_properties:
627
+ if default_val is None:
628
+ required_count += 1
629
+
630
+ if len(node.args) < required_count:
631
+ raise TypeError(f"Structure '{node.class_name}' expects at least {required_count} args, got {len(node.args)}")
632
+
623
633
  instance = Instance(class_def)
624
- for prop, arg_expr in zip(all_properties, node.args):
625
- val = self.visit(arg_expr)
626
- instance.data[prop] = val
634
+
635
+ for i, (prop_name, default_val) in enumerate(all_properties):
636
+ val = None
637
+ if i < len(node.args):
638
+ val = self.visit(node.args[i])
639
+ elif default_val is not None:
640
+ val = self.visit(default_val)
641
+ else:
642
+ # Should be caught by required_count check, but safety fallback
643
+ raise TypeError(f"Missing argument for property '{prop_name}' in '{node.class_name}'")
644
+
645
+ instance.data[prop_name] = val
646
+
627
647
  self.current_env.set(node.var_name, instance)
628
648
  return instance
629
649
  def visit_MethodCall(self, node: MethodCall):
@@ -675,7 +695,7 @@ class Interpreter:
675
695
  new_env.set(k, v)
676
696
  if len(node.args) > len(method_node.args):
677
697
  raise TypeError(f"Method '{node.method_name}' expects max {len(method_node.args)} arguments.")
678
- for i, (arg_name, default_node) in enumerate(method_node.args):
698
+ for i, (arg_name, default_node, type_hint) in enumerate(method_node.args):
679
699
  if i < len(node.args):
680
700
  val = self.visit(node.args[i])
681
701
  elif default_node is not None:
@@ -698,15 +718,39 @@ class Interpreter:
698
718
  return ret_val
699
719
  def visit_PropertyAccess(self, node: PropertyAccess):
700
720
  instance = self.current_env.get(node.instance_name)
721
+
722
+ # 1. ShellLite Instance
701
723
  if isinstance(instance, Instance):
702
724
  if node.property_name not in instance.data:
703
- raise AttributeError(f"Structure '{instance.class_def.name}' has no property '{node.property_name}'")
725
+ # Check for methods? PropertyAccess usually implies data.
726
+ # But in some cases we might want method reference?
727
+ raise AttributeError(f"Structure '{instance.class_def.name}' has no property '{node.property_name}'")
704
728
  return instance.data[node.property_name]
729
+
730
+ # 2. Dictionary
705
731
  elif isinstance(instance, dict):
706
732
  if node.property_name in instance:
707
733
  return instance[node.property_name]
708
734
  raise AttributeError(f"Dictionary has no key '{node.property_name}'")
709
- raise TypeError(f"'{node.instance_name}' is not a structure instance or dictionary.")
735
+
736
+ # 3. List
737
+ elif isinstance(instance, list):
738
+ if node.property_name == 'length':
739
+ return len(instance)
740
+
741
+ # 4. String
742
+ elif isinstance(instance, str):
743
+ if node.property_name == 'length':
744
+ return len(instance)
745
+
746
+ # 5. Python Object / Module Interop
747
+ # If the instance has the attribute natively, return it.
748
+ # This handles 'math.pi', 'os.name', etc.
749
+ if hasattr(instance, node.property_name):
750
+ return getattr(instance, node.property_name)
751
+
752
+ raise TypeError(f"Object '{node.instance_name}' (type {type(instance).__name__}) has no property '{node.property_name}'")
753
+
710
754
  def visit_Import(self, node: Import):
711
755
  if node.path in self.std_modules:
712
756
  self.current_env.set(node.path, self.std_modules[node.path])
@@ -750,8 +794,16 @@ class Interpreter:
750
794
  statements = parser.parse()
751
795
  for stmt in statements:
752
796
  self.visit(stmt)
753
- def _get_class_properties(self, class_def: ClassDef) -> List[str]:
754
- props = list(class_def.properties)
797
+ def _get_class_properties(self, class_def: ClassDef) -> List[tuple[str, Optional[Node]]]:
798
+ if not hasattr(class_def, 'properties'): return []
799
+ # Support both old string list and new tuple list for backward compat if needed, though we updated AST
800
+ props = []
801
+ for p in class_def.properties:
802
+ if isinstance(p, tuple):
803
+ props.append(p)
804
+ else:
805
+ props.append((p, None))
806
+
755
807
  if class_def.parent:
756
808
  if class_def.parent not in self.classes:
757
809
  raise NameError(f"Parent class '{class_def.parent}' not defined.")
@@ -950,6 +1002,14 @@ class Interpreter:
950
1002
  raise StopException()
951
1003
  def visit_Skip(self, node: Skip):
952
1004
  raise SkipException()
1005
+ def visit_PythonImport(self, node: PythonImport):
1006
+ try:
1007
+ mod = importlib.import_module(node.module_name)
1008
+ name = node.alias if node.alias else node.module_name.split('.')[0]
1009
+ self.global_env.set(name, mod)
1010
+ except ImportError as e:
1011
+ raise RuntimeError(f"Could not import python module '{node.module_name}': {e}")
1012
+
953
1013
  def visit_Throw(self, node: Throw):
954
1014
  message = self.visit(node.message)
955
1015
  raise ShellLiteError(str(message))
@@ -1088,18 +1148,152 @@ class Interpreter:
1088
1148
  code = 0
1089
1149
  if node.code:
1090
1150
  code = self.visit(node.code)
1091
- import sys
1092
- sys.exit(code)
1151
+ sys.exit(int(code))
1152
+ sys.exit(0)
1153
+
1154
+ # -------------------------------------------------------------------------
1155
+ # Project Polaris: Phase 2 (The Canvas - Native UI)
1156
+ # -------------------------------------------------------------------------
1157
+ def visit_App(self, node: App):
1158
+ # We need a root for the app
1159
+ import tkinter as tk
1160
+ from tkinter import messagebox
1161
+ root = tk.Tk()
1162
+ root.title(node.title)
1163
+ root.geometry(f"{node.width}x{node.height}")
1164
+
1165
+ # Store root for potential access, though mostly we use 'master' passed down
1166
+ # Ideally we pass 'parent' to visits, but we don't have that signature.
1167
+ # So we'll use a stack or a temporary context.
1168
+ self.ui_parent_stack = [root]
1169
+
1170
+ # Define a helpful alert function available in UI context
1171
+ def ui_alert(msg):
1172
+ messagebox.showinfo("Message", str(msg))
1173
+ self.current_env.set("alert", ui_alert)
1174
+
1175
+ try:
1176
+ for child in node.body:
1177
+ self.visit(child)
1178
+ finally:
1179
+ self.ui_parent_stack.pop()
1180
+
1181
+ root.mainloop()
1182
+
1183
+ def visit_Layout(self, node: Layout):
1184
+ parent = self.ui_parent_stack[-1]
1185
+
1186
+ # Create a frame for the layout
1187
+ frame = tk.Frame(parent)
1188
+
1189
+ # Pack options based on layout type of THIS container relative to parent??
1190
+ # Usually Layout implies how CHILDREN are arranged.
1191
+ # But here 'column' means "I am a column" -> children stacked vertically.
1192
+ # 'row' means "I am a row" -> children stacked horizontally.
1193
+
1194
+ # In Tkinter, pack() defaults to vertical (column).
1195
+ # side=LEFT makes it horizontal (row).
1196
+
1197
+ # We start by adding the frame to the parent.
1198
+ # If parent is a Column, we pack(side=TOP). If Row, pack(side=LEFT).
1199
+ # But simplified: Just use pack(fill=X) or something.
1200
+ frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
1201
+
1202
+ self.ui_parent_stack.append((frame, node.layout_type))
1203
+ try:
1204
+ for child in node.body:
1205
+ self.visit(child)
1206
+ finally:
1207
+ self.ui_parent_stack.pop()
1208
+
1209
+ def visit_Widget(self, node: Widget):
1210
+ from tkinter import messagebox
1211
+ parent_ctx = self.ui_parent_stack[-1]
1212
+ if isinstance(parent_ctx, tuple):
1213
+ parent, layout_mode = parent_ctx
1214
+ else:
1215
+ parent = parent_ctx
1216
+ layout_mode = 'column' # Default to column
1217
+
1218
+ widget = None
1219
+ if node.widget_type == 'button':
1220
+ # Handle event
1221
+ def on_click():
1222
+ if node.event_handler:
1223
+ try:
1224
+ for stmt in node.event_handler:
1225
+ self.visit(stmt)
1226
+ except Exception as e:
1227
+ messagebox.showerror("Error", str(e))
1228
+
1229
+ widget = tk.Button(parent, text=node.label, command=on_click)
1230
+
1231
+ elif node.widget_type == 'input':
1232
+ lbl = tk.Label(parent, text=node.label)
1233
+ pack_opts = {'side': tk.TOP, 'anchor': 'w'} if layout_mode == 'column' else {'side': tk.LEFT}
1234
+ lbl.pack(**pack_opts)
1235
+
1236
+ widget = tk.Entry(parent)
1237
+
1238
+ # Store accessor in Env so we can read .value
1239
+ if node.var_name:
1240
+ # We can't store the widget directly because .value access visits PropertyAccess
1241
+ # which expects dict, list, or python object.
1242
+ # Tkinter Entry has get().
1243
+ # We wrap it or just rely on 'visit_PropertyAccess' (Phase 1)
1244
+ # By default Tkinter widgets store config.
1245
+ # Let's verify if widget.value works natively? No.
1246
+ # So we wrap it.
1247
+ class InputWrapper:
1248
+ def __init__(self, w): self.w = w
1249
+ @property
1250
+ def value(self): return self.w.get()
1251
+ @property
1252
+ def text(self): return self.w.get()
1253
+
1254
+ self.current_env.set(node.var_name, InputWrapper(widget))
1255
+
1256
+ elif node.widget_type == 'heading':
1257
+ widget = tk.Label(parent, text=node.label, font=("Helvetica", 16, "bold"))
1258
+
1259
+ elif node.widget_type == 'text':
1260
+ widget = tk.Label(parent, text=node.label)
1261
+
1262
+ if widget:
1263
+ # Layout the widget
1264
+ if layout_mode == 'column':
1265
+ widget.pack(side=tk.TOP, pady=5, fill=tk.X)
1266
+ else:
1267
+ widget.pack(side=tk.LEFT, padx=5)
1268
+
1093
1269
  def visit_Make(self, node: Make):
1094
1270
  if node.class_name not in self.classes:
1095
1271
  raise NameError(f"Thing '{node.class_name}' not defined.")
1096
1272
  class_def = self.classes[node.class_name]
1097
1273
  props = self._get_class_properties(class_def)
1098
- if len(node.args) != len(props):
1099
- raise TypeError(f"Thing '{node.class_name}' expects {len(props)} values, got {len(node.args)}")
1274
+
1275
+ # Check args length
1276
+ required_count = 0
1277
+ for name, default_val in props:
1278
+ if default_val is None:
1279
+ required_count += 1
1280
+
1281
+ if len(node.args) < required_count:
1282
+ raise TypeError(f"Thing '{node.class_name}' expects at least {required_count} values, got {len(node.args)}")
1283
+
1100
1284
  instance = Instance(class_def)
1101
- for prop, arg in zip(props, node.args):
1102
- instance.data[prop] = self.visit(arg)
1285
+
1286
+ for i, (prop_name, default_val) in enumerate(props):
1287
+ val = None
1288
+ if i < len(node.args):
1289
+ val = self.visit(node.args[i])
1290
+ elif default_val is not None:
1291
+ val = self.visit(default_val)
1292
+ else:
1293
+ raise TypeError(f"Missing argument for property '{prop_name}' in '{node.class_name}'")
1294
+
1295
+ instance.data[prop_name] = val
1296
+
1103
1297
  return instance
1104
1298
  def visit_Convert(self, node: Convert):
1105
1299
  val = self.visit(node.expression)
@@ -149,12 +149,26 @@ class JSCompiler:
149
149
  code = f"class {node.name}{extends} {{\n"
150
150
  self.indentation += 1
151
151
  if node.properties:
152
- props = node.properties
152
+ props = []
153
+ assigns = []
154
+ for p in node.properties:
155
+ if isinstance(p, tuple):
156
+ name, default = p
157
+ if default:
158
+ # JS 6 supports defaults in args
159
+ props.append(f"{name} = {self.visit(default)}")
160
+ else:
161
+ props.append(name)
162
+ assigns.append(f"self.{name} = {name};")
163
+ else:
164
+ props.append(p)
165
+ assigns.append(f"self.{p} = {p};")
166
+
153
167
  code += f"{self.indent()}constructor({', '.join(props)}) {{\n"
154
168
  self.indentation += 1
155
169
  if parent: code += f"{self.indent()}super();\n"
156
- for p in props:
157
- code += f"{self.indent()}self.{p} = {p};\n"
170
+ for assign in assigns:
171
+ code += f"{self.indent()}{assign}\n"
158
172
  self.indentation -= 1
159
173
  code += f"{self.indent()}}}\n"
160
174
  for m in node.methods:
@@ -223,6 +223,9 @@ class Lexer:
223
223
  'count': 'COUNT', 'many': 'MANY', 'how': 'HOW',
224
224
  'field': 'FIELD', 'submit': 'SUBMIT', 'named': 'NAMED',
225
225
  'placeholder': 'PLACEHOLDER',
226
+ 'app': 'APP', 'title': 'TITLE', 'size': 'SIZE',
227
+ 'column': 'COLUMN', 'row': 'ROW',
228
+ 'button': 'BUTTON', 'heading': 'HEADING', 'text': 'TEXT',
226
229
  }
227
230
  token_type = keywords.get(value, 'ID')
228
231
  self.tokens.append(Token(token_type, value, self.line_number, current_col))