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()