aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2016-08-01 08:20:23 +0200
committerTrygve Laugstøl <trygvis@inamo.no>2016-08-01 08:20:23 +0200
commitbfeeac6e4889d1e9a1083b3c7efc59652981b168 (patch)
treea937d844a59da7c509685dcd1ddd9933772e526f /core
parentc307e9f234e544386fa3ae53083c7510668e1716 (diff)
downloadkicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.gz
kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.bz2
kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.xz
kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.zip
o Moving the generation logic to Python, embedding a Python interpreter with the help of pybind11.
o Adding install configuration to CMake to make it easier to reuse the project later on. o Splitting out the examples into its own project to make it easier to test the whole installation setup and python/template loading. o Trying to reorganize the code a bit, not very much better yet.
Diffstat (limited to 'core')
-rw-r--r--core/CMakeLists.txt22
-rw-r--r--core/KicadNetLexer.g438
-rw-r--r--core/KicadNetParser.g489
-rw-r--r--core/include-priv/trygvis/antlr.h67
-rw-r--r--core/include-priv/trygvis/string_utils.h23
-rw-r--r--core/include/trygvis/kicad.h102
-rw-r--r--core/kicad.cpp194
7 files changed, 535 insertions, 0 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
new file mode 100644
index 0000000..cbf8e78
--- /dev/null
+++ b/core/CMakeLists.txt
@@ -0,0 +1,22 @@
+find_package(Antlr4)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+antlr4_add_target(
+ TARGET KicadNet
+ LEXER KicadNetLexer.g4
+ PARSER KicadNetParser.g4
+ STATIC)
+
+add_library(kicad-utils-core
+ kicad.cpp)
+target_sources(kicad-utils-core PRIVATE $<TARGET_OBJECTS:KicadNet>)
+
+get_target_property(KicadNet_includes KicadNet INCLUDE_DIRECTORIES)
+
+target_include_directories(kicad-utils-core
+ PUBLIC include
+ PRIVATE include-priv ${KicadNet_includes})
+
+target_link_libraries(kicad-utils-core
+ Antlr4::antlr4_shared)
diff --git a/core/KicadNetLexer.g4 b/core/KicadNetLexer.g4
new file mode 100644
index 0000000..e10f7e7
--- /dev/null
+++ b/core/KicadNetLexer.g4
@@ -0,0 +1,38 @@
+lexer grammar KicadNetLexer;
+
+LPAREN: '(';
+RPAREN: ')';
+
+QUOTE: '"';
+
+CODE: 'code';
+COMP: 'comp';
+FIELD: 'field';
+LIBSOURCE: 'libsource';
+LIBPART: 'libpart';
+LIB: 'lib';
+NAME: 'name';
+NET: 'net';
+NODE: 'node';
+NUM: 'num';
+PART: 'part';
+PIN: 'pin';
+REF: 'ref';
+TYPE: 'type';
+VALUE: 'value';
+
+STRING: '"' ~["]* '"';
+
+INTEGER: [0-9]+;
+
+ID
+ : [/+~\_\-\.\*\?/a-zA-Z0-9]+
+ ;
+
+BlockComment
+ : '/*' .*? '*/' -> skip
+ ;
+
+WS
+ : [ \t\r\n]+ -> skip
+ ;
diff --git a/core/KicadNetParser.g4 b/core/KicadNetParser.g4
new file mode 100644
index 0000000..3971111
--- /dev/null
+++ b/core/KicadNetParser.g4
@@ -0,0 +1,89 @@
+parser grammar KicadNetParser;
+
+options {
+ tokenVocab = KicadNetLexer;
+}
+
+file:
+ form+
+ ;
+
+form:
+ name #formName
+ | component #formComponent
+ | field #formField
+ | net #formNet
+ | node #formNode
+ | pinDecl #formDecl
+ | ref #formRef
+ | value #formValue
+ | libpart #formLibpart
+ | keyValue #formKeyValue
+ | string #formString
+ ;
+
+code:
+ '(' 'code' INTEGER ')'
+ ;
+
+component:
+ '(' 'comp' ref value libsource keyValue* ')'
+ ;
+
+field:
+ '(' 'field' name string ')'
+ ;
+
+name:
+ '(' 'name' string ')'
+ ;
+
+net:
+ '(' 'net' code name node+ ')'
+ ;
+
+node:
+ '(' 'node' ref pinRef ')'
+ ;
+
+pinRef:
+ '(' 'pin' INTEGER ')'
+ ;
+
+pinDecl:
+ '(' 'pin' '(' 'num' INTEGER ')' '(' 'name' string ')' '(' 'type' string ')' ')'
+ ;
+
+ref:
+ '(' 'ref' string ')'
+ ;
+
+value:
+ '(' 'value' string ')'
+ ;
+
+lib:
+ '(' 'lib' string ')'
+ ;
+
+part:
+ '(' 'part' string ')'
+ ;
+
+libpart:
+ '(' 'libpart' lib part keyValue* ')'
+ ;
+
+libsource:
+ '(' 'libsource' lib part ')'
+ ;
+
+keyValue:
+ '(' string form* ')'
+ ;
+
+string:
+ ID #stringId
+ | INTEGER #stringInt
+ | STRING #stringText
+ ;
diff --git a/core/include-priv/trygvis/antlr.h b/core/include-priv/trygvis/antlr.h
new file mode 100644
index 0000000..f5656ea
--- /dev/null
+++ b/core/include-priv/trygvis/antlr.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "antlr4-runtime.h"
+
+namespace trygvis {
+namespace antlr {
+
+// This namespace is shared copied code
+
+using ParseTree = antlr4::tree::ParseTree;
+
+class MissingParseTreeProperty : public std::out_of_range {
+public:
+ explicit MissingParseTreeProperty(const std::string &what) : out_of_range(what) { }
+};
+
+template<typename V, bool debug = false>
+class ParseTreeProperty {
+public:
+ virtual V get(Ref<ParseTree> node) {
+ return get(node.get());
+ }
+
+ virtual V get(ParseTree *const node) {
+ if (!debug) {
+ return _annotations.at(node);
+ }
+
+ try {
+// cerr << "node = " << node->getText() << endl;
+ return _annotations.at(node);
+ } catch (std::out_of_range &e) {
+ std::cerr << "get(" << node << "), text=" << node->getText() << std::endl;
+ std::stringstream buf;
+ buf << "out of range: " << node << ", text=" << node->getText();
+ auto msg = buf.str();
+ std::cerr << msg << std::endl;
+ throw MissingParseTreeProperty(msg);
+ }
+ }
+
+ virtual void put(ParseTree *const node, V value) {
+ if (debug) {
+ std::cerr << "put(" << node << ", " << value << "), text: " << node->getText() << std::endl;
+ }
+ _annotations[node] = value;
+ }
+
+ virtual V removeFrom(ParseTree *const node) {
+ auto it = _annotations.find(node);
+
+ if (it == _annotations.end()) {
+ throw MissingParseTreeProperty(node->getText());
+ }
+
+ return it->second;
+ }
+
+protected:
+ std::map<ParseTree *, V> _annotations;
+
+private:
+};
+
+} // namespace antlr
+
+}
diff --git a/core/include-priv/trygvis/string_utils.h b/core/include-priv/trygvis/string_utils.h
new file mode 100644
index 0000000..9902a3f
--- /dev/null
+++ b/core/include-priv/trygvis/string_utils.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <string>
+
+namespace trygvis {
+namespace string_utils {
+
+/**
+ * Check if a starts with b.
+ */
+static bool startsWith(const std::string &a, const std::string &b) {
+ return b.length() <= a.length() && a.compare(0, b.length(), b) == 0;
+}
+
+/**
+ * Check if a ends with b.
+ */
+static bool endsWith(const std::string &a, const std::string &b) {
+ return b.length() <= a.length() && a.compare(a.length() - b.length(), b.length(), b) == 0;
+}
+
+} // namespace string_utils
+} // namespace trygvis
diff --git a/core/include/trygvis/kicad.h b/core/include/trygvis/kicad.h
new file mode 100644
index 0000000..0c5005d
--- /dev/null
+++ b/core/include/trygvis/kicad.h
@@ -0,0 +1,102 @@
+#pragma once
+
+#include <experimental/optional>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace trygvis {
+namespace kicad {
+template<typename T>
+using opt = std::experimental::optional<T>;
+using std::experimental::nullopt;
+} // namespace kicad
+} // namespace trygvis
+
+namespace trygvis {
+namespace kicad {
+namespace netlist {
+
+struct lib_source {
+ const std::string lib;
+ const std::string part;
+
+ lib_source(const std::string &lib, const std::string &part) : lib(lib), part(part) {}
+};
+
+struct component {
+ const std::string ref;
+ const std::string value;
+ const lib_source _lib_source;
+
+ component(const std::string &ref, const std::string &value, const lib_source &_lib_source) :
+ ref(ref), value(value), _lib_source(_lib_source) {}
+};
+
+struct pin {
+ int num;
+ std::string name;
+ std::string type;
+};
+
+struct part {
+ std::vector<pin> pins;
+};
+
+class node {
+public:
+ const std::string ref;
+ const int pin;
+
+ node(const std::string &ref, int pin) : ref(ref), pin(pin) {}
+};
+
+class net {
+public:
+ const int code;
+ const std::string name;
+ const std::vector<node> nodes;
+
+ net(int code, const std::string &name, const std::vector<node> &nodes) : code(code), name(name), nodes(nodes) {}
+
+ const node *node_for_ref(const std::string &ref) const;
+};
+
+struct netlist {
+ std::vector<component> components;
+ std::vector<part> parts;
+ std::vector<net> nets;
+
+ opt<const component *> find_component(const std::string &ref) const;
+
+ std::vector<const net *> find_usage_of(const std::string &ref) const;
+};
+
+class kicad_parse_exception : public std::runtime_error {
+public:
+ explicit kicad_parse_exception(const std::vector<std::string> &messages) :
+ runtime_error("Parse error"), messages(messages) {}
+
+ ~kicad_parse_exception() {}
+
+ const std::vector<std::string> messages;
+};
+
+class kicad_net_loader {
+public:
+ kicad_net_loader();
+
+ virtual ~kicad_net_loader();
+
+ netlist load(std::string path, std::ostream &err);
+
+ void setDebug(bool debug);
+
+private:
+ bool debug_;
+};
+
+} // namespace netlist
+} // namespace kicad
+} // namespace trygvis
diff --git a/core/kicad.cpp b/core/kicad.cpp
new file mode 100644
index 0000000..1a6ca2f
--- /dev/null
+++ b/core/kicad.cpp
@@ -0,0 +1,194 @@
+#include "trygvis/kicad.h"
+#include "trygvis/antlr.h"
+#include "trygvis/string_utils.h"
+#include <KicadNetLexer.h>
+#include <KicadNetParser.h>
+#include <KicadNetParserBaseListener.h>
+#include <exception>
+#include <stdexcept>
+
+using namespace std;
+using namespace trygvis::antlr;
+using namespace trygvis::string_utils;
+
+namespace trygvis {
+namespace kicad {
+namespace netlist {
+
+const node *net::node_for_ref(const std::string &ref) const {
+ for (auto &node: nodes) {
+ if (node.ref == ref) {
+ return &node;
+ }
+ }
+
+ return nullptr;
+}
+
+opt<const component *> netlist::find_component(const string &ref) const {
+ for (const component &c :components) {
+ int x = c.ref.compare(ref);
+ if (x == 0) {
+ return &c;
+ }
+ }
+ return std::experimental::nullopt;
+}
+
+vector<const net *> netlist::find_usage_of(const string &ref) const {
+ vector<const net *> usage;
+
+ for (auto &net : nets) {
+ if (net.nodes.size() <= 1) {
+ continue;
+ }
+
+ for (auto &node: net.nodes) {
+ if (node.ref == ref) {
+ usage.push_back(&net);
+ }
+ }
+ }
+
+ return usage;
+}
+
+static
+int parse(const Ref<antlr4::tree::TerminalNode> &integer) {
+ unsigned long long i = strtoull(integer->getText().c_str(), NULL, 10);
+
+ return static_cast<int>(i);
+}
+
+class KicadErrorListener : public BaseErrorListener {
+public:
+ vector<string> messages;
+
+ void syntaxError(IRecognizer *recognizer, Token *offendingSymbol, size_t line, int charPositionInLine,
+ const string &msg, exception_ptr e) override {
+ static_cast<void>(recognizer);
+ static_cast<void>(offendingSymbol);
+ static_cast<void>(e);
+ messages.push_back("line " + to_string(line) + ":" + to_string(charPositionInLine) + ": " + msg);
+ }
+};
+
+class kicad_main_listener : public KicadNetParserBaseListener {
+
+public:
+ vector<component> components;
+ vector<part> parts;
+ vector<net> nets;
+ vector<node> nodes;
+
+ ParseTreeProperty<string> strings;
+
+ virtual void exitNet(KicadNetParser::NetContext *ctx) override {
+ auto code = parse(ctx->code()->INTEGER());
+ auto name = strings.get(ctx->name()->string());
+
+ if (startsWith(name, "/")) {
+ name = name.substr(1);
+ }
+
+// cerr << "exitNet: " << "code=" << code << ", name=" << name << ", nodes=" << nodes.size() << endl;
+
+// if (nodes.size() > 1) {
+// cerr << "Net#" << code << ": " << name << endl;
+// for (auto &node: nodes) {
+// cerr << " Node: " << node.ref << "#" << node.pin << endl;
+// }
+// }
+ nets.emplace_back(code, name, nodes);
+ nodes.clear();
+ }
+
+ virtual void exitNode(KicadNetParser::NodeContext *ctx) override {
+ auto ref = strings.get(ctx->ref()->string());
+ auto pin = parse(ctx->pinRef()->INTEGER());
+
+// cerr << "exitNode: " << "ref=" << ref << ", pin=" << pin << endl;
+
+ nodes.emplace_back(ref, pin);
+ }
+
+ virtual void exitComponent(KicadNetParser::ComponentContext *ctx) override {
+ auto ref = strings.get(ctx->ref()->string());
+ auto value = strings.get(ctx->value()->string());
+
+ lib_source ls{strings.get(ctx->libsource()->lib()->string()),
+ strings.get(ctx->libsource()->part()->string())};
+
+ components.emplace_back(ref, value, ls);
+ }
+
+ virtual void exitStringId(KicadNetParser::StringIdContext *ctx) override {
+ strings.put(ctx, ctx->getText());
+ }
+
+ virtual void exitStringInt(KicadNetParser::StringIntContext *ctx) override {
+ strings.put(ctx, ctx->getText());
+ }
+
+ virtual void exitStringText(KicadNetParser::StringTextContext *ctx) override {
+ auto s = ctx->getText();
+ strings.put(ctx, s.substr(1, s.length() - 2));
+ }
+};
+
+netlist kicad_net_loader::load(string path, ostream &err) {
+
+ ANTLRFileStream input(path);
+ KicadNetLexer lexer(&input);
+ CommonTokenStream tokens(&lexer);
+
+ tokens.fill();
+
+ if (debug_) {
+ for (auto token : tokens.getTokens()) {
+ err << token->toString() << endl;
+ }
+ }
+
+ KicadNetParser parser(&tokens);
+ parser.removeErrorListeners();
+ KicadErrorListener errorListener;
+ parser.addErrorListener(&errorListener);
+
+ parser.file();
+
+ if (!errorListener.messages.empty()) {
+ throw kicad_parse_exception(errorListener.messages);
+ }
+
+ parser.reset();
+
+ kicad_main_listener mainListener;
+ parser.addParseListener(&mainListener);
+
+ auto file = parser.file();
+
+ if (debug_ && parser.getNumberOfSyntaxErrors() == 0) {
+ err << file->toStringTree(&parser) << endl;
+ }
+
+ return netlist {
+ mainListener.components,
+ mainListener.parts,
+ mainListener.nets,
+ };
+}
+
+kicad_net_loader::kicad_net_loader() : debug_(false) {
+}
+
+kicad_net_loader::~kicad_net_loader() {
+}
+
+void kicad_net_loader::setDebug(bool debug) {
+ debug_ = debug;
+}
+
+} // namespace netlist
+} // namespace trygvis
+} // namespace kicad