OEIScript/implementation.py

An implementation of OEIScript in Python 3. requests must be installed to use this interpreter, and an internet connection is required. Pass a file as an argument to execute it.
==Implementation==
#!/usr/bin/python
import sys
import requests
from enum import Enum
from collections import defaultdict
class Seq:
 def __init__(self, elems):
 self.elems = elems
 self.map = {}
 def __getitem__(self, key):
 if key < 0: return None
 try:
 if key in self.map:
 return self.map[key]
 else:
 return self.elems[key]
 except Exception:
 return None
 def __iter__(self):
 return iter(list(self.elems))
class Token(Enum):
 LINEBREAK = 0
 LBRACK = 1
 RBRACK = 2
 COLON = 3
 EQUALS = 4
 EXCLAM = 5
 QUESTION = 6
 NAME = 7
 INT = 8
def tokenize(text):
 tokens = []
 current = ""
 currentKind = None
 inComment = False
 for c in text:
 if inComment:
 if c == "\n":
 inComment = False
 else:
 continue
 done = False
 if currentKind == Token.INT and c in "0123456789":
 current += c
 done = True
 elif currentKind == Token.NAME and c.isalnum():
 current += c
 done = True
 elif currentKind in [Token.INT, Token.NAME]:
 tokens.append((currentKind, current))
 current = ""
 currentKind = None
 elif currentKind is None and c.isalnum():
 if c in "0123456789":
 currentKind = Token.INT
 else:
 currentKind = Token.NAME
 current = str(c)
 done = True
 if not done:
 if c == "\n": tokens.append((Token.LINEBREAK, None))
 elif c.isspace(): pass
 elif c == "{": tokens.append((Token.LBRACK, None))
 elif c == "}": tokens.append((Token.RBRACK, None))
 elif c == ":": tokens.append((Token.COLON, None))
 elif c == "=": tokens.append((Token.EQUALS, None))
 elif c == "!": tokens.append((Token.EXCLAM, None))
 elif c == "?": tokens.append((Token.QUESTION, None))
 elif c == "#": inComment = True
 else: raise Exception(f"Invalid character: {c}")
 return tokens
class Node:
 def __init__(self, parent):
 self.parent = parent
 self.contents = [[]]
def parse(tokens):
 node = Node(None)
 for t in tokens:
 if t[0] == Token.LBRACK:
 nxt = Node(node)
 node.contents[-1].append(nxt)
 node = nxt
 elif t[0] == Token.RBRACK:
 if node.parent is None:
 raise Exception("Unmatched brackets")
 node = node.parent
 elif t[0] == Token.LINEBREAK:
 if len(node.contents[-1]) != 0:
 node.contents.append([])
 else:
 node.contents[-1].append(t)
 if node.parent is not None:
 raise Exception("Unmatched brackets")
 return node
def evalchain(seqs, ctx):
 if len(seqs) == 0:
 return None
 if seqs[-1][0] == Token.NAME:
 val = ctx[seqs[-1][1]]
 elif seqs[-1][0] == Token.INT:
 val = int(seqs[-1][1])
 else:
 raise Exception(f"Invalid syntax: {seqs[-1]}")
 for s in reversed(seqs[:-1]):
 if val is None: return None
 if s[0] != Token.NAME:
 raise Exception(f"Invalid syntax: {s}")
 try:
 val = ctx[s[1]][val]
 except Exception:
 return None
 return val
def isAName(name):
 return len(name) >= 2 and name[0] == "A" and name[1:].isdigit()
def evaltree(lines, ctx, debug=False):
 for line in lines:
 if len(line) == 0: continue
 kinds = tuple((x[0] if isinstance(x, tuple) else Node) for x in line)
 if kinds == (Token.NAME, Token.COLON, Token.NAME):
 if isAName(line[2][1]):
 res = fetch(line[2][1])
 ctx[line[0][1]] = Seq(res)
 if debug: print(f"DEBUG: Loaded sequence {line[2][1]} to {line[0][1]}")
 else:
 raise Exception(f"Invalid syntax: {line[2][1]}")
 elif len(kinds) >= 2 and kinds[0] == Token.NAME and kinds[1] == Token.EQUALS:
 ctx[line[0][1]] = evalchain(line[2:], ctx)
 if debug: print(f"DEBUG: Saved chain result to {line[0][1]}")
 elif len(kinds) >= 1 and kinds[0] == Token.EXCLAM:
 res = evalchain(line[1:], ctx)
 if isinstance(res, int):
 print(res)
 if debug: print(f"DEBUG: Printed chain result")
 elif len(kinds) == 2 and kinds[0] == Token.NAME and kinds[1] == Token.QUESTION:
 try:
 ctx[line[0][1]] = int(input())
 except EOFError:
 ctx[line[0][1]] = None
 if debug: print(f"DEBUG: Input to {line[0][1]}")
 elif len(kinds) == 2 and kinds[0] == Token.NAME and kinds[1] == Node:
 if debug: print(f"DEBUG: Entering block based on {line[0][1]}")
 while ctx[line[0][1]] is not None:
 evaltree(line[1].contents, ctx, debug)
 if debug: print(f"DEBUG: Exiting block based on {line[0][1]}")
 else:
 raise Exception(f"Invalid syntax: {line}")
def fetch(name):
 res = requests.get(f"https://oeis.org/search?q=id:{name}&fmt=json")
 j = res.json()
 if j["count"] == 0:
 return ([], 0)
 return list(map(int, j["results"][0]["data"].split(",")))
def main():
 with open(sys.argv[1]) as infile:
 text = infile.read()
 res = parse(tokenize(text)).contents
 ctx = defaultdict(lambda: None)
 evaltree(res, ctx)
if __name__ == "__main__":
 main()
Category:Implementations