diff options
| author | mryouse | 2022-07-26 01:03:57 +0000 |
|---|---|---|
| committer | mryouse | 2022-07-26 01:03:57 +0000 |
| commit | 0de12784cfc54130e65812e64cbd9e975ceab9ff (patch) | |
| tree | 3142eb24095edcae0a2270f34a4fbdb83184741a /neb | |
| parent | 80257143d7c33ed218ecb8ce916ea710b6b1f6fa (diff) | |
parse single quotes, as well as defs and funcs
Diffstat (limited to 'neb')
| -rw-r--r-- | neb/__init__.py | 52 | ||||
| -rw-r--r-- | neb/parser.py | 105 | ||||
| -rw-r--r-- | neb/structs.py | 21 |
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 |
