aboutsummaryrefslogtreecommitdiff
path: root/neb
diff options
context:
space:
mode:
authormryouse2022-07-26 01:03:57 +0000
committermryouse2022-07-26 01:03:57 +0000
commit0de12784cfc54130e65812e64cbd9e975ceab9ff (patch)
tree3142eb24095edcae0a2270f34a4fbdb83184741a /neb
parent80257143d7c33ed218ecb8ce916ea710b6b1f6fa (diff)
parse single quotes, as well as defs and funcs
Diffstat (limited to 'neb')
-rw-r--r--neb/__init__.py52
-rw-r--r--neb/parser.py105
-rw-r--r--neb/structs.py21
3 files changed, 163 insertions, 15 deletions
diff --git a/neb/__init__.py b/neb/__init__.py
index a2d6541..f1f75d3 100644
--- a/neb/__init__.py
+++ b/neb/__init__.py
@@ -11,6 +11,29 @@ def interpret(exprs, env, ns=None):
ret = evaluate(expr, env, ns)
return ret
+def syntaxDef(symbol, arg, env, ns):
+ res = evaluate(arg, env, ns)
+ env.register(symbol.name, res)
+ return List([])
+
+def syntaxFunc(symbol, env, ns):
+ func = UserFunction(symbol.name, symbol.args, symbol.many, symbol.body)
+ func.return_type = symbol.return_type
+ if symbol.name != "<lambda>":
+ # register into the environment
+ if env.contains(symbol.name):
+ env_func = env.get(symbol.name)
+ if isinstance(env_func, MultiFunction):
+ env_func.register(func)
+ env.register(symbol.name, env_func)
+ else:
+ mf = MultiFunction(symbol.name)
+ mf.register(func)
+ env.register(symbol.name, mf)
+ return List([])
+ else:
+ return func
+
def evaluate(expr, env, ns=None):
if isinstance(expr, Literal) or isinstance(expr, Callable) or isinstance(expr, TypeWrap) or isinstance(expr, List) or isinstance(expr, Handle):
return expr
@@ -50,6 +73,12 @@ def evaluate(expr, env, ns=None):
elif isinstance(expr.args[0], Callable):
return expr.args[0].call(expr, env, ns)
+ elif isinstance(expr.args[0], NebDef):
+ return syntaxDef(expr.args[0], expr.args[1], env, ns)
+
+ elif isinstance(expr.args[0], NebFuncDef):
+ return syntaxFunc(expr.args[0], env, ns)
+
elif not isinstance(expr.args[0], Symbol):
raise NebPanic("can't evaluate without a symbol")
name = expr.args[0].name
@@ -194,18 +223,12 @@ class MultiFunction(Callable):
symbol = expr.args[0]
params = expr.args[1:]
- #print("in resolve!")
- #print(f"{self.impls}")
- #for f in self.impls.values():
- # print(f"{type(f)}")
-
# get compatable arities
compatable_arities = [k for k,v in self.impls.items() if v.sig.compatable_arity(len(params))]
if len(compatable_arities) == 0:
fmt = "|".join(f"{v.sig.arity_str()}" for v in self.impls.values())
raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(params)}")
- #print(f"compatable arities: {compatable_arities}")
ret = []
prev_candidates = []
current_types = []
@@ -224,7 +247,6 @@ class MultiFunction(Callable):
func = self.impls[candidate]
exp = func.sig.get_type_by_idx(param_idx)
expected_type = evaluate(exp.type_, env, ns)
- #print(f"expected type: {expected_type}")
current_types.append(expected_type)
valid = expected_type.validate_type(ev, env, ns)
@@ -242,8 +264,6 @@ class MultiFunction(Callable):
if len(next_candidates) != 1:
raise InterpretPanic(symbol, "ambiguous definition!")
- #return(f"returning!")
-
return self.impls[next_candidates[0]].call(Expr([symbol] + ret), env, ns)
class Special(Callable):
@@ -262,7 +282,6 @@ class NebSyntax(Special):
return f"syntax function {self.name}"
def call(self, expr, env, ns):
- #self.arity_check(expr.args[0], expr.args[1:])
return self.body(expr.args[0], expr.args[1:], env, ns)
@@ -271,6 +290,7 @@ class Function(Callable):
def __init__(self, name, params, body, args=None, many=None):
super().__init__(name, params, body, args, many)
+ """
def precall(self, symbol, params, env, ns):
ret = []
@@ -288,6 +308,7 @@ class Function(Callable):
raise InterpretPanic(symbol, f"received {rec}, expected {exp}", ev)
ret.append(ev)
return ret
+ """
#class Builtin(Function):
@@ -314,9 +335,12 @@ class Builtin(Callable):
#class UserFunction(Function):
class UserFunction(Callable):
- def __init__(self, name, params, body):
- newparams, args, many = self.process_params(name, params)
- super().__init__(name, newparams, body, args, many)
+ def __init__(self, name, params, many, body):
+ #newparams, args, many = self.process_params(name, params)
+ #super().__init__(name, newparams, body, args, many)
+ #for p in params:
+ # print(p)
+ super().__init__(name, params, body, params, many)
def __str__(self):
out = f"(func {self.name} {self.return_type} ("
@@ -329,6 +353,7 @@ class UserFunction(Callable):
return out.strip() + ")"
+ '''
def process_params(self, name, params):
newparams = []
args = []
@@ -357,6 +382,7 @@ class UserFunction(Callable):
raise NebPanic("invalid :func signature", param)
first = False
return newparams, args, many
+ '''
def call(self, expr, env, ns):
#self.arity_check(expr.args[0], expr.args[1:])
diff --git a/neb/parser.py b/neb/parser.py
index 22c51e4..360eac4 100644
--- a/neb/parser.py
+++ b/neb/parser.py
@@ -2,6 +2,99 @@ from .structs import *
from .exceptions import ParseError
+def parseDef(token, prev, tokens):
+ if len(tokens) < 2:
+ raise ParseError("invalid def", token.line)
+ elif tokens[0].type_ != TokenType.SYMBOL:
+ raise ParseError("'def' must take a symbol", token.line)
+ sym, inc = parseSymbol(tokens[0], token, tokens[1:])
+ return NebDef(sym), inc + 1
+
+def parseLambda(token, prev, tokens):
+ # lambda [type] ( [arg] [&] ) [(expr)]
+ # func <name> [type] ( [arg] [&] ) [(expr)]
+ idx = 0
+ sym = None
+ return_type = Type(":any")
+ if token.type_ == TokenType.FUNC:
+ if tokens[idx].type_ != TokenType.SYMBOL:
+ raise ParseError("'func' must take a symbol", tokens[idx].line)
+ sym, counter = parseSymbol(tokens[idx], token, tokens[idx+1:])
+ idx += counter
+ else:
+ sym = Symbol("<lambda>", token.line)
+
+ if tokens[idx].type_ == TokenType.COLON:
+ return_type, counter = parseType(tokens[idx], token, tokens[idx+1:])
+ idx += counter
+
+ if tokens[idx].type_ != TokenType.OPEN_PAREN:
+ raise ParseError("expecting argument list", tokens[idx].line)
+
+ args, many, counter = parseFunctionArguments(tokens[idx:], tokens[idx-1], tokens[idx+1:])
+ idx += counter
+
+ body = []
+ while tokens[idx].type_ != TokenType.CLOSE_PAREN:
+ token = tokens[idx]
+ counter = 1
+ if token.type_ == TokenType.OPEN_PAREN:
+ expr, counter = parseExpression(token, prev, tokens[idx+1:])
+ body.append(expr)
+ elif token.type_ in (TokenType.FALSE, TokenType.TRUE, TokenType.STRING, TokenType.INT, TokenType.FLOAT):
+ lit, counter = parseLiteral(token, prev, tokens[idx+1:])
+ body.append(lit)
+ elif token.type_ == TokenType.COLON:
+ typ, counter = parseType(token, prev, tokens[idx+1:])
+ body.append(typ)
+ else:
+ sym, counter = parseSymbol(token, prev, tokens[idx+1:])
+ body.append(sym)
+ idx += counter
+ prev = token
+
+ return NebFuncDef(sym, return_type, args, many, body), idx + 1
+
+def parseFunctionArguments(token, prev, tokens):
+ idx = 0
+ args = []
+ prev = token
+ many = None
+ prev_type = False
+ first = True
+ while tokens[idx].type_ != TokenType.CLOSE_PAREN:
+ token = tokens[idx]
+ if token.type_ == TokenType.SYMBOL:
+ if many is not None:
+ raise ParseError("& must be last argument", token.line)
+ sym, counter = parseSymbol(token, prev, tokens)
+ args.append(Arg(sym.name, TypeEnum.ANY))
+ prev_type = False
+ counter = 1
+ elif token.type_ == TokenType.MANY:
+ many = Arg("&", TypeEnum.ANY)
+ prev_type = False
+ counter = 1
+ elif token.type_ == TokenType.COLON and not prev_type and not first:
+ if prev_type:
+ raise ParseError("can't have two types in a row", token.line)
+ if first:
+ raise ParseError("type can't be first", token.line)
+ typ, counter = parseType(token, prev, tokens[idx+1:])
+ if many is None:
+ args[-1].type_ = typ
+ else:
+ many.type_ = typ
+ prev_type = True
+ else:
+ raise ParseError("invalid function signature", token)
+ first = False
+ idx += counter
+
+ # this should return function
+ return args, many, idx + 2 # parens
+
+
def parseExpression(token, prev, tokens):
idx = 0
args = []
@@ -20,6 +113,12 @@ def parseExpression(token, prev, tokens):
elif token.type_ == TokenType.COLON:
expr, inc = parseType(token, prev, tokens[idx+1:])
args.append(expr)
+ elif token.type_ == TokenType.DEF:
+ expr, inc = parseDef(token, prev, tokens[idx+1:])
+ args.append(expr)
+ elif token.type_ in (TokenType.LAMBDA, TokenType.FUNC):
+ expr, inc = parseLambda(token, prev, tokens[idx+1:])
+ args.append(expr)
else:
expr, inc = parseSymbol(token, prev, tokens[idx+1:])
args.append(expr)
@@ -29,8 +128,10 @@ def parseExpression(token, prev, tokens):
return Expr(args), idx + 2 # parens
def parseSymbol(token, prev, tokens):
- if token.text.startswith("'"):
- return Symbol(token.text[1:], token.line, True), 1
+ if token.type_ == TokenType.APOSTROPHE:
+ if len(tokens) == 0 or tokens[0].type_ != TokenType.SYMBOL:
+ raise ParseError("quote must be followed by a symbol", token.line)
+ return Symbol(tokens[0].text, tokens[0].line, True), 2
else:
return Symbol(token.text, token.line), 1
diff --git a/neb/structs.py b/neb/structs.py
index 27c7782..51cdb34 100644
--- a/neb/structs.py
+++ b/neb/structs.py
@@ -47,6 +47,8 @@ class TokenType(Enum):
MANY = auto()
COLON = auto()
+ APOSTROPHE = auto()
+
@dataclass
class Token:
type_: TokenType
@@ -127,6 +129,25 @@ class Symbol:
else:
return f"{self.name}"
+class NebDef:
+ def __init__(self, symbol, type_=ALL_TYPES[":any"]):
+ self.name = symbol.name
+ self.symbol = symbol
+ self.line = symbol.line
+ self.type_ = type_
+ def __str__(self):
+ return f"{self.name} (def)"
+
+class NebFuncDef:
+ def __init__(self, symbol, return_type, args, many, body):
+ self.name = symbol.name
+ self.symbol = symbol
+ self.line = symbol.line
+ self.return_type = return_type
+ self.args = args
+ self.many = many
+ self.body = body
+
class Expr:
def __init__(self, args):
self.args = args