2019-08-28 21:24:54 +03:00
|
|
|
#!/usr/bin/python3
|
|
|
|
# Slang REPL
|
|
|
|
|
|
|
|
import readline
|
|
|
|
from .ast import *
|
|
|
|
from .lexer import *
|
2020-03-18 07:00:12 +03:00
|
|
|
from utils.nolog import *
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTCodeNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
r = None
|
2019-08-28 21:24:54 +03:00
|
|
|
for i in node.nodes:
|
2020-03-18 07:00:12 +03:00
|
|
|
if (isinstance(i, ASTElseClauseNode) and r is not None): continue
|
2019-08-28 21:24:54 +03:00
|
|
|
r = execute_node(i, ns)
|
2020-03-18 07:00:12 +03:00
|
|
|
if (r not in (None, ...)):
|
2019-08-28 21:24:54 +03:00
|
|
|
ns.values['_'] = r
|
|
|
|
print(repr(r))
|
|
|
|
else: return
|
|
|
|
return r
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTVardefNode, ns):
|
|
|
|
ns.values[node.name] = node.value
|
|
|
|
|
2020-03-18 07:00:12 +03:00
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTBlockNode, ns):
|
|
|
|
return execute_node(node.code, ns)
|
|
|
|
|
2019-08-28 21:24:54 +03:00
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTFuncdefNode, ns):
|
|
|
|
ns.values[node.name.identifier] = (node.argdefs, node.code)
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTAssignmentNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
ns.values[node.name] = execute_node(ASTBinaryExprNode(node.name, node.inplace_operator, node.value, lineno=node.lineno, offset=node.offset), ns) if (node.inplace_operator is not None) else node.value
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTFunccallNode, ns):
|
|
|
|
code_ns = ns.derive(node.callable.value.identifier)
|
|
|
|
argdefs, func = ns.values[node.callable]
|
|
|
|
for ii, i in enumerate(node.callargs.callargs):
|
|
|
|
code_ns.values[argdefs[ii].name] = i
|
|
|
|
return execute_node(func, code_ns)
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTValueNode, ns):
|
|
|
|
return execute_node(node.value, ns)
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTIdentifierNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
try: return ns.values[node]
|
|
|
|
except KeyError: raise SlValidationError(f"{node} is not initialized", node, scope=ns.scope)
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTLiteralNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
return eval(node.literal) if (isinstance(node.literal, str)) else node.literal
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTListNode, ns):
|
|
|
|
return node.values
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTKeywordExprNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
#if (node.keyword.keyword == 'return'): return execute_node(node.value, ns) # TODO FIXME???
|
|
|
|
#el
|
|
|
|
if (node.keyword.keyword == 'delete'): ns.delete(node.value)
|
|
|
|
else: raise NotImplementedError(node.keyword)
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTConditionalNode, ns):
|
|
|
|
if (execute_node(node.condition, ns)):
|
|
|
|
execute_node(node.code, ns)
|
|
|
|
else: return
|
|
|
|
return ...
|
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTForLoopNode, ns):
|
|
|
|
for i in execute_node(node.iterable, ns):
|
|
|
|
execute_node(node.code, ns)
|
|
|
|
else: return
|
|
|
|
return ...
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTUnaryExprNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
value = execute_node(node.value, ns)
|
|
|
|
return eval(f"{node.operator.operator} value")
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
@dispatch
|
|
|
|
def execute_node(node: ASTBinaryExprNode, ns):
|
2020-03-18 07:00:12 +03:00
|
|
|
lvalue = execute_node(node.lvalue, ns)
|
|
|
|
rvalue = execute_node(node.rvalue, ns)
|
|
|
|
if (node.operator.operator == 'xor'): return eval("(lvalue and not rvalue) or (rvalue and not lvalue)")
|
|
|
|
elif (node.operator.operator == 'to'): return range(lvalue, rvalue)
|
|
|
|
else: return eval(f"lvalue {node.operator.operator} rvalue")
|
|
|
|
|
|
|
|
class Completer:
|
|
|
|
def __init__(self, namespace):
|
|
|
|
self.namespace = namespace
|
|
|
|
|
|
|
|
def complete(self, text, state):
|
|
|
|
if (state == 0):
|
|
|
|
if ('.' in text): self.matches = self.attr_matches(text)
|
|
|
|
else: self.matches = self.global_matches(text)
|
|
|
|
try: return self.matches[state]
|
|
|
|
except IndexError: return None
|
|
|
|
|
|
|
|
def _callable_postfix(self, val, word):
|
|
|
|
if (isinstance(val, ASTCallableNode)): word += '('
|
|
|
|
return word
|
|
|
|
|
|
|
|
def global_matches(self, text):
|
|
|
|
matches = list()
|
|
|
|
seen = set()
|
|
|
|
n = len(text)
|
|
|
|
|
|
|
|
for word in keywords:
|
|
|
|
if (word[:n] != text): continue
|
|
|
|
seen.add(word)
|
|
|
|
matches.append(word+' ')
|
|
|
|
|
|
|
|
for word, val in self.namespace.values.items():
|
|
|
|
if (word[:n] != text or word in seen): continue
|
|
|
|
seen.add(word)
|
|
|
|
matches.append(self._callable_postfix(val, word))
|
|
|
|
|
|
|
|
return matches
|
|
|
|
|
|
|
|
def attr_matches(self, text):
|
|
|
|
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
|
|
|
|
if (m is None): return ()
|
|
|
|
expr, attr = m.group(1, 3)
|
|
|
|
try: obj = self.namespace.values[expr] # TODO FIXME
|
|
|
|
except KeyError: return ()
|
|
|
|
|
|
|
|
words = set() # TODO FIXME attrs
|
|
|
|
|
|
|
|
matches = list()
|
|
|
|
n = len(attr)
|
|
|
|
if (attr == ''): noprefix = '_'
|
|
|
|
elif (attr == '_'): noprefix = '__'
|
|
|
|
else: noprefix = None
|
|
|
|
|
|
|
|
while (True):
|
|
|
|
for word in words:
|
|
|
|
if (word[:n] != attr or (noprefix and word[:n+1] == noprefix)): continue
|
|
|
|
match = f"{expr}.{word}"
|
|
|
|
try: val = getattr(obj, word)
|
|
|
|
except Exception: pass # Include even if attribute not set
|
|
|
|
else: match = self._callable_postfix(val, match)
|
|
|
|
matches.append(match)
|
|
|
|
if (matches or not noprefix): break
|
|
|
|
if (noprefix == '_'): noprefix = '__'
|
|
|
|
else: noprefix = None
|
|
|
|
|
|
|
|
matches.sort()
|
|
|
|
return matches
|
2019-08-28 21:24:54 +03:00
|
|
|
|
|
|
|
def repl():
|
|
|
|
ns = Namespace('<repl>')
|
2020-03-18 07:00:12 +03:00
|
|
|
ns.define(ASTIdentifierNode('_', lineno=None, offset=None), stdlib.Any())
|
|
|
|
|
|
|
|
completer = Completer(ns)
|
|
|
|
histfile = os.path.expanduser('~/.sli_history')
|
|
|
|
try: readline.read_history_file(histfile)
|
|
|
|
except FileNotFoundError: pass
|
|
|
|
for i in (
|
|
|
|
'set colored-completion-prefix on',
|
|
|
|
'set enable-bracketed-paste on',
|
|
|
|
#'set horizontal-scroll-mode on',
|
|
|
|
'set skip-completed-text on',
|
|
|
|
'tab: complete',
|
|
|
|
): readline.parse_and_bind(i)
|
|
|
|
readline.set_completer(completer.complete)
|
|
|
|
#readline.set_completion_display_matches_hook(completer.display) # TODO
|
|
|
|
|
2019-08-28 21:24:54 +03:00
|
|
|
l = list()
|
|
|
|
tl = list()
|
2020-03-18 07:00:12 +03:00
|
|
|
|
|
|
|
try:
|
|
|
|
while (True):
|
|
|
|
try:
|
|
|
|
l.append(input(f"\1\033[1;93m\2{'...' if (tl) else '>>>'}\1\033[0m\2 "))
|
|
|
|
tll = parse_string(l[-1], lnooff=len(l)-1)
|
|
|
|
if (not tll): l.pop(); continue
|
|
|
|
tl += tll
|
|
|
|
if (tl[-1][-1].token == '\\'): continue
|
|
|
|
#if (len(tl) >= 2 and tl[-2][-1].token == '\\'): tl[-1] = tl[-2][:-1]+tl.pop() # TODO FIXME?: [['a', '+', '\\'], 'b'] --> [['a', '+', 'b']]
|
|
|
|
if (tl[0][-1].token == '{' and tl[-1][-1].token != '}'): continue
|
|
|
|
ast = build_ast(tl, interactive=True)
|
|
|
|
validate_ast(ast, ns)
|
|
|
|
optimize_ast(ast, ns)
|
|
|
|
execute_node(ast.code, ns)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
buf = readline.get_line_buffer()
|
|
|
|
print(f"\r\033[2m^C{'> '+buf if (buf) else ' '}\033[0m")
|
|
|
|
except EOFError:
|
|
|
|
print(end='\r\033[K')
|
|
|
|
break
|
|
|
|
except (SlSyntaxError, SlValidationError) as ex:
|
|
|
|
ex.line = l[ex.lineno-1]
|
|
|
|
print(ex)
|
|
|
|
tl.clear()
|
|
|
|
l.clear()
|
|
|
|
finally: readline.write_history_file(histfile)
|
|
|
|
|
|
|
|
if (__name__ == '__main__'): repl()
|
|
|
|
|
|
|
|
# by Sdore, 2020
|