Created
September 20, 2017 01:21
-
-
Save stowell/b50c9e37b8724afcee55bcd4cfef4763 to your computer and use it in GitHub Desktop.
Slightly less rigid parser for sigcode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fileinput | |
import re | |
blockdefre = re.compile("^\s*(?P<blockname>\S+)\s+is\s*$") | |
vardefre = re.compile("^\s*(?P<varname>\S+)\s+is\s+(?P<expr>.+)$") | |
whitespacere = re.compile("\s+") | |
def isnumber(word): | |
try: | |
float(word) | |
return True | |
except ValueError: | |
return False | |
class Block(object): | |
def __init__(self, blockname): | |
self.blockname = blockname | |
self.values = [] | |
def addvalue(self, value): | |
self.values.append(value) | |
def vardefs(self): | |
allvardefs = [] | |
for value in self.values: | |
allvardefs.extend(value.vardefs()) | |
return allvardefs | |
def unknownwords(self, blocknames, vardefs): | |
allunknownwords = [] | |
for value in self.values: | |
allunknownwords.extend(value.unknownwords(blocknames, vardefs)) | |
return allunknownwords | |
class VarDef(object): | |
def __init__(self, varname, value): | |
self.varname = varname | |
self.value = value | |
def translate(self): | |
return "%s = %s" % (self.varname, self.value.translate()) | |
def vardefs(self): | |
return [self.varname] | |
def unknownwords(self, blocknames, vardefs): | |
#TODO: cleanvardefs that don't include this vardef | |
return self.value.unknownwords(blocknames, vardefs) | |
class Value(object): | |
def __init__(self): self.othervalues = [] | |
def addvalue(self, value): pass | |
def makessense(self): pass | |
def unknownwords(self, blocknames, vardefs): pass | |
def vardefs(self): return [] | |
class Function(Value): | |
def __init__(self, funname): | |
super(self.__class__, self).__init__() | |
self.funname = funname | |
self.argument = None | |
def addvalue(self, value): | |
if self.argument is None: | |
self.argument = value | |
else: | |
self.othervalues.append(value) | |
def makessense(self): | |
return self.funname is not None and self.argument is not None and len(self.othervalues) == 0 | |
def translate(self): | |
return "%s(%s)" % (self.funname, self.argument.translate()) | |
def unknownwords(self, blocknames, vardefs): | |
if self.funname not in funnames and self.funname not in blocknames: | |
return [self.funname] + self.argument.unknownwords(blocknames, vardefs) | |
else: | |
return self.argument.unknownwords(blocknames, vardefs) | |
class Operator(Value): | |
def __init__(self, left, opname): | |
super(self.__class__, self).__init__() | |
self.left = left | |
self.opname = opname | |
self.right = None | |
def addvalue(self, value): | |
if self.right is None: | |
self.right = value | |
else: | |
self.othervalues.append(value) | |
def makessense(self): | |
return self.left is not None and self.opname is not None and self.right is not None and len(self.othervalues) == 0 | |
def translate(self): | |
op = ops[self.opname] | |
return "(%s %s %s)" % (self.left.translate(), op, self.right.translate()) | |
def unknownwords(self, blocknames, vardefs): | |
if self.opname not in opnames: | |
return [self.opname] + self.left.unknownwords(blocknames, vardefs) + self.right.unknownwords(blocknames, vardefs) | |
else: | |
return self.left.unknownwords(blocknames, vardefs) + self.right.unknownwords(blocknames, vardefs) | |
#thought: just consume values, mark yourself as unknown | |
class Word(Value): | |
def __init__(self, word): | |
super(self.__class__, self).__init__() | |
self.word = word | |
def addvalue(self, value): | |
self.othervalues.append(value) | |
def makessense(self): | |
return self.word is not None and len(self.othervalues) == 0 | |
def translate(self): | |
return self.word | |
def unknownwords(self, blocknames, vardefs): | |
if self.word not in blocknames and self.word not in vardefs and self.word not in opnames and self.word not in funnames: | |
return [self.word] | |
else: | |
return [] | |
def translateblock(block, blocknames): | |
vardefs = block.vardefs() | |
translatedvalues = [("that = " + value.translate()) for value in block.values] | |
if block.blockname is None: | |
return "\n".join(translatedvalues) | |
else: | |
unknownwords = block.unknownwords(blocknames, vardefs) | |
cleanunknownwords = set() | |
for unknownword in unknownwords: | |
if unknownword == "that" or isnumber(unknownword): | |
continue | |
else: | |
cleanunknownwords.add(unknownword) | |
defline = "def %s(%s):" % (block.blockname, ",".join(sorted(cleanunknownwords))) | |
indentedvalues = [(" " + translatedvalue) for translatedvalue in translatedvalues] | |
returnline = [" return that"] | |
return "\n".join([defline] + indentedvalues + returnline) | |
header = """ | |
import numpy | |
def negative(v): return -v | |
def exp(v): return numpy.exp(v) | |
""" | |
def translateprogram(blocks): | |
blocknames = [block.blockname for block in blocks if block.blockname is not None] | |
translatedblocks = [translateblock(block, blocknames) for block in blocks] | |
return header + "\n".join(translatedblocks) | |
funnames = frozenset(("negative", "exp")) | |
ops = { | |
"plus": "+", | |
"minus": "-", | |
"over": "/", | |
} | |
opnames = ops.keys() | |
def lefttoright(expr, blocknames): | |
words = whitespacere.split(expr) | |
valsofar = None | |
for word in words: | |
if valsofar is None and (word in funnames or word in blocknames): | |
funname = word | |
valsofar = Function(funname=funname) | |
elif valsofar is not None and word in opnames: | |
opname = word | |
left = valsofar | |
valsofar = Operator(left=left, opname=opname) | |
else: | |
value = Word(word=word) | |
if valsofar is None: | |
valsofar = value | |
else: | |
valsofar.addvalue(value) | |
return valsofar | |
def main(): | |
blocks = [] | |
blocknames = set() | |
currentblock = None | |
for line in fileinput.input(): | |
stripped = line.strip() | |
blockdefmatch = blockdefre.match(stripped) | |
vardefmatch = vardefre.match(stripped) | |
if len(stripped) == 0: | |
currentblock = None | |
elif blockdefmatch and (currentblock is None): | |
blockname = blockdefmatch.groupdict()['blockname'] | |
blocknames.add(blockname) | |
block = Block(blockname=blockname) | |
blocks.append(block) | |
currentblock = block | |
elif vardefmatch: | |
varname = vardefmatch.groupdict()['varname'] | |
expr = vardefmatch.groupdict()['expr'] | |
value = lefttoright(expr, blocknames) | |
vardef = VarDef(varname=varname, value=value) | |
if currentblock is None: | |
block = Block(blockname=None) | |
blocks.append(block) | |
currentblock = block | |
currentblock.addvalue(vardef) | |
else: | |
value = lefttoright(stripped, blocknames) | |
if currentblock is None: | |
block = Block(blockname=None) | |
blocks.append(block) | |
currentblock = block | |
currentblock.addvalue(value) | |
print translateprogram(blocks) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment