kicad 0.8.0 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +2 -17
- data/README.md +11 -9
- data/kicad.gemspec +1 -1
- data/lib/kicad/ast.rb +301 -47
- data/lib/kicad/grammar.rb +547 -0
- data/lib/kicad/grammar.tt +5 -6
- data/lib/kicad/parser.rb +1 -1
- data/lib/kicad/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a91d99c4c831dbc121717ef0609e05c8fe07c20d1b2044bfddda163c75c6620
|
4
|
+
data.tar.gz: 9110254e21b4e2913160100ec75f2887ea327107a0fefb257a23e77a9dfba362
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44681ff57b45bebeea66e8a0dc92932e8219354dd2545e2dd4a9b4feb614aa7d2d528aff3946875c4373ff701e95015961e7865d0cc12a527404b84d29ae4b74
|
7
|
+
data.tar.gz: f2af698d30cd4b24a700a03a937f7a7a6c9913250c63aa99b3d75828191bb7d589d6fa06acd4a342f66c7d8a585d48ed8487aa0d1059e69a70262b2ce0da54cc
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
kicad (0.8.
|
4
|
+
kicad (0.8.1)
|
5
5
|
irb (~> 1.14, >= 1.14)
|
6
6
|
treetop (~> 1.6, >= 1.6.9)
|
7
7
|
|
@@ -9,7 +9,6 @@ GEM
|
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
date (3.4.1)
|
12
|
-
diff-lcs (1.6.1)
|
13
12
|
io-console (0.8.0)
|
14
13
|
irb (1.15.2)
|
15
14
|
pp (>= 0.6.0)
|
@@ -27,19 +26,6 @@ GEM
|
|
27
26
|
psych (>= 4.0.0)
|
28
27
|
reline (0.6.1)
|
29
28
|
io-console (~> 0.5)
|
30
|
-
rspec (3.13.0)
|
31
|
-
rspec-core (~> 3.13.0)
|
32
|
-
rspec-expectations (~> 3.13.0)
|
33
|
-
rspec-mocks (~> 3.13.0)
|
34
|
-
rspec-core (3.13.3)
|
35
|
-
rspec-support (~> 3.13.0)
|
36
|
-
rspec-expectations (3.13.4)
|
37
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
-
rspec-support (~> 3.13.0)
|
39
|
-
rspec-mocks (3.13.3)
|
40
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
-
rspec-support (~> 3.13.0)
|
42
|
-
rspec-support (3.13.3)
|
43
29
|
stringio (3.1.7)
|
44
30
|
treetop (1.6.14)
|
45
31
|
polyglot (~> 0.3)
|
@@ -51,8 +37,7 @@ PLATFORMS
|
|
51
37
|
DEPENDENCIES
|
52
38
|
bundler (>= 1.11)
|
53
39
|
kicad!
|
54
|
-
rake (
|
55
|
-
rspec (~> 3.3)
|
40
|
+
rake (~> 13)
|
56
41
|
|
57
42
|
BUNDLED WITH
|
58
43
|
2.6.2
|
data/README.md
CHANGED
@@ -4,29 +4,31 @@ Parse, load, modify and rewrite Kicad (s-epression) files into a convenient tree
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
```ruby
|
8
|
-
gem 'kicad'
|
9
|
-
```
|
10
|
-
|
11
|
-
or
|
12
|
-
|
13
7
|
gem install kicad
|
14
8
|
|
15
9
|
## Usage
|
16
10
|
|
17
11
|
$ irb -r kicad
|
18
|
-
irb(main):001> k = KiCad.load("my_file.
|
19
|
-
irb(main):001> k.children.filter{|c|
|
12
|
+
irb(main):001> k = KiCad.load("my_file.kicad_sym").value
|
13
|
+
irb(main):001> k.children.filter{|c| KiCad::AST::Symbol === c }.map{|c| c.values[1]}
|
20
14
|
["BC107", "CD4046"]
|
21
15
|
irb(main):001> puts k.emit
|
22
16
|
...
|
23
17
|
|
24
18
|
## Development
|
25
19
|
|
26
|
-
After checking out the repo, run `bundle` to install dependencies.
|
20
|
+
After checking out the repo, run `bundle` to install dependencies.
|
27
21
|
|
28
22
|
To install this gem onto your local machine from local source code, run `rake install`.
|
29
23
|
|
24
|
+
## Resources
|
25
|
+
|
26
|
+
KiCad uses a version of the Cadence SPECCTRA Design Language, defined in https://cdn.hackaday.io/files/1666717130852064/specctra.pdf
|
27
|
+
|
28
|
+
KiCad's documentation of this is at https://dev-docs.kicad.org/en/file-formats/sexpr-intro/
|
29
|
+
|
30
|
+
A related Rust library that was not consulted while building this is https://github.com/adom-inc/kicad_lib/tree/main/kicad_sexpr
|
31
|
+
|
30
32
|
## Contributing
|
31
33
|
|
32
34
|
Bug reports and pull requests are welcome on GitHub at https://github.com/cjheath/kicad-rb
|
data/kicad.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
# spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", "
|
23
|
+
spec.add_development_dependency "bundler", ">= 1.11"
|
24
24
|
spec.add_development_dependency "rake", "~> 13"
|
25
25
|
|
26
26
|
spec.add_runtime_dependency "treetop", ["~> 1.6", ">= 1.6.9"]
|
data/lib/kicad/ast.rb
CHANGED
@@ -11,136 +11,390 @@ module KiCad
|
|
11
11
|
def emit depth = 0
|
12
12
|
"\t"*depth +
|
13
13
|
'(' +
|
14
|
-
@values.map{|v|
|
14
|
+
@values.map{|v| String === v ? v.inspect : v.to_s }*' ' +
|
15
15
|
(@children.size == 0 ? '' : "\n" + @children.map{|c| c.emit(depth+1) }*''+"\t"*depth) +
|
16
16
|
")\n"
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
v.inspect
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
19
|
+
# Define setter and getter methods for each value type this class allows
|
20
|
+
def self.value_types vts
|
21
|
+
i = 1 # @values[0] is always the class symbol
|
22
|
+
vts.each do |k, v|
|
23
|
+
# puts "#{self.name} attribute #{k.to_s} => #{v.inspect}"
|
24
|
+
# puts "attr_accessor #{self.name}.#{k} (values #{v.inspect}) is stored in @values[#{i}]"
|
25
|
+
begin
|
26
|
+
o = i # Avoid capturing i after the loop ends
|
27
|
+
define_method(:"#{k}") do
|
28
|
+
# puts "accessing #{self.class.name}.#{k} as @values[#{o}]"
|
29
|
+
@values[o]
|
30
|
+
end
|
31
|
+
define_method(:"#{k}=") do |v|
|
32
|
+
# puts "setting #{self.class.name}.#{k} as @values[#{o}]"
|
33
|
+
# REVISIT: Check valid data type matching v
|
34
|
+
@values[o] = v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
i = i+1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def children_of_type *cts # cts is an array of AST class symbols or strings
|
42
|
+
class_names = cts.flatten.map{|k| 'KiCad::AST::'+self.class.to_class_name(k)}
|
43
|
+
@children.filter{|h| class_names.include?(h.class.name) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Define methods for each child type this class allows
|
47
|
+
def self.child_types *cts
|
48
|
+
# puts "#{self.name} allows child types #{cts.inspect}"
|
49
|
+
cts.each do |c|
|
50
|
+
if Array === c
|
51
|
+
define_method(:"all_#{c[0].to_s}") do
|
52
|
+
# puts "Looking for all [#{class_names*', '}] in #{@children.map{|q| q.class.name}.inspect}"
|
53
|
+
children_of_type(c)
|
54
|
+
end
|
55
|
+
# REVISIT: Allow deleting and adding instances to the array
|
56
|
+
# new_child = KiCad.parse('(some new node)').value
|
57
|
+
# @children.append(new_child)
|
58
|
+
else
|
59
|
+
class_name = 'KiCad::AST::'+to_class_name(c)
|
60
|
+
define_method(:"#{c}") do
|
61
|
+
# puts "Looking for first #{class_name} in #{@children.map{|q| q.class.name}.inspect}"
|
62
|
+
a = children_of_type(c)
|
63
|
+
puts "Choosing first #{self.class.name}.#{c} of #{a.size}" if a.size > 1
|
64
|
+
a.first
|
65
|
+
end
|
66
|
+
# Allow deleting this instance
|
67
|
+
define_method(:"unset_#{c.to_s}") do
|
68
|
+
child = send(:"#{c}")
|
69
|
+
@children = @children - [child] if child
|
70
|
+
child ? true : nil
|
71
|
+
end
|
72
|
+
end
|
29
73
|
end
|
30
74
|
end
|
75
|
+
|
76
|
+
def self.to_class_name sym
|
77
|
+
sym.to_s.gsub(/\A[a-z]|_[a-z]/) {|from| from[-1].upcase }
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.to_symbol class_name
|
81
|
+
class_name.to_s.gsub(/[A-Z]/) {|from| '_'+from[-1].downcase }.sub(/\A_/,'')
|
82
|
+
end
|
31
83
|
end
|
32
84
|
|
33
85
|
class KicadSymbolLib < Node
|
34
|
-
|
35
|
-
super
|
36
|
-
end
|
86
|
+
child_types :version, :generator, :generator_version, [:symbol]
|
37
87
|
end
|
38
88
|
|
39
|
-
|
89
|
+
class Generator < Node
|
90
|
+
end
|
40
91
|
|
41
|
-
class
|
92
|
+
class GeneratorVersion < Node
|
42
93
|
end
|
43
94
|
|
44
|
-
|
95
|
+
class Version < Node
|
96
|
+
end
|
97
|
+
|
98
|
+
# Uncomment or add whatever class you need to customise:
|
99
|
+
|
100
|
+
# Position Identifier
|
45
101
|
class At < Node
|
102
|
+
value_types :x => Float, :y => Float, :angle => Float
|
46
103
|
end
|
47
104
|
|
48
|
-
|
105
|
+
# Coordinate Point List
|
106
|
+
class Pts < Node
|
107
|
+
value_types({})
|
108
|
+
child_types [:xy]
|
49
109
|
end
|
50
110
|
|
51
|
-
class
|
111
|
+
class Xy < Node
|
112
|
+
value_types :x => Float, :y => Float
|
52
113
|
end
|
53
114
|
|
54
|
-
|
115
|
+
# Stroke Definition
|
116
|
+
class Stroke < Node
|
117
|
+
child_types :width, :type, :color
|
55
118
|
end
|
56
119
|
|
57
|
-
class
|
120
|
+
class Width < Node
|
121
|
+
value_types :width => Float
|
58
122
|
end
|
59
123
|
|
60
|
-
class
|
124
|
+
class Type < Node
|
125
|
+
value_types :type => [:dash, :dash_dot, :dash_dot_dot, :dot, :default, :solid]
|
61
126
|
end
|
62
127
|
|
63
|
-
class
|
128
|
+
class Color < Node
|
129
|
+
value_types :r => Integer, :g => Integer, :b => Integer, :a => Integer
|
64
130
|
end
|
65
131
|
|
66
|
-
|
132
|
+
# Text Effects
|
133
|
+
class Effects < Node
|
134
|
+
child_types :font, :justify, :hide
|
67
135
|
end
|
68
136
|
|
69
137
|
class Font < Node
|
138
|
+
child_types :face, :size, :thickness, :bold, :italic, :line_spacing
|
70
139
|
end
|
71
140
|
|
72
|
-
class
|
141
|
+
class Face < Node
|
142
|
+
value_types :face_name => String
|
73
143
|
end
|
74
144
|
|
75
|
-
class
|
145
|
+
class Size < Node
|
146
|
+
value_types :height => Float, :width => Float
|
76
147
|
end
|
77
148
|
|
78
|
-
class
|
149
|
+
class Thickness < Node
|
150
|
+
value_types :thickness => Float
|
79
151
|
end
|
80
152
|
|
81
|
-
class
|
153
|
+
class Bold < Node
|
154
|
+
value_types :bold => [:no, :yes]
|
82
155
|
end
|
83
156
|
|
84
|
-
class
|
157
|
+
class Italic < Node
|
158
|
+
value_types :bold => [:no, :yes]
|
85
159
|
end
|
86
160
|
|
87
|
-
class
|
161
|
+
class LineSpacing < Node
|
162
|
+
value_types :line_spacing => Float
|
88
163
|
end
|
89
164
|
|
90
|
-
class
|
165
|
+
class Justify < Node
|
166
|
+
value_types :justify => [[:right, :left, :top, :bottom, :mirror]] # Can have multiple
|
91
167
|
end
|
92
168
|
|
93
|
-
class
|
169
|
+
class Hide < Node
|
170
|
+
value_types :hide => [:no, :yes]
|
94
171
|
end
|
95
172
|
|
96
|
-
|
173
|
+
# Page Settings
|
174
|
+
class Paper < Node
|
175
|
+
# REVISIT: Either paper_size or width/height
|
176
|
+
value_types :paper_size => [:A0, :A1, :A2, :A3, :A4, :A5, :A, :B, :C, :D, :E],
|
177
|
+
:width => Float, :height => Float,
|
178
|
+
:portrait => [:portrait]
|
97
179
|
end
|
98
180
|
|
99
|
-
|
181
|
+
# Title Block
|
182
|
+
class TitleBlock < Node
|
183
|
+
child_types :title, :date, :rev, :company, # All take one string as value
|
184
|
+
[:comment] # N (1..9) and String # REVISIT: Implement Comment
|
100
185
|
end
|
101
186
|
|
102
|
-
|
187
|
+
# Properties
|
188
|
+
class Property < Node
|
189
|
+
value_types :key => String, :value => String
|
190
|
+
child_types :at, :effects
|
191
|
+
|
192
|
+
# Set or clear (hide) on the property_node
|
193
|
+
def hide=(h = true)
|
194
|
+
v = (h ? :yes : :no)
|
195
|
+
if !effects
|
196
|
+
# puts "No effects yet"
|
197
|
+
prop = KiCad.parse(%Q{(effects(hide #{v}))})&.value
|
198
|
+
@children.append(prop) if prop
|
199
|
+
elsif (existing = effects.hide)
|
200
|
+
# puts "Effects and hide already"
|
201
|
+
existing.hide = v
|
202
|
+
else
|
203
|
+
# Create new (hide) node:
|
204
|
+
# puts "Effects but no hide"
|
205
|
+
prop = KiCad.parse(%Q{(hide #{v})})&.value
|
206
|
+
@children.append(prop) if prop
|
207
|
+
end
|
208
|
+
end
|
103
209
|
end
|
104
210
|
|
105
|
-
|
211
|
+
# Universally Unique Identifier
|
212
|
+
class Uuid < Node
|
213
|
+
value_types :uuid => String
|
106
214
|
end
|
107
215
|
|
108
|
-
|
216
|
+
# Images
|
217
|
+
class Image < Node
|
218
|
+
child_types :at, :scale, :layer, :uuid, :data
|
109
219
|
end
|
110
220
|
|
111
|
-
class
|
221
|
+
class Data < Node
|
222
|
+
value_types :data => String # REVISIT: Base64 data - not encoded as a string I think?
|
112
223
|
end
|
113
224
|
|
114
|
-
class
|
225
|
+
class Circle < Node
|
226
|
+
child_types :center, :radius, :stroke, :fill
|
227
|
+
end
|
228
|
+
|
229
|
+
class Center < Xy
|
230
|
+
# value_types :x => Float, :y => Float
|
115
231
|
end
|
116
232
|
|
117
233
|
class Radius < Node
|
234
|
+
value_types :radius => Float
|
235
|
+
end
|
236
|
+
|
237
|
+
class Fill < Node
|
238
|
+
value_types :fill => [:no, :yes]
|
239
|
+
end
|
240
|
+
|
241
|
+
class Arc < Node
|
242
|
+
child_types :start, :mid, :end, :stroke, :fill
|
243
|
+
end
|
244
|
+
|
245
|
+
class Start < Xy
|
246
|
+
# value_types :x => Float, :y => Float
|
247
|
+
end
|
248
|
+
|
249
|
+
class Mid < Xy
|
250
|
+
# value_types :x => Float, :y => Float
|
251
|
+
end
|
252
|
+
|
253
|
+
class End < Xy
|
254
|
+
# value_types :x => Float, :y => Float
|
118
255
|
end
|
119
256
|
|
120
257
|
class Rectangle < Node
|
258
|
+
child_types :start, :end, :stroke, :fill
|
121
259
|
end
|
122
260
|
|
123
|
-
class
|
261
|
+
class Polyline < Node
|
262
|
+
child_types :pts, :stroke, :fill
|
124
263
|
end
|
125
264
|
|
126
|
-
class
|
265
|
+
class Text < Node
|
266
|
+
value_types :text => String
|
267
|
+
child_types :at, :effects
|
127
268
|
end
|
128
269
|
|
129
|
-
class
|
270
|
+
class Offset < Xy
|
271
|
+
# value_types :x => Float, :y => Float
|
130
272
|
end
|
131
273
|
|
132
|
-
|
274
|
+
# Symbol Pins
|
275
|
+
class Pin < Node
|
276
|
+
value_types :electrical_type => [
|
277
|
+
:input, # Pin is an input.
|
278
|
+
:output, # Pin is an output.
|
279
|
+
:bidirectional, # Pin can be both input and output.
|
280
|
+
:tri_state, # Pin is a tri-state output.
|
281
|
+
:passive, # Pin is electrically passive.
|
282
|
+
:free, # Not internally connected.
|
283
|
+
:unspecified, # Pin does not have a specified electrical type.
|
284
|
+
:power_in, # Pin is a power input.
|
285
|
+
:power_out, # Pin is a power output.
|
286
|
+
:open_collector, # Pin is an open collector output.
|
287
|
+
:open_emitter, # Pin is an open emitter output.
|
288
|
+
:no_connect, # Pin has no electrical connection.
|
289
|
+
], :graphic_style => [
|
290
|
+
:line,
|
291
|
+
:inverted,
|
292
|
+
:clock,
|
293
|
+
:inverted_clock,
|
294
|
+
:input_low,
|
295
|
+
:clock_low,
|
296
|
+
:output_low,
|
297
|
+
:edge_clock_high,
|
298
|
+
:non_logic
|
299
|
+
]
|
300
|
+
child_types :length, :name, :number
|
133
301
|
end
|
134
302
|
|
135
|
-
class
|
303
|
+
class Length < Node
|
304
|
+
value_types :length => Float
|
136
305
|
end
|
137
306
|
|
138
|
-
class
|
307
|
+
class Name < Node
|
308
|
+
value_types :name => String
|
309
|
+
child_types :effects
|
139
310
|
end
|
140
311
|
|
141
|
-
class
|
312
|
+
class Number < Node
|
313
|
+
value_types :number => String
|
314
|
+
child_types :effects
|
315
|
+
end
|
316
|
+
|
317
|
+
class Polygon < Node
|
318
|
+
child_types :pts, :stroke, :fill
|
319
|
+
end
|
320
|
+
|
321
|
+
# Symbols
|
322
|
+
class Symbol < Node
|
323
|
+
value_types :id => String
|
324
|
+
child_types :extends, :exclude_from_sym, :pin_numbers, :pin_names, :in_bom, :on_board,
|
325
|
+
[:property],
|
326
|
+
[:shape, :circle, :rectangle, :text, :polyline, :arc], # REVISIT: Lots more types of graphic items here...
|
327
|
+
[:pin],
|
328
|
+
[:symbol], # Child symbols (units) embedded in a parent
|
329
|
+
:unit_name,
|
330
|
+
:embedded_fonts
|
331
|
+
|
332
|
+
# Get or set Property values by key, or as a Hash
|
333
|
+
def property_node k
|
334
|
+
self.all_property.detect{|p| p.key == k}
|
335
|
+
end
|
336
|
+
|
337
|
+
def property k = nil
|
338
|
+
if k
|
339
|
+
property_node(k)&.value
|
340
|
+
else # Return all properties as a Hash
|
341
|
+
Hash[*self.all_property.map{|p| [p.key, p.value] }.flatten]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def [](k)
|
346
|
+
property[k]
|
347
|
+
end
|
348
|
+
|
349
|
+
def []=(k, v)
|
350
|
+
puts "Setting property #{k} to #{v.inspect}"
|
351
|
+
if (p = property_node)
|
352
|
+
p.send(:"#{self.class.to_symbol k}=", v)
|
353
|
+
else # Create new Property using the parser:
|
354
|
+
prop = KiCad.parse(%Q{
|
355
|
+
(property "#{k}" #{String === v ? v.inspect : v.to_s}
|
356
|
+
(at 0 0 0)
|
357
|
+
(effects(font(size 1.27 1.27)))
|
358
|
+
(hide yes)
|
359
|
+
)
|
360
|
+
})&.value
|
361
|
+
@children.append(prop) if prop
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
class Extends < Node
|
367
|
+
value_types :library_id => String
|
368
|
+
end
|
369
|
+
|
370
|
+
class PinNumbers < Node
|
371
|
+
value_types :hide => [:no, :yes]
|
372
|
+
end
|
373
|
+
|
374
|
+
class PinNames < Node
|
375
|
+
child_types :offset
|
376
|
+
value_types :hide => [:no, :yes]
|
377
|
+
end
|
378
|
+
|
379
|
+
class InBom < Node
|
380
|
+
value_types :in_bom => [:no, :yes]
|
381
|
+
end
|
382
|
+
|
383
|
+
class OnBoard < Node
|
384
|
+
value_types :on_board => [:no, :yes]
|
385
|
+
end
|
386
|
+
|
387
|
+
class UnitName < Node
|
388
|
+
value_types :name => String
|
389
|
+
end
|
390
|
+
|
391
|
+
class EmbeddedFonts < Node
|
392
|
+
value_types :embedded_fonts => [:no, :yes]
|
393
|
+
end
|
394
|
+
|
395
|
+
class ExcludeFromSim < Node
|
396
|
+
value_types :exclude_from_sim => [:no, :yes]
|
142
397
|
end
|
143
|
-
=end
|
144
398
|
|
145
399
|
end
|
146
400
|
end
|
@@ -0,0 +1,547 @@
|
|
1
|
+
# Autogenerated from a Treetop grammar. Edits may be lost.
|
2
|
+
|
3
|
+
|
4
|
+
require 'kicad/ast'
|
5
|
+
|
6
|
+
module KiCad
|
7
|
+
module SExpr
|
8
|
+
include Treetop::Runtime
|
9
|
+
|
10
|
+
def root
|
11
|
+
@root ||= :node
|
12
|
+
end
|
13
|
+
|
14
|
+
module Node0
|
15
|
+
def value
|
16
|
+
elements[0]
|
17
|
+
end
|
18
|
+
|
19
|
+
def s
|
20
|
+
elements[1]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Node1
|
25
|
+
def node
|
26
|
+
elements[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
def s
|
30
|
+
elements[1]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Node2
|
35
|
+
def s1
|
36
|
+
elements[0]
|
37
|
+
end
|
38
|
+
|
39
|
+
def s2
|
40
|
+
elements[2]
|
41
|
+
end
|
42
|
+
|
43
|
+
def values
|
44
|
+
elements[3]
|
45
|
+
end
|
46
|
+
|
47
|
+
def nodes
|
48
|
+
elements[4]
|
49
|
+
end
|
50
|
+
|
51
|
+
def s3
|
52
|
+
elements[6]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Node3
|
57
|
+
def value
|
58
|
+
klass_name = values.elements[0].value.value
|
59
|
+
klass = KiCad::AST::Node
|
60
|
+
if klass_name.is_a? ::Symbol # See if we have a defined class for this node type
|
61
|
+
klass_name = klass_name.to_s.gsub(/\A[a-z]|_[a-z]/) {|from| from[-1].upcase }
|
62
|
+
klass = KiCad::AST.const_get(klass_name, false) rescue KiCad::AST::Node
|
63
|
+
end
|
64
|
+
klass.new values.elements.map(&:value).map(&:value),
|
65
|
+
nodes.elements.map(&:node).map(&:value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def _nt_node
|
70
|
+
start_index = index
|
71
|
+
if node_cache[:node].has_key?(index)
|
72
|
+
cached = node_cache[:node][index]
|
73
|
+
if cached
|
74
|
+
node_cache[:node][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
75
|
+
@index = cached.interval.end
|
76
|
+
end
|
77
|
+
return cached
|
78
|
+
end
|
79
|
+
|
80
|
+
i0, s0 = index, []
|
81
|
+
r1 = _nt_s
|
82
|
+
s0 << r1
|
83
|
+
if r1
|
84
|
+
if (match_len = has_terminal?('(', false, index))
|
85
|
+
r2 = true
|
86
|
+
@index += match_len
|
87
|
+
else
|
88
|
+
terminal_parse_failure('\'(\'')
|
89
|
+
r2 = nil
|
90
|
+
end
|
91
|
+
s0 << r2
|
92
|
+
if r2
|
93
|
+
r3 = _nt_s
|
94
|
+
s0 << r3
|
95
|
+
if r3
|
96
|
+
s4, i4 = [], index
|
97
|
+
loop do
|
98
|
+
i5, s5 = index, []
|
99
|
+
r6 = _nt_value
|
100
|
+
s5 << r6
|
101
|
+
if r6
|
102
|
+
r7 = _nt_s
|
103
|
+
s5 << r7
|
104
|
+
end
|
105
|
+
if s5.last
|
106
|
+
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
|
107
|
+
r5.extend(Node0)
|
108
|
+
else
|
109
|
+
@index = i5
|
110
|
+
r5 = nil
|
111
|
+
end
|
112
|
+
if r5
|
113
|
+
s4 << r5
|
114
|
+
else
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
if s4.empty?
|
119
|
+
@index = i4
|
120
|
+
r4 = nil
|
121
|
+
else
|
122
|
+
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
|
123
|
+
end
|
124
|
+
s0 << r4
|
125
|
+
if r4
|
126
|
+
s8, i8 = [], index
|
127
|
+
loop do
|
128
|
+
i9, s9 = index, []
|
129
|
+
r10 = _nt_node
|
130
|
+
s9 << r10
|
131
|
+
if r10
|
132
|
+
r11 = _nt_s
|
133
|
+
s9 << r11
|
134
|
+
end
|
135
|
+
if s9.last
|
136
|
+
r9 = instantiate_node(SyntaxNode,input, i9...index, s9)
|
137
|
+
r9.extend(Node1)
|
138
|
+
else
|
139
|
+
@index = i9
|
140
|
+
r9 = nil
|
141
|
+
end
|
142
|
+
if r9
|
143
|
+
s8 << r9
|
144
|
+
else
|
145
|
+
break
|
146
|
+
end
|
147
|
+
end
|
148
|
+
r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
|
149
|
+
s0 << r8
|
150
|
+
if r8
|
151
|
+
if (match_len = has_terminal?(')', false, index))
|
152
|
+
r12 = true
|
153
|
+
@index += match_len
|
154
|
+
else
|
155
|
+
terminal_parse_failure('\')\'')
|
156
|
+
r12 = nil
|
157
|
+
end
|
158
|
+
s0 << r12
|
159
|
+
if r12
|
160
|
+
r13 = _nt_s
|
161
|
+
s0 << r13
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
if s0.last
|
169
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
170
|
+
r0.extend(Node2)
|
171
|
+
r0.extend(Node3)
|
172
|
+
else
|
173
|
+
@index = i0
|
174
|
+
r0 = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
node_cache[:node][start_index] = r0
|
178
|
+
|
179
|
+
r0
|
180
|
+
end
|
181
|
+
|
182
|
+
def _nt_value
|
183
|
+
start_index = index
|
184
|
+
if node_cache[:value].has_key?(index)
|
185
|
+
cached = node_cache[:value][index]
|
186
|
+
if cached
|
187
|
+
node_cache[:value][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
188
|
+
@index = cached.interval.end
|
189
|
+
end
|
190
|
+
return cached
|
191
|
+
end
|
192
|
+
|
193
|
+
i0 = index
|
194
|
+
r1 = _nt_string
|
195
|
+
if r1
|
196
|
+
r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
|
197
|
+
r0 = r1
|
198
|
+
else
|
199
|
+
r2 = _nt_number
|
200
|
+
if r2
|
201
|
+
r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
|
202
|
+
r0 = r2
|
203
|
+
else
|
204
|
+
r3 = _nt_symbol
|
205
|
+
if r3
|
206
|
+
r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true
|
207
|
+
r0 = r3
|
208
|
+
else
|
209
|
+
@index = i0
|
210
|
+
r0 = nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
node_cache[:value][start_index] = r0
|
216
|
+
|
217
|
+
r0
|
218
|
+
end
|
219
|
+
|
220
|
+
module Symbol0
|
221
|
+
def value; :"#{text_value}"; end
|
222
|
+
end
|
223
|
+
|
224
|
+
def _nt_symbol
|
225
|
+
start_index = index
|
226
|
+
if node_cache[:symbol].has_key?(index)
|
227
|
+
cached = node_cache[:symbol][index]
|
228
|
+
if cached
|
229
|
+
node_cache[:symbol][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
230
|
+
@index = cached.interval.end
|
231
|
+
end
|
232
|
+
return cached
|
233
|
+
end
|
234
|
+
|
235
|
+
s0, i0 = [], index
|
236
|
+
loop do
|
237
|
+
if has_terminal?(@regexps[gr = '\A[a-zA-Z_]'] ||= Regexp.new(gr), :regexp, index)
|
238
|
+
r1 = true
|
239
|
+
@index += 1
|
240
|
+
else
|
241
|
+
terminal_parse_failure('[a-zA-Z_]')
|
242
|
+
r1 = nil
|
243
|
+
end
|
244
|
+
if r1
|
245
|
+
s0 << r1
|
246
|
+
else
|
247
|
+
break
|
248
|
+
end
|
249
|
+
end
|
250
|
+
if s0.empty?
|
251
|
+
@index = i0
|
252
|
+
r0 = nil
|
253
|
+
else
|
254
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
255
|
+
r0.extend(Symbol0)
|
256
|
+
r0.extend(Symbol0)
|
257
|
+
end
|
258
|
+
|
259
|
+
node_cache[:symbol][start_index] = r0
|
260
|
+
|
261
|
+
r0
|
262
|
+
end
|
263
|
+
|
264
|
+
module String0
|
265
|
+
end
|
266
|
+
|
267
|
+
module String1
|
268
|
+
def contents
|
269
|
+
elements[1]
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
module String2
|
275
|
+
def value; contents.text_value.force_encoding(Encoding::UTF_8); end
|
276
|
+
end
|
277
|
+
|
278
|
+
def _nt_string
|
279
|
+
start_index = index
|
280
|
+
if node_cache[:string].has_key?(index)
|
281
|
+
cached = node_cache[:string][index]
|
282
|
+
if cached
|
283
|
+
node_cache[:string][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
284
|
+
@index = cached.interval.end
|
285
|
+
end
|
286
|
+
return cached
|
287
|
+
end
|
288
|
+
|
289
|
+
i0, s0 = index, []
|
290
|
+
if (match_len = has_terminal?('"', false, index))
|
291
|
+
r1 = true
|
292
|
+
@index += match_len
|
293
|
+
else
|
294
|
+
terminal_parse_failure('\'"\'')
|
295
|
+
r1 = nil
|
296
|
+
end
|
297
|
+
s0 << r1
|
298
|
+
if r1
|
299
|
+
s2, i2 = [], index
|
300
|
+
loop do
|
301
|
+
i3 = index
|
302
|
+
if (match_len = has_terminal?('\\"', false, index))
|
303
|
+
r4 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
304
|
+
@index += match_len
|
305
|
+
else
|
306
|
+
terminal_parse_failure('\'\\\\"\'')
|
307
|
+
r4 = nil
|
308
|
+
end
|
309
|
+
if r4
|
310
|
+
r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true
|
311
|
+
r3 = r4
|
312
|
+
else
|
313
|
+
i5, s5 = index, []
|
314
|
+
i6 = index
|
315
|
+
if (match_len = has_terminal?('"', false, index))
|
316
|
+
r7 = true
|
317
|
+
@index += match_len
|
318
|
+
else
|
319
|
+
terminal_parse_failure('\'"\'')
|
320
|
+
r7 = nil
|
321
|
+
end
|
322
|
+
if r7
|
323
|
+
@index = i6
|
324
|
+
r6 = nil
|
325
|
+
terminal_parse_failure('\'"\'', true)
|
326
|
+
else
|
327
|
+
@terminal_failures.pop
|
328
|
+
@index = i6
|
329
|
+
r6 = instantiate_node(SyntaxNode,input, index...index)
|
330
|
+
end
|
331
|
+
s5 << r6
|
332
|
+
if r6
|
333
|
+
if index < input_length
|
334
|
+
r8 = true
|
335
|
+
@index += 1
|
336
|
+
else
|
337
|
+
terminal_parse_failure("any character")
|
338
|
+
r8 = nil
|
339
|
+
end
|
340
|
+
s5 << r8
|
341
|
+
end
|
342
|
+
if s5.last
|
343
|
+
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
|
344
|
+
r5.extend(String0)
|
345
|
+
else
|
346
|
+
@index = i5
|
347
|
+
r5 = nil
|
348
|
+
end
|
349
|
+
if r5
|
350
|
+
r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true
|
351
|
+
r3 = r5
|
352
|
+
else
|
353
|
+
@index = i3
|
354
|
+
r3 = nil
|
355
|
+
end
|
356
|
+
end
|
357
|
+
if r3
|
358
|
+
s2 << r3
|
359
|
+
else
|
360
|
+
break
|
361
|
+
end
|
362
|
+
end
|
363
|
+
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
|
364
|
+
s0 << r2
|
365
|
+
if r2
|
366
|
+
if (match_len = has_terminal?('"', false, index))
|
367
|
+
r9 = true
|
368
|
+
@index += match_len
|
369
|
+
else
|
370
|
+
terminal_parse_failure('\'"\'')
|
371
|
+
r9 = nil
|
372
|
+
end
|
373
|
+
s0 << r9
|
374
|
+
end
|
375
|
+
end
|
376
|
+
if s0.last
|
377
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
378
|
+
r0.extend(String1)
|
379
|
+
r0.extend(String2)
|
380
|
+
else
|
381
|
+
@index = i0
|
382
|
+
r0 = nil
|
383
|
+
end
|
384
|
+
|
385
|
+
node_cache[:string][start_index] = r0
|
386
|
+
|
387
|
+
r0
|
388
|
+
end
|
389
|
+
|
390
|
+
module Number0
|
391
|
+
end
|
392
|
+
|
393
|
+
module Number1
|
394
|
+
end
|
395
|
+
|
396
|
+
module Number2
|
397
|
+
def value; eval(text_value); end
|
398
|
+
end
|
399
|
+
|
400
|
+
def _nt_number
|
401
|
+
start_index = index
|
402
|
+
if node_cache[:number].has_key?(index)
|
403
|
+
cached = node_cache[:number][index]
|
404
|
+
if cached
|
405
|
+
node_cache[:number][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
406
|
+
@index = cached.interval.end
|
407
|
+
end
|
408
|
+
return cached
|
409
|
+
end
|
410
|
+
|
411
|
+
i0, s0 = index, []
|
412
|
+
if (match_len = has_terminal?('-', false, index))
|
413
|
+
r2 = true
|
414
|
+
@index += match_len
|
415
|
+
else
|
416
|
+
terminal_parse_failure('\'-\'')
|
417
|
+
r2 = nil
|
418
|
+
end
|
419
|
+
if r2
|
420
|
+
r1 = r2
|
421
|
+
else
|
422
|
+
r1 = instantiate_node(SyntaxNode,input, index...index)
|
423
|
+
end
|
424
|
+
s0 << r1
|
425
|
+
if r1
|
426
|
+
s3, i3 = [], index
|
427
|
+
loop do
|
428
|
+
if has_terminal?(@regexps[gr = '\A[0-9]'] ||= Regexp.new(gr), :regexp, index)
|
429
|
+
r4 = true
|
430
|
+
@index += 1
|
431
|
+
else
|
432
|
+
terminal_parse_failure('[0-9]')
|
433
|
+
r4 = nil
|
434
|
+
end
|
435
|
+
if r4
|
436
|
+
s3 << r4
|
437
|
+
else
|
438
|
+
break
|
439
|
+
end
|
440
|
+
end
|
441
|
+
if s3.empty?
|
442
|
+
@index = i3
|
443
|
+
r3 = nil
|
444
|
+
else
|
445
|
+
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
|
446
|
+
end
|
447
|
+
s0 << r3
|
448
|
+
if r3
|
449
|
+
i6, s6 = index, []
|
450
|
+
if (match_len = has_terminal?('.', false, index))
|
451
|
+
r7 = true
|
452
|
+
@index += match_len
|
453
|
+
else
|
454
|
+
terminal_parse_failure('\'.\'')
|
455
|
+
r7 = nil
|
456
|
+
end
|
457
|
+
s6 << r7
|
458
|
+
if r7
|
459
|
+
s8, i8 = [], index
|
460
|
+
loop do
|
461
|
+
if has_terminal?(@regexps[gr = '\A[0-9]'] ||= Regexp.new(gr), :regexp, index)
|
462
|
+
r9 = true
|
463
|
+
@index += 1
|
464
|
+
else
|
465
|
+
terminal_parse_failure('[0-9]')
|
466
|
+
r9 = nil
|
467
|
+
end
|
468
|
+
if r9
|
469
|
+
s8 << r9
|
470
|
+
else
|
471
|
+
break
|
472
|
+
end
|
473
|
+
end
|
474
|
+
r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
|
475
|
+
s6 << r8
|
476
|
+
end
|
477
|
+
if s6.last
|
478
|
+
r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
|
479
|
+
r6.extend(Number0)
|
480
|
+
else
|
481
|
+
@index = i6
|
482
|
+
r6 = nil
|
483
|
+
end
|
484
|
+
if r6
|
485
|
+
r5 = r6
|
486
|
+
else
|
487
|
+
r5 = instantiate_node(SyntaxNode,input, index...index)
|
488
|
+
end
|
489
|
+
s0 << r5
|
490
|
+
end
|
491
|
+
end
|
492
|
+
if s0.last
|
493
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
494
|
+
r0.extend(Number1)
|
495
|
+
r0.extend(Number2)
|
496
|
+
else
|
497
|
+
@index = i0
|
498
|
+
r0 = nil
|
499
|
+
end
|
500
|
+
|
501
|
+
node_cache[:number][start_index] = r0
|
502
|
+
|
503
|
+
r0
|
504
|
+
end
|
505
|
+
|
506
|
+
def _nt_s
|
507
|
+
start_index = index
|
508
|
+
if node_cache[:s].has_key?(index)
|
509
|
+
cached = node_cache[:s][index]
|
510
|
+
if cached
|
511
|
+
node_cache[:s][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
512
|
+
@index = cached.interval.end
|
513
|
+
end
|
514
|
+
return cached
|
515
|
+
end
|
516
|
+
|
517
|
+
s0, i0 = [], index
|
518
|
+
loop do
|
519
|
+
if has_terminal?(@regexps[gr = '\A[ \\t\\r\\n]'] ||= Regexp.new(gr), :regexp, index)
|
520
|
+
r1 = true
|
521
|
+
@index += 1
|
522
|
+
else
|
523
|
+
terminal_parse_failure('[ \\t\\r\\n]')
|
524
|
+
r1 = nil
|
525
|
+
end
|
526
|
+
if r1
|
527
|
+
s0 << r1
|
528
|
+
else
|
529
|
+
break
|
530
|
+
end
|
531
|
+
end
|
532
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
533
|
+
|
534
|
+
node_cache[:s][start_index] = r0
|
535
|
+
|
536
|
+
r0
|
537
|
+
end
|
538
|
+
|
539
|
+
|
540
|
+
class Parser < Treetop::Runtime::CompiledParser
|
541
|
+
include SExpr
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
SExprParser = SExpr::Parser
|
546
|
+
|
547
|
+
end
|
data/lib/kicad/grammar.tt
CHANGED
@@ -3,13 +3,12 @@ require 'kicad/ast'
|
|
3
3
|
module KiCad
|
4
4
|
grammar SExpr
|
5
5
|
rule node
|
6
|
-
'(' s values:( value s)+ nodes:(node s)* ')' s
|
6
|
+
s '(' s values:( value s)+ nodes:(node s)* ')' s
|
7
7
|
{ def value
|
8
8
|
klass_name = values.elements[0].value.value
|
9
9
|
klass = KiCad::AST::Node
|
10
|
-
if klass_name.is_a? ::Symbol
|
11
|
-
|
12
|
-
klass_name = klass_name.to_s.gsub(/\A[a-z]|_[a-z]/) {|from| from[-1].upcase }
|
10
|
+
if klass_name.is_a? ::Symbol # See if we have a defined class for this node type
|
11
|
+
klass_name = klass_name.to_s.gsub(/\A[a-z]|_[a-z]/) {|from| from[-1].upcase }
|
13
12
|
klass = KiCad::AST.const_get(klass_name, false) rescue KiCad::AST::Node
|
14
13
|
end
|
15
14
|
klass.new values.elements.map(&:value).map(&:value),
|
@@ -28,8 +27,8 @@ module KiCad
|
|
28
27
|
end
|
29
28
|
|
30
29
|
rule string
|
31
|
-
'"' ('\\"' / !'"' .)* '"'
|
32
|
-
{ def value;
|
30
|
+
'"' contents:( ('\\"' / !'"' .)* ) '"'
|
31
|
+
{ def value; contents.text_value.force_encoding(Encoding::UTF_8); end }
|
33
32
|
end
|
34
33
|
|
35
34
|
rule number
|
data/lib/kicad/parser.rb
CHANGED
data/lib/kicad/version.rb
CHANGED
metadata
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kicad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clifford Heath
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-09 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bundler
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
|
-
- - "
|
16
|
+
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
18
|
version: '1.11'
|
19
19
|
type: :development
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- - "
|
23
|
+
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '1.11'
|
26
26
|
- !ruby/object:Gem::Dependency
|
@@ -92,6 +92,7 @@ files:
|
|
92
92
|
- kicad.gemspec
|
93
93
|
- lib/kicad.rb
|
94
94
|
- lib/kicad/ast.rb
|
95
|
+
- lib/kicad/grammar.rb
|
95
96
|
- lib/kicad/grammar.tt
|
96
97
|
- lib/kicad/parser.rb
|
97
98
|
- lib/kicad/version.rb
|