From 128e53d220d97225803d61d86f8e43439563181d Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 26 Jul 2016 00:22:55 +0200 Subject: WIP: kicad_gen is a util to generate schematic.h files from KiCAD netlist files. Current code contains a lexer and parser for KiCAD's netlist files and code to build a tree of the netlist which can be used for generation. Contains CMake code for integrating the generation into CMake too. --- .editorconfig | 4 + .gitignore | 5 + CMakeLists.txt | 67 +++++ KicadNetLexer.g4 | 36 +++ KicadNetParser.g4 | 72 +++++ examples/.gitignore | 2 + examples/CMakeLists.txt | 1 + examples/arduino-led/CMakeLists.txt | 14 + examples/arduino-led/arduino-led.ino | 13 + examples/arduino-led/schematic/arduino-led.net | 166 +++++++++++ examples/arduino-led/schematic/arduino-led.pro | 61 ++++ examples/arduino-led/schematic/arduino-led.sch | 101 +++++++ include/trygvis/kicad.h | 80 ++++++ java/pom.xml | 87 ++++++ .../io/trygvis/ld/CollectingErrorListener.java | 17 ++ .../java/io/trygvis/ld/ElfinfoExprVisitor.java | 318 +++++++++++++++++++++ .../java/io/trygvis/ld/ElfinfoGnuLdListener.java | 170 +++++++++++ .../java/io/trygvis/ld/ElfinfoGnuLdVisitor.java | 75 +++++ java/src/main/java/io/trygvis/ld/LdLoader.java | 121 ++++++++ .../java/io/trygvis/ld/StringGnuLdVisitor.java | 59 ++++ kicad.cpp | 219 ++++++++++++++ kicad/.gitignore | 2 + kicad/kicad_utils.dcm | 3 + kicad/kicad_utils.lib | 46 +++ main.cpp | 89 ++++++ test-input/lcd3310.net | 163 +++++++++++ 26 files changed, 1991 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 KicadNetLexer.g4 create mode 100644 KicadNetParser.g4 create mode 100644 examples/.gitignore create mode 100644 examples/CMakeLists.txt create mode 100644 examples/arduino-led/CMakeLists.txt create mode 100644 examples/arduino-led/arduino-led.ino create mode 100644 examples/arduino-led/schematic/arduino-led.net create mode 100644 examples/arduino-led/schematic/arduino-led.pro create mode 100644 examples/arduino-led/schematic/arduino-led.sch create mode 100644 include/trygvis/kicad.h create mode 100644 java/pom.xml create mode 100644 java/src/main/java/io/trygvis/ld/CollectingErrorListener.java create mode 100644 java/src/main/java/io/trygvis/ld/ElfinfoExprVisitor.java create mode 100644 java/src/main/java/io/trygvis/ld/ElfinfoGnuLdListener.java create mode 100644 java/src/main/java/io/trygvis/ld/ElfinfoGnuLdVisitor.java create mode 100644 java/src/main/java/io/trygvis/ld/LdLoader.java create mode 100644 java/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java create mode 100644 kicad.cpp create mode 100644 kicad/.gitignore create mode 100644 kicad/kicad_utils.dcm create mode 100644 kicad/kicad_utils.lib create mode 100644 main.cpp create mode 100644 test-input/lcd3310.net diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b661808 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b4f096 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +*.iml +build +target +*.tokens diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..914bfb9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.5) +project(kicad_utils) + +find_package(Antlr4) +antlr4_add_target(TARGET KicadNet LEXER KicadNetLexer.g4 PARSER KicadNetParser.g4 STATIC) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(SOURCE_FILES main.cpp kicad.cpp include/trygvis/kicad.h) +add_executable(kicad_utils ${SOURCE_FILES}) +target_include_directories(kicad_utils PUBLIC include) +target_link_libraries(kicad_utils KicadNet Antlr4::antlr4_shared) +target_compile_options(kicad_utils PUBLIC --std=c++14) +target_compile_options(kicad_utils PUBLIC -Wall -Wextra) + +add_custom_target(all_examples) + +function(kicad_gen) + set(options IN_SOURCE) + set(oneValueArgs TARGET NET REF OUTPUT_DIR) + + cmake_parse_arguments(kicad_gen "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT kicad_gen_TARGET) + message(FATAL_ERROR "TARGET is required") + endif () + set(TARGET "${kicad_gen_TARGET}") + + if (NOT kicad_gen_NET) + message(FATAL_ERROR "NET is required") + endif () + set(NET "${CMAKE_CURRENT_SOURCE_DIR}/${kicad_gen_NET}") + + if (NOT kicad_gen_REF) + message(FATAL_ERROR "REF is required") + endif () + set(REF "${kicad_gen_REF}") + + if (kicad_gen_OUTPUT_DIR) + if (kicad_gen_IN_SOURCE) + message(FATAL_ERROR "IN_SOURCE can't be used if OUTPUT_DIR is used") + endif () + + set(OUTPUT_DIR "${kicad_gen_OUTPUT_DIR}") + else () + message("IN_SOURCE=${kicad_gen_IN_SOURCE}") + if (kicad_gen_IN_SOURCE) + set(OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + else () + set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + endif () + endif () + + set(OUTPUT_FILE "${OUTPUT_DIR}/${TARGET}") + message("OUTPUT_FILE=${OUTPUT_FILE}") + message("NET=${NET}") + + add_custom_command(OUTPUT ${OUTPUT_FILE} + COMMAND kicad_utils -f ${NET} -r ${REF} > kicad-${TARGET}.tmp + COMMAND cmake -E make_directory -p ${OUTPUT_DIR} + COMMAND cmake -E rename kicad-${TARGET}.tmp ${OUTPUT_FILE} + MAIN_DEPENDENCY ${NET} + DEPENDS kicad_utils + COMMENT "Generating ${TARGET}") +endfunction() + +add_subdirectory(examples) diff --git a/KicadNetLexer.g4 b/KicadNetLexer.g4 new file mode 100644 index 0000000..55bda85 --- /dev/null +++ b/KicadNetLexer.g4 @@ -0,0 +1,36 @@ +lexer grammar KicadNetLexer; + +LPAREN: '('; +RPAREN: ')'; + +QUOTE: '"'; + +CODE: 'code'; +COMP: 'comp'; +FIELD: 'field'; +NAME: 'name'; +NET: 'net'; +NODE: 'node'; +NUM: 'num'; +PIN: 'pin'; +REF: 'ref'; +TYPE: 'type'; +VALUE: 'value'; + + +//STRING: '"' (~[\\"] | '\\' [\\"])* '"'; +STRING: '"' ~["]* '"'; + +INTEGER: [0-9]+; + +ID + : [/+~\_\-\.\*/a-zA-Z0-9]+ + ; + +BlockComment + : '/*' .*? '*/' -> skip + ; + +WS + : [ \t\r\n]+ -> skip + ; diff --git a/KicadNetParser.g4 b/KicadNetParser.g4 new file mode 100644 index 0000000..dc45c4b --- /dev/null +++ b/KicadNetParser.g4 @@ -0,0 +1,72 @@ +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 + | keyValue #formKeyValue + | string #formString + ; + +code: + '(' 'code' INTEGER ')' + ; + +component: + '(' 'comp' ref value 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 ')' + ; + +keyValue: + '(' string form* ')' + ; + +string: + ID #stringId + | INTEGER #stringInt + | STRING #stringText + ; diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..a84bc5f --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,2 @@ +*.bak +*-cache.lib diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..6f09874 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(arduino-led) diff --git a/examples/arduino-led/CMakeLists.txt b/examples/arduino-led/CMakeLists.txt new file mode 100644 index 0000000..7bd82af --- /dev/null +++ b/examples/arduino-led/CMakeLists.txt @@ -0,0 +1,14 @@ +kicad_gen( + TARGET schematic.h + NET schematic/arduino-led.net + REF U1 + IN_SOURCE) + +if (FALSE) + # If you have Arduino support for CMake something like this would work: + + #add_executable(arduino-led arduino-led.ino schematic.h) + #target_include_directories(arduino-led PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/kicad-include) +else () + add_custom_target(arduino-led ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/schematic.h) +endif () diff --git a/examples/arduino-led/arduino-led.ino b/examples/arduino-led/arduino-led.ino new file mode 100644 index 0000000..01c83ea --- /dev/null +++ b/examples/arduino-led/arduino-led.ino @@ -0,0 +1,13 @@ +#include +#include "Arduino.h" +#include "schematic.h" + +void setup() { +} + +void loop() { + digitalWrite(SCHEMATIC_STATUS_LED, HIGH); + delay(1000); + digitalWrite(SCHEMATIC_STATUS_LED, LOW); + delay(1000); +} diff --git a/examples/arduino-led/schematic/arduino-led.net b/examples/arduino-led/schematic/arduino-led.net new file mode 100644 index 0000000..f230aaa --- /dev/null +++ b/examples/arduino-led/schematic/arduino-led.net @@ -0,0 +1,166 @@ +(export (version D) + (design + (source /home/trygvis/dev/io.trygvis/2016/07/kicad-utils/examples/arduino+led/schematic/arduino+led.sch) + (date "Mon 25 Jul 2016 19:16:49 CEST") + (tool "Eeschema 4.0.2+dfsg1-stable") + (sheet (number 1) (name /) (tstamps /) + (title_block + (title) + (company) + (rev) + (date) + (source arduino+led.sch) + (comment (number 1) (value "")) + (comment (number 2) (value "")) + (comment (number 3) (value "")) + (comment (number 4) (value ""))))) + (components + (comp (ref U1) + (value ARDUINO_UNO) + (libsource (lib kicad_utils) (part ARDUINO_UNO)) + (sheetpath (names /) (tstamps /)) + (tstamp 57913050)) + (comp (ref R1) + (value R) + (libsource (lib device) (part R)) + (sheetpath (names /) (tstamps /)) + (tstamp 579130F0)) + (comp (ref D1) + (value LED) + (libsource (lib device) (part LED)) + (sheetpath (names /) (tstamps /)) + (tstamp 5791311D))) + (libparts + (libpart (lib kicad_utils) (part ARDUINO_UNO) + (fields + (field (name Reference) U) + (field (name Value) ARDUINO_UNO)) + (pins + (pin (num 1) (name ~RST) (type input)) + (pin (num 2) (name 3V3) (type input)) + (pin (num 3) (name 5V) (type input)) + (pin (num 4) (name GND) (type input)) + (pin (num 5) (name GND) (type input)) + (pin (num 6) (name Vin) (type input)) + (pin (num 7) (name A0) (type input)) + (pin (num 8) (name A1) (type input)) + (pin (num 9) (name A2) (type input)) + (pin (num 10) (name A3) (type input)) + (pin (num 11) (name A4) (type power_out)) + (pin (num 12) (name A5) (type input)) + (pin (num 13) (name D0) (type input)) + (pin (num 14) (name D1) (type input)) + (pin (num 15) (name D2) (type input)) + (pin (num 16) (name D3) (type input)) + (pin (num 17) (name D4) (type input)) + (pin (num 18) (name D5) (type input)) + (pin (num 19) (name D6) (type input)) + (pin (num 20) (name D7) (type input)) + (pin (num 21) (name D8) (type input)) + (pin (num 22) (name D9) (type input)) + (pin (num 23) (name D10) (type input)) + (pin (num 24) (name D11) (type input)) + (pin (num 25) (name D12) (type input)) + (pin (num 26) (name D13) (type input)) + (pin (num 27) (name GND) (type input)) + (pin (num 28) (name Aref) (type input)) + (pin (num 29) (name SDA) (type input)) + (pin (num 30) (name SCL) (type input)))) + (libpart (lib device) (part LED) + (footprints + (fp LED-3MM) + (fp LED-5MM) + (fp LED-10MM) + (fp LED-0603) + (fp LED-0805) + (fp LED-1206) + (fp LEDV)) + (fields + (field (name Reference) D) + (field (name Value) LED)) + (pins + (pin (num 1) (name K) (type passive)) + (pin (num 2) (name A) (type passive)))) + (libpart (lib device) (part R) + (description Resistor) + (footprints + (fp R_*) + (fp Resistor_*)) + (fields + (field (name Reference) R) + (field (name Value) R)) + (pins + (pin (num 1) (name ~) (type passive)) + (pin (num 2) (name ~) (type passive))))) + (libraries + (library (logical device) + (uri /usr/share/kicad/library/device.lib)) + (library (logical kicad_utils) + (uri /home/trygvis/dev/io.trygvis/2016/07/kicad-utils/kicad/kicad_utils.lib))) + (nets + (net (code 1) (name "Net-(U1-Pad25)") + (node (ref U1) (pin 25))) + (net (code 2) (name "Net-(U1-Pad26)") + (node (ref U1) (pin 26))) + (net (code 3) (name "Net-(U1-Pad17)") + (node (ref U1) (pin 17))) + (net (code 4) (name "Net-(U1-Pad27)") + (node (ref U1) (pin 27))) + (net (code 5) (name "Net-(U1-Pad18)") + (node (ref U1) (pin 18))) + (net (code 6) (name "Net-(U1-Pad28)") + (node (ref U1) (pin 28))) + (net (code 7) (name "Net-(U1-Pad19)") + (node (ref U1) (pin 19))) + (net (code 8) (name "Net-(U1-Pad29)") + (node (ref U1) (pin 29))) + (net (code 9) (name "Net-(U1-Pad15)") + (node (ref U1) (pin 15))) + (net (code 10) (name GND) + (node (ref D1) (pin 1))) + (net (code 11) (name "Net-(D1-Pad2)") + (node (ref D1) (pin 2)) + (node (ref R1) (pin 2))) + (net (code 12) (name /STATUS_LED) + (node (ref U1) (pin 16)) + (node (ref R1) (pin 1))) + (net (code 13) (name "Net-(U1-Pad20)") + (node (ref U1) (pin 20))) + (net (code 14) (name "Net-(U1-Pad2)") + (node (ref U1) (pin 2))) + (net (code 15) (name "Net-(U1-Pad3)") + (node (ref U1) (pin 3))) + (net (code 16) (name "Net-(U1-Pad4)") + (node (ref U1) (pin 4))) + (net (code 17) (name "Net-(U1-Pad5)") + (node (ref U1) (pin 5))) + (net (code 18) (name "Net-(U1-Pad6)") + (node (ref U1) (pin 6))) + (net (code 19) (name "Net-(U1-Pad7)") + (node (ref U1) (pin 7))) + (net (code 20) (name "Net-(U1-Pad8)") + (node (ref U1) (pin 8))) + (net (code 21) (name "Net-(U1-Pad9)") + (node (ref U1) (pin 9))) + (net (code 22) (name "Net-(U1-Pad10)") + (node (ref U1) (pin 10))) + (net (code 23) (name "Net-(U1-Pad1)") + (node (ref U1) (pin 1))) + (net (code 24) (name "Net-(U1-Pad30)") + (node (ref U1) (pin 30))) + (net (code 25) (name "Net-(U1-Pad11)") + (node (ref U1) (pin 11))) + (net (code 26) (name "Net-(U1-Pad21)") + (node (ref U1) (pin 21))) + (net (code 27) (name "Net-(U1-Pad12)") + (node (ref U1) (pin 12))) + (net (code 28) (name "Net-(U1-Pad22)") + (node (ref U1) (pin 22))) + (net (code 29) (name "Net-(U1-Pad13)") + (node (ref U1) (pin 13))) + (net (code 30) (name "Net-(U1-Pad23)") + (node (ref U1) (pin 23))) + (net (code 31) (name "Net-(U1-Pad14)") + (node (ref U1) (pin 14))) + (net (code 32) (name "Net-(U1-Pad24)") + (node (ref U1) (pin 24))))) \ No newline at end of file diff --git a/examples/arduino-led/schematic/arduino-led.pro b/examples/arduino-led/schematic/arduino-led.pro new file mode 100644 index 0000000..e707bbb --- /dev/null +++ b/examples/arduino-led/schematic/arduino-led.pro @@ -0,0 +1,61 @@ +update=Mon 25 Jul 2016 19:13:30 CEST +version=1 +last_client=kicad +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[general] +version=1 +[eeschema] +version=1 +LibDir=../../../kicad +[eeschema/libraries] +LibName1=power +LibName2=device +LibName3=transistors +LibName4=conn +LibName5=linear +LibName6=regul +LibName7=74xx +LibName8=cmos4000 +LibName9=adc-dac +LibName10=memory +LibName11=xilinx +LibName12=microcontrollers +LibName13=dsp +LibName14=microchip +LibName15=analog_switches +LibName16=motorola +LibName17=texas +LibName18=intel +LibName19=audio +LibName20=interface +LibName21=digital-audio +LibName22=philips +LibName23=display +LibName24=cypress +LibName25=siliconi +LibName26=opto +LibName27=atmel +LibName28=contrib +LibName29=valves +LibName30=kicad_utils diff --git a/examples/arduino-led/schematic/arduino-led.sch b/examples/arduino-led/schematic/arduino-led.sch new file mode 100644 index 0000000..642cd4b --- /dev/null +++ b/examples/arduino-led/schematic/arduino-led.sch @@ -0,0 +1,101 @@ +EESchema Schematic File Version 2 +LIBS:power +LIBS:device +LIBS:transistors +LIBS:conn +LIBS:linear +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:valves +LIBS:kicad_utils +LIBS:arduino+mod-lcd3310-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L ARDUINO_UNO U1 +U 1 1 57913050 +P 5250 3750 +F 0 "U1" H 5250 3750 60 0000 C CNN +F 1 "ARDUINO_UNO" H 5250 3850 60 0000 C CNN +F 2 "" H 5250 3750 60 0000 C CNN +F 3 "" H 5250 3750 60 0000 C CNN + 1 5250 3750 + 1 0 0 -1 +$EndComp +$Comp +L GND #PWR1 +U 1 1 579130D3 +P 7100 5200 +F 0 "#PWR1" H 7100 4950 50 0001 C CNN +F 1 "GND" H 7100 5050 50 0000 C CNN +F 2 "" H 7100 5200 50 0000 C CNN +F 3 "" H 7100 5200 50 0000 C CNN + 1 7100 5200 + 1 0 0 -1 +$EndComp +Wire Wire Line + 6000 4050 7100 4050 +Wire Wire Line + 7100 4050 7100 4150 +$Comp +L R R1 +U 1 1 579130F0 +P 7100 4300 +F 0 "R1" V 7180 4300 50 0000 C CNN +F 1 "R" V 7100 4300 50 0000 C CNN +F 2 "" V 7030 4300 50 0000 C CNN +F 3 "" H 7100 4300 50 0000 C CNN + 1 7100 4300 + 1 0 0 -1 +$EndComp +$Comp +L LED D1 +U 1 1 5791311D +P 7100 4750 +F 0 "D1" H 7100 4850 50 0000 C CNN +F 1 "LED" H 7100 4650 50 0000 C CNN +F 2 "" H 7100 4750 50 0000 C CNN +F 3 "" H 7100 4750 50 0000 C CNN + 1 7100 4750 + 0 -1 -1 0 +$EndComp +Wire Wire Line + 7100 4450 7100 4550 +Wire Wire Line + 7100 4950 7100 5200 +Text Label 6400 4050 0 60 ~ 0 +STATUS_LED +$EndSCHEMATC diff --git a/include/trygvis/kicad.h b/include/trygvis/kicad.h new file mode 100644 index 0000000..4cff944 --- /dev/null +++ b/include/trygvis/kicad.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +namespace trygvis { +namespace kicad { + +template +using opt = std::experimental::optional; + +class component { +public: + std::string ref; + std::string value; + + component(const std::string &ref, const std::string &value) : ref(ref), value(value) { } +}; + +struct pin { + int num; + std::string name; + std::string type; +}; + +struct part { + std::vector pins; +}; + +class node { +public: + std::string ref; + int pin; + + node(const std::string &ref, int pin) : ref(ref), pin(pin) { } +}; + +class net { +public: + int code; + std::string name; + std::vector nodes; + + net(int code, const std::string &name, const std::vector &nodes) : code(code), name(name), nodes(nodes) { } +}; + +struct netlist { + std::vector components; + std::vector parts; + std::vector nets; + + opt find_component(std::string ref) const; +}; + +class kicad_parse_exception : public std::runtime_error { +public: + explicit kicad_parse_exception(const std::vector &messages) : + runtime_error("Parse error"), messages(messages) { } + + const std::vector messages; +}; + +class kicad_net_loader { +public: + kicad_net_loader(); + + virtual ~kicad_net_loader(); + + netlist load(std::string path); + + void setDebug(bool debug); + +private: + bool debug_; +}; + +} // namespace kicad +} // namespace trygvis diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 0000000..4821b17 --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + io.trygvis.kicad + kicad-utils + 1.0-SNAPSHOT + + + org.antlr + antlr4 + ${antlr4-runtime.version} + + + org.antlr + antlr4-runtime + ${antlr4-runtime.version} + + + com.google.code.findbugs + jsr305 + 3.0.1 + + + junit + junit + 4.12 + test + + + + 4.5.3 + + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.antlr + antlr4-maven-plugin + ${antlr4-runtime.version} + + + gnu + + antlr4 + + + + -package + io.trygvis.ld.antlr + + + GnuLd*.g4 + + . + target/generated-sources/antlr4/io/trygvis/ld/antlr + + + + test1 + + antlr4 + + + + -package + io.trygvis.ld.test1 + + + **/Test1*.g4 + + target/generated-sources/antlr4/io/trygvis/ld/test1 + + + + + true + target/generated-sources/antlr4 + + + + + diff --git a/java/src/main/java/io/trygvis/ld/CollectingErrorListener.java b/java/src/main/java/io/trygvis/ld/CollectingErrorListener.java new file mode 100644 index 0000000..968a02b --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/CollectingErrorListener.java @@ -0,0 +1,17 @@ +package io.trygvis.ld; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import java.util.ArrayList; +import java.util.List; + +public class CollectingErrorListener extends BaseErrorListener { + public final List errors = new ArrayList<>(); + + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + String s = line + ":" + charPositionInLine + " " + msg; + errors.add(s); + } +} diff --git a/java/src/main/java/io/trygvis/ld/ElfinfoExprVisitor.java b/java/src/main/java/io/trygvis/ld/ElfinfoExprVisitor.java new file mode 100644 index 0000000..af5bea5 --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/ElfinfoExprVisitor.java @@ -0,0 +1,318 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdParser; +import io.trygvis.ld.antlr.GnuLdParserBaseVisitor; +import org.antlr.v4.runtime.tree.ParseTreeProperty; + +import java.math.BigInteger; + +class ElfinfoExprVisitor extends GnuLdParserBaseVisitor { + private BigInteger value; + private ParseTreeProperty es = new ParseTreeProperty<>(); + + public static BigInteger evaluate(GnuLdParser.Mustbe_expContext ctx) { + ElfinfoExprVisitor v = new ElfinfoExprVisitor(); + v.visitMustbe_exp(ctx); + return v.value(); + } + + @Override + public ElfinfoExprVisitor visitExpLt(GnuLdParser.ExpLtContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpBlock(GnuLdParser.ExpBlockContext ctx) { + return visitChildren(ctx); + + } + + @Override + public ElfinfoExprVisitor visitExpAbsolute(GnuLdParser.ExpAbsoluteContext ctx) { + return visitChildren(ctx); + + } + + @Override + public ElfinfoExprVisitor visitExpLog2ceil(GnuLdParser.ExpLog2ceilContext ctx) { + return visitChildren(ctx); + + } + + @Override + public ElfinfoExprVisitor visitExpAddr(GnuLdParser.ExpAddrContext ctx) { + return visitChildren(ctx); + + } + + @Override + public ElfinfoExprVisitor visitExpDataSegmentEnd(GnuLdParser.ExpDataSegmentEndContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpSub(GnuLdParser.ExpSubContext ctx) { + System.out.println("ElfinfoExprVisitor.visitExpSub"); + + ElfinfoExprVisitor ret = visitChildren(ctx); + BigInteger a = es.removeFrom(ctx.exp(0)); + BigInteger b = es.removeFrom(ctx.exp(1)); + + BigInteger x = a.subtract(b); + + es.put(ctx, x); + return ret; + } + + @Override + public ElfinfoExprVisitor visitExpDefined(GnuLdParser.ExpDefinedContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpMod(GnuLdParser.ExpModContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpInvert(GnuLdParser.ExpInvertContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAlign(GnuLdParser.ExpAlignContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpMul(GnuLdParser.ExpMulContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAnd(GnuLdParser.ExpAndContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpXor(GnuLdParser.ExpXorContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpParen(GnuLdParser.ExpParenContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpMinus(GnuLdParser.ExpMinusContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpDiv(GnuLdParser.ExpDivContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpGe(GnuLdParser.ExpGeContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpMin(GnuLdParser.ExpMinContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAlignK(GnuLdParser.ExpAlignKContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpNegate(GnuLdParser.ExpNegateContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpName(GnuLdParser.ExpNameContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpOr(GnuLdParser.ExpOrContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpTrinary(GnuLdParser.ExpTrinaryContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpOror(GnuLdParser.ExpOrorContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpDataSegmentAlign(GnuLdParser.ExpDataSegmentAlignContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpLengthExp(GnuLdParser.ExpLengthExpContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAdd(GnuLdParser.ExpAddContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpLoadaddr(GnuLdParser.ExpLoadaddrContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpGt(GnuLdParser.ExpGtContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpOrigin(GnuLdParser.ExpOriginContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpEq(GnuLdParser.ExpEqContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpMax(GnuLdParser.ExpMaxContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpLshift(GnuLdParser.ExpLshiftContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpSizeofHeaders(GnuLdParser.ExpSizeofHeadersContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpLe(GnuLdParser.ExpLeContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpNe(GnuLdParser.ExpNeContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAndand(GnuLdParser.ExpAndandContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpRshift(GnuLdParser.ExpRshiftContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpNextParen(GnuLdParser.ExpNextParenContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAlignof(GnuLdParser.ExpAlignofContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpSegmentStart(GnuLdParser.ExpSegmentStartContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpInt(GnuLdParser.ExpIntContext ctx) { + System.out.println("ElfinfoExprVisitor.visitExpInt"); + + BigInteger i = parseInt(ctx.INT().getText()); + es.put(ctx, i); + + return this; + } + + public static BigInteger parseInt(String str) { + str = str.toLowerCase(); + int base = 10; + if (str.startsWith("0x")) { + base = 16; + str = str.substring(2); + } + + int factor = 1; + if (str.endsWith("k")) { + factor = 1024; + str = str.substring(0, str.length() - 1); + } else if (str.endsWith("k")) { + factor = 1024 * 1024; + str = str.substring(0, str.length() - 1); + } + + BigInteger i = new BigInteger(str, base); + if (factor > 1) { + i = i.multiply(BigInteger.valueOf(factor)); + } + return i; + } + + @Override + public ElfinfoExprVisitor visitExpConstant(GnuLdParser.ExpConstantContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpPlus(GnuLdParser.ExpPlusContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpSizeof(GnuLdParser.ExpSizeofContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpDataSegmentRelRoEnd(GnuLdParser.ExpDataSegmentRelRoEndContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitExpAssert(GnuLdParser.ExpAssertContext ctx) { + return visitChildren(ctx); + } + + @Override + public ElfinfoExprVisitor visitMustbe_exp(GnuLdParser.Mustbe_expContext ctx) { + ElfinfoExprVisitor ret = visitChildren(ctx); + + value = es.removeFrom(ctx.exp()); + es.put(ctx, value); + + return ret; + } + + public BigInteger value() { + if (value != null) { + return value; + } + + throw new RuntimeException("Something bad happened, probably an unevaluated expression part."); + } +} diff --git a/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdListener.java b/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdListener.java new file mode 100644 index 0000000..fe40547 --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdListener.java @@ -0,0 +1,170 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdParser; +import io.trygvis.ld.antlr.GnuLdParserBaseListener; +import org.antlr.v4.runtime.tree.ParseTreeProperty; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class ElfinfoGnuLdListener extends GnuLdParserBaseListener { + public List memoryAreas = new ArrayList<>(); + + private final ParseTreeProperty expr = new ParseTreeProperty<>(); + + @Override + public void exitExpInt(GnuLdParser.ExpIntContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitExpInt: ctx.INT().getText() = " + ctx.INT().getText()); + expr.put(ctx, ElfinfoExprVisitor.parseInt(ctx.INT().getText())); + } + + @Override + public void exitExpSub(GnuLdParser.ExpSubContext ctx) { + BigInteger a = expr.get(ctx.exp(0)); + BigInteger b = expr.get(ctx.exp(1)); + BigInteger x = a.subtract(b); + expr.put(ctx, x); + } + + @Override + public void exitExpAdd(GnuLdParser.ExpAddContext ctx) { + BigInteger a = expr.get(ctx.exp(0)); + BigInteger b = expr.get(ctx.exp(1)); + BigInteger x = a.add(b); + expr.put(ctx, x); + } + + @Override + public void exitExpName(GnuLdParser.ExpNameContext ctx) { + expr.put(ctx, BigInteger.ZERO); + } + + @Override + public void exitExpAddr(GnuLdParser.ExpAddrContext ctx) { + expr.put(ctx, BigInteger.ZERO); + } + + @Override + public void exitExpSizeof(GnuLdParser.ExpSizeofContext ctx) { + expr.put(ctx, BigInteger.ZERO); + } + + @Override + public void exitExpLengthExp(GnuLdParser.ExpLengthExpContext ctx) { + LdLoader.MemoryArea ma = getMemoryArea(ctx.NAME().getText()); +// System.out.println("ma.length = " + ma.length); + expr.put(ctx, ma.length); + } + + @Override + public void exitExpOrigin(GnuLdParser.ExpOriginContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitExpOrigin: " + ctx.getText()); + LdLoader.MemoryArea ma = getMemoryArea(ctx.NAME().getText()); +// System.out.println("ma.origin = " + ma.origin); + expr.put(ctx, ma.origin); + } + + private LdLoader.MemoryArea getMemoryArea(String name) { + LdLoader.MemoryArea m = null; + for (LdLoader.MemoryArea ma : memoryAreas) { + if (ma.name.equals(name)) { + m = ma; + } + } + if (m == null) { + throw new RuntimeException("No such memory area: " + name); + } + return m; + } + + @Override + public void enterMustbe_exp(GnuLdParser.Mustbe_expContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterMustbe_exp"); + } + + @Override + public void exitMustbe_exp(GnuLdParser.Mustbe_expContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitMustbe_exp"); + + expr.put(ctx, expr.get(ctx.exp())); + } + + + @Override + public void enterOrigin_spec(GnuLdParser.Origin_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterOrigin_spec"); + } + + @Override + public void exitOrigin_spec(GnuLdParser.Origin_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitOrigin_spec"); + } + + @Override + public void enterLength_spec(GnuLdParser.Length_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterLength_spec"); + } + + @Override + public void exitLength_spec(GnuLdParser.Length_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitLength_spec"); + } + + @Override + public void enterMemory_spec(GnuLdParser.Memory_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterMemory_spec"); + } + + @Override + public void exitMemory_spec(GnuLdParser.Memory_specContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitMemory_spec"); + LdLoader.MemoryArea ma = new LdLoader.MemoryArea(); + ma.name = ctx.NAME().getText(); + ma.attributes = attributes; +// System.out.println("ctx.origin_spec() = " + ctx.origin_spec()); + ma.origin = expr.get(ctx.origin_spec().mustbe_exp()); + ma.length = expr.get(ctx.length_spec().mustbe_exp()); + System.out.println(ma); + memoryAreas.add(ma); + } + + LdLoader.MemoryAttribute attribute; + boolean attributesInverted; + Set attributes; + + @Override + public void exitAttributes_opt(GnuLdParser.Attributes_optContext ctx) { +// System.out.println("ElfinfoGnuLdListener.exitAttributes_opt"); + attributes = new HashSet<>(); + } + + @Override + public void enterAttributeInverted(GnuLdParser.AttributeInvertedContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterAttributeInverted"); + + if (!attributes.isEmpty()) { + throw new RuntimeException("Attributes for memory areas can only be attributesInverted (with '!') as the first character in a specification; foo(!rw), not foo(x!rw)."); + } +// String name = ctx.name().getText(); + String name = ctx.NAME().getText(); +// System.out.println("ctx.ATTRIBUTE().getText() = " + name); + + attributesInverted = true; + } + + @Override + public void enterAttributeNormal(GnuLdParser.AttributeNormalContext ctx) { +// System.out.println("ElfinfoGnuLdListener.enterAttributeNormal"); + + String name = ctx.NAME().getText(); +// System.out.println("ctx.ATTRIBUTE().getText() = " + name); + for (int i = 0; i < name.length(); i++) { + attribute = LdLoader.MemoryAttribute.valueOf(String.valueOf(Character.toUpperCase(name.charAt(i)))); + attributes.add(attribute); + } + attributesInverted = false; + } +} diff --git a/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdVisitor.java b/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdVisitor.java new file mode 100644 index 0000000..3062aad --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/ElfinfoGnuLdVisitor.java @@ -0,0 +1,75 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdParser; +import io.trygvis.ld.antlr.GnuLdParserBaseVisitor; +import org.antlr.v4.runtime.tree.ParseTreeProperty; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ElfinfoGnuLdVisitor extends GnuLdParserBaseVisitor { + public List memoryAreas = new ArrayList<>(); + + ParseTreeProperty es = new ParseTreeProperty<>(); + + @Override + public ElfinfoGnuLdVisitor visitMemory_spec(GnuLdParser.Memory_specContext ctx) { + System.out.println("ElfinfoGnuLdBaseVisitor.visitMemory_spec"); + visitChildren(ctx); + + if (attributesInverted) { + // not quite sure how this is supposed to work yet + throw new RuntimeException("Inverted memory region attributes is not implemented yet."); + } + + LdLoader.MemoryArea ma = new LdLoader.MemoryArea(); + ma.name = ctx.NAME().getText(); + ma.attributes = attributes; + ma.origin = ElfinfoExprVisitor.evaluate(ctx.origin_spec().mustbe_exp()); + ma.length = ElfinfoExprVisitor.evaluate(ctx.length_spec().mustbe_exp()); + System.out.println(ma); + memoryAreas.add(ma); + return this; + } + + LdLoader.MemoryAttribute attribute; + boolean attributesInverted; + Set attributes; + + @Override + public ElfinfoGnuLdVisitor visitAttributes_opt(GnuLdParser.Attributes_optContext ctx) { + System.out.println("ElfinfoGnuLdBaseVisitor.visitAttributes_opt"); + attributes = new HashSet<>(); + return visitChildren(ctx); + } + + @Override + public ElfinfoGnuLdVisitor visitAttributeNormal(GnuLdParser.AttributeNormalContext ctx) { + System.out.println("ElfinfoGnuLdBaseVisitor.visitAttributeNormal"); + String name = ctx.NAME().getText(); + System.out.println("ctx.ATTRIBUTE().getText() = " + name); + for (int i = 0; i < name.length(); i++) { + attribute = LdLoader.MemoryAttribute.valueOf(String.valueOf(Character.toUpperCase(name.charAt(i)))); + attributes.add(attribute); + } + attributesInverted = false; + return visitChildren(ctx); + } + + @Override + public ElfinfoGnuLdVisitor visitAttributeInverted(GnuLdParser.AttributeInvertedContext ctx) { + System.out.println("ElfinfoGnuLdBaseVisitor.visitAttributeInverted"); + if (!attributes.isEmpty()) { + throw new RuntimeException("Attributes for memory areas can only be attributesInverted (with '!') as the first character in a specification; foo(!rw), not foo(x!rw)."); + } +// String name = ctx.name().getText(); + String name = ctx.NAME().getText(); + System.out.println("ctx.ATTRIBUTE().getText() = " + name); + + attributesInverted = true; + return visitChildren(ctx); + } +} diff --git a/java/src/main/java/io/trygvis/ld/LdLoader.java b/java/src/main/java/io/trygvis/ld/LdLoader.java new file mode 100644 index 0000000..0b71720 --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/LdLoader.java @@ -0,0 +1,121 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdLexer; +import io.trygvis.ld.antlr.GnuLdParser; +import org.antlr.v4.runtime.ANTLRFileStream; +import org.antlr.v4.runtime.BufferedTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; + +import java.io.File; +import java.math.BigInteger; +import java.util.List; +import java.util.Set; + +public class LdLoader { + + public final List memoryAreas; + + public LdLoader(List memoryAreas) { + this.memoryAreas = memoryAreas; + } + + public enum MemoryAttribute { + R, W, X, A, I, + } + + public static class MemoryArea { + private final BigInteger MEGA = BigInteger.valueOf(1024 * 1024); + private final BigInteger KILO = BigInteger.valueOf(1024); + + String name; + BigInteger origin; + BigInteger length; + Set attributes; + + @Override + public String toString() { + return "MemoryArea{" + + "name='" + name + '\'' + + ", origin=" + origin + + ", length=" + length + + ", attributes='" + attributes + '\'' + + '}'; + } + + public String prettyOrigin() { + if (origin == null) { + return ""; + } + String hex = origin.toString(16); + + return "0x" + ("00000000").substring(0, 8 - hex.length()) + hex; + } + + public String prettyLength() { + if (length == null) { + return ""; + } + + if (length.compareTo(MEGA) >= 0 && length.mod(MEGA).equals(BigInteger.ZERO)) { + return length.divide(MEGA) + "M"; + } + + if (length.compareTo(KILO) >= 0 && length.mod(KILO).equals(BigInteger.ZERO)) { + return length.divide(KILO) + "k"; + } + + return String.valueOf(length); + } + + public String prettyAttributes() { + String s = ""; + for (MemoryAttribute attribute : attributes) { + s += attribute.name().toLowerCase(); + } + return s; + } + } + + public static LdLoader load(File path) throws Exception { + GnuLdLexer lexer = new GnuLdLexer(new ANTLRFileStream(path.getAbsolutePath())); + BufferedTokenStream tokens = new BufferedTokenStream(lexer); + GnuLdParser parser = new GnuLdParser(tokens); + parser.setBuildParseTree(true); + parser.removeErrorListeners(); + ConsoleErrorListener errorListener = new ConsoleErrorListener(); + parser.addErrorListener(errorListener); + CollectingErrorListener collectingErrorListener = new CollectingErrorListener(); + parser.addErrorListener(collectingErrorListener); + + ElfinfoGnuLdListener listener = new ElfinfoGnuLdListener(); + parser.addParseListener(listener); + + GnuLdParser.FileContext file = parser.file(); + + if (parser.getNumberOfSyntaxErrors() > 0) { + throw new ParseErrorException(lexer, tokens, parser, collectingErrorListener.errors + ); + } + +// ElfinfoGnuLdVisitor visitor = new ElfinfoGnuLdVisitor(); +// visitor.visit(file); + +// return new LdLoader(visitor.memoryAreas); + return new LdLoader(listener.memoryAreas); + } + + public static class ParseErrorException extends Exception { + public final GnuLdLexer lexer; + public final BufferedTokenStream tokens; + public final GnuLdParser parser; + public final List errors; + + public ParseErrorException(GnuLdLexer lexer, BufferedTokenStream tokens, GnuLdParser parser, List errors) { + this.lexer = lexer; + this.tokens = tokens; + this.parser = parser; + this.errors = errors; + } + } + +} diff --git a/java/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java b/java/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java new file mode 100644 index 0000000..a975497 --- /dev/null +++ b/java/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java @@ -0,0 +1,59 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdParserBaseVisitor; +import io.trygvis.ld.antlr.GnuLdParser; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.List; + +class StringGnuLdVisitor extends GnuLdParserBaseVisitor { + +// public static String parseName(GnuLdParser.NameContext ctx) { +// return ""; +// } + + /* + public static String parseName(GnuLdParser.NameContext ctx) { + StringGnuLdVisitor v = new StringGnuLdVisitor(); + return v.visit(ctx); + } + + private StringBuilder string = new StringBuilder(); + + @Override + public String visitNamePlain(GnuLdParser.NamePlainContext ctx) { + System.out.println("NameTest.visitNamePlain"); + String s = ctx.NAME().getText(); + string.append(s); + return s; + } + +// @Override +// public String visitName_or_space(GnuLdParser.Name_or_spaceContext ctx) { +// System.out.println("StringGnuLdBaseVisitor.visitName_or_space"); +// +// TerminalNode name = ctx.NAME(); +// String s = name != null ? name.getText() : " "; +// string.append(s); +// return s; +// } + + @Override + public String visitNameQuoted(GnuLdParser.NameQuotedContext ctx) { + System.out.println("StringGnuLdBaseVisitor.visitNameQuoted"); + List children = ctx.children; + System.out.println("children.size()) = " + children.size()); + + String s = ""; + for (int i = 1; i < children.size() - 1; i++) { + ParseTree part = children.get(i); + System.out.println("part.getText() = " + part.getText()); + s += part.getText(); + } + + return s; +// +// return visitChildren(ctx); + } + */ +} diff --git a/kicad.cpp b/kicad.cpp new file mode 100644 index 0000000..2af9bc6 --- /dev/null +++ b/kicad.cpp @@ -0,0 +1,219 @@ +#include "trygvis/kicad.h" +#include +#include +#include + +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 +class ParseTreeProperty { +public: + virtual V get(Ref 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 _annotations; + +private: +}; + +} // namespace antlr + +} + +using namespace std; +using namespace trygvis::antlr; + +namespace trygvis { +namespace kicad { + +opt netlist::find_component(string ref) const { + for (const component &c :components) { + int x = c.ref.compare(ref); + if (x == 0) { + return &c; + } + } + return std::experimental::nullopt; +} + +static +int parse(const Ref &integer) { + unsigned long long i = strtoull(integer->getText().c_str(), NULL, 10); + + return static_cast(i); +} + +class KicadErrorListener : public BaseErrorListener { +public: + vector messages; + + void syntaxError(IRecognizer *recognizer, Token *offendingSymbol, size_t line, int charPositionInLine, + const string &msg, exception_ptr e) override { + static_cast(recognizer); + static_cast(offendingSymbol); + static_cast(e); + messages.push_back("line " + to_string(line) + ":" + to_string(charPositionInLine) + ": " + msg); + } +}; + +class kicad_main_listener : public KicadNetParserBaseListener { + +public: + vector components; + vector parts; + vector nets; + vector nodes; + + ParseTreeProperty strings; + + virtual void exitNet(KicadNetParser::NetContext *ctx) override { + auto code = parse(ctx->code()->INTEGER()); + auto name = strings.get(ctx->name()->string()); + +// 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()); + + cerr << "exitComponent, ref=" << ref << ", value=" << value << endl; + + components.emplace_back(ref, value); + } + + 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) { + + ANTLRFileStream input(path); + KicadNetLexer lexer(&input); + CommonTokenStream tokens(&lexer); + + tokens.fill(); + + if (debug_) { + for (auto token : tokens.getTokens()) { + cerr << 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) { + cerr << 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 trygvis +} // namespace kicad diff --git a/kicad/.gitignore b/kicad/.gitignore new file mode 100644 index 0000000..68c3cd9 --- /dev/null +++ b/kicad/.gitignore @@ -0,0 +1,2 @@ +*.bak +*.bck diff --git a/kicad/kicad_utils.dcm b/kicad/kicad_utils.dcm new file mode 100644 index 0000000..5f3ed79 --- /dev/null +++ b/kicad/kicad_utils.dcm @@ -0,0 +1,3 @@ +EESchema-DOCLIB Version 2.0 +# +#End Doc Library diff --git a/kicad/kicad_utils.lib b/kicad/kicad_utils.lib new file mode 100644 index 0000000..586f2c9 --- /dev/null +++ b/kicad/kicad_utils.lib @@ -0,0 +1,46 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# ARDUINO_UNO +# +DEF ARDUINO_UNO U 0 40 Y Y 1 F N +F0 "U" 0 0 60 H V C CNN +F1 "ARDUINO_UNO" 0 100 60 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +S -550 -700 550 1300 0 1 0 N +X ~RST 1 -750 600 200 R 50 50 1 1 I +X 3V3 2 -750 500 200 R 50 50 1 1 I +X 5V 3 -750 400 200 R 50 50 1 1 I +X GND 4 -750 300 200 R 50 50 1 1 I +X GND 5 -750 200 200 R 50 50 1 1 I +X Vin 6 -750 100 200 R 50 50 1 1 I +X A0 7 -750 -100 200 R 50 50 1 1 I +X A1 8 -750 -200 200 R 50 50 1 1 I +X A2 9 -750 -300 200 R 50 50 1 1 I +X A3 10 -750 -400 200 R 50 50 1 1 I +X D7 20 750 100 200 L 50 50 1 1 I +X SCL 30 750 1200 200 L 50 50 1 1 I +X A4 11 -750 -500 200 R 50 50 1 1 w +X D8 21 750 300 200 L 50 50 1 1 I +X A5 12 -750 -600 200 R 50 50 1 1 I +X D9 22 750 400 200 L 50 50 1 1 I +X D0 13 750 -600 200 L 50 50 1 1 I +X D10 23 750 500 200 L 50 50 1 1 I +X D1 14 750 -500 200 L 50 50 1 1 I +X D11 24 750 600 200 L 50 50 1 1 I +X D2 15 750 -400 200 L 50 50 1 1 I +X D12 25 750 700 200 L 50 50 1 1 I +X D3 16 750 -300 200 L 50 50 1 1 I +X D13 26 750 800 200 L 50 50 1 1 I +X D4 17 750 -200 200 L 50 50 1 1 I +X GND 27 750 900 200 L 50 50 1 1 I +X D5 18 750 -100 200 L 50 50 1 1 I +X Aref 28 750 1000 200 L 50 50 1 1 I +X D6 19 750 0 200 L 50 50 1 1 I +X SDA 29 750 1100 200 L 50 50 1 1 I +ENDDRAW +ENDDEF +# +#End Library diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..b5e998d --- /dev/null +++ b/main.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include "trygvis/kicad.h" + +using namespace std; +using namespace trygvis::kicad; + +char *program; + +__attribute__((noreturn)) +void usage(const char *reason = nullptr) { + if (reason) { + fprintf(stderr, "%s\n", reason); + } + fprintf(stderr, "usage: %s -f file -r ref\n", program); + exit(EX_USAGE); +} + +void parse_args(int argc, char **argv, bool *debug, char **filename, char **ref) { + *debug = false; + *filename = *ref = nullptr; + + int c; + while ((c = getopt(argc, argv, "Df:r:")) != -1) { + switch (c) { + case 'D': + *debug = true; + break; + case 'f': + *filename = optarg; + break; + case 'r': + *ref = optarg; + break; + default: + abort(); + } + } + + if (!*filename) { + usage(); + } +} + +bool generate(const char *ref, const trygvis::kicad::netlist &netlist) { + opt componentO = netlist.find_component(ref); + + if (!componentO) { + cerr << "Could not find component '" << ref << "'" << endl; + return false; + } + + const component *c = *componentO; + + cerr << "Generating connections for " << ref << endl; + + cerr << c->value << endl; + + return true; +} + +int main(int argc, char **argv) { + program = argv[0]; + + bool debug; + char *filename; + char *ref; + parse_args(argc, argv, &debug, &filename, &ref); + + kicad_net_loader loader; + + try { + auto netlist = loader.load(filename); + + auto ok = generate(ref, netlist); + + if (ok) { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; + } catch (kicad_parse_exception &e) { + for (auto &m: e.messages) { + fprintf(stderr, "%s\n", m.c_str()); + } + errx(EX_DATAERR, "Could not parse netlist"); + } +} diff --git a/test-input/lcd3310.net b/test-input/lcd3310.net new file mode 100644 index 0000000..e7f6a79 --- /dev/null +++ b/test-input/lcd3310.net @@ -0,0 +1,163 @@ +(export (version D) + (design + (source /home/trygvis/dev/io.trygvis/2016/06/intel-quark-d2000-playground/apps/lcd3310/schematic/lcd3310.sch) + (date "Thu 21 Jul 2016 20:51:53 CEST") + (tool "Eeschema 4.0.2+dfsg1-stable") + (sheet (number 1) (name /) (tstamps /) + (title_block + (title) + (company) + (rev) + (date) + (source lcd3310.sch) + (comment (number 1) (value "")) + (comment (number 2) (value "")) + (comment (number 3) (value "")) + (comment (number 4) (value ""))))) + (components + (comp (ref U1) + (value MOD-LCD3310) + (libsource (lib olimex) (part MOD-LCD3310)) + (sheetpath (names /) (tstamps /)) + (tstamp 578E9EE0)) + (comp (ref U2) + (value D2000_DP) + (libsource (lib intel-quark) (part D2000_DP)) + (sheetpath (names /) (tstamps /)) + (tstamp 578E9F5B)) + (comp (ref W1) + (value TEST_1P) + (libsource (lib conn) (part TEST_1P)) + (sheetpath (names /) (tstamps /)) + (tstamp 578F91E5))) + (libparts + (libpart (lib intel-quark) (part D2000_DP) + (fields + (field (name Reference) U) + (field (name Value) D2000_DP)) + (pins + (pin (num 1) (name NC) (type NotConnected)) + (pin (num 2) (name ~RESET) (type input)) + (pin (num 3) (name 3.3V) (type input)) + (pin (num 4) (name 5V) (type power_out)) + (pin (num 5) (name GND) (type power_out)) + (pin (num 6) (name GND) (type power_out)) + (pin (num 7) (name NC) (type NotConnected)) + (pin (num 8) (name A0) (type input)) + (pin (num 9) (name A1) (type input)) + (pin (num 10) (name A2) (type input)) + (pin (num 11) (name A3) (type input)) + (pin (num 12) (name A4) (type input)) + (pin (num 13) (name A5_SDA) (type input)) + (pin (num 14) (name A6_SCL) (type input)) + (pin (num 15) (name D0_UART_A_RX) (type input)) + (pin (num 16) (name D1_UART_A_TX) (type input)) + (pin (num 17) (name D2) (type input)) + (pin (num 18) (name D3) (type input)) + (pin (num 19) (name D4) (type input)) + (pin (num 20) (name D5) (type input)) + (pin (num 21) (name ~~D6) (type input)) + (pin (num 22) (name D7) (type input)) + (pin (num 23) (name D8) (type input)) + (pin (num 24) (name ~~D9) (type input)) + (pin (num 25) (name D10_SSO) (type input)) + (pin (num 26) (name D11_MOSI) (type input)) + (pin (num 27) (name D12_MISO) (type input)) + (pin (num 28) (name D13_SCK) (type input)) + (pin (num 29) (name GND) (type power_out)) + (pin (num 30) (name Aref) (type input)) + (pin (num 31) (name SDA) (type BiDi)) + (pin (num 32) (name SCL) (type output)))) + (libpart (lib olimex) (part MOD-LCD3310) + (fields + (field (name Reference) U) + (field (name Value) MOD-LCD3310)) + (pins + (pin (num 1) (name GND) (type power_in)) + (pin (num 2) (name 5V) (type power_in)) + (pin (num 3) (name SDA) (type BiDi)) + (pin (num 4) (name SCL) (type input)))) + (libpart (lib conn) (part TEST_1P) + (description point) + (fields + (field (name Reference) W) + (field (name Value) TEST_1P)) + (pins + (pin (num 1) (name 1) (type passive))))) + (libraries + (library (logical intel-quark) + (uri /home/trygvis/dev/io.trygvis/2016/06/intel-quark-d2000-playground/apps/lcd3310/schematic/intel-quark.lib)) + (library (logical olimex) + (uri /home/trygvis/dev/io.trygvis/2016/06/intel-quark-d2000-playground/apps/lcd3310/schematic/olimex.lib)) + (library (logical conn) + (uri /usr/share/kicad/library/conn.lib))) + (nets + (net (code 1) (name "Net-(U2-Pad30)") + (node (ref U2) (pin 30))) + (net (code 2) (name "Net-(U2-Pad24)") + (node (ref U2) (pin 24))) + (net (code 3) (name "Net-(U2-Pad14)") + (node (ref U2) (pin 14))) + (net (code 4) (name "Net-(U2-Pad23)") + (node (ref U2) (pin 23))) + (net (code 5) (name "Net-(U2-Pad13)") + (node (ref U2) (pin 13))) + (net (code 6) (name "Net-(U2-Pad22)") + (node (ref U2) (pin 22))) + (net (code 7) (name "Net-(U2-Pad12)") + (node (ref U2) (pin 12))) + (net (code 8) (name "Net-(U2-Pad21)") + (node (ref U2) (pin 21))) + (net (code 9) (name "Net-(U2-Pad11)") + (node (ref U2) (pin 11))) + (net (code 10) (name "Net-(U2-Pad15)") + (node (ref U2) (pin 15))) + (net (code 11) (name "Net-(U2-Pad20)") + (node (ref U2) (pin 20))) + (net (code 12) (name "Net-(U2-Pad10)") + (node (ref U2) (pin 10))) + (net (code 13) (name "Net-(U2-Pad9)") + (node (ref U2) (pin 9))) + (net (code 14) (name "Net-(U2-Pad8)") + (node (ref U2) (pin 8))) + (net (code 15) (name "Net-(U2-Pad7)") + (node (ref U2) (pin 7))) + (net (code 16) (name "Net-(U2-Pad6)") + (node (ref U2) (pin 6))) + (net (code 17) (name "Net-(U2-Pad5)") + (node (ref U2) (pin 5))) + (net (code 18) (name "Net-(U2-Pad3)") + (node (ref U2) (pin 3))) + (net (code 19) (name "Net-(U2-Pad2)") + (node (ref U2) (pin 2))) + (net (code 20) (name /LCD_SDA) + (node (ref U1) (pin 3)) + (node (ref U2) (pin 31)) + (node (ref W1) (pin 1))) + (net (code 21) (name "Net-(U2-Pad1)") + (node (ref U2) (pin 1))) + (net (code 22) (name "Net-(U2-Pad19)") + (node (ref U2) (pin 19))) + (net (code 23) (name "Net-(U2-Pad28)") + (node (ref U2) (pin 28))) + (net (code 24) (name "Net-(U2-Pad18)") + (node (ref U2) (pin 18))) + (net (code 25) (name "Net-(U2-Pad27)") + (node (ref U2) (pin 27))) + (net (code 26) (name "Net-(U2-Pad17)") + (node (ref U2) (pin 17))) + (net (code 27) (name "Net-(U2-Pad26)") + (node (ref U2) (pin 26))) + (net (code 28) (name "Net-(U2-Pad16)") + (node (ref U2) (pin 16))) + (net (code 29) (name "Net-(U2-Pad25)") + (node (ref U2) (pin 25))) + (net (code 30) (name +5V) + (node (ref U2) (pin 4)) + (node (ref U1) (pin 2))) + (net (code 31) (name "Net-(U1-Pad1)") + (node (ref U1) (pin 1)) + (node (ref U2) (pin 29))) + (net (code 32) (name /LCD_SCL) + (node (ref U1) (pin 4)) + (node (ref U2) (pin 32))))) \ No newline at end of file -- cgit v1.2.3