shell-lite 0.4.3__py3-none-any.whl → 0.4.5__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.
shell_lite/js_compiler.py DELETED
@@ -1,220 +0,0 @@
1
- import random
2
- from typing import List
3
- from .ast_nodes import *
4
- class JSCompiler:
5
- def __init__(self):
6
- self.indentation = 0
7
- def indent(self):
8
- return " " * self.indentation
9
- def visit(self, node: Node) -> str:
10
- method_name = f'visit_{type(node).__name__}'
11
- visitor = getattr(self, method_name, self.generic_visit)
12
- return visitor(node)
13
- def generic_visit(self, node: Node):
14
- raise Exception(f"JSCompiler does not support {type(node).__name__}")
15
- def compile_block(self, statements: List[Node]) -> str:
16
- if not statements:
17
- return ""
18
- code = ""
19
- for stmt in statements:
20
- stmt_code = self.visit(stmt)
21
- if not stmt_code: continue
22
- indented_stmt = "\n".join([f"{self.indent()}{line}" for line in stmt_code.split('\n')])
23
- code += indented_stmt + "\n"
24
- return code.rstrip()
25
- def compile(self, statements: List[Node]) -> str:
26
- code = [
27
- "// ShellLite Runtime (JS)",
28
- "const fs = require('fs');",
29
- "const path = require('path');",
30
- "const https = require('https');",
31
- "const { execSync } = require('child_process');",
32
- "",
33
- "// Builtins",
34
- "const say = console.log;",
35
- "const print = console.log;",
36
- "const range = (n) => [...Array(n).keys()];",
37
- "const int = (x) => parseInt(x);",
38
- "const str = (x) => String(x);",
39
- "const float = (x) => parseFloat(x);",
40
- "const len = (x) => x.length;",
41
- "",
42
- "// Utils",
43
- "const _slang_download = (url) => { console.log('Download not impl in minimal JS runtime'); };",
44
- "",
45
- "// --- User Code ---",
46
- ""
47
- ]
48
- code.append(self.compile_block(statements))
49
- return "\n".join(code)
50
- def visit_Number(self, node: Number):
51
- return str(node.value)
52
- def visit_String(self, node: String):
53
- return repr(node.value)
54
- def visit_Boolean(self, node: Boolean):
55
- return "true" if node.value else "false"
56
- def visit_Regex(self, node: Regex):
57
- return f"/{node.pattern}/"
58
- def visit_ListVal(self, node: ListVal):
59
- elements = [self.visit(e) for e in node.elements]
60
- return f"[{', '.join(elements)}]"
61
- def visit_Dictionary(self, node: Dictionary):
62
- pairs = [f"{self.visit(k)}: {self.visit(v)}" for k, v in node.pairs]
63
- return f"{{{', '.join(pairs)}}}"
64
- def visit_SetVal(self, node: SetVal):
65
- elements = [self.visit(e) for e in node.elements]
66
- return f"new Set([{', '.join(elements)}])"
67
- def visit_VarAccess(self, node: VarAccess):
68
- return node.name
69
- def visit_Assign(self, node: Assign):
70
- return f"var {node.name} = {self.visit(node.value)};"
71
- def visit_ConstAssign(self, node: ConstAssign):
72
- return f"const {node.name} = {self.visit(node.value)};"
73
- def visit_PropertyAssign(self, node: PropertyAssign):
74
- return f"{node.instance_name}.{node.property_name} = {self.visit(node.value)};"
75
- def visit_BinOp(self, node: BinOp):
76
- left = self.visit(node.left)
77
- right = self.visit(node.right)
78
- op = node.op
79
- js_ops = {
80
- 'matches': None,
81
- 'and': '&&',
82
- 'or': '||',
83
- '==': '==='
84
- }
85
- if op == 'matches':
86
- return f"new RegExp({right}).test({left})"
87
- real_op = js_ops.get(op, op)
88
- return f"({left} {real_op} {right})"
89
- def visit_UnaryOp(self, node: UnaryOp):
90
- return f"({node.op} {self.visit(node.right)})"
91
- def visit_Print(self, node: Print):
92
- return f"console.log({self.visit(node.expression)});"
93
- def visit_Input(self, node: Input):
94
- return f"require('readline-sync').question({repr(node.prompt) if node.prompt else '\"\"'})"
95
- def visit_If(self, node: If):
96
- code = f"if ({self.visit(node.condition)}) {{\n"
97
- self.indentation += 1
98
- code += self.compile_block(node.body)
99
- self.indentation -= 1
100
- code += f"\n{self.indent()}}}"
101
- if node.else_body:
102
- code += f" else {{\n"
103
- self.indentation += 1
104
- code += self.compile_block(node.else_body)
105
- self.indentation -= 1
106
- code += f"\n{self.indent()}}}"
107
- return code
108
- def visit_While(self, node: While):
109
- code = f"while ({self.visit(node.condition)}) {{\n"
110
- self.indentation += 1
111
- code += self.compile_block(node.body)
112
- self.indentation -= 1
113
- code += f"\n{self.indent()}}}"
114
- return code
115
- def visit_For(self, node: For):
116
- count = self.visit(node.count)
117
- var = f"_i_{random.randint(0,1000)}"
118
- code = f"for (let {var} = 0; {var} < {count}; {var}++) {{\n"
119
- self.indentation += 1
120
- code += self.compile_block(node.body)
121
- self.indentation -= 1
122
- code += f"\n{self.indent()}}}"
123
- return code
124
- def visit_ForIn(self, node: ForIn):
125
- code = f"for (let {node.var_name} of {self.visit(node.iterable)}) {{\n"
126
- self.indentation += 1
127
- code += self.compile_block(node.body)
128
- self.indentation -= 1
129
- code += f"\n{self.indent()}}}"
130
- return code
131
- def visit_Repeat(self, node: Repeat):
132
- return self.visit_For(For(node.count, node.body))
133
- def visit_FunctionDef(self, node: FunctionDef):
134
- args = [arg[0] for arg in node.args]
135
- code = f"function {node.name}({', '.join(args)}) {{\n"
136
- self.indentation += 1
137
- code += self.compile_block(node.body)
138
- self.indentation -= 1
139
- code += f"\n{self.indent()}}}"
140
- return code
141
- def visit_Return(self, node: Return):
142
- return f"return {self.visit(node.value)};"
143
- def visit_Call(self, node: Call):
144
- args = [self.visit(a) for a in node.args]
145
- return f"{node.name}({', '.join(args)})"
146
- def visit_ClassDef(self, node: ClassDef):
147
- parent = node.parent if node.parent else ""
148
- extends = f" extends {parent}" if parent else ""
149
- code = f"class {node.name}{extends} {{\n"
150
- self.indentation += 1
151
- if 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
-
167
- code += f"{self.indent()}constructor({', '.join(props)}) {{\n"
168
- self.indentation += 1
169
- if parent: code += f"{self.indent()}super();\n"
170
- for assign in assigns:
171
- code += f"{self.indent()}{assign}\n"
172
- self.indentation -= 1
173
- code += f"{self.indent()}}}\n"
174
- for m in node.methods:
175
- args = [arg[0] for arg in m.args]
176
- code += f"\n{self.indent()}{m.name}({', '.join(args)}) {{\n"
177
- self.indentation += 1
178
- code += self.compile_block(m.body)
179
- self.indentation -= 1
180
- code += f"\n{self.indent()}}}"
181
- self.indentation -= 1
182
- code += f"\n{self.indent()}}}"
183
- return code
184
- def visit_Instantiation(self, node: Instantiation):
185
- args = [self.visit(a) for a in node.args]
186
- return f"var {node.var_name} = new {node.class_name}({', '.join(args)});"
187
- def visit_MethodCall(self, node: MethodCall):
188
- args = [self.visit(a) for a in node.args]
189
- return f"{node.instance_name}.{node.method_name}({', '.join(args)})"
190
- def visit_PropertyAccess(self, node: PropertyAccess):
191
- return f"{node.instance_name}.{node.property_name}"
192
- def visit_Import(self, node: Import):
193
- base = node.path
194
- if base == 'vscode': return 'const vscode = require("vscode");'
195
- return f"const {base} = require('./{base}');"
196
- def visit_ImportAs(self, node: ImportAs):
197
- path = node.path
198
- if path == 'vscode': return f"const {node.alias} = require('vscode');"
199
- return f"const {node.alias} = require('./{path}');"
200
- def visit_Try(self, node: Try):
201
- code = f"try {{\n"
202
- self.indentation += 1
203
- code += self.compile_block(node.try_body)
204
- self.indentation -= 1
205
- code += f"\n{self.indent()}}} catch ({node.catch_var}) {{\n"
206
- self.indentation += 1
207
- code += self.compile_block(node.catch_body)
208
- self.indentation -= 1
209
- code += f"\n{self.indent()}}}"
210
- return code
211
- def visit_Throw(self, node: Throw):
212
- return f"throw new Error({self.visit(node.message)});"
213
- def visit_Skip(self, node: Skip):
214
- return "continue;"
215
- def visit_Stop(self, node: Stop):
216
- return "break;"
217
- def visit_Lambda(self, node: Lambda):
218
- return f"({', '.join(node.params)}) => {self.visit(node.body)}"
219
- def visit_Execute(self, node: Execute):
220
- pass
shell_lite/lexer_new.py DELETED
@@ -1,252 +0,0 @@
1
- import re
2
- from dataclasses import dataclass
3
- from typing import List, Optional
4
- @dataclass
5
- @dataclass
6
- class Token:
7
- type: str
8
- value: str
9
- line: int
10
- column: int = 1
11
- class Lexer:
12
- def __init__(self, source_code: str):
13
- self.source_code = source_code
14
- self.tokens: List[Token] = []
15
- self.current_char_index = 0
16
- self.line_number = 1
17
- self.indent_stack = [0]
18
- def tokenize(self) -> List[Token]:
19
- source = self._remove_multiline_comments(self.source_code)
20
- lines = source.split('\n')
21
- for line_num, line in enumerate(lines, 1):
22
- self.line_number = line_num
23
- stripped_line = line.strip()
24
- if not stripped_line:
25
- continue
26
- indent_level = len(line) - len(line.lstrip())
27
- if stripped_line.startswith('#'):
28
- # self.tokens.append(Token('COMMENT', stripped_line, self.line_number, indent_level + 1))
29
- # self.tokens.append(Token('NEWLINE', '', self.line_number, len(line) + 1))
30
- continue
31
- if indent_level > self.indent_stack[-1]:
32
- self.indent_stack.append(indent_level)
33
- self.tokens.append(Token('INDENT', '', self.line_number, indent_level + 1))
34
- elif indent_level < self.indent_stack[-1]:
35
- while indent_level < self.indent_stack[-1]:
36
- self.indent_stack.pop()
37
- self.tokens.append(Token('DEDENT', '', self.line_number, indent_level + 1))
38
- if indent_level != self.indent_stack[-1]:
39
- raise IndentationError(f"Unindent does not match any outer indentation level on line {self.line_number}")
40
- self.tokenize_line(stripped_line, indent_level + 1)
41
- self.tokens.append(Token('NEWLINE', '', self.line_number, len(line) + 1))
42
- while len(self.indent_stack) > 1:
43
- self.indent_stack.pop()
44
- self.tokens.append(Token('DEDENT', '', self.line_number, 1))
45
- self.tokens.append(Token('EOF', '', self.line_number, 1))
46
- return self.tokens
47
- def _remove_multiline_comments(self, source: str) -> str:
48
- result = []
49
- i = 0
50
- while i < len(source):
51
- if source[i:i+2] == '/*':
52
- end = source.find('*/', i + 2)
53
- if end == -1:
54
- raise SyntaxError("Unterminated multi-line comment")
55
- comment = source[i:end+2]
56
- result.append('\n' * comment.count('\n'))
57
- i = end + 2
58
- else:
59
- result.append(source[i])
60
- i += 1
61
- return ''.join(result)
62
- def tokenize_line(self, line: str, start_col: int = 1):
63
- pos = 0
64
- while pos < len(line):
65
- match = None
66
- current_col = start_col + pos
67
- if line[pos] == '#':
68
- self.tokens.append(Token('COMMENT', line[pos:], self.line_number, current_col))
69
- break
70
- if line[pos].isspace():
71
- pos += 1
72
- continue
73
- if line[pos].isdigit():
74
- match = re.match(r'^\d+(\.\d+)?', line[pos:])
75
- if match:
76
- value = match.group(0)
77
- self.tokens.append(Token('NUMBER', value, self.line_number, current_col))
78
- pos += len(value)
79
- continue
80
- if line[pos:pos+3] in ('"""', "'''"):
81
- quote_char = line[pos:pos+3]
82
- pass
83
- if line[pos] in ('"', "'"):
84
- quote_char = line[pos]
85
- end_quote = line.find(quote_char, pos + 1)
86
- if end_quote == -1:
87
- raise SyntaxError(f"Unterminated string on line {self.line_number}")
88
- value = line[pos+1:end_quote]
89
- value = value.replace("\\n", "\n").replace("\\t", "\t").replace("\\r", "\r").replace("\\\"", "\"").replace("\\\'", "\'")
90
- self.tokens.append(Token('STRING', value, self.line_number, current_col))
91
- pos = end_quote + 1
92
- continue
93
- if line[pos:pos+3] == '...':
94
- self.tokens.append(Token('DOTDOTDOT', '...', self.line_number, current_col))
95
- pos += 3
96
- continue
97
- two_char = line[pos:pos+2]
98
- two_char_tokens = {
99
- '=>': 'ARROW', '==': 'EQ', '!=': 'NEQ',
100
- '<=': 'LE', '>=': 'GE', '+=': 'PLUSEQ',
101
- '-=': 'MINUSEQ', '*=': 'MULEQ', '/=': 'DIVEQ',
102
- '%=': 'MODEQ'
103
- }
104
- if two_char in two_char_tokens:
105
- self.tokens.append(Token(two_char_tokens[two_char], two_char, self.line_number, current_col))
106
- pos += 2
107
- continue
108
- char = line[pos]
109
- rest_of_line = line[pos:]
110
- if rest_of_line.startswith('is at least '):
111
- self.tokens.append(Token('GE', '>=', self.line_number, current_col))
112
- pos += 12
113
- continue
114
- elif rest_of_line.startswith('is exactly '):
115
- self.tokens.append(Token('EQ', '==', self.line_number, current_col))
116
- pos += 11
117
- continue
118
- elif rest_of_line.startswith('is less than '):
119
- self.tokens.append(Token('LT', '<', self.line_number, current_col))
120
- pos += 13
121
- continue
122
- elif rest_of_line.startswith('is more than '):
123
- self.tokens.append(Token('GT', '>', self.line_number, current_col))
124
- pos += 13
125
- continue
126
- if rest_of_line.startswith('the') and (len(rest_of_line) == 3 or not rest_of_line[3].isalnum()):
127
- pos += 3
128
- continue
129
- if char == '/':
130
- last_type = self.tokens[-1].type if self.tokens else None
131
- is_division = False
132
- if last_type in ('NUMBER', 'STRING', 'ID', 'RPAREN', 'RBRACKET'):
133
- is_division = True
134
- if not is_division:
135
- end_slash = line.find('/', pos + 1)
136
- if end_slash != -1:
137
- pattern = line[pos+1:end_slash]
138
- flags = ""
139
- k = end_slash + 1
140
- while k < len(line) and line[k].isalpha():
141
- flags += line[k]
142
- k += 1
143
- self.tokens.append(Token('REGEX', pattern, self.line_number, current_col))
144
- pos = k
145
- continue
146
- single_char_tokens = {
147
- '+': 'PLUS', '-': 'MINUS', '*': 'MUL', '/': 'DIV',
148
- '%': 'MOD', '=': 'ASSIGN', '>': 'GT', '<': 'LT',
149
- '?': 'QUESTION', '(': 'LPAREN', ')': 'RPAREN',
150
- '[': 'LBRACKET', ']': 'RBRACKET', ':': 'COLON',
151
- '{': 'LBRACE', '}': 'RBRACE', ',': 'COMMA', '.': 'DOT'
152
- }
153
- if char in single_char_tokens:
154
- self.tokens.append(Token(single_char_tokens[char], char, self.line_number, current_col))
155
- pos += 1
156
- continue
157
- if char.isalpha() or char == '_':
158
- match = re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*', line[pos:])
159
- if match:
160
- value = match.group(0)
161
- keywords = {
162
- 'if': 'IF', 'else': 'ELSE', 'elif': 'ELIF',
163
- 'for': 'FOR', 'in': 'IN', 'range': 'RANGE',
164
- 'loop': 'LOOP', 'times': 'TIMES',
165
- 'while': 'WHILE', 'until': 'UNTIL',
166
- 'repeat': 'REPEAT', 'forever': 'FOREVER',
167
- 'stop': 'STOP', 'skip': 'SKIP', 'exit': 'EXIT',
168
- 'each': 'FOR',
169
- 'unless': 'UNLESS', 'when': 'WHEN', 'otherwise': 'OTHERWISE',
170
- 'then': 'THEN', 'do': 'DO',
171
- 'print': 'PRINT', 'say': 'SAY', 'show': 'SAY',
172
- 'input': 'INPUT', 'ask': 'ASK',
173
- 'to': 'TO', 'can': 'TO',
174
- 'return': 'RETURN', 'give': 'RETURN',
175
- 'fn': 'FN',
176
- 'structure': 'STRUCTURE', 'thing': 'STRUCTURE', 'class': 'STRUCTURE',
177
- 'has': 'HAS', 'with': 'WITH',
178
- 'is': 'IS', 'extends': 'EXTENDS', 'from': 'FROM',
179
- 'make': 'MAKE', 'new': 'MAKE',
180
- 'yes': 'YES', 'no': 'NO',
181
- 'true': 'YES', 'false': 'NO',
182
- 'const': 'CONST',
183
- 'and': 'AND', 'or': 'OR', 'not': 'NOT',
184
- 'try': 'TRY', 'catch': 'CATCH', 'always': 'ALWAYS',
185
- 'use': 'USE', 'as': 'AS', 'share': 'SHARE',
186
- 'execute': 'EXECUTE', 'run': 'EXECUTE',
187
- 'alert': 'ALERT', 'prompt': 'PROMPT', 'confirm': 'CONFIRM',
188
- 'spawn': 'SPAWN', 'await': 'AWAIT',
189
- 'matches': 'MATCHES',
190
- 'on': 'ON',
191
- 'download': 'DOWNLOAD',
192
- 'compress': 'COMPRESS', 'extract': 'EXTRACT', 'folder': 'FOLDER',
193
- 'load': 'LOAD', 'save': 'SAVE', 'csv': 'CSV',
194
- 'copy': 'COPY', 'paste': 'PASTE', 'clipboard': 'CLIPBOARD',
195
- 'press': 'PRESS', 'type': 'TYPE', 'click': 'CLICK', 'at': 'AT',
196
- 'notify': 'NOTIFY',
197
- 'date': 'ID', 'today': 'ID', 'after': 'AFTER', 'before': 'BEFORE',
198
- 'list': 'LIST', 'set': 'SET', 'unique': 'UNIQUE', 'of': 'OF',
199
- 'wait': 'WAIT',
200
- 'convert': 'CONVERT', 'json': 'JSON',
201
- 'listen': 'LISTEN', 'port': 'PORT',
202
- 'every': 'EVERY', 'minute': 'MINUTE', 'minutes': 'MINUTE',
203
- 'second': 'SECOND', 'seconds': 'SECOND',
204
- 'progress': 'PROGRESS',
205
- 'bold': 'BOLD',
206
- 'red': 'RED', 'green': 'GREEN', 'blue': 'BLUE',
207
- 'yellow': 'YELLOW', 'cyan': 'CYAN', 'magenta': 'MAGENTA',
208
- 'serve': 'SERVE', 'static': 'STATIC',
209
- 'write': 'WRITE', 'append': 'APPEND', 'read': 'READ', 'file': 'FILE',
210
- 'write': 'WRITE', 'append': 'APPEND', 'read': 'READ', 'file': 'FILE',
211
- 'db': 'DB', 'database': 'DB',
212
- 'query': 'QUERY', 'open': 'OPEN', 'close': 'CLOSE', 'exec': 'EXEC',
213
- 'middleware': 'MIDDLEWARE', 'before': 'BEFORE',
214
- 'when': 'WHEN', 'someone': 'SOMEONE', 'visits': 'VISITS',
215
- 'submits': 'SUBMITS', 'start': 'START', 'server': 'SERVER',
216
- 'files': 'FILES',
217
- 'define': 'DEFINE', 'page': 'PAGE', 'called': 'CALLED',
218
- 'using': 'USING', 'component': 'PAGE',
219
- 'heading': 'HEADING', 'paragraph': 'PARAGRAPH',
220
- 'image': 'IMAGE',
221
- 'add': 'ADD', 'put': 'ADD', 'into': 'INTO',
222
- 'count': 'COUNT', 'many': 'MANY', 'how': 'HOW',
223
- 'field': 'FIELD', 'submit': 'SUBMIT', 'named': 'NAMED',
224
- 'placeholder': 'PLACEHOLDER',
225
- 'app': 'APP', 'title': 'ID', 'size': 'SIZE',
226
- 'column': 'COLUMN', 'row': 'ROW',
227
- 'button': 'BUTTON', 'heading': 'HEADING',
228
- 'sum': 'SUM', 'upper': 'UPPER', 'lower': 'LOWER',
229
- 'increment': 'INCREMENT', 'decrement': 'DECREMENT',
230
- 'multiply': 'MULTIPLY', 'divide': 'DIVIDE',
231
- 'be': 'BE', 'by': 'BY',
232
- 'plus': 'PLUS', 'minus': 'MINUS', 'divided': 'DIV',
233
- 'greater': 'GREATER', 'less': 'LESS', 'equal': 'EQUAL',
234
- 'define': 'DEFINE', 'function': 'FUNCTION',
235
- 'contains': 'CONTAINS', 'empty': 'EMPTY',
236
- 'remove': 'REMOVE',
237
- 'than': 'THAN',
238
- 'doing': 'DOING',
239
- 'make': 'MAKE', 'be': 'BE',
240
- 'as': 'AS', 'long': 'LONG',
241
- 'otherwise': 'OTHERWISE',
242
- 'ask': 'ASK',
243
- }
244
- token_type = keywords.get(value, 'ID')
245
- self.tokens.append(Token(token_type, value, self.line_number, current_col))
246
- pos += len(value)
247
- continue
248
- if char in single_char_tokens:
249
- self.tokens.append(Token(single_char_tokens[char], char, self.line_number, current_col))
250
- pos += 1
251
- continue
252
- raise SyntaxError(f"Illegal character '{char}' at line {self.line_number}")
@@ -1,25 +0,0 @@
1
-
2
- class Environment:
3
- def __init__(self, parent=None):
4
- self.variables = {}
5
- self.parent = parent
6
- def get(self, name):
7
- if name in self.variables: return self.variables[name]
8
- if self.parent: return self.parent.get(name)
9
- raise NameError(f"Var '{name}' not found")
10
- def set(self, name, val):
11
- self.variables[name] = val
12
-
13
- class Interpreter:
14
- def __init__(self):
15
- print("DEBUG: MINIMAL INTERPRETER LOADED")
16
- self.global_env = Environment()
17
- self.current_env = self.global_env
18
- self.functions = {}
19
- self.builtins = {
20
- 'str': str,
21
- 'print': print
22
- }
23
- def visit(self, node):
24
- print(f"Visiting {type(node).__name__}")
25
- return None