diff options
| author | mryouse | 2022-06-10 04:02:10 +0000 |
|---|---|---|
| committer | mryouse | 2022-06-10 04:04:06 +0000 |
| commit | 05f68ac90f83df0c7d1c994cb11cd6e69ab7611f (patch) | |
| tree | c94b536e1aa08b8d2e6e5169b0e3d2297310a4c6 /interpreter.py | |
| parent | 2821c14272c4296a64d94532fa8665ed53f5a0ef (diff) | |
initial commit of more thorough type checking
Diffstat (limited to 'interpreter.py')
| -rw-r--r-- | interpreter.py | 234 |
1 files changed, 154 insertions, 80 deletions
diff --git a/interpreter.py b/interpreter.py index 0136a04..84fae67 100644 --- a/interpreter.py +++ b/interpreter.py @@ -2,59 +2,95 @@ from structs import * from exceptions import * from lexer import lex from parser import parse +from structs import T from pathlib import Path from glob import glob +from collections import namedtuple +from dataclasses import dataclass import subprocess import shlex import random import sys + +@dataclass +class Arg: + name: str + type_: T + optional: bool + lazy: bool + + class Function: - def __init__(self, name, params, body, min_arity=0, max_arity=-1): + def __init__(self, name, params, body, args=None, many=None): self.name = name self.params = params self.body = body - self.min_arity = min_arity - self.max_arity = max_arity - - def arity_check(self, symbol, args): - if len(args) < self.min_arity or (self.max_arity >= 0 and len(args) > self.max_arity): - if self.max_arity < 0: - fmt = f"{self.min_arity}+" - elif self.min_arity != self.max_arity: - fmt = f"{self.min_arity}-{self.max_arity}" + self.args = args + self.many = many + + def arity_check(self, symbol, params): + min_arity = len([a for a in self.args if not a.optional]) + max_arity = -1 if self.many is not None else len(self.args) + + if len(params) < min_arity or (max_arity >= 0 and len(params) > max_arity): + if max_arity < 0: + fmt = f"{min_arity}+" + elif min_arity != max_arity: + fmt = f"{min_arity}-{max_arity}" else: - fmt = f"{self.min_arity}" - #if self.arities is not None and len(args) not in self.arities: - #fmt = ", ".join([f"{arity}" for arity in self.arities]) - raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(args)}") + fmt = f"{min_arity}" + raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(params)}") return True + def evaluate_args(self, symbol, params, env): + self.arity_check(symbol, params) + ret = [] + + for idx, param in enumerate(params): + if idx < len(self.args): + arg = self.args[idx] + else: + arg = self.many + if arg.lazy: + ret.append(param) + continue + ev = evaluate(param, env) + if not isinstance(ev, arg.type_): + exp = f":{arg.type_.__name__.lower()}" + rec = f":{ev.type_.__name__.lower()}" + raise InterpretPanic(symbol, f"received {rec}, expected {exp}", ev) + ret.append(ev) + return ret + def call(self, expr, env): pass class Builtin(Function): - def __init__(self, callable_, *arities): - super().__init__("<builtin>", None, callable_, *arities) + def __init__(self, callable_, args=None, many=None): + super().__init__("<builtin>", None, callable_, args, many) def call(self, expr, env): self.arity_check(expr.args[0], expr.args[1:]) - return self.body(expr.args[0], expr.args[1:], env) + evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env) + return self.body(expr.args[0], evaluated_args, env) + class UserFunction(Function): def __init__(self, name, params, body): - super().__init__(name, params, body, len(params)) + # TODO this doesn't do type checking, or optional, or lazy + args = [Arg("arg", T.Any, False, False)] * len(params) + super().__init__(name, params, body, args) def call(self, expr, env): self.arity_check(expr.args[0], expr.args[1:]) + evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env) this_env = Environment(env) for idx, param in enumerate(self.params): - # TODO this is wrong!!! this won't always be a literal - #this_env.register(param.name, Literal(evaluate(expr.args[idx+1],env))) - this_env.register(param.name, evaluate(expr.args[idx+1],env)) + this_env.register(param.name, evaluated_args[idx]) return interpret(self.body, this_env) class Environment: @@ -140,7 +176,9 @@ def interpretOr(symbol, args, env): return ev return Bool(False) -GLOBALS.register("or", Builtin(interpretOr, 2)) +#GLOBALS.register("or", Builtin(interpretOr, 2)) +or_arg = Arg("arg", T.Bool, False, True) +GLOBALS.register("or", Builtin(interpretOr, [or_arg, or_arg], or_arg)) def interpretAnd(symbol, args, env): # and returns false for the first expression that returns false @@ -152,7 +190,7 @@ def interpretAnd(symbol, args, env): return ev return Bool(True) -GLOBALS.register("and", Builtin(interpretAnd, 2)) +GLOBALS.register("and", Builtin(interpretAnd, [or_arg, or_arg], or_arg)) def interpretEq(symbol, args, env): # equal @@ -168,7 +206,8 @@ def interpretEq(symbol, args, env): else: return Bool(False) -GLOBALS.register("eq?", Builtin(interpretEq, 2, 2)) +eq_arg = Arg("value", T.Literal, False, False) +GLOBALS.register("eq?", Builtin(interpretEq, [eq_arg, eq_arg])) def interpretGreaterThan(symbol, args, env): left = evaluate(args[0], env) @@ -179,7 +218,8 @@ def interpretGreaterThan(symbol, args, env): raise InterpretPanic(symbol, "second argument must be a :number", right) return Bool(left.value > right.value) -GLOBALS.register(">", Builtin(interpretGreaterThan, 2, 2)) +compare_arg = Arg("num", T.Number, False, False) +GLOBALS.register(">", Builtin(interpretGreaterThan, [compare_arg, compare_arg])) def interpretGreaterThanEqual(symbol, args, env): left = evaluate(args[0], env) @@ -190,7 +230,7 @@ def interpretGreaterThanEqual(symbol, args, env): raise InterpretPanic(symbol, "second argument must be a :number", right) return Bool(left.value >= right.value) -GLOBALS.register(">=", Builtin(interpretGreaterThanEqual, 2, 2)) +GLOBALS.register(">=", Builtin(interpretGreaterThanEqual, [compare_arg, compare_arg])) def interpretLessThan(symbol, args, env): left = evaluate(args[0], env) @@ -201,7 +241,7 @@ def interpretLessThan(symbol, args, env): raise InterpretPanic(symbol, "second argument must be a :number", right) return Bool(left.value < right.value) -GLOBALS.register("<", Builtin(interpretLessThan, 2, 2)) +GLOBALS.register("<", Builtin(interpretLessThan, [compare_arg, compare_arg])) def interpretLessThanEqual(symbol, args, env): left = evaluate(args[0], env) @@ -212,7 +252,7 @@ def interpretLessThanEqual(symbol, args, env): raise InterpretPanic(symbol, "second argument must be a :number", right) return Bool(left.value <= right.value) -GLOBALS.register("<=", Builtin(interpretLessThanEqual, 2, 2)) +GLOBALS.register("<=", Builtin(interpretLessThanEqual, [compare_arg, compare_arg])) def interpretAddition(symbol, args, env): res = 0 @@ -226,7 +266,8 @@ def interpretAddition(symbol, args, env): else: return Int(res) -GLOBALS.register("+", Builtin(interpretAddition, 1)) +term_arg = Arg("term", T.Number, False, False) +GLOBALS.register("+", Builtin(interpretAddition, [term_arg], term_arg)) def interpretSubtraction(symbol, args, env): first = evaluate(args[0], env) @@ -246,7 +287,7 @@ def interpretSubtraction(symbol, args, env): else: return Int(res) -GLOBALS.register("-", Builtin(interpretSubtraction, 1)) +GLOBALS.register("-", Builtin(interpretSubtraction, [term_arg], term_arg)) def interpretMultiplication(symbol, args, env): first = evaluate(args[0], env) @@ -263,7 +304,8 @@ def interpretMultiplication(symbol, args, env): else: return Int(res) -GLOBALS.register("*", Builtin(interpretMultiplication, 2)) +factor_arg = Arg("factor", T.Number, False, False) +GLOBALS.register("*", Builtin(interpretMultiplication, [factor_arg, factor_arg], factor_arg)) def interpretDivision(symbol, args, env): num = evaluate(args[0], env) @@ -278,7 +320,7 @@ def interpretDivision(symbol, args, env): else: return Float(ret) -GLOBALS.register("/", Builtin(interpretDivision, 2, 2)) +GLOBALS.register("/", Builtin(interpretDivision, [factor_arg, factor_arg])) def interpretNot(symbol, args, env): res = evaluate(args[0], env) @@ -286,7 +328,8 @@ def interpretNot(symbol, args, env): raise InterpretPanic(symbol, "requires a :bool", res) return Bool(not res.value) -GLOBALS.register("not", Builtin(interpretNot, 1, 1)) +not_arg = Arg("not", T.Bool, False, False) +GLOBALS.register("not", Builtin(interpretNot, [not_arg])) def interpretIf(symbol, args, env): # if cond t-branch [f-branch] @@ -299,7 +342,10 @@ def interpretIf(symbol, args, env): return evaluate(args[2], env) return List([]) # this shouldn't be reached -GLOBALS.register("if", Builtin(interpretIf, 2, 3)) +cond = Arg("cond", T.Bool, False, False) +t_branch = Arg("t-branch", T.Any, False, True) +f_branch = Arg("f-branch", T.Any, True, True) +GLOBALS.register("if", Builtin(interpretIf, [cond, t_branch, f_branch])) def interpretPrint(symbol, args, env): ev = evaluate(args[0], env) @@ -309,7 +355,7 @@ def interpretPrint(symbol, args, env): return List([]) # print returns nothing -GLOBALS.register("print", Builtin(interpretPrint, 1, 1)) +GLOBALS.register("print", Builtin(interpretPrint, [Arg("arg", T.String, False, False)])) def interpretDef(symbol, args, env): if not isinstance(args[0], Symbol): @@ -322,7 +368,9 @@ def interpretDef(symbol, args, env): env.register(name, ev) return List([]) -GLOBALS.register("def", Builtin(interpretDef, 2, 2)) +def_name_arg = Arg("name", T.Any, False, True) +def_val_arg = Arg("value", T.Any, False, False) +GLOBALS.register("def", Builtin(interpretDef, [def_name_arg, def_val_arg])) def interpretRedef(symbol, args, env): if not isinstance(args[0], Symbol): @@ -335,7 +383,7 @@ def interpretRedef(symbol, args, env): env.reregister(name, ev) return List([]) -GLOBALS.register("redef", Builtin(interpretRedef, 2, 2)) +GLOBALS.register("redef", Builtin(interpretRedef, [def_name_arg, def_val_arg])) def interpretLambda(symbol, args, env): if len(args[0].args) != 0: @@ -344,7 +392,9 @@ def interpretLambda(symbol, args, env): func = UserFunction("<lambda>", [], args[1:]) return func -GLOBALS.register("lambda", Builtin(interpretLambda)) +lambda_args_arg = Arg("args", T.Any, False, True) +lambda_body_arg = Arg("body", T.Any, False, True) +GLOBALS.register("lambda", Builtin(interpretLambda, [lambda_args_arg, lambda_body_arg], lambda_body_arg)) def interpretToString(symbol, args, env): ev = evaluate(args[0], env) @@ -355,7 +405,7 @@ def interpretToString(symbol, args, env): else: return String(f"{ev}") -GLOBALS.register("->string", Builtin(interpretToString, 1, 1)) +GLOBALS.register("->string", Builtin(interpretToString, [Arg("arg", T.Any, False, False)])) def interpretConcat(symbol, args, env): # concat str1 str2...strN @@ -367,7 +417,8 @@ def interpretConcat(symbol, args, env): out += tmp.value return String(out) -GLOBALS.register("concat", Builtin(interpretConcat, 2)) +string_arg = Arg("arg", T.String, False, False) +GLOBALS.register("concat", Builtin(interpretConcat, [string_arg, string_arg], string_arg)) def interpretForCount(symbol, args, env): # for-count int exprs @@ -384,7 +435,9 @@ def interpretForCount(symbol, args, env): return List([]) return ret -GLOBALS.register("for-count", Builtin(interpretForCount, 2)) +for_count_arg = Arg("count", T.Int, False, False) +for_body_arg = Arg("body", T.Any, False, True) +GLOBALS.register("for-count", Builtin(interpretForCount, [for_count_arg, for_body_arg], for_body_arg)) def interpretForEach(symbol, args, env): # for-each list exprs @@ -401,7 +454,8 @@ def interpretForEach(symbol, args, env): return List([]) return ret -GLOBALS.register("for-each", Builtin(interpretForEach, 2)) +for_each_arg = Arg("list", T.List, False, False) +GLOBALS.register("for-each", Builtin(interpretForEach, [for_each_arg, for_body_arg], for_body_arg)) def interpretPipe(symbol, args, env): new_env = Environment(env) @@ -414,6 +468,7 @@ def interpretPipe(symbol, args, env): return List([]) return pipe +# TODO GLOBALS.register("|", Builtin(interpretPipe, 2)) def interpretBranch(symbol, args, env): @@ -427,10 +482,18 @@ def interpretBranch(symbol, args, env): return evaluate(arg.args[1], env) return List([]) +# TODO GLOBALS.register("branch", Builtin(interpretBranch, 1)) def interpretFunc(symbol, args, env): # func <name> (args) (exprs) + + # maybe: + # arg [:type] -> type is optional + # ?arg default -> 'arg' is optional, defaulted + # *arg [:type] -> 'arg' is a list containing the remaining args + + if not isinstance(args[0], Symbol): raise InterpretPanic(symbol, "requires a :string name") name = args[0].name # NOTE: we are not evaluating the name!! @@ -441,7 +504,7 @@ def interpretFunc(symbol, args, env): env.register(name, func) return List([]) -GLOBALS.register("func", Builtin(interpretFunc, 3)) +GLOBALS.register("func", Builtin(interpretFunc, [def_name_arg, lambda_args_arg, lambda_body_arg], lambda_body_arg)) # THINGS NEEDED FOR AOC # - read the contents of a file @@ -455,7 +518,7 @@ def interpretReadLines(symbol, args, env): out = List([String(d) for d in data], True) # all lines are strings return out -GLOBALS.register("read-lines", Builtin(interpretReadLines, 1, 1)) +GLOBALS.register("read-lines", Builtin(interpretReadLines, [Arg("filename", T.String, False, False)])) # - strip whitespace from string def interpretStrip(symbol, args, env): @@ -464,7 +527,7 @@ def interpretStrip(symbol, args, env): raise InterpretPanic(symbol, "requires a :string", out) return String(out.value.strip()) -GLOBALS.register("strip", Builtin(interpretStrip, 1, 1)) +GLOBALS.register("strip", Builtin(interpretStrip, [Arg("filename", T.String, False, False)])) # - string->int and string->float def interpretStringToInt(symbol, args, env): @@ -477,7 +540,7 @@ def interpretStringToInt(symbol, args, env): except: raise InterpretPanic(symbol, "can't convert to an :int", ev) -GLOBALS.register("string->int", Builtin(interpretStringToInt, 1, 1)) +GLOBALS.register("string->int", Builtin(interpretStringToInt, [Arg("arg", T.String, False, False)])) # - split a string by a given field def interpretSplit(symbol, args, env): @@ -492,7 +555,7 @@ def interpretSplit(symbol, args, env): ret = target.value.split(splitter.value) return List([String(r) for r in ret], True) -GLOBALS.register("split", Builtin(interpretSplit, 1, 2)) +GLOBALS.register("split", Builtin(interpretSplit, [Arg("target", T.String, False, False)], Arg("splitter", T.String, True, False))) # - get the length of a list def interpretListLength(symbol, args, env): @@ -501,7 +564,7 @@ def interpretListLength(symbol, args, env): raise InterpretPanic(symbol, "requires a :list", ev) return Int(len(ev.args)) -GLOBALS.register("list-length", Builtin(interpretListLength, 1, 1)) +GLOBALS.register("list-length", Builtin(interpretListLength, [Arg("arg", T.List, False, False)])) # - first/rest of list def interpretFirst(symbol, args, env): @@ -512,7 +575,7 @@ def interpretFirst(symbol, args, env): raise InterpretPanic(symbol, "list is empty") return evaluate(ev.args[0], env) -GLOBALS.register("first", Builtin(interpretFirst, 1, 1)) +GLOBALS.register("first", Builtin(interpretFirst, [Arg("arg", T.List, False, False)])) def interpretRest(symbol, args, env): ev = evaluate(args[0], env) @@ -521,7 +584,7 @@ def interpretRest(symbol, args, env): # TODO do we know it's not evaluated? return List(ev.args[1:], True) # we don't evaluate the remainder of the list -GLOBALS.register("rest", Builtin(interpretRest, 1, 1)) +GLOBALS.register("rest", Builtin(interpretRest, [Arg("arg", T.List, False, False)])) # - iterate over list # - map @@ -539,7 +602,7 @@ def interpretMap(symbol, args, env): out.append(ev) return List(out, True) -GLOBALS.register("map", Builtin(interpretMap, 2, 2)) +GLOBALS.register("map", Builtin(interpretMap, [Arg("func", T.Any, False, True), Arg("list", T.List, False, False)])) def interpretZip(symbol, args, env): z1 = evaluate(args[0], env) @@ -557,7 +620,8 @@ def interpretZip(symbol, args, env): out.append(List([f, s], True)) return List(out, True) -GLOBALS.register("zip", Builtin(interpretZip, 2, 2)) +zip_arg = Arg("list", T.List, False, False) +GLOBALS.register("zip", Builtin(interpretZip, [zip_arg, zip_arg])) def interpretList(symbol, args, env): out = [] @@ -565,7 +629,7 @@ def interpretList(symbol, args, env): out.append(evaluate(arg, env)) return List(out, True) -GLOBALS.register("list", Builtin(interpretList, 0)) +GLOBALS.register("list", Builtin(interpretList, [], Arg("item", T.Any, False, False))) def interpretListReverse(symbol, args, env): lst = evaluate(args[0], env) @@ -575,7 +639,7 @@ def interpretListReverse(symbol, args, env): new_args.reverse() return List(new_args, True) -GLOBALS.register("list-reverse", Builtin(interpretListReverse, 1, 1)) +GLOBALS.register("list-reverse", Builtin(interpretListReverse, [Arg("list", T.List, False, False)])) def interpretApply(symbol, args, env): func = args[0] @@ -587,7 +651,7 @@ def interpretApply(symbol, args, env): new_lst = List([func] + lst.args) return evaluate(new_lst, env) -GLOBALS.register("apply", Builtin(interpretApply, 2, 2)) +GLOBALS.register("apply", Builtin(interpretApply, [Arg("func", T.Any, False, True), Arg("list", T.List, False, False)])) def interpretGlob(symbol, args, env): ev = evaluate(args[0], env) @@ -596,7 +660,7 @@ def interpretGlob(symbol, args, env): items = glob(ev.value) return List([String(item) for item in items], True) -GLOBALS.register("glob", Builtin(interpretGlob, 1, 1)) +GLOBALS.register("glob", Builtin(interpretGlob, [Arg("regex", T.String, False, False)])) def interpretShell(symbol, args, env): ev = evaluate(args[0], env) @@ -606,7 +670,7 @@ def interpretShell(symbol, args, env): ret = subprocess.run(shlex.split(ev.value), capture_output=True) return List([String(r) for r in ret.stdout.decode("utf-8").split("\n")], True) -GLOBALS.register("$", Builtin(interpretShell, 1, 1)) +GLOBALS.register("$", Builtin(interpretShell, [Arg("command", T.String, False, False)])) def interpretEmpty(symbol, args, env): ev = evaluate(args[0], env) @@ -614,7 +678,7 @@ def interpretEmpty(symbol, args, env): raise InterpretPanic(symbol, "requires a :list", ev) return Bool(len(ev.args) == 0) -GLOBALS.register("empty?", Builtin(interpretEmpty, 1, 1)) +GLOBALS.register("empty?", Builtin(interpretEmpty, [Arg("list", T.List, False, False)])) def interpretShuf(symbol, args, env): ev = evaluate(args[0], env) @@ -624,13 +688,13 @@ def interpretShuf(symbol, args, env): random.shuffle(items) return List(items, True) -GLOBALS.register("shuf", Builtin(interpretShuf, 1, 1)) +GLOBALS.register("shuf", Builtin(interpretShuf, [Arg("list", T.List, False, False)])) def interpretIsList(symbol, args, env): ev = evaluate(args[0], env) return Bool(isinstance(ev, List)) -GLOBALS.register("list?", Builtin(interpretIsList, 1, 1)) +GLOBALS.register("list?", Builtin(interpretIsList, [Arg("arg", T.Any, False, False)])) def interpretBlock(symbol, args, env): ret = List([]) @@ -638,7 +702,8 @@ def interpretBlock(symbol, args, env): ret = evaluate(arg, env) return ret -GLOBALS.register("block", Builtin(interpretBlock, 1)) +block_arg = Arg("expr", T.Any, False, True) +GLOBALS.register("block", Builtin(interpretBlock, [block_arg], block_arg)) def interpretExit(symbol, args, env): status = 0 if len(args) == 0 else evaluate(args[0], env).value @@ -647,7 +712,8 @@ def interpretExit(symbol, args, env): sys.exit(status) return List([]) -GLOBALS.register("exit", Builtin(interpretExit, 0, 1)) +exit_arg = Arg("status", T.Int, True, False) +GLOBALS.register("exit", Builtin(interpretExit, [exit_arg])) def interpretUnlink(symbol, args, env): ev = evaluate(args[0], env) @@ -659,7 +725,7 @@ def interpretUnlink(symbol, args, env): target_path.unlink() return List([]) -GLOBALS.register("unlink", Builtin(interpretUnlink, 1, 1)) +GLOBALS.register("unlink", Builtin(interpretUnlink, [Arg("filename", T.String, False, False)])) def interpretArgv(symbol, args, env): out = [] @@ -667,7 +733,7 @@ def interpretArgv(symbol, args, env): out.append(String(arg)) return List(out, True) -GLOBALS.register("argv", Builtin(interpretArgv, 0, 0)) +GLOBALS.register("argv", Builtin(interpretArgv, [])) def interpretIn(symbol, args, env): target = evaluate(args[0], env) @@ -682,7 +748,9 @@ def interpretIn(symbol, args, env): return Bool(True) return Bool(False) -GLOBALS.register("in?", Builtin(interpretIn, 2, 2)) +in_target_arg = Arg("target", T.Literal, False, False) +in_list_arg = Arg("list", T.List, False, False) +GLOBALS.register("in?", Builtin(interpretIn, [in_target_arg, in_list_arg])) def interpretLast(symbol, args, env): ev = evaluate(args[0], env) @@ -692,7 +760,7 @@ def interpretLast(symbol, args, env): raise InterpretPanic("List is empty") return evaluate(ev.args[-1], env) -GLOBALS.register("last", Builtin(interpretLast, 1, 1)) +GLOBALS.register("last", Builtin(interpretLast, [Arg("list", T.List, False, False)])) def interpretJoin(symbol, args, env): lst = evaluate(args[0], env) @@ -703,7 +771,9 @@ def interpretJoin(symbol, args, env): raise InterpretPanic(symbol, "expects a :string as its second argument", target) return String(target.value.join([a.value for a in lst.args])) -GLOBALS.register("join", Builtin(interpretJoin, 2, 2)) +join_list_arg = Arg("list", T.List, False, False) +join_string_arg = Arg("joiner", T.String, False, False) +GLOBALS.register("join", Builtin(interpretJoin, [join_list_arg, join_string_arg])) def interpretWithWrite(symbol, args, env): if len(args) == 0: @@ -720,7 +790,7 @@ def interpretWithWrite(symbol, args, env): ret = evaluate(arg, new_env) return ret -GLOBALS.register("with-write", Builtin(interpretWithWrite, 1)) +GLOBALS.register("with-write", Builtin(interpretWithWrite, [Arg("filename", T.String, False, False)])) def interpretWrite(symbol, args, env): # write :string :filehandle @@ -731,12 +801,12 @@ def interpretWrite(symbol, args, env): handle.args[0].write(line.value) # TODO wrong! how do we evaluate a handle? return Literal([]) -GLOBALS.register("write", Builtin(interpretWrite, 2, 2)) +GLOBALS.register("write", Builtin(interpretWrite, [Arg("string", T.String, False, False), Arg("filename", T.List, False, False)])) def interpretNewline(symbol, args, env): return String("\n") -GLOBALS.register("newline", Builtin(interpretNewline, 0, 0)) +GLOBALS.register("newline", Builtin(interpretNewline, [])) def interpretExists(symbol, args, env): file_or_dir = evaluate(args[0], env) @@ -744,7 +814,7 @@ def interpretExists(symbol, args, env): raise InterpretPanic(symbol, "expects a :string", file_or_dir) return Bool(Path(file_or_dir.value).resolve().exists()) -GLOBALS.register("exists?", Builtin(interpretExists, 1, 1)) +GLOBALS.register("exists?", Builtin(interpretExists, [Arg("filename", T.String, False, False)])) def interpretFirstChar(symbol, args, env): ev = evaluate(args[0], env) @@ -754,7 +824,7 @@ def interpretFirstChar(symbol, args, env): raise InterpretPanic(symbol, ":string is empty", ev) return String(ev.value[0]) -GLOBALS.register("first-char", Builtin(interpretFirstChar, 1, 1)) +GLOBALS.register("first-char", Builtin(interpretFirstChar, [Arg("string", T.String, False, False)])) def interpretRestChar(symbol, args, env): ev = evaluate(args[0], env) @@ -762,7 +832,7 @@ def interpretRestChar(symbol, args, env): raise InterpretPanic(symbol, "expects a string", ev) return String(ev.value[1:]) -GLOBALS.register("rest-char", Builtin(interpretRestChar, 1, 1)) +GLOBALS.register("rest-char", Builtin(interpretRestChar, [Arg("string", T.String, False, False)])) def interpretSlice(symbol, args, env): lst = evaluate(args[0], env) @@ -779,13 +849,16 @@ def interpretSlice(symbol, args, env): diff = idx.value - 1 + length.value return List(lst.args[idx.value - 1:diff]) -GLOBALS.register("slice", Builtin(interpretSlice, 2, 3)) +slice_list_arg = Arg("list", T.List, False, False) +slice_idx_arg = Arg("idx", T.Int, False, False) +slice_length_arg = Arg("length", T.Int, True, False) +GLOBALS.register("slice", Builtin(interpretSlice, [slice_list_arg, slice_idx_arg, slice_length_arg])) def interpretClear(symbol, args, env): subprocess.run(["clear"]) return List([]) -GLOBALS.register("clear", Builtin(interpretClear, 0, 0)) +GLOBALS.register("clear", Builtin(interpretClear, [])) def interpretInput(symbol, args, env): ev = evaluate(args[0], env) @@ -794,7 +867,7 @@ def interpretInput(symbol, args, env): ret = input(ev.value) return String(ret) -GLOBALS.register("input", Builtin(interpretInput, 1, 1)) +GLOBALS.register("input", Builtin(interpretInput, [Arg("prompt", T.String, False, False)])) def interpretAppend(symbol, args, env): lst = evaluate(args[0], env) @@ -804,7 +877,7 @@ def interpretAppend(symbol, args, env): items = lst.args[:] return List(items + [val], True) -GLOBALS.register("append", Builtin(interpretAppend, 2, 2)) +GLOBALS.register("append", Builtin(interpretAppend, [Arg("list", T.List, False, False), Arg("item", T.Any, False, False)])) def interpretRemove(symbol, args, env): lst = evaluate(args[0], env) @@ -817,6 +890,7 @@ def interpretRemove(symbol, args, env): out.append(arg) return List(out, True) +# TODO GLOBALS.register("remove", Builtin(interpretRemove, 2, 2)) def interpretWhile(symbol, args, env): @@ -832,12 +906,12 @@ def interpretWhile(symbol, args, env): ret = evaluate(arg, env) return ret -GLOBALS.register("while", Builtin(interpretWhile, 2)) +GLOBALS.register("while", Builtin(interpretWhile, [Arg("cond", T.Bool, False, True)], Arg("expr", T.Any, False, True))) def interpretAnsiEscape(symbol, args, env): return String(f"\033") -GLOBALS.register("ansi-escape", Builtin(interpretAnsiEscape, 0, 0)) +GLOBALS.register("ansi-escape", Builtin(interpretAnsiEscape, [])) def interpretUse(symbol, args, env): target_file_name = evaluate(args[0], env).value @@ -849,5 +923,5 @@ def interpretUse(symbol, args, env): interpret(parse(lex(data))) return List([]) -GLOBALS.register("use", Builtin(interpretUse, 1, 1)) +GLOBALS.register("use", Builtin(interpretUse, [Arg("filename", T.String, False, False)])) |
