Franck Pommereau

started net compilation API

import importlib, string, collections
import importlib, string, collections, io, tempfile, os, os.path
from snakes import LanguageError
......@@ -11,6 +11,44 @@ def getlang (name) :
module.name = name
return module
def build (lang, tree, saveto) :
if saveto is None :
out = io.StringIO()
elif isinstance(saveto, str) :
out = open(str, "w+")
else :
try :
saveto.write("hello world!")
saveto.seek(0)
if saveto.read(12) != "hello world!" :
raise IOError()
saveto.seek(0)
saveto.truncate()
saveto.flush()
if not os.path.exists(saveto.name) :
raise IOError()
except :
raise ValueError("%r object does not have expected file-like feature"
% saveto.__class__.__name__)
out = saveto
gen = lang.codegen.CodeGenerator(out)
gen.visit(tree)
out.flush()
out.seek(0)
if lang.INMEM :
mod = lang.build(tree, out, tree.name + lang.EXT)
elif saveto is None :
try :
tmp = tempfile.NamedTemporaryFile(mode="w+", suffix=lang.EXT, delete=False)
tmp.write(out.read())
tmp.close()
mod = lang.build(tree, tmp.name, tree.name + lang.EXT)
finally :
os.unlink(tmp.name)
else :
mod = lang.build(tree, out.name, os.path.basename(out.name))
mod.ast = tree
class BaseDeclare (object) :
_levels = [None]
_default = None
......@@ -34,9 +72,27 @@ class BaseDeclare (object) :
return new
class CompilationError (Exception) :
def __init__ (self, msg, blame) :
Exception.__init__(self, msg)
self.blame = blame
def __init__ (self, ast, path, msg, loc=None, details=None) :
if loc is None :
short = "%s: %s" % (path, msg.strip())
self.blame = None
elif not isinstance(loc, tuple) :
short = "%s[%s]: %s" % (path, loc, msg.strip())
self.blame = ast.blame(int(loc) - 1, 0)
else :
short = "%s[%s:%s]: %s" % (path, loc[0] - 1, loc[1], msg.strip())
self.blame = ast.blame(*loc)
Exception.__init__(self, short)
self.ast = ast
self.path = path
self.loc = loc
self.details = details
def message (self) :
msg = ["[ERROR] %s" % self]
if self.blame is not None :
msg.append("[BLAME] %s" % self.blame)
msg.append(self.details)
return "\n".join(msg)
class Context (object) :
def __init__ (self, **attrs) :
......
import snakes.io.snk, sys
from . import build, CompilationError
net = snakes.io.snk.load(open(sys.argv[-1]))
ast = net.__ast__()
try :
build(net.lang, ast, None)
except CompilationError as err :
print(err.message())
......@@ -163,17 +163,14 @@ class AST (_Record) :
found.extend(sub.locate(line, column))
return found
def locate (self, line, column) :
if not hasattr(self, "loc") :
raise ValueError("AST has no locations")
elif self.loc is None :
return []
(l0, c0), (l1, c1) = self.loc
if ((l0 == line == l1 and c0 <= column < c1)
or (l0 == line < l1 and c0 <= column)
or (l0 < line == l1 and column <= c1)
or (l0 < line < l1)) :
return [self] + self._locate_children(line, column)
return []
if hasattr(self, "loc") and self.loc is not None :
(l0, c0), (l1, c1) = self.loc
if ((l0 == line == l1 and c0 <= column < c1)
or (l0 == line < l1 and c0 <= column)
or (l0 < line == l1 and column <= c1)
or (l0 < line < l1)) :
return [self] + self._locate_children(line, column)
return self._locate_children(line, column)
def blame (self, line, column) :
for node in reversed(self.locate(line, column)) :
if hasattr(node, "BLAME") :
......@@ -414,8 +411,10 @@ class CodeGenerator (object) :
node.body = []
for lvl, line in node.decl :
node.body.append(ContextLine(line, BLAME=ContextBlame(line)))
self.fill(line)
self.children_visit(node.body)
self.fill()
def visit_ContextLine (self, node) :
self.fill(node.code)
def visit_SuccProcName (self, node) :
if not node.trans :
name = "addsucc"
......
from snakes.compil import BaseDeclare
import subprocess, os.path, os, inspect
import snakes
from snakes.compil import BaseDeclare, CompilationError
from snakes.compil.go.rename import rename
from . import codegen
NONETYPE = False
BOOL = "bool"
EXT = ".go"
INMEM = False
class Declare (BaseDeclare) :
_levels = ["import", "decl"]
_default = "decl"
def ast2code (tree, output=None) :
pass
class GoModule (object) :
def __init__ (self, srcpath) :
self.path = os.path.splitext(srcpath)[0]
def load (tree, name="net") :
pass
def update_gopath () :
# TODO: fix this wrt installation path
path = os.path.join(os.path.dirname(os.path.dirname(inspect.getfile(snakes))),
"libs", "go")
if "GOPATH" not in os.environ :
os.environ["GOPATH"] = path
elif path not in os.environ["GOPATH"].split(":") :
os.environ["GOPATH"] = ":".join([path, os.environ["GOPATH"]])
def build (ast, src, name) :
update_gopath()
try :
subprocess.check_output(["go", "build", src], stderr=subprocess.STDOUT)
except Exception as err :
out = err.output.decode()
for line in out.splitlines() :
try :
path, lineno, message = line.split(":", 2)
if path.endswith(src) :
raise CompilationError(ast, name, message.strip(), int(lineno), out)
except ValueError :
continue
raise CompilationError(ast, name, out)
return GoModule(src)
......
import io, types, traceback, sys
from . import codegen as _codegen
import types, traceback, sys, importlib
from snakes.compil import CompilationError, BaseDeclare
from snakes.compil.python.rename import rename
from . import codegen as codegen
from .rename import rename
NONETYPE = True
BOOL = "bool"
EXT = ".py"
INMEM = True
class Declare (BaseDeclare) :
pass
def ast2code (tree, output=None) :
if output is None :
out = io.StringIO()
elif isinstance(output, str) :
out = open(output)
else :
out = output
gen = _codegen.CodeGenerator(out)
gen.visit(tree)
if output is None :
return gen
def load (tree, name="net") :
gen = ast2code(tree)
def build (ast, src, name) :
if isinstance(src, str) :
src = open(src)
ctx = {}
try :
exec(gen.output.getvalue(), ctx)
spec = importlib.util.spec_from_file_location(module_name, file_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
except Exception as err :
c, v, t = sys.exc_info()
msg = traceback.format_exception_only(c, v)
msg.insert(0, "[ERROR] " + msg.pop(-1))
if hasattr(err, "lineno") and hasattr(err, "offset") :
line, column = err.lineno - 1, err.offset -1
loc = (err.lineno - 1, err.offset -1)
else :
tb = t
while tb.tb_next :
tb = tb.tb_next
line, column = tb.tb_lineno, 0
blame = tree.blame(line, column)
if blame :
raise CompilationError("%s[BLAME] %s" % ("".join(msg), blame), blame)
raise CompilationError("".join(msg).rstrip(), None)
ctx["__succfunc__"] = gen.succfunc
ctx["__succproc__"] = gen.succproc
ctx["__initfunc__"] = gen.initfunc
mod = types.ModuleType(name)
mod.__dict__.update(ctx)
loc = tb.tb_lineno
raise CompilationError(ast, name, msg[-1], loc, "".join(msg).rstrip())
return mod
......
......@@ -5,7 +5,6 @@ from snakes.data import *
from snakes.tokens import *
import snakes.compil
from snakes.compil import Context
class PetriNet (object) :
def __init__ (self, name, lang="python") :
......@@ -17,13 +16,15 @@ class PetriNet (object) :
self._node = {}
def __repr__ (self) :
return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.lang.name)
def build (self, name="net", saveto=None) :
return snakes.compil.build(self.lang, self.__ast__(name), saveto)
def __ast__ (self, name="net") :
ctx = Context(net=self)
ctx = snakes.compil.Context(net=self)
mod = ctx.Module(name,
[ctx.Context(self.declare.copy()),
ctx.DefineMarking(list(self._place.values()))])
for name, trans in sorted(self._trans.items()) :
mod.body.extend(trans.__ast__(Context(net=self)))
mod.body.extend(trans.__ast__(snakes.compil.Context(net=self)))
mod.body.append(ctx.DefSuccProc(ctx.SuccProcName(), "marking", "succ", [
ctx.CallSuccProc(ctx.SuccProcName(t.name), "marking", "succ")
for n, t in sorted(self._trans.items())]))
......@@ -33,7 +34,7 @@ class PetriNet (object) :
ctx.ReturnSucc("succ")]))
mod.body.append(ctx.DefSuccIter(ctx.SuccIterName(), "marking", [
ctx.SuccIterName(t.name) for n, t in sorted(self._trans.items())]))
marking = self.get_marking().__ast__(Context(net=self))
marking = self.get_marking().__ast__(snakes.compil.Context(net=self))
mod.body.extend([ctx.DefInitFunc(ctx.InitName(), marking),
ctx.SuccProcTable(),
ctx.SuccFuncTable(),
......
......@@ -5,7 +5,7 @@ MAX = 5
"""
net "my net" :
place p1 int = 0, 1, 2
place p1 int = 0, 1, 2, FOO
place p2 int = 1, 2, 3
place p3 int = MAX
place p4
......