diff options
| -rw-r--r-- | lexer.py | 28 | ||||
| -rw-r--r-- | parser.py | 36 | ||||
| -rw-r--r-- | runner.py | 2 | ||||
| -rw-r--r-- | std.py | 24 | ||||
| -rw-r--r-- | tokens.py | 20 | 
5 files changed, 101 insertions, 9 deletions
| @@ -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) @@ -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) @@ -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: @@ -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() @@ -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}]" + | 
