aboutsummaryrefslogtreecommitdiff
path: root/interpreter.py
diff options
context:
space:
mode:
authormryouse2022-06-10 04:02:10 +0000
committermryouse2022-06-10 04:04:06 +0000
commit05f68ac90f83df0c7d1c994cb11cd6e69ab7611f (patch)
treec94b536e1aa08b8d2e6e5169b0e3d2297310a4c6 /interpreter.py
parent2821c14272c4296a64d94532fa8665ed53f5a0ef (diff)
initial commit of more thorough type checking
Diffstat (limited to 'interpreter.py')
-rw-r--r--interpreter.py234
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)]))