OEIScript/implementation.py
Jump to navigation
Jump to search
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()