aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lexer.py28
-rw-r--r--parser.py36
-rw-r--r--runner.py2
-rw-r--r--std.py24
-rw-r--r--tokens.py20
5 files changed, 101 insertions, 9 deletions
diff --git a/lexer.py b/lexer.py
index e6116cd..0d9ed87 100644
--- a/lexer.py
+++ b/lexer.py
@@ -5,6 +5,8 @@ DOUBLE_QUOTE = '"'
BACKSLASH = "\\"
OPEN_PAREN = "("
CLOSE_PAREN = ")"
+OPEN_BRACE = "["
+CLOSE_BRACE = "]"
DIGITS = "0123456789"
LETTERS = "abcdefghijklmnopqrstuvwxyz"
PUNCTUATION = "-_!*$@%^&=+/?<>~"
@@ -48,7 +50,7 @@ def lex_bool(inp):
else:
raise Exception("invalid boolean")
- if peek(inp[len(str(token)):]) not in (None, " ", CLOSE_PAREN):
+ if peek(inp[len(str(token)):]) not in (None, " ", CLOSE_PAREN, CLOSE_BRACE):
raise Exception("invalid boolean")
#return token, inp[len(str(token)):]
@@ -58,7 +60,7 @@ def lex_bool(inp):
def lex_number(inp):
token = ""
for idx, c in enumerate(inp):
- if c in (" ", CLOSE_PAREN):
+ if c in (" ", CLOSE_PAREN, CLOSE_BRACE):
if "." in token:
#return float(token), inp[idx:]
return NebFloat(float(token)), inp[idx:]
@@ -96,14 +98,13 @@ def lex_number(inp):
def lex_symbol(inp):
token = ""
for idx, c in enumerate(inp):
- if c in (CLOSE_PAREN, " "):
+ if c in (CLOSE_PAREN, CLOSE_BRACE, " "):
return NebSymbol(token), inp[idx:]
elif c in SYMBOL_VALS:
token += c
else:
raise Exception("improper symbol")
return NebSymbol(token), ""
-
def peek(inp):
if len(inp) == 0:
@@ -123,6 +124,13 @@ def lex(inp, tokens):
elif nxt == CLOSE_PAREN:
tokens.append(NebClose())
return lex(inp[1:], tokens)
+ # braces
+ elif nxt == OPEN_BRACE:
+ tokens.append(NebListStart())
+ return lex(inp[1:], tokens)
+ elif nxt == CLOSE_BRACE:
+ tokens.append(NebListEnd())
+ return lex(inp[1:], tokens)
# numbers
elif nxt in list(DIGITS) or nxt in ("+", "-", "."):
# + and - are symbols, too
@@ -130,7 +138,8 @@ def lex(inp, tokens):
after = peek(inp[1:])
if after not in DIGITS: # parse a symbol
token, remainder = lex_symbol(inp)
- if peek(remainder) not in (None, CLOSE_PAREN, " "):
+ if peek(remainder) not in (None, CLOSE_PAREN, CLOSE_BRACE, " "):
+ print(f"{peek(remainder)}")
raise Exception("spaces required between tokens")
tokens.append(token)
return lex(remainder, tokens)
@@ -141,21 +150,24 @@ def lex(inp, tokens):
elif nxt == DOUBLE_QUOTE:
token, remainder = lex_string(inp[1:])
#print(f"received [{token}] [{remainder}]")
- if peek(remainder) not in (None, CLOSE_PAREN, " "):
+ if peek(remainder) not in (None, CLOSE_PAREN, " ", CLOSE_BRACE):
+ print(f"{peek(remainder)}")
raise Exception("spaces required between tokens")
tokens.append(token)
return lex(remainder, tokens)
# bool
elif nxt == "#":
token, remainder = lex_bool(inp[1:])
- if peek(remainder) not in (None, CLOSE_PAREN, " "):
+ if peek(remainder) not in (None, CLOSE_PAREN, " ", CLOSE_BRACE):
+ print(f"{peek(remainder)}")
raise Exception("spaces required between tokens")
tokens.append(token)
return lex(remainder, tokens)
# symbols
elif nxt in SYMBOL_VALS:
token, remainder = lex_symbol(inp)
- if peek(remainder) not in (None, CLOSE_PAREN, " "):
+ if peek(remainder) not in (None, CLOSE_PAREN, " ", CLOSE_BRACE):
+ print(f"{peek(remainder)}")
raise Exception("spaces required between tokens")
tokens.append(token)
return lex(remainder, tokens)
diff --git a/parser.py b/parser.py
index 97717bf..98a4712 100644
--- a/parser.py
+++ b/parser.py
@@ -12,6 +12,7 @@ def parse_expression(tkns):
add_idx = 0
for idx, t in enumerate(tkns):
# if we evaluated a nested expression, skip ahead
+ # TODO or list?
if add_idx > 0:
add_idx -= 1
continue
@@ -22,15 +23,46 @@ def parse_expression(tkns):
symbol = t
elif isinstance(t, NebClose):
return NebExpression(symbol, args), tkns[idx + 1:], idx + 1
+
# nested expressions
elif isinstance(t, NebOpen):
expr, remainder, add_idx = parse_expression(tkns[idx + 1:])
args.append(expr)
+
+ elif isinstance(t, NebListStart):
+ # TODO this might need to be add_idx + (return of parse_list)
+ lst, remainder, add_idx = parse_list(tkns[idx + 1:])
+ args.append(lst)
+
else:
args.append(t)
raise Exception("couldn't parse expression!")
+def parse_list(tkns):
+ items = []
+ add_idx = 0
+ for idx, t in enumerate(tkns):
+ if add_idx > 0:
+ add_idx -= 1
+ continue
+
+ if isinstance(t, NebListEnd):
+ return NebList(items), tkns[idx + 1:], idx + 1
+ elif isinstance(t, NebOpen):
+ expr, remainder, add_idx = parse_expression(tkns[1 + idx:])
+ items.append(expr)
+ elif isinstance(t, NebLiteral):
+ items.append(t)
+ elif isinstance(t, NebSymbol):
+ items.append(t)
+ elif isinstance(t, NebListStart):
+ # TODO: this probably means lists of lists don't work
+ lst, remainder, add_idx = parse_list(tkns[1:])
+ items.append(lst)
+
+ raise Exception("couldn't parse list!")
+
def parse(tkns, parsed):
nxt = peek(tkns)
if nxt is None:
@@ -39,6 +71,10 @@ def parse(tkns, parsed):
expr, remainder, _ = parse_expression(tkns[1:])
parsed.append(expr)
return parse(remainder, parsed)
+ elif isinstance(nxt, NebListStart):
+ lst, remainder, _ = parse_list(tkns[1:])
+ parsed.append(lst)
+ return parse(remainder, parsed)
elif isinstance(nxt, NebLiteral):
parsed.append(nxt)
return parse(tkns[1:], parsed)
diff --git a/runner.py b/runner.py
index f9c8a4d..6f2f8ce 100644
--- a/runner.py
+++ b/runner.py
@@ -11,7 +11,7 @@ def evaluate(items, pop):
nxt = peek(items)
if nxt is None:
return pop
- elif isinstance(nxt, NebLiteral):
+ elif isinstance(nxt, NebLiteral) or isinstance(nxt, NebList):
return evaluate(items[1:], nxt)
elif isinstance(nxt, NebSymbol):
if not nxt.name in STD:
diff --git a/std.py b/std.py
index fe0ede1..0cd5aee 100644
--- a/std.py
+++ b/std.py
@@ -1,6 +1,7 @@
from tokens import *
import sys
from collections import namedtuple
+import subprocess
FuncImpl = namedtuple("FuncImpl", ("func", "impl"))
@@ -22,6 +23,16 @@ def std_print(arg):
#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
@@ -103,6 +114,12 @@ def std_is_bool(arg):
else:
return NebBool(False)
+# shell
+def std_shell(arg):
+ assert isinstance(arg, NebString)
+ subprocess.run(arg.value)
+ return NebBool(True)
+
def evaluate_expression(expr):
if not expr.symbol.name in STD:
raise Exception(f"no such symbol: {expr.symbol.name}")
@@ -135,6 +152,9 @@ 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]
@@ -175,4 +195,8 @@ def build_std():
is_bool = FuncImpl(NebFunction("string?", [NebAny], NebBool), std_is_bool)
STD["bool?"] = [is_bool]
+ # shell
+ shell_string = FuncImpl(NebFunction("$", [NebString], NebBool), std_shell)
+ STD["$"] = [shell_string]
+
build_std()
diff --git a/tokens.py b/tokens.py
index 450ef99..de482c2 100644
--- a/tokens.py
+++ b/tokens.py
@@ -14,6 +14,7 @@ class NebType(Enum):
BOOL = auto()
EXPR = auto()
SYMBOL = auto()
+ LIST = auto()
def __str__(self):
return self.name.lower()
@@ -57,6 +58,14 @@ class NebClose(NebSeparator):
def __str__(self):
return ")"
+class NebListStart(NebSeparator):
+ def __str__(self):
+ return "["
+
+class NebListEnd(NebSeparator):
+ def __str__(self):
+ return "]"
+
class NebSymbol(NebBaseType):
def __init__(self, name):
@@ -119,3 +128,14 @@ class NebFloat(NebNumber):
def __init__(self, value):
super().__init__("float", NebType.FLOAT, value)
+class NebList(NebAny):
+ def __init__(self, items):
+ super().__init__("list", NebType.LIST)
+ self.items = items
+
+ def __str__(self):
+ out = "[ "
+ for item in self.items:
+ out += f"{item} "
+ return f"{out}]"
+