from tokens import * import sys from collections import namedtuple import subprocess FuncImpl = namedtuple("FuncImpl", ("func", "impl")) STD = {} DEBUG = True USER = {} def _get_debug(): return DEBUG def std_exit(status=None): out = 0 if status is None else status.value sys.exit(out) return NebBool(True) # this should never be reached def std_print(arg): print(arg.value) #return [] # TODO this should return empty list return NebBool(True) def std_print_all(arg): for idx, item in enumerate(arg.items): if isinstance(item, NebExpression): arg.items[idx] = evaluate_expression(item) elif not isinstance(item, NebString): raise exception("print-all needs all strings!") for x in arg.items: std_print(x) return NebBool(True) def std_debug_on(): global DEBUG DEBUG = True return NebBool(True) def std_debug_off(): global DEBUG DEBUG = False return NebBool(True) # math def std_add(arg1, arg2): res = arg1.value + arg2.value if isinstance(arg1, NebFloat) or isinstance(arg2, NebFloat): return NebFloat(res) else: return NebInt(res) def std_subtract(arg1, arg2): res = arg1.value - arg2.value if isinstance(arg1, NebFloat) or isinstance(arg2, NebFloat): return NebFloat(res) else: return NebInt(res) def std_multiply(arg1, arg2): res = arg1.value * arg2.value if isinstance(arg1, NebFloat) or isinstance(arg2, NebFloat): return NebFloat(res) else: return NebInt(res) # strings def std_concat(arg1, arg2): return NebString(f"{arg1.value}{arg2.value}") # flow control def std_if(cond, t_branch, f_branch=None): if cond.value: if isinstance(t_branch, NebExpression): return evaluate_expression(t_branch) else: return t_branch elif f_branch is not None: if isinstance(f_branch, NebExpression): return evaluate_expression(f_branch) else: return f_branch return NebBool(True) # type validation def std_is_string(arg): if isinstance(arg, NebString): return NebBool(True) else: return NebBool(False) def std_is_int(arg): if isinstance(arg, NebInt): return NebBool(True) else: return NebBool(False) def std_is_float(arg): if isinstance(arg, NebFloat): return NebBool(True) else: return NebBool(False) def std_is_number(arg): if isinstance(arg, NebNumber): return NebBool(True) else: return NebBool(False) def std_is_bool(arg): if isinstance(arg, NebBool): return NebBool(True) else: return NebBool(False) # type conversion def std_literal_to_string(arg): if isinstance(arg, NebString): return arg else: return NebString(f"{arg.value}".lower()) # shell def std_shell(arg): assert isinstance(arg, NebString) lst = arg.value.split(" ") ret = subprocess.run(lst, capture_output=True) stdout_as_list = ret.stdout.decode("utf-8").split("\n")[:-1] return NebList([ret.returncode] + stdout_as_list) def std_shell_pipe(args): prev = None stdout = subprocess.PIPE for idx, arg in enumerate(args.items): if idx == len(args.items) - 1: stdout = None lst = arg.value.split(" ") if prev is None: if stdout is None: ret = subprocess.run(lst, capture_output=True) else: ret = subprocess.run(lst, stdout=stdout) else: if stdout is None: ret = subprocess.run(lst, input=prev.stdout, capture_output=True) else: ret = subprocess.run(lst, stdout=stdout, input=prev.stdout) # TODO should we bail here if the return code is non-zero? prev = ret stdout_as_list = prev.stdout.decode("utf-8").split("\n")[:-1] return NebList([prev.returncode] + stdout_as_list) def evaluate_expression(expr): if not expr.symbol.name in STD: raise Exception(f"no such symbol: {expr.symbol.name}") this_func = STD[expr.symbol.name] # try to match the signature validated = 0 in_sig = expr.args for value in this_func: sig = value.func.args validated = 0 if len(sig) != len(in_sig): continue for idx in range(len(sig)): if isinstance(in_sig[idx], sig[idx]): validated += 1 if validated == len(sig): ret = value.impl(*(expr.args)) return ret # evaluate inner expressions, if possible/necessary for idx, arg in enumerate(expr.args): if isinstance(arg, NebExpression): expr.args[idx] = evaluate_expression(arg) return evaluate_expression(expr) raise Exception(f"'{expr.symbol.name}' called with unknown signature: '{expr.in_sig()}'") def build_std(): print_string = FuncImpl(NebFunction("print", [NebString], NebString), std_print) STD["print"] = [print_string] print_all = FuncImpl(NebFunction("print-all", [NebList], NebBool), std_print_all) STD["print-all"] = [print_all] exit_ = FuncImpl(NebFunction("exit", [], NebBool), std_exit) exit_int = FuncImpl(NebFunction("exit", [NebInt], NebBool), std_exit) STD["exit"] = [exit_, exit_int] debug_on = FuncImpl(NebFunction("debug-on", [], NebBool), std_debug_on) STD["debug-on"] = [debug_on] debug_off = FuncImpl(NebFunction("debug-off", [], NebBool), std_debug_off) STD["debug-off"] = [debug_off] # arithmetic add = FuncImpl(NebFunction("+", [NebNumber, NebNumber], NebNumber), std_add) STD["+"] = [add] subtract = FuncImpl(NebFunction("-", [NebNumber, NebNumber], NebNumber), std_subtract) STD["-"] = [subtract] multiply = FuncImpl(NebFunction("*", [NebNumber, NebNumber], NebNumber), std_multiply) STD["*"] = [multiply] # strings concat_string_string = FuncImpl(NebFunction("concat", [NebString, NebString], NebString), std_concat) STD["concat"] = [concat_string_string] # flow control #if_bool_any_any = FuncImpl(NebFunction("if", [NebBool, NebAny, NebAny], NebAny), std_if) #if_bool_any = FuncImpl(NebFunction("if", [NebBool, NebAny], NebAny), std_if) if_bool_expr_expr = FuncImpl(NebFunction("if", [NebBool, NebExpression, NebExpression], NebAny), std_if) if_bool_expr_any = FuncImpl(NebFunction("if", [NebBool, NebExpression, NebAny], NebAny), std_if) if_bool_any_expr = FuncImpl(NebFunction("if", [NebBool, NebAny, NebExpression], NebAny), std_if) if_bool_any_any = FuncImpl(NebFunction("if", [NebBool, NebAny, NebAny], NebAny), std_if) if_bool_expr = FuncImpl(NebFunction("if", [NebBool, NebExpression], NebAny), std_if) if_bool_any = FuncImpl(NebFunction("if", [NebBool, NebAny], NebAny), std_if) STD["if"] = [ if_bool_expr_expr, if_bool_expr_any, if_bool_any_expr, if_bool_any_any, if_bool_expr, if_bool_any] # type checking is_string = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_string) STD["string?"] = [is_string] is_int = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_int) STD["int?"] = [is_int] is_float = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_float) STD["float?"] = [is_float] is_number = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_number) STD["number?"] = [is_number] is_bool = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_bool) STD["bool?"] = [is_bool] # type conversion int_to_string = FuncImpl(NebFunction("int->string", [NebInt], NebString), std_literal_to_string) STD["int->string"] = [int_to_string] float_to_string = FuncImpl(NebFunction("float->string", [NebFloat], NebString), std_literal_to_string) STD["float->string"] = [float_to_string] number_to_string = FuncImpl(NebFunction("number->string", [NebNumber], NebString), std_literal_to_string) STD["number->string"] = [number_to_string] bool_to_string = FuncImpl(NebFunction("bool->string", [NebBool], NebString), std_literal_to_string) STD["bool->string"] = [bool_to_string] to_string = FuncImpl(NebFunction("->string", [NebLiteral], NebString), std_literal_to_string) STD["->string"] = [to_string] # shell shell_string = FuncImpl(NebFunction("$", [NebString], NebList), std_shell) STD["$"] = [shell_string] shell_pipe = FuncImpl(NebFunction("$|", [NebList], NebList), std_shell_pipe) STD["$|"] = [shell_pipe] build_std()