Skip to content

Instantly share code, notes, and snippets.

@stowell
Created September 20, 2017 01:21
Show Gist options
  • Save stowell/b50c9e37b8724afcee55bcd4cfef4763 to your computer and use it in GitHub Desktop.
Save stowell/b50c9e37b8724afcee55bcd4cfef4763 to your computer and use it in GitHub Desktop.
Slightly less rigid parser for sigcode
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