shell-lite 0.3.5__tar.gz → 0.4.1__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.1/PKG-INFO +77 -0
  2. shell_lite-0.4.1/README.md +64 -0
  3. {shell_lite-0.3.5 → shell_lite-0.4.1}/pyproject.toml +2 -2
  4. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/ast_nodes.py +24 -1
  5. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/compiler.py +14 -2
  6. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/interpreter.py +212 -17
  7. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/js_compiler.py +17 -3
  8. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/lexer.py +4 -1
  9. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/main.py +263 -81
  10. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/parser.py +227 -23
  11. shell_lite-0.4.1/shell_lite.egg-info/PKG-INFO +77 -0
  12. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite.egg-info/SOURCES.txt +1 -0
  13. shell_lite-0.4.1/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.1}/LICENSE +0 -0
  18. {shell_lite-0.3.5 → shell_lite-0.4.1}/setup.cfg +0 -0
  19. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/__init__.py +0 -0
  20. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/cli.py +0 -0
  21. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/formatter.py +0 -0
  22. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite/runtime.py +0 -0
  23. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite.egg-info/dependency_links.txt +0 -0
  24. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite.egg-info/entry_points.txt +0 -0
  25. {shell_lite-0.3.5 → shell_lite-0.4.1}/shell_lite.egg-info/top_level.txt +0 -0
  26. {shell_lite-0.3.5 → shell_lite-0.4.1}/tests/test_interpreter.py +0 -0
  27. {shell_lite-0.3.5 → shell_lite-0.4.1}/tests/test_lexer.py +0 -0
  28. {shell_lite-0.3.5 → shell_lite-0.4.1}/tests/test_parser.py +0 -0
  29. {shell_lite-0.3.5 → shell_lite-0.4.1}/tests/test_stdlib.py +0 -0
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: shell-lite
3
+ Version: 0.4.1
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
+ <img src="assets/logo.png" align="right" width="250" alt="ShellLite Logo" />
15
+
16
+ # ShellLite: The English-Like Programming Language
17
+ # By Shrey Naithani
18
+ 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.
19
+
20
+ ## Version 0.04.0 (Polaris Update)
21
+
22
+ This release transforms ShellLite from a scripting tool into a comprehensive platform with three major pillars:
23
+ 1. **The Bridge**: Import and use any Python library (pandas, requests, etc.) natively.
24
+ 2. **The Canvas**: Build native desktop GUI applications with declarative syntax.
25
+ 3. **The Universe**: Integrated package management for project dependencies.
26
+
27
+ ## Installation
28
+
29
+ ### Via PyPI (Recommended)
30
+ You can install ShellLite directly from PyPI:
31
+ ```bash
32
+ pip install shell-lite
33
+ ```
34
+
35
+ ### Windows Installer
36
+ Download the latest `shl.exe` from the Releases page.
37
+
38
+ ## Quick Start
39
+
40
+ ### 1. Hello World
41
+ Save this as `hello.shl`:
42
+ ```shell-lite
43
+ say "Hello, World"
44
+ ```
45
+ Run it:
46
+ ```bash
47
+ shl hello.shl
48
+ ```
49
+ ## Project Management
50
+
51
+ Initialize a new project:
52
+ ```bash
53
+ shl init
54
+ ```
55
+
56
+ Install dependencies defined in `shell-lite.toml`:
57
+ ```bash
58
+ shl install
59
+ ```
60
+
61
+ ## Ecosystem & Tools
62
+
63
+ | Tool | Description | Link |
64
+ | :--- | :--- | :--- |
65
+ | **ShellDesk** | The official IDE for ShellLite. | [GitHub](https://github.com/Shrey-N/ShellDesk) |
66
+ | **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) |
67
+ | **Website** | Official website source code. | [GitHub](https://github.com/Shrey-N/ShellLite-Website) |
68
+
69
+ ## Documentation
70
+
71
+ Full documentation is available directly in this repository:
72
+ - [**Language Guide**](https://github.com/Shrey-N/ShellLite)
73
+ - [**Examples**](https://github.com/Shrey-N/ShellLite/tree/main/examples)
74
+
75
+ License: MIT
76
+ Made by Shrey Naithani
77
+
@@ -0,0 +1,64 @@
1
+ <img src="assets/logo.png" align="right" width="250" alt="ShellLite Logo" />
2
+
3
+ # ShellLite: The English-Like Programming Language
4
+ # By Shrey Naithani
5
+ 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.
6
+
7
+ ## Version 0.04.0 (Polaris Update)
8
+
9
+ This release transforms ShellLite from a scripting tool into a comprehensive platform with three major pillars:
10
+ 1. **The Bridge**: Import and use any Python library (pandas, requests, etc.) natively.
11
+ 2. **The Canvas**: Build native desktop GUI applications with declarative syntax.
12
+ 3. **The Universe**: Integrated package management for project dependencies.
13
+
14
+ ## Installation
15
+
16
+ ### Via PyPI (Recommended)
17
+ You can install ShellLite directly from PyPI:
18
+ ```bash
19
+ pip install shell-lite
20
+ ```
21
+
22
+ ### Windows Installer
23
+ Download the latest `shl.exe` from the Releases page.
24
+
25
+ ## Quick Start
26
+
27
+ ### 1. Hello World
28
+ Save this as `hello.shl`:
29
+ ```shell-lite
30
+ say "Hello, World"
31
+ ```
32
+ Run it:
33
+ ```bash
34
+ shl hello.shl
35
+ ```
36
+ ## Project Management
37
+
38
+ Initialize a new project:
39
+ ```bash
40
+ shl init
41
+ ```
42
+
43
+ Install dependencies defined in `shell-lite.toml`:
44
+ ```bash
45
+ shl install
46
+ ```
47
+
48
+ ## Ecosystem & Tools
49
+
50
+ | Tool | Description | Link |
51
+ | :--- | :--- | :--- |
52
+ | **ShellDesk** | The official IDE for ShellLite. | [GitHub](https://github.com/Shrey-N/ShellDesk) |
53
+ | **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) |
54
+ | **Website** | Official website source code. | [GitHub](https://github.com/Shrey-N/ShellLite-Website) |
55
+
56
+ ## Documentation
57
+
58
+ Full documentation is available directly in this repository:
59
+ - [**Language Guide**](https://github.com/Shrey-N/ShellLite)
60
+ - [**Examples**](https://github.com/Shrey-N/ShellLite/tree/main/examples)
61
+
62
+ License: MIT
63
+ Made by Shrey Naithani
64
+
@@ -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.1"
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))
@@ -1009,6 +1069,7 @@ class Interpreter:
1009
1069
  result = None
1010
1070
  for stmt in statements:
1011
1071
  result = self.visit(stmt)
1072
+ self.current_env.set('__exec_result__', result)
1012
1073
  return result
1013
1074
  def visit_ImportAs(self, node: ImportAs):
1014
1075
  if node.path in self.std_modules:
@@ -1088,18 +1149,152 @@ class Interpreter:
1088
1149
  code = 0
1089
1150
  if node.code:
1090
1151
  code = self.visit(node.code)
1091
- import sys
1092
- sys.exit(code)
1152
+ sys.exit(int(code))
1153
+ sys.exit(0)
1154
+
1155
+ # -------------------------------------------------------------------------
1156
+ # Project Polaris: Phase 2 (The Canvas - Native UI)
1157
+ # -------------------------------------------------------------------------
1158
+ def visit_App(self, node: App):
1159
+ # We need a root for the app
1160
+ import tkinter as tk
1161
+ from tkinter import messagebox
1162
+ root = tk.Tk()
1163
+ root.title(node.title)
1164
+ root.geometry(f"{node.width}x{node.height}")
1165
+
1166
+ # Store root for potential access, though mostly we use 'master' passed down
1167
+ # Ideally we pass 'parent' to visits, but we don't have that signature.
1168
+ # So we'll use a stack or a temporary context.
1169
+ self.ui_parent_stack = [root]
1170
+
1171
+ # Define a helpful alert function available in UI context
1172
+ def ui_alert(msg):
1173
+ messagebox.showinfo("Message", str(msg))
1174
+ self.current_env.set("alert", ui_alert)
1175
+
1176
+ try:
1177
+ for child in node.body:
1178
+ self.visit(child)
1179
+ finally:
1180
+ self.ui_parent_stack.pop()
1181
+
1182
+ root.mainloop()
1183
+
1184
+ def visit_Layout(self, node: Layout):
1185
+ parent = self.ui_parent_stack[-1]
1186
+
1187
+ # Create a frame for the layout
1188
+ frame = tk.Frame(parent)
1189
+
1190
+ # Pack options based on layout type of THIS container relative to parent??
1191
+ # Usually Layout implies how CHILDREN are arranged.
1192
+ # But here 'column' means "I am a column" -> children stacked vertically.
1193
+ # 'row' means "I am a row" -> children stacked horizontally.
1194
+
1195
+ # In Tkinter, pack() defaults to vertical (column).
1196
+ # side=LEFT makes it horizontal (row).
1197
+
1198
+ # We start by adding the frame to the parent.
1199
+ # If parent is a Column, we pack(side=TOP). If Row, pack(side=LEFT).
1200
+ # But simplified: Just use pack(fill=X) or something.
1201
+ frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
1202
+
1203
+ self.ui_parent_stack.append((frame, node.layout_type))
1204
+ try:
1205
+ for child in node.body:
1206
+ self.visit(child)
1207
+ finally:
1208
+ self.ui_parent_stack.pop()
1209
+
1210
+ def visit_Widget(self, node: Widget):
1211
+ from tkinter import messagebox
1212
+ parent_ctx = self.ui_parent_stack[-1]
1213
+ if isinstance(parent_ctx, tuple):
1214
+ parent, layout_mode = parent_ctx
1215
+ else:
1216
+ parent = parent_ctx
1217
+ layout_mode = 'column' # Default to column
1218
+
1219
+ widget = None
1220
+ if node.widget_type == 'button':
1221
+ # Handle event
1222
+ def on_click():
1223
+ if node.event_handler:
1224
+ try:
1225
+ for stmt in node.event_handler:
1226
+ self.visit(stmt)
1227
+ except Exception as e:
1228
+ messagebox.showerror("Error", str(e))
1229
+
1230
+ widget = tk.Button(parent, text=node.label, command=on_click)
1231
+
1232
+ elif node.widget_type == 'input':
1233
+ lbl = tk.Label(parent, text=node.label)
1234
+ pack_opts = {'side': tk.TOP, 'anchor': 'w'} if layout_mode == 'column' else {'side': tk.LEFT}
1235
+ lbl.pack(**pack_opts)
1236
+
1237
+ widget = tk.Entry(parent)
1238
+
1239
+ # Store accessor in Env so we can read .value
1240
+ if node.var_name:
1241
+ # We can't store the widget directly because .value access visits PropertyAccess
1242
+ # which expects dict, list, or python object.
1243
+ # Tkinter Entry has get().
1244
+ # We wrap it or just rely on 'visit_PropertyAccess' (Phase 1)
1245
+ # By default Tkinter widgets store config.
1246
+ # Let's verify if widget.value works natively? No.
1247
+ # So we wrap it.
1248
+ class InputWrapper:
1249
+ def __init__(self, w): self.w = w
1250
+ @property
1251
+ def value(self): return self.w.get()
1252
+ @property
1253
+ def text(self): return self.w.get()
1254
+
1255
+ self.current_env.set(node.var_name, InputWrapper(widget))
1256
+
1257
+ elif node.widget_type == 'heading':
1258
+ widget = tk.Label(parent, text=node.label, font=("Helvetica", 16, "bold"))
1259
+
1260
+ elif node.widget_type == 'text':
1261
+ widget = tk.Label(parent, text=node.label)
1262
+
1263
+ if widget:
1264
+ # Layout the widget
1265
+ if layout_mode == 'column':
1266
+ widget.pack(side=tk.TOP, pady=5, fill=tk.X)
1267
+ else:
1268
+ widget.pack(side=tk.LEFT, padx=5)
1269
+
1093
1270
  def visit_Make(self, node: Make):
1094
1271
  if node.class_name not in self.classes:
1095
1272
  raise NameError(f"Thing '{node.class_name}' not defined.")
1096
1273
  class_def = self.classes[node.class_name]
1097
1274
  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)}")
1275
+
1276
+ # Check args length
1277
+ required_count = 0
1278
+ for name, default_val in props:
1279
+ if default_val is None:
1280
+ required_count += 1
1281
+
1282
+ if len(node.args) < required_count:
1283
+ raise TypeError(f"Thing '{node.class_name}' expects at least {required_count} values, got {len(node.args)}")
1284
+
1100
1285
  instance = Instance(class_def)
1101
- for prop, arg in zip(props, node.args):
1102
- instance.data[prop] = self.visit(arg)
1286
+
1287
+ for i, (prop_name, default_val) in enumerate(props):
1288
+ val = None
1289
+ if i < len(node.args):
1290
+ val = self.visit(node.args[i])
1291
+ elif default_val is not None:
1292
+ val = self.visit(default_val)
1293
+ else:
1294
+ raise TypeError(f"Missing argument for property '{prop_name}' in '{node.class_name}'")
1295
+
1296
+ instance.data[prop_name] = val
1297
+
1103
1298
  return instance
1104
1299
  def visit_Convert(self, node: Convert):
1105
1300
  val = self.visit(node.expression)
@@ -1324,7 +1519,7 @@ class Interpreter:
1324
1519
  self.wfile.write(str(e).encode())
1325
1520
  except: pass
1326
1521
  server = HTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
1327
- print(f"\n ShellLite Server v0.03.4 is running!")
1522
+ print(f"\n ShellLite Server v0.04.1 is running!")
1328
1523
  print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
1329
1524
  try: server.serve_forever()
1330
1525
  except KeyboardInterrupt:
@@ -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:
@@ -195,7 +195,7 @@ class Lexer:
195
195
  'copy': 'COPY', 'paste': 'PASTE', 'clipboard': 'CLIPBOARD',
196
196
  'press': 'PRESS', 'type': 'TYPE', 'click': 'CLICK', 'at': 'AT',
197
197
  'notify': 'NOTIFY',
198
- 'date': 'DATE', 'today': 'TODAY', 'after': 'AFTER', 'before': 'BEFORE',
198
+ 'date': 'ID', 'today': 'ID', 'after': 'AFTER', 'before': 'BEFORE',
199
199
  'list': 'LIST', 'set': 'SET', 'unique': 'UNIQUE', 'of': 'OF',
200
200
  'wait': 'WAIT',
201
201
  'convert': 'CONVERT', 'json': 'JSON',
@@ -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': 'ID', 'size': 'ID',
227
+ 'column': 'ID', 'row': 'ID',
228
+ 'button': 'ID', 'heading': 'HEADING', 'text': 'ID',
226
229
  }
227
230
  token_type = keywords.get(value, 'ID')
228
231
  self.tokens.append(Token(token_type, value, self.line_number, current_col))