diff options
-rw-r--r-- | Makefile | 41 | ||||
-rw-r--r-- | __init__.py | 5 | ||||
-rw-r--r-- | examples/python-api/.gitignore | 1 | ||||
-rw-r--r-- | examples/python-api/netlist_graph.py | 472 | ||||
-rwxr-xr-x | examples/python-api/run.sh | 6 | ||||
-rw-r--r-- | kernel/cost.h | 4 | ||||
-rw-r--r-- | kernel/driver.cc | 8 | ||||
-rw-r--r-- | kernel/rtlil.cc | 98 | ||||
-rw-r--r-- | kernel/rtlil.h | 27 | ||||
-rw-r--r-- | kernel/yosys.cc | 38 | ||||
-rw-r--r-- | kernel/yosys.h | 12 | ||||
-rw-r--r-- | passes/cmds/plugin.cc | 51 | ||||
-rw-r--r-- | py_wrap_generator.py | 2101 |
13 files changed, 2856 insertions, 8 deletions
@@ -16,9 +16,14 @@ ENABLE_READLINE := 1 ENABLE_EDITLINE := 0 ENABLE_VERIFIC := 0 ENABLE_COVER := 1 -ENABLE_LIBYOSYS := 0 +ENABLE_LIBYOSYS := 1 ENABLE_PROTOBUF := 0 +# python wrappers +ENABLE_PYOSYS := 1 +PYTHON_VERSION := 3.5 +PYTHON_DESTDIR := /usr/local/lib/python$(PYTHON_VERSION)/dist-packages + # other configuration flags ENABLE_GCOV := 0 ENABLE_GPROF := 0 @@ -64,8 +69,8 @@ all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) -CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include -LDFLAGS := $(LDFLAGS) -L$(LIBDIR) +CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include -ferror-limit=0 +LDFLAGS := $(LDFLAGS) -L$(LIBDIR) -ferror-limit=0 LDLIBS := $(LDLIBS) -lstdc++ -lm PLUGIN_LDFLAGS := @@ -261,6 +266,15 @@ ifeq ($(ENABLE_LIBYOSYS),1) TARGETS += libyosys.so endif +ifeq ($(ENABLE_PYOSYS),1) +LDLIBS += -lpython$(PYTHON_VERSION)m -lboost_python-py$(subst .,,$(PYTHON_VERSION)) -lboost_system +CXXFLAGS += -I/usr/include/python$(PYTHON_VERSION) -D WITH_PYTHON +PY_WRAPPER_FILE = kernel/python_wrappers +OBJS += $(PY_WRAPPER_FILE).o +PY_GEN_SCRIPT= py_wrap_generator +PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") +endif + ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE ifeq ($(OS), FreeBSD) @@ -510,6 +524,14 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< +%.pyh: %.h + $(Q) mkdir -p $(dir $@) + $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P $(CPPFLAGS) $(CXXFLAGS) -Qunused-arguments - + +$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) + $(Q) mkdir -p $(dir $@) + $(P) python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" + %.o: %.cpp $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< @@ -638,6 +660,11 @@ ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR) $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so $(INSTALL_SUDO) ldconfig +ifeq ($(ENABLE_PYOSYS),1) + $(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys + $(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys + $(INSTALL_SUDO) cp __init__.py $(PYTHON_DESTDIR)/pyosys +endif endif uninstall: @@ -645,6 +672,11 @@ uninstall: $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so +ifeq ($(ENABLE_PYOSYS),1) + $(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/libyosys.so + $(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/__init__.py + $(INSTALL_SUDO) rmdir $(PYTHON_DESTDIR)/pyosys +endif endif update-manual: $(TARGETS) $(EXTRA_TARGETS) @@ -657,8 +689,9 @@ manual: $(TARGETS) $(EXTRA_TARGETS) clean: rm -rf share + rm -rf kernel/*.pyh if test -d manual; then cd manual && sh clean.sh; fi - rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) + rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d rm -rf tests/asicworld/*.out tests/asicworld/*.log diff --git a/__init__.py b/__init__.py new file mode 100644 index 000000000..330fd6d86 --- /dev/null +++ b/__init__.py @@ -0,0 +1,5 @@ +import os +import sys +sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL) + +__all__ = ["libyosys"] diff --git a/examples/python-api/.gitignore b/examples/python-api/.gitignore new file mode 100644 index 000000000..758de1134 --- /dev/null +++ b/examples/python-api/.gitignore @@ -0,0 +1 @@ +out/** diff --git a/examples/python-api/netlist_graph.py b/examples/python-api/netlist_graph.py new file mode 100644 index 000000000..c8da76e3d --- /dev/null +++ b/examples/python-api/netlist_graph.py @@ -0,0 +1,472 @@ +from libyosys import * +from scipy.sparse import coo_matrix +from numpy import savetxt + +from enum import Enum +class NodeType(Enum): + GRAPH_CELL = 0 + GRAPH_PI = 1 + GRAPH_PO = 2 + GRAPH_CONST = 3 + GRAPH_WIRE = 4 + +class NetlistElement: + + def __init__(self, design, module, name): + self.design = design + self.module = module + self.name = name + +class Bit(NetlistElement): + + def __init__(self, bit, design, module, node, port, pos): + super().__init__(design, module, IdString("\\__BIT__")) + self.bit = bit + self.node = node + self.port = port + self.pos = pos + +class Port(NetlistElement): + + def __init__(self, name): + super().__init__(None, None, name) + self.input = False + self.output = False + self.bits = [] + +class Node(NetlistElement): + + def __init__(self, design, module, name, nodeType): + super().__init__(design, module, name) + self.nodeType = nodeType + self.ports = [] + + def __lt__(self, other): + if isinstance(other, self.__class): + if self.type == other.type: + return self.name.str() < other.name.str() + return self.type < other.type + return False + +class PyCell(Node): + + def __init__(self, design, module, name, cell): + super().__init__(design, module, name, NodeType.GRAPH_CELL) + self.cell = cell + +class PyWire(Node): + + def __init__(self, design, module, name): + super().__init__(design, module, name, NodeType.GRAPH_WIRE) + +class NetlistGraph: + + def __init__(self, design, module = None): + self.design = design + if module != None: + self.module = module + else: + self.module = list(design.modules_.values())[0] + self.cells = [] + self.wires = [] + self.nodes = [] + self.node_bits = [] + self.wire_bits = [] + self.node_index = {} + self.node_bit_index = {} + self.wire_bit_index = {} + + self.incoming = None + self.outgoing = None + self.create() + + def create(self): + + log_header(self.design, "Creating abstract graph representation of " + + "module " + self.module.name.str() + "\n") + log_push() + + sigmap = SigMap(self.module) + + log(" Creating const node\n") + const_node = Node(self.design, self.module, IdString("\\__CONST__"), NodeType.GRAPH_CONST) + const_port = Port(IdString("\\__CONST__")) + const_port.input = False + const_port.output = True + cb = SigBit(State.Sx) + const_bit = Bit(cb, self.design, self.module, const_node, const_port, 0) + const_node.ports.append(const_port) + const_port.bits.append(const_bit) + + self.nodes.append(const_node) + self.wires.append(const_node) + log(" Creating cell nodes\n") + + for cell in self.module.selected_cells(): + c = PyCell(self.design, self.module, cell.name, cell) + for first, second in cell.connections_.items(): + p = Port(first) + p.input = cell.input(p.name) + p.output = cell.output(p.name) + for bit in sigmap(second).to_sigbit_vector(): + b = Bit(bit, self.design, self.module, c, p, len(p.bits)) + p.bits.append(b) + c.ports.append(p) + + self.cells.append(c) + + log(" Creating wire nodes\n") + + for wire in self.module.selected_wires(): + node = PyWire(self.design, self.module, wire.name) + p = Port(IdString("")) + if wire.port_input: + node.nodeType = NodeType.GRAPH_PI + p.name = IdString("\\PI") + p.input = False + p.output = True + elif wire.port_output: + node.nodeType = NodeType.GRAPH_PO + p.name = IdString("\\PO") + p.input = True + p.output = False + for bit in sigmap(wire).to_sigbit_set(): + b = Bit(bit, self.design, self.module, node, p, len(p.bits)) + p.bits.append(b) + node.ports.append(p) + self.wires.append(node) + + self.nodes.extend(self.cells) + self.nodes.extend(wire for wire in self.wires if wire.nodeType in [NodeType.GRAPH_PI, NodeType.GRAPH_PO]) + + log(" Creating node index for fast lookup\n") + + idx = 0 + + for node in self.nodes: + self.node_index[node.name] = idx + idx += 1 + + log(" Creating node bits (= const + cell + PI + PO)\n") + + for node in self.nodes: + for port in node.ports: + for bit in port.bits: + self.node_bits.append(bit) + + log(" Creating wire bits\n") + + for wire in self.wires: + for port in wire.ports: + for bit in port.bits: + self.wire_bits.append(bit) + + log(" Creating node bit index for fast lookup\n") + + idx = 0 + + for bit in self.node_bits: + self.node_bit_index[bit] = idx + idx += 1 + + log(" Creating wire bit index for fast lookup\n") + + idx = 0 + + for bit in self.wire_bits: + self.wire_bit_index[bit] = idx + idx += 1 + + log(" Mapping port.wire connections to wire bit index\n") + + idx = 0 + + wbitmap = {} + for wbit in self.wire_bits: + wbitmap[wbit.bit] = idx + idx += 1 + + inputTriplets = [] + outputTriplets = [(0,0,1)] + + log(" Mapping node bits to wire bits\n") + + idx = 0 + + for nbit in self.node_bits: + row = idx + idx += 1 + col = 0 + val = 1 + + def check_wire(): + nonlocal nbit + try: + wire = nbit.bit.wire + return True + except: + return False + + if check_wire() and not self.design.selected_member(self.module.name, self.module.wire(nbit.bit.wire.name).name): + continue + + if check_wire(): + col = wbitmap[nbit.bit] + + triplet = (row, col, val) + + if col == 0 and row != 0: + inputTriplets.append(triplet) + continue + + if nbit.node.nodeType == NodeType.GRAPH_CELL: + cell = nbit.node + if check_wire() and self.design.selected_member(self.module.name, self.module.wire(nbit.bit.wire.name).name): + if cell.cell.input(nbit.port.name): + inputTriplets.append(triplet) + if cell.cell.output(nbit.port.name): + outputTriplets.append(triplet) + continue + + if nbit.node.nodeType == NodeType.GRAPH_PI and self.design.selected_member(self.module.name, self.module.wire(nbit.bit.wire.name).name): + outputTriplets.append(triplet) + continue + + if nbit.node.nodeType == NodeType.GRAPH_PO and self.design.selected_member(self.module.name, self.module.wire(nbit.bit.wire.name).name): + inputTriplets.append(triplet) + continue + + log(" Creating port-to-wire incidence matrices\n") + + sizeX = len(self.node_bits) + sizeY= len(self.wire_bits) + + inputRows = [i[0] for i in inputTriplets] + inputCols = [i[1] for i in inputTriplets] + inputVals = [i[2] for i in inputTriplets] + self.incoming = coo_matrix((inputVals, (inputRows, inputCols)), shape=(sizeX, sizeY), dtype='int32') + + outputRows = [i[0] for i in outputTriplets] + outputCols = [i[1] for i in outputTriplets] + outputVals = [i[2] for i in outputTriplets] + self.outgoing = coo_matrix((outputVals, (outputRows, outputCols)), shape=(sizeX, sizeY), dtype='int32') + + def dot(self): + log_header(self.design, "Creating 'dot' bipartite module graph representation of module " + self.module.name.str() + "\n") + log_push() + bitmap = {} + + ss = "digraph g{\n" + ss += " rankdir = LR\n" + nidx = 0 + pidx = 0 + bidx = 0 + cells_wires = [] + cells_wires.extend(self.cells) + cells_wires.extend(self.wires) + + idx = 0 + + for node in cells_wires: + for port in node.ports: + for bit in port.bits: + bitmap[bit] = idx + idx += 1 + + for node in cells_wires: + ss += " subgraph cluster" + str(nidx) + " {\n" + ss += " style = \"setlinewidth(2)\";\n" + ss += " margin = .2;\n" + ss += " n" + str(node.name.index_) + + def s_cell(): + nonlocal ss + ss += "[shape=ellipse,label=\"" + str(nidx) + ":" + ss += unescape_id(node.cell.type) + "\"" + def s_pi(): + nonlocal ss + ss += "[shape = box, label=\"" + str(nidx) + ":" + ss += unescape_id(node.name.str()) + "\"" + def s_po(): + nonlocal ss + ss += "[shape = diamond, label=\"" + str(nidx) + ":" + ss += unescape_id(node.name.str()) + "\"" + def s_const(): + nonlocal ss + ss += "[shape = octagon, label=\"" + str(nidx) + ":CO\"" + def s_wire(): + nonlocal ss + ss += "[shape = plaintext, label=\"" + str(nidx - len(self.cells)) + ":" + ss += unescape_id(node.name.str()) + "\"" + switch = { + NodeType.GRAPH_CELL : s_cell, + NodeType.GRAPH_PI : s_pi, + NodeType.GRAPH_PO : s_po, + NodeType.GRAPH_CONST : s_const, + NodeType.GRAPH_WIRE : s_wire + } + switch[node.nodeType]() + + ss += "];\n" + + pidx = 0 + for port in node.ports: + ss += " port_" + str(node.name.index_) + "_" + str(port.name.index_) + ss += "[shape=none,label=<\n" + ss += " <table border=\"0\" cellborder=\"1\" cellspacing=\"0\" cellpadding=\"4\" >\n" + ss += " <tr><td bgcolor=\"lightgray\" port=\"p" + str(node.name.index_) + "_" + ss += str(port.name.index_) + "\"> " + ss += unescape_id(port.name.str()) + ss += "</td></tr>\n" + + bidx = 0; + for bit in port.bits: + + ss += " <tr><td bgcolor=\"white\" port=\"b" + str(node.name.index_) + "_" + str(port.name.index_) + "_" + str(bit.pos) + "\"> " + str(bitmap[bit]) + ":" + str(bidx) + "</td></tr>\n" + + bidx += 1 + + ss += " </table>\n >];\n" + + if node.nodeType == NodeType.GRAPH_CELL: + if node.cell.output(port.name): + ss += " n" + str(node.name.index_) + " -> " + "port_" + str(node.name.index_) + "_" + str(port.name.index_) + ":p" + str(node.name.index_) + "_" + str(port.name.index_) + ";\n" + else: + ss += " port_" + str(node.name.index_) + "_" + str(port.name.index_) + ":p" + str(node.name.index_) + "_" + str(port.name.index_) + " -> " + "n" + str(node.name.index_) + ";\n" + if node.nodeType == NodeType.GRAPH_PI or node.nodeType == NodeType.GRAPH_CONST: + ss += " n" + str(node.name.index_) + " -> " + "port_" + str(node.name.index_) + "_" + str(port.name.index_) + ":p" + str(node.name.index_) + "_" + str(port.name.index_) + ";\n" + if node.nodeType == NodeType.GRAPH_PO: + ss += " port_" + str(node.name.index_) + "_" + str(port.name.index_) + ":p" + str(node.name.index_) + "_" + str(port.name.index_) + " -> " + "n" + str(node.name.index_) + ";\n" + + pidx += 1 + ss += " }\n" + nidx += 1 + + for i in range(len(self.incoming.nonzero()[0])): + b1 = self.node_bits[self.incoming.nonzero()[0][i]] + b2 = self.wire_bits[self.incoming.nonzero()[1][i]] + + if b1.node.nodeType == NodeType.GRAPH_PO or b1.node.nodeType == NodeType.GRAPH_CONST: + continue + + ss += " " + ss += "port_" + str(b2.node.name.index_) + "_" + str(b2.port.name.index_) + ":" + ss += "b" + str(b2.node.name.index_) + "_" + str(b2.port.name.index_) + "_" + str(b2.pos) + ss += " -> " + ss += "port_" + str(b1.node.name.index_) + "_" + str(b1.port.name.index_) + ":" + ss += "b" + str(b1.node.name.index_) + "_" + str(b1.port.name.index_) + "_" + str(b1.pos) + ss += ";\n" + + for i in range(len(self.outgoing.nonzero()[0])): + b1 = self.node_bits[self.outgoing.nonzero()[0][i]] + b2 = self.wire_bits[self.outgoing.nonzero()[1][i]] + + if b1.node.nodeType == NodeType.GRAPH_PI: + continue + + ss += " " + ss += "port_" + str(b1.node.name.index_) + "_" + str(b1.port.name.index_) + ":" + ss += "b" + str(b1.node.name.index_) + "_" + str(b1.port.name.index_) + "_" + str(b1.pos) + ss += " -> " + ss += "port_" + str(b2.node.name.index_) + "_" + str(b2.port.name.index_) + ":" + ss += "b" + str(b2.node.name.index_) + "_" + str(b2.port.name.index_) + "_" + str(b2.pos) + ss += ";\n" + + ss += "}\n" + + log_pop() + + return ss + + def save_dot(self, filename): + savetxt(filename, [self.dot()], fmt="%s") + + def save_incoming(self, filename, delimiter = ","): + savetxt(filename, self.incoming.todense(), "%d", delimiter=delimiter) + + def save_outgoing(self, filename, delimiter = ","): + savetxt(filename, self.outgoing.todense(), "%d", delimiter=delimiter) + + def save_adjacency(self, filename, delimiter = ","): + savetxt(filename, (self.outgoing*self.incoming.transpose()).todense(), "%d", delimiter=delimiter) + +p = None + +class NetlistGraphPass(Pass): + + def __init__(self): + super().__init__("netlist_graph", "Generates the Netlist-Graph of a module") + + import argparse + self.parser = argparse.ArgumentParser() + + self.parser.add_argument("-mod", nargs=1, metavar="MOD", help="The Netlist-Graph of the module with the id-string <module> will be generated. If this argument is not given, the first module will be used") + self.parser.add_argument("-dot", nargs=1, metavar="FILE", help="Write the Netlist-Graph to FILE in dot format") + self.parser.add_argument("-i","-incoming", nargs=1, metavar="FILE", help="Write the incoming incidence matrix to FILE in csv format") + self.parser.add_argument("-o","-outgoing", nargs=1, metavar="FILE", help="Write the outgoing incidence matrix to FILE in csv format") + self.parser.add_argument("-a","-adjacency", nargs=1, metavar="FILE", help="Write the adjacency matrix to FILE in csv format") + + def py_help(self): + + log("This pass generates the Netlist-Graph of a module\n") + log(self.parser.format_help()) + + def py_execute(self, args, des): + + args = self.parser.parse_args(args[1:]) + + graph = None + if args.mod: + try: + graph = NetlistGraph(des, des.modules_[IdString(args.mod[0])]) + except KeyError: + log("Module \"" + args.mod[0] + "\" not found!\n") + exit() + else: + graph = NetlistGraph(des, list(des.modules_.values())[0]) + + if args.dot: + graph.save_dot(args.dot[0]) + + if args.i: + graph.save_incoming(args.i[0]) + + if args.o: + graph.save_outgoing(args.o[0]) + + if args.a: + graph.save_adjacency(args.a[0]) + + def py_clear_flags(self): + log("Clear\n") + +if __name__ == "__main__": + + designs = {} + graphs = {} + + testdir = "../../tests/simple/" + + import os + for testcase in os.listdir(testdir): + if not testcase.endswith(".v"): + continue + designs[testcase] = Design() + run_pass("read_verilog " + testdir + testcase, designs[testcase]) + run_pass("hierarchy -check -auto-top", designs[testcase]) + run_pass("proc", designs[testcase]) + run_pass("clean", designs[testcase]) + run_pass("memory", designs[testcase]) + run_pass("clean", designs[testcase]) + run_pass("opt -full", designs[testcase]) + run_pass("clean", designs[testcase]) + graphs[testcase] = NetlistGraph(designs[testcase]) + + file_prefix = "out/" + testcase + graphs[testcase].save_dot(file_prefix + ".dot") + graphs[testcase].save_incoming(file_prefix + "_in.csv") + graphs[testcase].save_outgoing(file_prefix + "_out.csv") + graphs[testcase].save_adjacency(file_prefix + "_adjacency.csv") + +else: + p = NetlistGraphPass() diff --git a/examples/python-api/run.sh b/examples/python-api/run.sh new file mode 100755 index 000000000..5852ea9ac --- /dev/null +++ b/examples/python-api/run.sh @@ -0,0 +1,6 @@ +PYTHONPATH=`pwd`/../../:$PYTHONPATH +mkdir -p out +if [ ! -f ../../libyosys.so ]; then + make -C ../.. +fi +python3.5 netlist_graph.py diff --git a/kernel/cost.h b/kernel/cost.h index e795b571b..41a09eb63 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr); -int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters = dict<RTLIL::IdString, RTLIL::Const>(), +inline int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters = dict<RTLIL::IdString, RTLIL::Const>(), RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr) { static dict<RTLIL::IdString, int> gate_cost = { @@ -76,7 +76,7 @@ int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const return 1; } -int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache) +inline int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache) { return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache); } diff --git a/kernel/driver.cc b/kernel/driver.cc index a0bb7e60a..c539ba569 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -110,6 +110,10 @@ int main(int argc, char **argv) log_error_stderr = true; yosys_banner(); yosys_setup(); +#ifdef WITH_PYTHON + PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str()); + PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str()); +#endif if (argc == 2) { @@ -469,6 +473,10 @@ int main(int argc, char **argv) #endif yosys_setup(); +#ifdef WITH_PYTHON + PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str()); + PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str()); +#endif log_error_atexit = yosys_atexit; for (auto &fn : plugin_filenames) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index b3214579d..efe2c3559 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -76,6 +76,13 @@ RTLIL::Const::Const(const std::vector<bool> &bits) this->bits.push_back(b ? RTLIL::S1 : RTLIL::S0); } +RTLIL::Const::Const(const RTLIL::Const &c) +{ + flags = c.flags; + for (auto b : c.bits) + this->bits.push_back(b); +} + bool RTLIL::Const::operator <(const RTLIL::Const &other) const { if (bits.size() != other.bits.size()) @@ -360,6 +367,10 @@ RTLIL::Design::Design() refcount_modules_ = 0; selection_stack.push_back(RTLIL::Selection()); + +#ifdef WITH_PYTHON + RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this)); +#endif } RTLIL::Design::~Design() @@ -370,8 +381,19 @@ RTLIL::Design::~Design() delete n; for (auto n : verilog_globals) delete n; +#ifdef WITH_PYTHON + RTLIL::Design::get_all_designs()->erase(hashidx_); +#endif } +#ifdef WITH_PYTHON +static std::map<unsigned int, RTLIL::Design*> *all_designs = new std::map<unsigned int, RTLIL::Design*>(); +std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void) +{ + return all_designs; +} +#endif + RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules() { return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_); @@ -627,6 +649,10 @@ RTLIL::Module::Module() design = nullptr; refcount_wires_ = 0; refcount_cells_ = 0; + +#ifdef WITH_PYTHON + RTLIL::Module::get_all_modules()->insert(std::pair<unsigned int, RTLIL::Module*>(hashidx_, this)); +#endif } RTLIL::Module::~Module() @@ -639,7 +665,18 @@ RTLIL::Module::~Module() delete it->second; for (auto it = processes.begin(); it != processes.end(); ++it) delete it->second; +#ifdef WITH_PYTHON + RTLIL::Module::get_all_modules()->erase(hashidx_); +#endif +} + +#ifdef WITH_PYTHON +static std::map<unsigned int, RTLIL::Module*> *all_modules = new std::map<unsigned int, RTLIL::Module*>(); +std::map<unsigned int, RTLIL::Module*> *RTLIL::Module::get_all_modules(void) +{ + return all_modules; } +#endif void RTLIL::Module::reprocess_module(RTLIL::Design *, dict<RTLIL::IdString, RTLIL::Module *>) { @@ -2202,8 +2239,27 @@ RTLIL::Wire::Wire() port_input = false; port_output = false; upto = false; + +#ifdef WITH_PYTHON + RTLIL::Wire::get_all_wires()->insert(std::pair<unsigned int, RTLIL::Wire*>(hashidx_, this)); +#endif +} + +RTLIL::Wire::~Wire() +{ +#ifdef WITH_PYTHON + RTLIL::Wire::get_all_wires()->erase(hashidx_); +#endif } +#ifdef WITH_PYTHON +static std::map<unsigned int, RTLIL::Wire*> *all_wires = new std::map<unsigned int, RTLIL::Wire*>(); +std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void) +{ + return all_wires; +} +#endif + RTLIL::Memory::Memory() { static unsigned int hashidx_count = 123456789; @@ -2213,6 +2269,9 @@ RTLIL::Memory::Memory() width = 1; start_offset = 0; size = 0; +#ifdef WITH_PYTHON + RTLIL::Memory::get_all_memorys()->insert(std::pair<unsigned int, RTLIL::Memory*>(hashidx_, this)); +#endif } RTLIL::Cell::Cell() : module(nullptr) @@ -2223,8 +2282,27 @@ RTLIL::Cell::Cell() : module(nullptr) // log("#memtrace# %p\n", this); memhasher(); + +#ifdef WITH_PYTHON + RTLIL::Cell::get_all_cells()->insert(std::pair<unsigned int, RTLIL::Cell*>(hashidx_, this)); +#endif } +RTLIL::Cell::~Cell() +{ +#ifdef WITH_PYTHON + RTLIL::Cell::get_all_cells()->erase(hashidx_); +#endif +} + +#ifdef WITH_PYTHON +static std::map<unsigned int, RTLIL::Cell*> *all_cells = new std::map<unsigned int, RTLIL::Cell*>(); +std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void) +{ + return all_cells; +} +#endif + bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const { return connections_.count(portname) != 0; @@ -2484,6 +2562,14 @@ RTLIL::SigChunk::SigChunk(RTLIL::SigBit bit) width = 1; } +RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) : data(sigchunk.data) +{ + wire = sigchunk.wire; + data = sigchunk.data; + width = sigchunk.width; + offset = sigchunk.offset; +} + RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const { RTLIL::SigChunk ret; @@ -3868,5 +3954,15 @@ RTLIL::Process *RTLIL::Process::clone() const return new_proc; } +#ifdef WITH_PYTHON +RTLIL::Memory::~Memory() +{ + RTLIL::Memory::get_all_memorys()->erase(hashidx_); +} +static std::map<unsigned int, RTLIL::Memory*> *all_memorys = new std::map<unsigned int, RTLIL::Memory*>(); +std::map<unsigned int, RTLIL::Memory*> *RTLIL::Memory::get_all_memorys(void) +{ + return all_memorys; +} +#endif YOSYS_NAMESPACE_END - diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 52496e702..fcc79e894 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -517,6 +517,7 @@ struct RTLIL::Const Const(RTLIL::State bit, int width = 1); Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector<bool> &bits); + Const(const RTLIL::Const &c); bool operator <(const RTLIL::Const &other) const; bool operator ==(const RTLIL::Const &other) const; @@ -591,6 +592,7 @@ struct RTLIL::SigChunk SigChunk(int val, int width = 32); SigChunk(RTLIL::State bit, int width = 1); SigChunk(RTLIL::SigBit bit); + SigChunk(const RTLIL::SigChunk &sigchunk); RTLIL::SigChunk extract(int offset, int length) const; @@ -615,6 +617,7 @@ struct RTLIL::SigBit SigBit(const RTLIL::SigChunk &chunk); SigBit(const RTLIL::SigChunk &chunk, int index); SigBit(const RTLIL::SigSpec &sig); + SigBit(const RTLIL::SigBit &sigbit); bool operator <(const RTLIL::SigBit &other) const; bool operator ==(const RTLIL::SigBit &other) const; @@ -936,9 +939,13 @@ struct RTLIL::Design } } + std::vector<RTLIL::Module*> selected_modules() const; std::vector<RTLIL::Module*> selected_whole_modules() const; std::vector<RTLIL::Module*> selected_whole_modules_warn() const; +#ifdef WITH_PYTHON + static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void); +#endif }; struct RTLIL::Module : public RTLIL::AttrObject @@ -1194,6 +1201,10 @@ public: RTLIL::SigSpec Allconst (RTLIL::IdString name, int width = 1, const std::string &src = ""); RTLIL::SigSpec Allseq (RTLIL::IdString name, int width = 1, const std::string &src = ""); RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = ""); + +#ifdef WITH_PYTHON + static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void); +#endif }; struct RTLIL::Wire : public RTLIL::AttrObject @@ -1205,7 +1216,7 @@ protected: // use module->addWire() and module->remove() to create or destroy wires friend struct RTLIL::Module; Wire(); - ~Wire() { }; + ~Wire(); public: // do not simply copy wires @@ -1216,6 +1227,10 @@ public: RTLIL::IdString name; int width, start_offset, port_id; bool port_input, port_output, upto; + +#ifdef WITH_PYTHON + static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void); +#endif }; struct RTLIL::Memory : public RTLIL::AttrObject @@ -1227,6 +1242,10 @@ struct RTLIL::Memory : public RTLIL::AttrObject RTLIL::IdString name; int width, start_offset, size; +#ifdef WITH_PYTHON + ~Memory(); + static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void); +#endif }; struct RTLIL::Cell : public RTLIL::AttrObject @@ -1238,6 +1257,7 @@ protected: // use module->addCell() and module->remove() to create or destroy cells friend struct RTLIL::Module; Cell(); + ~Cell(); public: // do not simply copy cells @@ -1278,6 +1298,10 @@ public: } template<typename T> void rewrite_sigspecs(T &functor); + +#ifdef WITH_PYTHON + static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void); +#endif }; struct RTLIL::CaseRule @@ -1338,6 +1362,7 @@ inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_as inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); } inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; } inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; } +inline RTLIL::SigBit::SigBit(const RTLIL::SigBit &sigbit) : wire(sigbit.wire), data(sigbit.data){if(wire) offset = sigbit.offset;} inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const { if (wire == other.wire) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 450e4e4cf..ceb1d1d39 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -57,6 +57,16 @@ # include <sys/sysctl.h> #endif +#ifdef WITH_PYTHON +#if PY_MAJOR_VERSION >= 3 +# define INIT_MODULE PyInit_libyosys + extern "C" PyObject* INIT_MODULE(); +#else +# define INIT_MODULE initlibyosys + extern "C" void INIT_MODULE(); +#endif +#endif + #include <limits.h> #include <errno.h> @@ -477,21 +487,42 @@ int GetSize(RTLIL::Wire *wire) return wire->width; } +bool already_setup = false; + void yosys_setup() { + if(already_setup) + return; + already_setup = true; // if there are already IdString objects then we have a global initialization order bug IdString empty_id; log_assert(empty_id.index_ == 0); IdString::get_reference(empty_id.index_); + #ifdef WITH_PYTHON + PyImport_AppendInittab((char*)"libyosys", INIT_MODULE); + Py_Initialize(); + PyRun_SimpleString("import sys"); + #endif + Pass::init_register(); yosys_design = new RTLIL::Design; yosys_celltypes.setup(); log_push(); } +bool yosys_already_setup() +{ + return already_setup; +} + +bool already_shutdown = false; + void yosys_shutdown() { + if(already_shutdown) + return; + already_shutdown = true; log_pop(); delete yosys_design; @@ -519,9 +550,16 @@ void yosys_shutdown() dlclose(it.second); loaded_plugins.clear(); +#ifdef WITH_PYTHON + loaded_python_plugins.clear(); +#endif loaded_plugin_aliases.clear(); #endif +#ifdef WITH_PYTHON + Py_Finalize(); +#endif + IdString empty_id; IdString::put_reference(empty_id.index_); } diff --git a/kernel/yosys.h b/kernel/yosys.h index c9f973318..a630798bb 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -66,6 +66,10 @@ #include <stdio.h> #include <limits.h> +#ifdef WITH_PYTHON +#include <Python.h> +#endif + #ifndef _YOSYS_ # error It looks like you are trying to build Yosys without the config defines set. \ When building Yosys with a custom make system, make sure you set all the \ @@ -276,6 +280,11 @@ namespace hashlib { } void yosys_setup(); + +#ifdef WITH_PYTHON +bool yosys_already_setup(); +#endif + void yosys_shutdown(); #ifdef YOSYS_ENABLE_TCL @@ -317,6 +326,9 @@ extern std::vector<RTLIL::Design*> pushed_designs; // from passes/cmds/pluginc.cc extern std::map<std::string, void*> loaded_plugins; +#ifdef WITH_PYTHON +extern std::map<std::string, void*> loaded_python_plugins; +#endif extern std::map<std::string, std::string> loaded_plugin_aliases; void load_plugin(std::string filename, std::vector<std::string> aliases); diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index aa6d5b6cc..2b06690f9 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -23,9 +23,17 @@ # include <dlfcn.h> #endif +#ifdef WITH_PYTHON +# include <boost/algorithm/string/predicate.hpp> +# include <Python.h> +#endif + YOSYS_NAMESPACE_BEGIN std::map<std::string, void*> loaded_plugins; +#ifdef WITH_PYTHON +std::map<std::string, void*> loaded_python_plugins; +#endif std::map<std::string, std::string> loaded_plugin_aliases; #ifdef YOSYS_ENABLE_PLUGINS @@ -36,7 +44,37 @@ void load_plugin(std::string filename, std::vector<std::string> aliases) if (filename.find('/') == std::string::npos) filename = "./" + filename; + #ifdef WITH_PYTHON + if (!loaded_plugins.count(filename) && !loaded_python_plugins.count(filename)) { + #else if (!loaded_plugins.count(filename)) { + #endif + + #ifdef WITH_PYTHON + if(boost::algorithm::ends_with(filename, ".py")) + { + int last_slash = filename.find_last_of('/'); + std::string path = filename.substr(0, last_slash); + filename = filename.substr(last_slash+1, filename.size()); + filename = filename.substr(0,filename.size()-3); + PyRun_SimpleString(("sys.path.insert(0,\""+path+"\")").c_str()); + PyObject *filename_p = PyUnicode_FromString(filename.c_str()); + if(filename_p == NULL) + { + log_cmd_error("Issues converting `%s' to Python\n", filename.c_str()); + return; + } + PyObject *module_p = PyImport_Import(filename_p); + if(module_p == NULL) + { + log_cmd_error("Can't load python module `%s'\n", filename.c_str()); + return; + } + loaded_python_plugins[orig_filename] = module_p; + Pass::init_register(); + } else { + #endif + void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); if (hdl == NULL && orig_filename.find('/') == std::string::npos) hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL); @@ -44,6 +82,10 @@ void load_plugin(std::string filename, std::vector<std::string> aliases) log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); loaded_plugins[orig_filename] = hdl; Pass::init_register(); + + #ifdef WITH_PYTHON + } + #endif } for (auto &alias : aliases) @@ -107,7 +149,11 @@ struct PluginPass : public Pass { if (list_mode) { log("\n"); +#ifdef WITH_PYTHON + if (loaded_plugins.empty() and loaded_python_plugins.empty()) +#else if (loaded_plugins.empty()) +#endif log("No plugins loaded.\n"); else log("Loaded plugins:\n"); @@ -115,6 +161,11 @@ struct PluginPass : public Pass { for (auto &it : loaded_plugins) log(" %s\n", it.first.c_str()); +#ifdef WITH_PYTHON + for (auto &it : loaded_python_plugins) + log(" %s\n", it.first.c_str()); +#endif + if (!loaded_plugin_aliases.empty()) { log("\n"); int max_alias_len = 1; diff --git a/py_wrap_generator.py b/py_wrap_generator.py new file mode 100644 index 000000000..658779ca6 --- /dev/null +++ b/py_wrap_generator.py @@ -0,0 +1,2101 @@ +# +# yosys -- Yosys Open SYnthesis Suite +# +# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Author Benedikt Tutzer +# + +import copy + +#Map c++ operator Syntax to Python functions +wrappable_operators = { + "<" : "__lt__", + "==": "__eq__", + "!=": "__ne__", + "+" : "__add__", + "-" : "__sub__", + "*" : "__mul__", + "/" : "__div__", + "()": "__call__" + } + +#Restrict certain strings from being function names in Python +keyword_aliases = { + "in" : "in_", + "False" : "False_", + "None" : "None_", + "True" : "True_", + "and" : "and_", + "as" : "as_", + "assert" : "assert_", + "break" : "break_", + "class" : "class_", + "continue" : "continue_", + "def" : "def_", + "del" : "del_", + "elif" : "elif_", + "else" : "else_", + "except" : "except_", + "for" : "for_", + "from" : "from_", + "global" : "global_", + "if" : "if_", + "import" : "import_", + "in" : "in_", + "is" : "is_", + "lambda" : "lambda_", + "nonlocal" : "nonlocal_", + "not" : "not_", + "or" : "or_", + "pass" : "pass_", + "raise" : "raise_", + "return" : "return_", + "try" : "try_", + "while" : "while_", + "with" : "with_", + "yield" : "yield_" + } + +#These can be used without any explicit conversion +primitive_types = ["void", "bool", "int", "double", "size_t", "std::string", + "string", "State", "char_p"] + +from enum import Enum + +#Ways to link between Python- and C++ Objects +class link_types(Enum): + global_list = 1 #Manage a global list of objects in C++, the Python + #object contains a key to find the corresponding C++ + #object and a Pointer to the object to verify it is + #still the same, making collisions unlikely to happen + ref_copy = 2 #The Python object contains a copy of the C++ object. + #The C++ object is deleted when the Python object gets + #deleted + pointer = 3 #The Python Object contains a pointer to it's C++ + #counterpart + derive = 4 #The Python-Wrapper is derived from the C++ object. + +class attr_types(Enum): + star = "*" + amp = "&" + ampamp = "&&" + default = "" + +#For source-files +class Source: + name = "" + classes = [] + + def __init__(self, name, classes): + self.name = name + self.classes = classes + +#Splits a list by the given delimiter, without splitting strings inside +#pointy-brackets (< and >) +def split_list(str_def, delim): + str_def = str_def.strip() + if len(str_def) == 0: + return [] + if str_def.count(delim) == 0: + return [str_def] + if str_def.count("<") == 0: + return str_def.split(delim) + if str_def.find("<") < str_def.find(" "): + closing = find_closing(str_def[str_def.find("<")+1:], "<", ">") + str_def.find("<") + comma = str_def[closing:].find(delim) + if comma == -1: + return [str_def] + comma = closing + comma + else: + comma = str_def.find(delim) + rest = split_list(str_def[comma+1:], delim) + ret = [str_def[:comma]] + if rest != None and len(rest) != 0: + ret.extend(rest) + return ret + +#Represents a Type +class WType: + name = "" + cont = None + attr_type = attr_types.default + + def __init__(self, name = "", cont = None, attr_type = attr_types.default): + self.name = name + self.cont = cont + self.attr_type = attr_type + + #Python type-string + def gen_text(self): + text = self.name + if self.name in enum_names: + text = enum_by_name(self.name).namespace + "::" + self.name + if self.cont != None: + return known_containers[self.name].typename + return text + + #C++ type-string + def gen_text_cpp(self): + postfix = "" + if self.attr_type == attr_types.star: + postfix = "*" + if self.name in primitive_types: + return self.name + postfix + if self.name in enum_names: + return enum_by_name(self.name).namespace + "::" + self.name + postfix + if self.name in classnames: + return class_by_name(self.name).namespace + "::" + self.name + postfix + text = self.name + if self.cont != None: + text += "<" + for a in self.cont.args: + text += a.gen_text_cpp() + ", " + text = text[:-2] + text += ">" + return text + + @staticmethod + def from_string(str_def, containing_file, line_number): + str_def = str_def.strip() + if len(str_def) == 0: + return None + str_def = str_def.replace("RTLIL::SigSig", "std::pair<SigSpec, SigSpec>").replace("SigSig", "std::pair<SigSpec, SigSpec>") + t = WType() + t.name = "" + t.cont = None + t.attr_type = attr_types.default + if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "): + candidate = WContainer.from_string(str_def, containing_file, line_number) + if candidate == None: + return None + t.name = str_def[:str_def.find("<")] + + if t.name.count("*") + t.name.count("&") > 1: + return None + + if t.name.count("*") == 1 or str_def[0] == '*' or str_def[-1] == '*': + t.attr_type = attr_types.star + t.name = t.name.replace("*","") + elif t.name.count("&&") == 1: + t.attr_type = attr_types.ampamp + t.name = t.name.replace("&&","") + elif t.name.count("&") == 1 or str_def[0] == '&' or str_def[-1] == '&': + t.attr_type = attr_types.amp + t.name = t.name.replace("&","") + + t.cont = candidate + if(t.name not in known_containers): + return None + return t + + prefix = "" + + if str.startswith(str_def, "unsigned "): + prefix = "unsigned " + str_def = str_def[9:] + while str.startswith(str_def, "long "): + prefix= "long " + prefix + str_def = str_def[5:] + while str.startswith(str_def, "short "): + prefix = "short " + prefix + str_def = str_def[6:] + + str_def = str_def.split("::")[-1] + + if str_def.count("*") + str_def.count("&") >= 2: + return None + + if str_def.count("*") == 1: + t.attr_type = attr_types.star + str_def = str_def.replace("*","") + elif str_def.count("&&") == 1: + t.attr_type = attr_types.ampamp + str_def = str_def.replace("&&","") + elif str_def.count("&") == 1: + t.attr_type = attr_types.amp + str_def = str_def.replace("&","") + + if len(str_def) > 0 and str_def.split("::")[-1] not in primitive_types and str_def.split("::")[-1] not in classnames and str_def.split("::")[-1] not in enum_names: + return None + + if str_def.count(" ") == 0: + t.name = (prefix + str_def).replace("char_p", "char *") + t.cont = None + return t + return None + +#Represents a container-type +class WContainer: + name = "" + args = [] + + def from_string(str_def, containing_file, line_number): + if str_def == None or len(str_def) < 4: + return None + cont = WContainer() + cont.name = str_def[:str_def.find("<")] + str_def = str_def[str_def.find("<")+1:find_closing(str_def, "<", ">")] + cont.args = [] + for arg in split_list(str_def, ","): + candidate = WType.from_string(arg.strip(), containing_file, line_number) + if candidate == None: + return None + cont.args.append(candidate) + return cont + +#Translators between Python and C++ containers +#Base Type +class Translator: + tmp_cntr = 0 + typename = "DefaultType" + orig_name = "DefaultCpp" + + @classmethod + def gen_type(c, types): + return "\nImplement a function that outputs the c++ type of this container here\n" + + @classmethod + def translate(c, varname, types, prefix): + return "\nImplement a function translating a python container to a c++ container here\n" + + @classmethod + def translate_cpp(c, varname, types, prefix, ref): + return "\nImplement a function translating a c++ container to a python container here\n" + +#Translates list-types (vector, pool, set), that only differ in their name and +#the name of the insertion function +class PythonListTranslator(Translator): + typename = "boost::python::list" + insert_name = "Default" + + #generate the c++ type string + @classmethod + def gen_type(c, types): + text = c.orig_name + "<" + if types[0].name in primitive_types: + text += types[0].name + elif types[0].name in known_containers: + text += known_containers[types[0].name].gen_type(types[0].cont.args) + else: + text += class_by_name(types[0].name).namespace + "::" + types[0].name + if types[0].attr_type == attr_types.star: + text += "*" + text += ">" + return text + + #Generate C++ code to translate from a boost::python::list + @classmethod + def translate(c, varname, types, prefix): + text = prefix + c.gen_type(types) + " " + varname + "___tmp;" + cntr_name = "cntr_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "); " + cntr_name + "++)" + text += prefix + "{" + tmp_name = "tmp_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + if types[0].name in known_containers: + text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);" + text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t") + tmp_name = tmp_name + "___tmp" + text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");" + elif types[0].name in classnames: + text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);" + if types[0].attr_type == attr_types.star: + text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + "->get_cpp_obj());" + else: + text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());" + else: + text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);" + text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");" + text += prefix + "}" + return text + + #Generate C++ code to translate to a boost::python::list + @classmethod + def translate_cpp(c, varname, types, prefix, ref): + text = prefix + c.typename + " " + varname + "___tmp;" + tmp_name = "tmp_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + if ref: + text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" + else: + text += prefix + "for(auto " + tmp_name + " : " + varname + ")" + text += prefix + "{" + if types[0].name in classnames: + if types[0].attr_type == attr_types.star: + text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));" + else: + text += prefix + "\t" + varname + "___tmp.append(*" + types[0].name + "::get_py_obj(&" + tmp_name + "));" + elif types[0].name in known_containers: + text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[0].attr_type == attr_types.star) + text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + "___tmp);" + else: + text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");" + text += prefix + "}" + return text + +#Sub-type for std::set +class SetTranslator(PythonListTranslator): + insert_name = "insert" + orig_name = "std::set" + +#Sub-type for std::vector +class VectorTranslator(PythonListTranslator): + insert_name = "push_back" + orig_name = "std::vector" + +#Sub-type for pool +class PoolTranslator(PythonListTranslator): + insert_name = "insert" + orig_name = "pool" + +#Translates dict-types (dict, std::map), that only differ in their name and +#the name of the insertion function +class PythonDictTranslator(Translator): + typename = "boost::python::dict" + insert_name = "Default" + + @classmethod + def gen_type(c, types): + text = c.orig_name + "<" + if types[0].name in primitive_types: + text += types[0].name + elif types[0].name in known_containers: + text += known_containers[types[0].name].gen_type(types[0].cont.args) + else: + text += class_by_name(types[0].name).namespace + "::" + types[0].name + if types[0].attr_type == attr_types.star: + text += "*" + text += ", " + if types[1].name in primitive_types: + text += types[1].name + elif types[1].name in known_containers: + text += known_containers[types[1].name].gen_type(types[1].cont.args) + else: + text += class_by_name(types[1].name).namespace + "::" + types[1].name + if types[1].attr_type == attr_types.star: + text += "*" + text += ">" + return text + + #Generate c++ code to translate from a boost::python::dict + @classmethod + def translate(c, varname, types, prefix): + text = prefix + c.gen_type(types) + " " + varname + "___tmp;" + text += prefix + "boost::python::list " + varname + "_keylist = " + varname + ".keys();" + cntr_name = "cntr_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "_keylist); " + cntr_name + "++)" + text += prefix + "{" + key_tmp_name = "key_tmp_" + str(Translator.tmp_cntr) + val_tmp_name = "val_tmp_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + + if types[0].name in known_containers: + text += prefix + "\t" + known_containers[types[0].name].typename + " " + key_tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "_keylist[ " + cntr_name + " ]);" + text += known_containers[types[0].name].translate(key_tmp_name, types[0].cont.args, prefix+"\t") + key_tmp_name = key_tmp_name + "___tmp" + elif types[0].name in classnames: + text += prefix + "\t" + types[0].name + "* " + key_tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "_keylist[ " + cntr_name + " ]);" + else: + text += prefix + "\t" + types[0].name + " " + key_tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "_keylist[ " + cntr_name + " ]);" + + if types[1].name in known_containers: + text += prefix + "\t" + known_containers[types[1].name].typename + " " + val_tmp_name + " = boost::python::extract<" + known_containers[types[1].name].typename + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);" + text += known_containers[types[1].name].translate(val_tmp_name, types[1].cont.args, prefix+"\t") + val_tmp_name = val_tmp_name + "___tmp" + elif types[1].name in classnames: + text += prefix + "\t" + types[1].name + "* " + val_tmp_name + " = boost::python::extract<" + types[1].name + "*>(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);" + else: + text += prefix + "\t" + types[1].name + " " + val_tmp_name + " = boost::python::extract<" + types[1].name + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);" + + text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(std::pair<" + types[0].gen_text_cpp() + ", " + types[1].gen_text_cpp() + ">(" + + if types[0].name not in classnames: + text += key_tmp_name + else: + if types[0].attr_type != attr_types.star: + text += "*" + text += key_tmp_name + "->get_cpp_obj()" + + text += ", " + if types[1].name not in classnames: + text += val_tmp_name + else: + if types[1].attr_type != attr_types.star: + text += "*" + text += val_tmp_name + "->get_cpp_obj()" + text += "));\n" + prefix + "}" + return text + + #Generate c++ code to translate to a boost::python::dict + @classmethod + def translate_cpp(c, varname, types, prefix, ref): + text = prefix + c.typename + " " + varname + "___tmp;" + tmp_name = "tmp_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + if ref: + text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" + else: + text += prefix + "for(auto " + tmp_name + " : " + varname + ")" + text += prefix + "{" + if types[1].name in known_containers: + text += prefix + "\tauto " + tmp_name + "_second = " + tmp_name + ".second;" + text += known_containers[types[1].name].translate_cpp(tmp_name + "_second", types[1].cont.args, prefix + "\t", types[1].attr_type == attr_types.star) + + if types[0].name in classnames: + text += prefix + "\t" + varname + "___tmp[" + types[0].name + "::get_py_obj(" + tmp_name + ".first)] = " + elif types[0].name not in known_containers: + text += prefix + "\t" + varname + "___tmp[" + tmp_name + ".first] = " + + if types[1].name in classnames: + if types[1].attr_type == attr_types.star: + text += types[1].name + "::get_py_obj(" + tmp_name + ".second);" + else: + text += "*" + types[1].name + "::get_py_obj(&" + tmp_name + ".second);" + elif types[1].name in known_containers: + text += tmp_name + "_second___tmp;" + else: + text += tmp_name + ".second;" + text += prefix + "}" + return text + +#Sub-type for dict +class DictTranslator(PythonDictTranslator): + insert_name = "insert" + orig_name = "dict" + +#Sub_type for std::map +class MapTranslator(PythonDictTranslator): + insert_name = "insert" + orig_name = "std::map" + +#Translator for std::pair. Derived from PythonDictTranslator because the +#gen_type function is the same (because both have two template parameters) +class TupleTranslator(PythonDictTranslator): + typename = "boost::python::tuple" + orig_name = "std::pair" + + #Generate c++ code to translate from a boost::python::tuple + @classmethod + def translate(c, varname, types, prefix): + text = prefix + types[0].name + " " + varname + "___tmp_0 = boost::python::extract<" + types[0].name + ">(" + varname + "[0]);" + text += prefix + types[1].name + " " + varname + "___tmp_1 = boost::python::extract<" + types[1].name + ">(" + varname + "[1]);" + text += prefix + TupleTranslator.gen_type(types) + " " + varname + "___tmp(" + if types[0].name.split(" ")[-1] in primitive_types: + text += varname + "___tmp_0, " + else: + text += varname + "___tmp_0.get_cpp_obj(), " + if types[1].name.split(" ")[-1] in primitive_types: + text += varname + "___tmp_1);" + else: + text += varname + "___tmp_1.get_cpp_obj());" + return text + + #Generate c++ code to translate to a boost::python::tuple + @classmethod + def translate_cpp(c, varname, types, prefix, ref): + text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);" + return text + tmp_name = "tmp_" + str(Translator.tmp_cntr) + Translator.tmp_cntr = Translator.tmp_cntr + 1 + if ref: + text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" + else: + text += prefix + "for(auto " + tmp_name + " : " + varname + ")" + text += prefix + "{" + if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names: + text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");" + elif types[0].name in known_containers: + text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[1].attr_type == attr_types.star) + text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "___tmp);" + elif types[0].name in classnames: + text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));" + text += prefix + "}" + return text + +#Associate the Translators with their c++ type +known_containers = { + "std::set" : SetTranslator, + "std::vector" : VectorTranslator, + "pool" : PoolTranslator, + "dict" : DictTranslator, + "std::pair" : TupleTranslator, + "std::map" : MapTranslator +} + +class Attribute: + wtype = None + varname = None + is_const = False + default_value = None + pos = None + pos_counter = 0 + + def __init__(self, wtype, varname, is_const = False, default_value = None): + self.wtype = wtype + self.varname = varname + self.is_const = is_const + self.default_value = None + self.container = None + + @staticmethod + def from_string(str_def, containing_file, line_number): + if len(str_def) < 3: + return None + orig = str_def + arg = Attribute(None, None) + prefix = "" + arg.wtype = None + arg.varname = None + arg.is_const = False + arg.default_value = None + arg.container = None + if str.startswith(str_def, "const "): + arg.is_const = True + str_def = str_def[6:] + if str.startswith(str_def, "unsigned "): + prefix = "unsigned " + str_def = str_def[9:] + while str.startswith(str_def, "long "): + prefix= "long " + prefix + str_def = str_def[5:] + while str.startswith(str_def, "short "): + prefix = "short " + prefix + str_def = str_def[6:] + + if str_def.find("<") != -1 and str_def.find("<") < str_def.find(" "): + closing = find_closing(str_def[str_def.find("<"):], "<", ">") + str_def.find("<") + 1 + arg.wtype = WType.from_string(str_def[:closing].strip(), containing_file, line_number) + str_def = str_def[closing+1:] + else: + if str_def.count(" ") > 0: + arg.wtype = WType.from_string(prefix + str_def[:str_def.find(" ")].strip(), containing_file, line_number) + str_def = str_def[str_def.find(" ")+1:] + else: + arg.wtype = WType.from_string(prefix + str_def.strip(), containing_file, line_number) + str_def = "" + arg.varname = "" + + if arg.wtype == None: + return None + if str_def.count("=") == 0: + arg.varname = str_def.strip() + if arg.varname.find(" ") > 0: + return None + else: + arg.varname = str_def[:str_def.find("=")].strip() + if arg.varname.find(" ") > 0: + return None + str_def = str_def[str_def.find("=")+1:].strip() + arg.default_value = str_def[arg.varname.find("=")+1:].strip() + if len(arg.varname) == 0: + arg.varname = None + return arg + if arg.varname[0] == '*': + arg.wtype.attr_type = attr_types.star + arg.varname = arg.varname[1:] + elif arg.varname[0] == '&': + if arg.wtype.attr_type != attr_types.default: + return None + if arg.varname[1] == '&': + arg.wtype.attr_type = attr_types.ampamp + arg.varname = arg.varname[2:] + else: + arg.wtype.attr_type = attr_types.amp + arg.varname = arg.varname[1:] + return arg + + #Generates the varname. If the attribute has no name in the header file, + #a name is generated + def gen_varname(self): + if self.varname != None: + return self.varname + if self.wtype.name == "void": + return "" + if self.pos == None: + self.pos = Attribute.pos_counter + Attribute.pos_counter = Attribute.pos_counter + 1 + return "gen_varname_" + str(self.pos) + + #Generates the text for the function headers with wrapper types + def gen_listitem(self): + prefix = "" + if self.is_const: + prefix = "const " + if self.wtype.name in classnames: + return prefix + self.wtype.name + "* " + self.gen_varname() + if self.wtype.name in known_containers: + return prefix + known_containers[self.wtype.name].typename + " " + self.gen_varname() + return prefix + self.wtype.name + " " + self.gen_varname() + + #Generates the test for the function headers with c++ types + def gen_listitem_cpp(self): + prefix = "" + if self.is_const: + prefix = "const " + infix = "" + if self.wtype.attr_type == attr_types.star: + infix = "*" + elif self.wtype.attr_type == attr_types.amp: + infix = "&" + elif self.wtype.attr_type == attr_types.ampamp: + infix = "&&" + if self.wtype.name in known_containers: + return prefix + known_containers[self.wtype.name].gen_type(self.wtype.cont.args) + " " + infix + self.gen_varname() + if self.wtype.name in classnames: + return prefix + class_by_name(self.wtype.name).namespace + "::" + self.wtype.name + " " + infix + self.gen_varname() + return prefix + self.wtype.name + " " + infix + self.gen_varname() + + #Generates the listitem withtout the varname, so the signature can be + #compared + def gen_listitem_hash(self): + prefix = "" + if self.is_const: + prefix = "const " + if self.wtype.name in classnames: + return prefix + self.wtype.name + "* " + if self.wtype.name in known_containers: + return known_containers[self.wtype.name].typename + return prefix + self.wtype.name + + #Generate Translation code for the attribute + def gen_translation(self): + if self.wtype.name in known_containers: + return known_containers[self.wtype.name].translate(self.gen_varname(), self.wtype.cont.args, "\n\t\t") + return "" + + #Generate Translation code from c++ for the attribute + def gen_translation_cpp(self): + if self.wtype.name in known_containers: + return known_containers[self.wtype.name].translate_cpp(self.gen_varname(), self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star) + return "" + + #Generate Text for the call + def gen_call(self): + ret = self.gen_varname() + if self.wtype.name in known_containers: + if self.wtype.attr_type == attr_types.star: + return "&" + ret + "___tmp" + return ret + "___tmp" + if self.wtype.name in classnames: + if self.wtype.attr_type != attr_types.star: + ret = "*" + ret + return ret + "->get_cpp_obj()" + if self.wtype.name == "char *" and self.gen_varname() in ["format", "fmt"]: + return "\"%s\", " + self.gen_varname() + if self.wtype.attr_type == attr_types.star: + return "&" + ret + return ret + + def gen_call_cpp(self): + ret = self.gen_varname() + if self.wtype.name.split(" ")[-1] in primitive_types or self.wtype.name in enum_names: + if self.wtype.attr_type == attr_types.star: + return "&" + ret + return ret + if self.wtype.name not in classnames: + if self.wtype.attr_type == attr_types.star: + return "&" + ret + "___tmp" + return ret + "___tmp" + if self.wtype.attr_type != attr_types.star: + ret = "*" + ret + return self.wtype.name + "::get_py_obj(" + self.gen_varname() + ")" + + #Generate cleanup code + def gen_cleanup(self): + if self.wtype.name in primitive_types or self.wtype.name in classnames or self.wtype.name in enum_names or not self.wtype.attr_type == attr_types.star or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star): + return "" + return "\n\t\tdelete " + self.gen_varname() + "___tmp;" + +class WClass: + name = None + namespace = None + link_type = None + id_ = None + string_id = None + hash_id = None + needs_clone = False + found_funs = [] + found_vars = [] + found_constrs = [] + + def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False): + self.name = name + self.namespace = None + self.link_type = link_type + self.id_ = id_ + self.string_id = string_id + self.hash_id = hash_id + self.needs_clone = needs_clone + self.found_funs = [] + self.found_vars = [] + self.found_constrs = [] + + def printable_constrs(self): + ret = 0 + for con in self.found_constrs: + if not con.protected: + ret += 1 + return ret + + def gen_decl(self, filename): + long_name = self.namespace + "::" + self.name + + text = "\n\t// WRAPPED from " + filename + text += "\n\tstruct " + self.name + if self.link_type == link_types.derive: + text += " : public " + self.namespace + "::" + self.name + text += "\n\t{\n" + + if self.link_type != link_types.derive: + + text += "\t\t" + long_name + "* ref_obj;\n" + + if self.link_type == link_types.ref_copy or self.link_type == link_types.pointer: + text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn ref_obj;\n\t\t}\n" + elif self.link_type == link_types.global_list: + text += "\t\t" + self.id_.wtype.name + " " + self.id_.varname + ";\n" + text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{" + text += "\n\t\t\t" + long_name + "* ret = " + long_name + "::get_all_" + self.name.lower() + "s()->at(this->" + self.id_.varname + ");" + text += "\n\t\t\tif(ret != NULL && ret == this->ref_obj)" + text += "\n\t\t\t\treturn ret;" + text += "\n\t\t\tthrow std::runtime_error(\"" + self.name + "'s c++ object does not exist anymore.\");" + text += "\n\t\t\treturn NULL;" + text += "\n\t\t}\n" + + #if self.link_type != link_types.pointer: + text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" + text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" + if self.link_type == link_types.pointer: + text += "\n\t\t\tret->ref_obj = ref;" + if self.link_type == link_types.ref_copy: + if self.needs_clone: + text += "\n\t\t\tret->ref_obj = ref->clone();" + else: + text += "\n\t\t\tret->ref_obj = new "+long_name+"(*ref);" + if self.link_type == link_types.global_list: + text += "\n\t\t\tret->ref_obj = ref;" + text += "\n\t\t\tret->" + self.id_.varname + " = ret->ref_obj->" + self.id_.varname + ";" + text += "\n\t\t\treturn ret;" + text += "\n\t\t}\n" + + if self.link_type == link_types.ref_copy: + text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + " ref)\n\t\t{" + text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" + if self.needs_clone: + text += "\n\t\t\tret->ref_obj = ref.clone();" + else: + text += "\n\t\t\tret->ref_obj = new "+long_name+"(ref);" + text += "\n\t\t\treturn ret;" + text += "\n\t\t}\n" + + if self.link_type != link_types.global_list: + text += "\n\t\t~" + self.name + "()\n\t\t{" + text += "\n\t\t\t//delete(this->ref_obj);\n\t\t}\n" + + for con in self.found_constrs: + text += con.gen_decl() + for var in self.found_vars: + text += var.gen_decl() + for fun in self.found_funs: + text += fun.gen_decl() + + + if self.link_type == link_types.derive: + duplicates = {} + for fun in self.found_funs: + if fun.name in duplicates: + fun.gen_alias() + duplicates[fun.name].gen_alias() + else: + duplicates[fun.name] = fun + + text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn (" + self.namespace + "::" + self.name +"*)this;\n\t\t}\n" + text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" + text += "\n\t\t\treturn (" + self.name + "*)ref;" + text += "\n\t\t}\n" + + for con in self.found_constrs: + text += con.gen_decl_derive() + for var in self.found_vars: + text += var.gen_decl() + for fun in self.found_funs: + text += fun.gen_decl_virtual() + + if self.hash_id != None: + text += "\n\t\tunsigned int get_hash_py()" + text += "\n\t\t{" + text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";" + text += "\n\t\t}" + + text += "\n\t};\n" + + if self.link_type == link_types.derive: + text += "\n\tstruct " + self.name + "Wrap : " + self.name + ", boost::python::wrapper<" + self.name + ">" + text += "\n\t{" + + for con in self.found_constrs: + text += con.gen_decl_wrapperclass() + for fun in self.found_funs: + text += fun.gen_default_impl() + + text += "\n\t};" + + text += "\n\tstd::ostream &operator<<(std::ostream &ostr, const " + self.name + " &ref)" + text += "\n\t{" + text += "\n\t\tostr << \"" + self.name + if self.string_id != None: + text +=" \\\"\"" + text += " << ref.get_cpp_obj()->" + self.string_id + text += " << \"\\\"\"" + else: + text += " at \" << ref.get_cpp_obj()" + text += ";" + text += "\n\t\treturn ostr;" + text += "\n\t}" + text += "\n" + + return text + + def gen_funs(self, filename): + text = "" + if self.link_type != link_types.derive: + for con in self.found_constrs: + text += con.gen_def() + for var in self.found_vars: + text += var.gen_def() + for fun in self.found_funs: + text += fun.gen_def() + else: + for var in self.found_vars: + text += var.gen_def() + for fun in self.found_funs: + text += fun.gen_def_virtual() + return text + + def gen_boost_py(self): + text = "\n\t\tclass_<" + self.name + if self.link_type == link_types.derive: + text += "Wrap, boost::noncopyable" + text += ">(\"" + self.name + "\"" + if self.printable_constrs() == 0 or not self.contains_default_constr(): + text += ", no_init" + text += ")" + text += "\n\t\t\t.def(boost::python::self_ns::str(boost::python::self_ns::self))" + text += "\n\t\t\t.def(boost::python::self_ns::repr(boost::python::self_ns::self))" + for con in self.found_constrs: + text += con.gen_boost_py() + for var in self.found_vars: + text += var.gen_boost_py() + static_funs = [] + for fun in self.found_funs: + text += fun.gen_boost_py() + if fun.is_static and fun.alias not in static_funs: + static_funs.append(fun.alias) + for fun in static_funs: + text += "\n\t\t\t.staticmethod(\"" + fun + "\")" + + if self.hash_id != None: + text += "\n\t\t\t.def(\"__hash__\", &" + self.name + "::get_hash_py)" + text += "\n\t\t\t;\n" + return text + + def contains_default_constr(self): + for c in self.found_constrs: + if len(c.args) == 0: + return True + return False + +#CONFIGURE HEADER-FILES TO BE PARSED AND CLASSES EXPECTED IN THEM HERE + +sources = [ + Source("kernel/celltypes",[ + WClass("CellType", link_types.pointer, None, None, "type.hash()", True), + WClass("CellTypes", link_types.pointer, None, None, None, True) + ] + ), + Source("kernel/consteval",[ + WClass("ConstEval", link_types.pointer, None, None, None, True) + ] + ), + Source("kernel/log",[]), + Source("kernel/register",[ + WClass("Pass", link_types.derive, None, None, None, True), + ] + ), + Source("kernel/rtlil",[ + WClass("IdString", link_types.ref_copy, None, "str()", "hash()"), + WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"), + WClass("AttrObject", link_types.ref_copy, None, None, None), + WClass("Selection", link_types.ref_copy, None, None, None), + WClass("Monitor", link_types.derive, None, None, None), + WClass("CaseRule",link_types.ref_copy, None, None, None, True), + WClass("SwitchRule",link_types.ref_copy, None, None, None, True), + WClass("SyncRule", link_types.ref_copy, None, None, None, True), + WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"), + WClass("SigChunk", link_types.ref_copy, None, None, None), + WClass("SigBit", link_types.ref_copy, None, None, "hash()"), + WClass("SigSpec", link_types.ref_copy, None, None, "hash()"), + WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), + WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), + WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), + WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), + WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()") + ] + ), + #Source("kernel/satgen",[ + # ] + # ), + #Source("libs/ezsat/ezsat",[ + # ] + # ), + #Source("libs/ezsat/ezminisat",[ + # ] + # ), + Source("kernel/sigtools",[ + WClass("SigMap", link_types.pointer, None, None, None, True) + ] + ), + Source("kernel/yosys",[ + ] + ), + Source("kernel/cost",[]) + ] + +blacklist_methods = ["Yosys::Pass::run_register", "Yosys::Module::Pow", "Yosys::Module::Bu0", "Yosys::CaseRule::optimize"] + +enum_names = ["State","SyncType","ConstFlags"] + +enums = [] #Do not edit + +unowned_functions = [] + +classnames = [] +for source in sources: + for wclass in source.classes: + classnames.append(wclass.name) + +def class_by_name(name): + for source in sources: + for wclass in source.classes: + if wclass.name == name: + return wclass + return None + +def enum_by_name(name): + for e in enums: + if e.name == name: + return e + return None + +def find_closing(text, open_tok, close_tok): + if text.find(open_tok) == -1 or text.find(close_tok) <= text.find(open_tok): + return text.find(close_tok) + return text.find(close_tok) + find_closing(text[text.find(close_tok)+1:], open_tok, close_tok) + 1 + +def unpretty_string(s): + s = s.strip() + while s.find(" ") != -1: + s = s.replace(" "," ") + while s.find("\t") != -1: + s = s.replace("\t"," ") + s = s.replace(" (","(") + return s + +class WEnum: + name = None + namespace = None + values = [] + + def from_string(str_def, namespace, line_number): + str_def = str_def.strip() + if not str.startswith(str_def, "enum "): + return None + if str_def.count(";") != 1: + return None + str_def = str_def[5:] + enum = WEnum() + split = str_def.split(":") + if(len(split) != 2): + return None + enum.name = split[0].strip() + if enum.name not in enum_names: + return None + str_def = split[1] + if str_def.count("{") != str_def.count("}") != 1: + return None + if len(str_def) < str_def.find("}")+2 or str_def[str_def.find("}")+1] != ';': + return None + str_def = str_def.split("{")[-1].split("}")[0] + enum.values = [] + for val in str_def.split(','): + enum.values.append(val.strip().split('=')[0].strip()) + enum.namespace = namespace + return enum + + def gen_boost_py(self): + text = "\n\t\tenum_<" + self.namespace + "::" + self.name + ">(\"" + self.name + "\")\n" + for value in self.values: + text += "\t\t\t.value(\"" + value + "\"," + self.namespace + "::" + value + ")\n" + text += "\t\t\t;\n" + return text + + def __str__(self): + ret = "Enum " + self.namespace + "::" + self.name + "(\n" + for val in self.values: + ret = ret + "\t" + val + "\n" + return ret + ")" + + def __repr__(self): + return __str__(self) + +class WConstructor: + orig_text = None + args = [] + containing_file = None + member_of = None + duplicate = False + protected = False + + def __init__(self, containing_file, class_): + self.orig_text = "Auto generated default constructor" + self.args = [] + self.containing_file = containing_file + self.member_of = class_ + self.protected = False + + def from_string(str_def, containing_file, class_, line_number, protected = False): + if class_ == None: + return None + if str_def.count("delete;") > 0: + return None + con = WConstructor(containing_file, class_) + con.orig_text = str_def + con.args = [] + con.duplicate = False + con.protected = protected + if not str.startswith(str_def, class_.name + "("): + return None + str_def = str_def[len(class_.name)+1:] + found = find_closing(str_def, "(", ")") + if found == -1: + return None + str_def = str_def[0:found].strip() + if len(str_def) == 0: + return con + for arg in split_list(str_def, ","): + parsed = Attribute.from_string(arg.strip(), containing_file, line_number) + if parsed == None: + return None + con.args.append(parsed) + return con + + def gen_decl(self): + if self.duplicate or self.protected: + return "" + text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t\t" + self.member_of.name + "(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ");\n" + return text + + def gen_decl_derive(self): + if self.duplicate or self.protected: + return "" + text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t\t" + self.member_of.name + "(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ")" + if len(self.args) == 0: + return text + "{}" + text += " : " + text += self.member_of.namespace + "::" + self.member_of.name + "(" + for arg in self.args: + text += arg.gen_call() + ", " + if len(self.args) > 0: + text = text[:-2] + text += "){}\n" + return text + + def gen_decl_wrapperclass(self): + if self.duplicate or self.protected: + return "" + text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t\t" + self.member_of.name + "Wrap(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ")" + if len(self.args) == 0: + return text + "{}" + text += " : " + text += self.member_of.name + "(" + for arg in self.args: + text += arg.gen_call() + ", " + if len(self.args) > 0: + text = text[:-2] + text += "){}\n" + return text + + def gen_decl_hash_py(self): + text = self.member_of.name + "(" + for arg in self.args: + text += arg.gen_listitem_hash() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ");" + return text + + def gen_def(self): + if self.duplicate or self.protected: + return "" + text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t" + self.member_of.name + "::" + self.member_of.name + "(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + text +=")\n\t{" + for arg in self.args: + text += arg.gen_translation() + if self.member_of.link_type != link_types.derive: + text += "\n\t\tthis->ref_obj = new " + self.member_of.namespace + "::" + self.member_of.name + "(" + for arg in self.args: + text += arg.gen_call() + ", " + if len(self.args) > 0: + text = text[:-2] + if self.member_of.link_type != link_types.derive: + text += ");" + if self.member_of.link_type == link_types.global_list: + text += "\n\t\tthis->" + self.member_of.id_.varname + " = this->ref_obj->" + self.member_of.id_.varname + ";" + for arg in self.args: + text += arg.gen_cleanup() + text += "\n\t}\n" + return text + + def gen_boost_py(self): + if self.duplicate or self.protected or len(self.args) == 0: + return "" + text = "\n\t\t\t.def(init" + text += "<" + for a in self.args: + text += a.gen_listitem_hash() + ", " + text = text[0:-2] + ">())" + return text + +class WFunction: + orig_text = None + is_static = False + is_inline = False + is_virtual = False + ret_attr_type = attr_types.default + is_operator = False + ret_type = None + name = None + alias = None + args = [] + containing_file = None + member_of = None + duplicate = False + namespace = "" + + def from_string(str_def, containing_file, class_, line_number, namespace): + if str_def.count("delete;") > 0: + return None + func = WFunction() + func.is_static = False + func.is_inline = False + func.is_virtual = False + func.ret_attr_type = attr_types.default + func.is_operator = False + func.member_of = None + func.orig_text = str_def + func.args = [] + func.containing_file = containing_file + func.member_of = class_ + func.duplicate = False + func.namespace = namespace + str_def = str_def.replace("operator ","operator") + if str.startswith(str_def, "static "): + func.is_static = True + str_def = str_def[7:] + else: + func.is_static = False + if str.startswith(str_def, "inline "): + func.is_inline = True + str_def = str_def[7:] + else: + func.is_inline = False + if str.startswith(str_def, "virtual "): + func.is_virtual = True + str_def = str_def[8:] + else: + func.is_virtual = False + + if str_def.count(" ") == 0: + return None + + parts = split_list(str_def.strip(), " ") + + prefix = "" + i = 0 + for part in parts: + if part in ["unsigned", "long", "short"]: + prefix += part + " " + i = i + 1 + else: + break + parts = parts[i:] + + if len(parts) <= 1: + return None + + func.ret_type = WType.from_string(prefix + parts[0], containing_file, line_number) + + if func.ret_type == None: + return None + + str_def = parts[1] + for part in parts[2:]: + str_def = str_def + " " + part + + found = str_def.find("(") + if found == -1 or (str_def.find(" ") != -1 and found > str_def.find(" ")): + return None + func.name = str_def[:found] + str_def = str_def[found:] + if func.name.find("operator") != -1 and str.startswith(str_def, "()("): + func.name += "()" + str_def = str_def[2:] + str_def = str_def[1:] + if func.name.find("operator") != -1: + func.is_operator = True + if func.name.find("*") == 0: + func.name = func.name.replace("*", "") + func.ret_type.attr_type = attr_types.star + if func.name.find("&&") == 0: + func.name = func.name.replace("&&", "") + func.ret_type.attr_type = attr_types.ampamp + if func.name.find("&") == 0: + func.name = func.name.replace("&", "") + func.ret_type.attr_type = attr_types.amp + + found = find_closing(str_def, "(", ")") + if found == -1: + return None + str_def = str_def[0:found] + if func.name in blacklist_methods: + return None + if func.namespace != None and func.namespace != "": + if (func.namespace + "::" + func.name) in blacklist_methods: + return None + if func.member_of != None: + if (func.namespace + "::" + func.member_of.name + "::" + func.name) in blacklist_methods: + return None + if func.is_operator and func.name.replace(" ","").replace("operator","").split("::")[-1] not in wrappable_operators: + return None + + testname = func.name + if func.is_operator: + testname = testname[:testname.find("operator")] + if testname.count(")") != 0 or testname.count("(") != 0 or testname.count("~") != 0 or testname.count(";") != 0 or testname.count(">") != 0 or testname.count("<") != 0 or testname.count("throw") != 0: + return None + + func.alias = func.name + if func.name in keyword_aliases: + func.alias = keyword_aliases[func.name] + str_def = str_def[:found].strip() + if(len(str_def) == 0): + return func + for arg in split_list(str_def, ","): + if arg.strip() == "...": + continue + parsed = Attribute.from_string(arg.strip(), containing_file, line_number) + if parsed == None: + return None + func.args.append(parsed) + return func + + def gen_alias(self): + self.alias = self.name + for arg in self.args: + self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","") + + def gen_decl(self): + if self.duplicate: + return "" + text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t\t" + if self.is_static: + text += "static " + text += self.ret_type.gen_text() + " " + self.alias + "(" + for arg in self.args: + text += arg.gen_listitem() + text += ", " + if len(self.args) > 0: + text = text[:-2] + text += ");\n" + return text + + def gen_decl_virtual(self): + if self.duplicate: + return "" + if not self.is_virtual: + return self.gen_decl() + text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t\tvirtual " + if self.is_static: + text += "static " + text += self.ret_type.gen_text() + " py_" + self.alias + "(" + for arg in self.args: + text += arg.gen_listitem() + text += ", " + if len(self.args) > 0: + text = text[:-2] + text += ")" + if len(self.args) == 0: + text += "{}" + else: + text += "\n\t\t{" + for arg in self.args: + text += "\n\t\t\t(void)" + arg.gen_varname() + ";" + text += "\n\t\t}\n" + text += "\n\t\tvirtual " + if self.is_static: + text += "static " + text += self.ret_type.gen_text() + " " + self.name + "(" + for arg in self.args: + text += arg.gen_listitem_cpp() + text += ", " + if len(self.args) > 0: + text = text[:-2] + text += ") YS_OVERRIDE;\n" + return text + + def gen_decl_hash_py(self): + text = self.ret_type.gen_text() + " " + self.alias + "(" + for arg in self.args: + text += arg.gen_listitem_hash() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ");" + return text + + def gen_def(self): + if self.duplicate: + return "" + text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t" + self.ret_type.gen_text() + " " + if self.member_of != None: + text += self.member_of.name + "::" + text += self.alias + "(" + for arg in self.args: + text += arg.gen_listitem() + text += ", " + if len(self.args) > 0: + text = text[:-2] + text +=")\n\t{" + for arg in self.args: + text += arg.gen_translation() + text += "\n\t\t" + if self.ret_type.name != "void": + if self.ret_type.name in known_containers: + text += self.ret_type.gen_text_cpp() + else: + text += self.ret_type.gen_text() + if self.ret_type.name in classnames or (self.ret_type.name in known_containers and self.ret_type.attr_type == attr_types.star): + text += "*" + text += " ret_ = " + if self.ret_type.name in classnames: + text += self.ret_type.name + "::get_py_obj(" + if self.member_of == None: + text += "::" + self.namespace + "::" + self.alias + "(" + elif self.is_static: + text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "(" + else: + text += "this->get_cpp_obj()->" + self.name + "(" + for arg in self.args: + text += arg.gen_call() + ", " + if len(self.args) > 0: + text = text[:-2] + if self.ret_type.name in classnames: + text += ")" + text += ");" + for arg in self.args: + text += arg.gen_cleanup() + if self.ret_type.name != "void": + if self.ret_type.name in classnames: + text += "\n\t\treturn *ret_;" + elif self.ret_type.name in known_containers: + text += known_containers[self.ret_type.name].translate_cpp("ret_", self.ret_type.cont.args, "\n\t\t", self.ret_type.attr_type == attr_types.star) + text += "\n\t\treturn ret____tmp;" + else: + text += "\n\t\treturn ret_;" + text += "\n\t}\n" + return text + + def gen_def_virtual(self): + if self.duplicate: + return "" + if not self.is_virtual: + return self.gen_def() + text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file + text += "\n\t" + if self.is_static: + text += "static " + text += self.ret_type.gen_text() + " " + self.member_of.name + "::" + self.name + "(" + for arg in self.args: + text += arg.gen_listitem_cpp() + text += ", " + if len(self.args) > 0: + text = text[:-2] + text += ")\n\t{" + for arg in self.args: + text += arg.gen_translation_cpp() + text += "\n\t\t" + if self.member_of == None: + text += "::" + self.namespace + "::" + self.alias + "(" + elif self.is_static: + text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "(" + else: + text += "py_" + self.alias + "(" + for arg in self.args: + text += arg.gen_call_cpp() + ", " + if len(self.args) > 0: + text = text[:-2] + if self.ret_type.name in classnames: + text += ")" + text += ");" + for arg in self.args: + text += arg.gen_cleanup() + text += "\n\t}\n" + return text + + def gen_default_impl(self): + if self.duplicate: + return "" + if not self.is_virtual: + return "" + text = "\n\n\t\t" + self.ret_type.gen_text() + " py_" + self.alias + "(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + + call_string = "py_" + self.alias + "(" + for arg in self.args: + call_string += arg.gen_varname() + ", " + if len(self.args) > 0: + call_string = call_string[0:-2] + call_string += ");" + + text += ")\n\t\t{" + text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))" + text += "\n\t\t\t\t" + call_string + text += "\n\t\t\telse" + text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string + text += "\n\t\t}" + + text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "(" + for arg in self.args: + text += arg.gen_listitem() + ", " + if len(self.args) > 0: + text = text[:-2] + text += ")\n\t\t{" + text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string + text += "\n\t\t}" + return text + + + def gen_boost_py(self): + if self.duplicate: + return "" + if self.member_of == None: + text = "\n\t\tdef" + else: + text = "\n\t\t\t.def" + if len(self.args) > -1: + if self.ret_type.name in known_containers: + text += "<" + known_containers[self.ret_type.name].typename + " " + else: + text += "<" + self.ret_type.name + " " + if self.member_of == None or self.is_static: + text += "(*)(" + else: + text += "(" + self.member_of.name + "::*)(" + for a in self.args: + text += a.gen_listitem_hash() + ", " + if len(self.args) > 0: + text = text[0:-2] + ")>" + else: + text += "void)>" + + if self.is_operator: + text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\"" + else: + if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual: + text += "(\"py_" + self.alias + "\"" + else: + text += "(\"" + self.alias + "\"" + if self.member_of != None: + text += ", &" + self.member_of.name + "::" + if self.member_of.link_type == link_types.derive and self.is_virtual: + text += "py_" + self.alias + text += ", &" + self.member_of.name + "Wrap::default_py_" + self.alias + else: + text += self.alias + + text += ")" + else: + text += ", " + "YOSYS_PYTHON::" + self.alias + ");" + return text + +class WMember: + orig_text = None + wtype = attr_types.default + name = None + containing_file = None + member_of = None + namespace = "" + is_const = False + + def from_string(str_def, containing_file, class_, line_number, namespace): + member = WMember() + member.orig_text = str_def + member.wtype = None + member.name = "" + member.containing_file = containing_file + member.member_of = class_ + member.namespace = namespace + member.is_const = False + + if str.startswith(str_def, "const "): + member.is_const = True + str_def = str_def[6:] + + if str_def.count(" ") == 0: + return None + + parts = split_list(str_def.strip(), " ") + + prefix = "" + i = 0 + for part in parts: + if part in ["unsigned", "long", "short"]: + prefix += part + " " + i = i + 1 + else: + break + parts = parts[i:] + + if len(parts) <= 1: + return None + + member.wtype = WType.from_string(prefix + parts[0], containing_file, line_number) + + if member.wtype == None: + return None + + str_def = parts[1] + for part in parts[2:]: + str_def = str_def + " " + part + + if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1: + return None + + found = str_def.find(";") + if found == -1: + return None + + found_eq = str_def.find("=") + if found_eq != -1: + found = found_eq + + member.name = str_def[:found] + str_def = str_def[found+1:] + if member.name.find("*") == 0: + member.name = member.name.replace("*", "") + member.wtype.attr_type = attr_types.star + if member.name.find("&&") == 0: + member.name = member.name.replace("&&", "") + member.wtype.attr_type = attr_types.ampamp + if member.name.find("&") == 0: + member.name = member.name.replace("&", "") + member.wtype.attr_type = attr_types.amp + + if(len(str_def.strip()) != 0): + return None + + if len(member.name.split(",")) > 1: + member_list = [] + for name in member.name.split(","): + name = name.strip(); + member_list.append(WMember()) + member_list[-1].orig_text = member.orig_text + member_list[-1].wtype = member.wtype + member_list[-1].name = name + member_list[-1].containing_file = member.containing_file + member_list[-1].member_of = member.member_of + member_list[-1].namespace = member.namespace + member_list[-1].is_const = member.is_const + return member_list + + return member + + def gen_decl(self): + text = "\n\t\t" + self.wtype.gen_text() + " get_var_py_" + self.name + "();\n" + if self.is_const: + return text + if self.wtype.name in classnames: + text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs);\n" + else: + text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs);\n" + return text + + def gen_def(self): + text = "\n\t" + self.wtype.gen_text() + " " + self.member_of.name +"::get_var_py_" + self.name + "()" + text += "\n\t{\n\t\t" + if self.wtype.attr_type == attr_types.star: + text += "if(this->get_cpp_obj()->" + self.name + " == NULL)\n\t\t\t" + text += "throw std::runtime_error(\"Member \\\"" + self.name + "\\\" is NULL\");\n\t\t" + if self.wtype.name in known_containers: + text += self.wtype.gen_text_cpp() + else: + text += self.wtype.gen_text() + + if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star): + text += "*" + text += " ret_ = " + if self.wtype.name in classnames: + text += self.wtype.name + "::get_py_obj(" + if self.wtype.attr_type != attr_types.star: + text += "&" + text += "this->get_cpp_obj()->" + self.name + if self.wtype.name in classnames: + text += ")" + text += ";" + + if self.wtype.name in classnames: + text += "\n\t\treturn *ret_;" + elif self.wtype.name in known_containers: + text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star) + text += "\n\t\treturn ret____tmp;" + else: + text += "\n\t\treturn ret_;" + text += "\n\t}\n" + + if self.is_const: + return text + + ret = Attribute(self.wtype, "rhs"); + + if self.wtype.name in classnames: + text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)" + else: + text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)" + text += "\n\t{" + text += ret.gen_translation() + text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";" + text += "\n\t}\n" + + return text; + + def gen_boost_py(self): + text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name + if not self.is_const: + text += ", &" + self.member_of.name + "::set_var_py_" + self.name + text += ")" + return text + +def concat_namespace(tuple_list): + if len(tuple_list) == 0: + return "" + ret = "" + for namespace in tuple_list: + ret += "::" + namespace[0] + return ret[2:] + +def calc_ident(text): + if len(text) == 0 or text[0] != ' ': + return 0 + return calc_ident(text[1:]) + 1 + +def assure_length(text, length, left = False): + if len(text) > length: + return text[:length] + if left: + return text + " "*(length - len(text)) + return " "*(length - len(text)) + text + +def parse_header(source): + debug("Parsing " + source.name + ".pyh",1) + source_file = open(source.name + ".pyh", "r") + + source_text = [] + in_line = source_file.readline() + + namespaces = [] + + while(in_line): + if(len(in_line)>1): + source_text.append(in_line.replace("char *", "char_p ").replace("char* ", "char_p ")) + in_line = source_file.readline() + + i = 0 + + namespaces = [] + class_ = None + private_segment = False + + while i < len(source_text): + line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace Yosys{").replace("YOSYS_NAMESPACE_END"," }") + ugly_line = unpretty_string(line) + + if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1: + namespace_name = ugly_line[10:].replace("{","").strip() + namespaces.append((namespace_name, ugly_line.count("{"))) + debug("-----NAMESPACE " + concat_namespace(namespaces) + "-----",3) + i = i + 1 + continue + + if len(namespaces) != 0: + namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}")) + if namespaces[-1][1] == 0: + debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3) + del namespaces[-1] + i = i + 1 + continue + + if class_ == None and (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0: + + struct_name = ugly_line.split(" ")[1].split("::")[-1] + impl_namespaces = ugly_line.split(" ")[1].split("::")[:-1] + complete_namespace = concat_namespace(namespaces) + for namespace in impl_namespaces: + complete_namespace += "::" + namespace + debug("\tFound " + struct_name + " in " + complete_namespace,2) + class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line)) + if struct_name in classnames: + class_[0].namespace = complete_namespace + i = i + 1 + continue + + if class_ != None: + class_ = (class_[0], class_[1] + ugly_line.count("{") - ugly_line.count("}")) + if class_[1] == 0: + if class_[0] == None: + debug("\tExiting unknown class", 3) + else: + debug("\tExiting class " + class_[0].name, 3) + class_ = None + private_segment = False + i = i + 1 + continue + + if class_ != None and (line.find("private:") != -1 or line.find("protected:") != -1): + private_segment = True + i = i + 1 + continue + if class_ != None and line.find("public:") != -1: + private_segment = False + i = i + 1 + continue + + candidate = None + + if private_segment and class_ != None and class_[0] != None: + candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i, True) + if candidate != None: + debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) + class_[0].found_constrs.append(candidate) + i = i + 1 + continue + + if not private_segment and (class_ == None or class_[0] != None): + if class_ != None: + candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) + else: + candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces)) + if candidate != None and candidate.name.find("::") == -1: + if class_ == None: + debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) + unowned_functions.append(candidate) + else: + debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) + class_[0].found_funs.append(candidate) + else: + candidate = WEnum.from_string(ugly_line, concat_namespace(namespaces), i) + if candidate != None: + enums.append(candidate) + debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) + elif class_ != None and class_[1] == 1: + candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i) + if candidate != None: + debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) + class_[0].found_constrs.append(candidate) + else: + candidate = WMember.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) + if candidate != None: + if type(candidate) == list: + for c in candidate: + debug("\t\tFound member \"" + c.name + "\" of class \"" + class_[0].name + "\" of type \"" + c.wtype.name + "\"", 2) + class_[0].found_vars.extend(candidate) + else: + debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2) + class_[0].found_vars.append(candidate) + + j = i + line = unpretty_string(line) + while candidate == None and j+1 < len(source_text) and line.count(';') <= 1 and line.count("(") >= line.count(")"): + j = j + 1 + line = line + "\n" + unpretty_string(source_text[j]) + if class_ != None: + candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) + else: + candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces)) + if candidate != None and candidate.name.find("::") == -1: + if class_ == None: + debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) + unowned_functions.append(candidate) + else: + debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) + class_[0].found_funs.append(candidate) + continue + candidate = WEnum.from_string(line, concat_namespace(namespaces), i) + if candidate != None: + enums.append(candidate) + debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) + continue + if class_ != None: + candidate = WConstructor.from_string(line, source.name, class_[0], i) + if candidate != None: + debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) + class_[0].found_constrs.append(candidate) + continue + if candidate != None: + while i < j: + i = i + 1 + line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace Yosys{").replace("YOSYS_NAMESPACE_END"," }") + ugly_line = unpretty_string(line) + if len(namespaces) != 0: + namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}")) + if namespaces[-1][1] == 0: + debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3) + del namespaces[-1] + if class_ != None: + class_ = (class_[0] , class_[1] + ugly_line.count("{") - ugly_line.count("}")) + if class_[1] == 0: + if class_[0] == None: + debug("\tExiting unknown class", 3) + else: + debug("\tExiting class " + class_[0].name, 3) + class_ = None + private_segment = False + i = i + 1 + #i = j + 1 + else: + i = i + 1 + +def debug(message, level): + if level <= debug.debug_level: + print(message) + +def expand_function(f): + fun_list = [] + arg_list = [] + for arg in f.args: + if arg.default_value != None and (arg.wtype.name.split(" ")[-1] in primitive_types or arg.wtype.name in enum_names or (arg.wtype.name in classnames and arg.default_value == "nullptr")): + fi = copy.deepcopy(f) + fi.args = copy.deepcopy(arg_list) + fun_list.append(fi) + arg_list.append(arg) + fun_list.append(f) + return fun_list + +def expand_functions(): + global unowned_functions + new_funs = [] + for fun in unowned_functions: + new_funs.extend(expand_function(fun)) + unowned_functions = new_funs + for source in sources: + for class_ in source.classes: + new_funs = [] + for fun in class_.found_funs: + new_funs.extend(expand_function(fun)) + class_.found_funs = new_funs + +def clean_duplicates(): + for source in sources: + for class_ in source.classes: + known_decls = {} + for fun in class_.found_funs: + if fun.gen_decl_hash_py() in known_decls: + debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) + other = known_decls[fun.gen_decl_hash_py()] + other.gen_alias() + fun.gen_alias() + if fun.gen_decl_hash_py() == other.gen_decl_hash_py(): + fun.duplicate = True + debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3) + else: + known_decls[fun.gen_decl_hash_py()] = fun + known_decls = [] + for con in class_.found_constrs: + if con.gen_decl_hash_py() in known_decls: + debug("Multiple declarations of " + con.gen_decl_hash_py(),3) + con.duplicate = True + else: + known_decls.append(con.gen_decl_hash_py()) + known_decls = [] + for fun in unowned_functions: + if fun.gen_decl_hash_py() in known_decls: + debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) + fun.duplicate = True + else: + known_decls.append(fun.gen_decl_hash_py()) + +def gen_wrappers(filename, debug_level_ = 0): + debug.debug_level = debug_level_ + for source in sources: + parse_header(source) + + expand_functions() + clean_duplicates() + + import shutil + import math + col = shutil.get_terminal_size((80,20)).columns + debug("-"*col, 1) + debug("-"*math.floor((col-7)/2)+"SUMMARY"+"-"*math.ceil((col-7)/2), 1) + debug("-"*col, 1) + for source in sources: + for class_ in source.classes: + debug("Class " + assure_length(class_.name, len(max(classnames, key=len)), True) + " contains " + assure_length(str(len(class_.found_vars)), 3, False) + " member variables, "+ assure_length(str(len(class_.found_funs)), 3, False) + " methods and " + assure_length(str(len(class_.found_constrs)), 2, False) + " constructors", 1) + if len(class_.found_constrs) == 0: + class_.found_constrs.append(WConstructor(source.name, class_)) + debug(str(len(unowned_functions)) + " functions are unowned", 1) + for enum in enums: + debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1) + debug("-"*col, 1) + wrapper_file = open(filename, "w+") + wrapper_file.write( +"""/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * This is a generated file and can be overwritten by make + */ + +#ifdef WITH_PYTHON +""") + for source in sources: + wrapper_file.write("#include \""+source.name+".h\"\n") + wrapper_file.write(""" +#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/wrapper.hpp> +#include <boost/python/call.hpp> +#include <boost/python.hpp> +#include <boost/log/exceptions.hpp> + +using namespace Yosys; + +namespace YOSYS_PYTHON { +""") + + for source in sources: + for wclass in source.classes: + wrapper_file.write("\n\tstruct " + wclass.name + ";") + + wrapper_file.write("\n") + + for source in sources: + for wclass in source.classes: + wrapper_file.write(wclass.gen_decl(source.name)) + + wrapper_file.write("\n") + + for source in sources: + for wclass in source.classes: + wrapper_file.write(wclass.gen_funs(source.name)) + + for fun in unowned_functions: + wrapper_file.write(fun.gen_def()) + + wrapper_file.write(""" struct Initializer + { + Initializer() { + if(!Yosys::yosys_already_setup()) + { + Yosys::log_streams.push_back(&std::cout); + Yosys::log_error_stderr = true; + Yosys::yosys_setup(); + Yosys::yosys_banner(); + } + } + + Initializer(Initializer const &) {} + + ~Initializer() { + Yosys::yosys_shutdown(); + } + }; + + BOOST_PYTHON_MODULE(libyosys) + { + using namespace boost::python; + + class_<Initializer>("Initializer"); + scope().attr("_hidden") = new Initializer(); +""") + + for enum in enums: + wrapper_file.write(enum.gen_boost_py()) + + for source in sources: + for wclass in source.classes: + wrapper_file.write(wclass.gen_boost_py()) + + for fun in unowned_functions: + wrapper_file.write(fun.gen_boost_py()) + + wrapper_file.write("\n\t}\n}\n#endif") + +def print_includes(): + for source in sources: + print(source.name + ".pyh") |