shell-lite 0.4.3__py3-none-any.whl → 0.4.4__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/interpreter.py +23 -23
- shell_lite/lexer.py +16 -8
- shell_lite/main.py +5 -5
- shell_lite/parser.py +263 -113
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.4.dist-info}/METADATA +1 -1
- shell_lite-0.4.4.dist-info/RECORD +15 -0
- shell_lite/fix_nulls.py +0 -29
- shell_lite/formatter.py +0 -75
- shell_lite/interpreter_backup.py +0 -1781
- shell_lite/interpreter_final.py +0 -1773
- shell_lite/interpreter_new.py +0 -1773
- shell_lite/js_compiler.py +0 -220
- shell_lite/lexer_new.py +0 -252
- shell_lite/minimal_interpreter.py +0 -25
- shell_lite/parser_new.py +0 -2229
- shell_lite/patch_parser.py +0 -41
- shell_lite-0.4.3.dist-info/RECORD +0 -25
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.4.dist-info}/LICENSE +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.4.dist-info}/WHEEL +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.4.dist-info}/entry_points.txt +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.4.dist-info}/top_level.txt +0 -0
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
|