hdl 1.0.0

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.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ ## HDL
2
+
3
+ A parser and emulator for a minimalist [hardware description language](http://en.wikipedia.org/wiki/Hardware_description_language).
4
+
5
+ ## Chips
6
+
7
+ Here's an example definition of a chip called 'and.hdl':
8
+
9
+ ```ruby
10
+ # and.hdl
11
+ inputs a, b
12
+ outputs out
13
+
14
+ nand(a=a, b=b, out=x)
15
+ nand(a=x, b=x, out=out)
16
+ ```
17
+
18
+ The equivalent circuit diagram is:
19
+
20
+ !['and' gate from 'nand'](http://upload.wikimedia.org/wikipedia/commons/1/16/AND_from_NAND.svg)
21
+
22
+ ## Tables
23
+
24
+ The above chip references another chip called 'nand'.
25
+
26
+ Here's its definition, as a truth table:
27
+
28
+ ```ruby
29
+ # nand.hdl
30
+ inputs a, b
31
+ outputs out
32
+
33
+ | a | b | out |
34
+ | 0 | 0 | 1 |
35
+ | 0 | 1 | 1 |
36
+ | 1 | 0 | 1 |
37
+ | 1 | 1 | 0 |
38
+ ```
39
+
40
+ If you'd prefer, you can use 'T' and 'F'.
41
+
42
+ ## Ruby
43
+
44
+ Now that we've satisfied the 'nand' dependency, we can write some Ruby:
45
+
46
+ ```ruby
47
+ require "hdl"
48
+
49
+ chip = HDL.load("and")
50
+ chip.evaluate(a: true, b: false)
51
+ #=> { out: false }
52
+ ```
53
+
54
+ The 'nand' chip is automatically loaded when it is referenced.
55
+
56
+ ## Path
57
+
58
+ By default, chips in the current directory will be discovered.
59
+
60
+ You can expand this search by adding to your path:
61
+
62
+ ```ruby
63
+ HDL.path << "chips"
64
+ ```
65
+
66
+ ## Parsing
67
+
68
+ If you'd rather parse your chips directly in Ruby, you can do so with:
69
+
70
+ ```ruby
71
+ chip = HDL.parse(name, definition)
72
+ ```
73
+
74
+ This might be useful if you're storing definitions in a database.
75
+
76
+ ## Miscellaneous
77
+
78
+ Here are some useful methods for querying properties of chips:
79
+
80
+ ```ruby
81
+ chip.name
82
+ #=> "and"
83
+
84
+ chip.path
85
+ #=> "./and.hdl"
86
+
87
+ chip.inputs
88
+ #=> [:a, :b]
89
+
90
+ chip.outputs
91
+ #=> [:out]
92
+
93
+ chip.internal
94
+ #=> [:x]
95
+
96
+ chip.components
97
+ #=> { #<HDL::Chip nand> => 2 }
98
+
99
+ chip.primitive? # Does this chip contain a truth table?
100
+ #=> false
101
+
102
+ chip.primitives
103
+ #=> [#<HDL::Chip nand>]
104
+
105
+ chip.dependents # Chips that this chip uses.
106
+ #=> [#<HDL::Chip nand>]
107
+
108
+ chip.dependees # Chips that use this chip.
109
+ #=> []
110
+ ```
111
+
112
+ ## Contribution
113
+
114
+ I'm not an electronics engineer. If you are, you could probably build in all kinds of cool features and optimisations.
115
+
116
+ Here a few features that might be in the pipeline:
117
+
118
+ * Let me build a CNF expression for a chip
119
+ * Query method for circuit fan-outs for chips
120
+ * Evaluate and return outputs with internal pins
121
+ * Support for buses, mostly for convenience
122
+ * Pushing stricter validations to parse time
123
+ * Cleaner decoupling between the parser and evaluator
124
+
125
+ If you'd like to build any of these features, I'd be super grateful. Send me a pull request, or open an issue.
126
+
127
+ You should follow me on [twitter](http://twitter.com/cpatuzzo).
data/lib/hdl/base.rb ADDED
@@ -0,0 +1,24 @@
1
+ module HDL
2
+ class << self
3
+
4
+ def version
5
+ "1.0.0"
6
+ end
7
+
8
+ def path
9
+ Loader.path
10
+ end
11
+
12
+ def load(name)
13
+ Loader.load(name, :force => true)
14
+ end
15
+
16
+ def parse(name, definition)
17
+ Loader.load(name,
18
+ :force => true,
19
+ :definition => definition
20
+ )
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,141 @@
1
+ class HDL::SchemaChip::Evaluator
2
+
3
+ def initialize(chip, schema)
4
+ @chip = chip
5
+ @schema = schema
6
+ @parts = partition_io
7
+ @order = evaluation_order(@parts, chip.inputs)
8
+
9
+ check_multi_internal!
10
+ end
11
+
12
+ def evaluate(pins)
13
+ results = @order.inject(pins) do |known_pins, i|
14
+ known_pins.merge(evaluate_line(@parts[i], known_pins))
15
+ end
16
+
17
+ filter_outputs(results)
18
+ end
19
+
20
+ private
21
+ def evaluate_line(line, pins)
22
+ chip, inputs, outputs = line
23
+
24
+ chip_in = connect_inputs(inputs, pins)
25
+ chip_out = chip.evaluate(chip_in)
26
+
27
+ connect_outputs(outputs, chip_out)
28
+ end
29
+
30
+ def connect_inputs(inputs, pins)
31
+ is_bool = lambda { |x| [true, false].include?(x) }
32
+
33
+ inputs.inject({}) do |hash, (dest, source)|
34
+ value = is_bool[source] ? source : pins[source]
35
+ hash.merge(dest => value)
36
+ end
37
+ end
38
+
39
+ def connect_outputs(outputs, pins)
40
+ outputs.inject({}) do |hash, (dest, source)|
41
+ hash.merge(source => pins[dest])
42
+ end
43
+ end
44
+
45
+ def filter_outputs(pins)
46
+ f = pins.select { |k, _| @chip.outputs.include?(k) }
47
+ Hash[f]
48
+ end
49
+
50
+ def evaluation_order(schema_lines, known_pins, first_call = true)
51
+ schema_lines = schema_lines.each_with_index if first_call
52
+
53
+ known, unknown = schema_lines.partition do |line, _|
54
+ all_inputs_known?(line, known_pins)
55
+ end
56
+
57
+ known_outputs = known.map do |line, _|
58
+ outputs_for_line(line)
59
+ end.flatten
60
+
61
+ check_for_new_information!(known_outputs, unknown)
62
+
63
+ if unknown.any?
64
+ # Recursive case.
65
+ next_pins = known_pins + known_outputs
66
+ tail = evaluation_order(unknown, next_pins, false)
67
+ else
68
+ # Base case.
69
+ tail = []
70
+ end
71
+
72
+ known_indexes = known.map(&:last)
73
+ known_indexes + tail
74
+ end
75
+
76
+ def all_inputs_known?(line, known_pins)
77
+ _, inputs, _ = line
78
+ inputs.values.all? do |pin|
79
+ (known_pins + [true, false]).include?(pin)
80
+ end
81
+ end
82
+
83
+ def partition_io
84
+ @schema.map do |line|
85
+ chip_name = line.keys.first
86
+ wiring = line.values.first
87
+ dest_pins = wiring.keys
88
+ source_pins = wiring.values
89
+ chip = fetch_chip(chip_name)
90
+
91
+ inputs, outputs = wiring.partition do |dest, source|
92
+ chip.inputs.include?(dest)
93
+ end
94
+
95
+ [chip, Hash[inputs], Hash[outputs]]
96
+ end
97
+ end
98
+
99
+ def outputs_for_line(line)
100
+ line.last.values
101
+ end
102
+
103
+ def check_for_new_information!(information, unknown)
104
+ if information.empty?
105
+ p = unknown.map do |(chip, _, _), exp|
106
+ { (exp + 1) => chip.name }.inspect
107
+ end.join(", ")
108
+
109
+ if unknown.size == 1
110
+ err = "Unknown internal pin for expression: #{p}"
111
+ else
112
+ err = "Unknowable internal pins for expressions: #{p}"
113
+ end
114
+
115
+ raise CircularError, err
116
+ end
117
+ end
118
+
119
+ # We can't check this at parse time because we
120
+ # don't know which are getters/setters.
121
+ def check_multi_internal!
122
+ set_pins = @parts.map do |_, _, outputs|
123
+ outputs.values
124
+ end.flatten
125
+
126
+ repetitions = set_pins.select { |p| set_pins.count(p) > 1 }.uniq
127
+ if repetitions.size == 1
128
+ err = "The internal pin `#{repetitions.first}' is set multiple times"
129
+ raise ParseError, err
130
+ elsif repetitions.size > 1
131
+ list = repetitions.join(', ')
132
+ err = "These internal pins are set multiple times: #{list}"
133
+ raise ParseError, err
134
+ end
135
+ end
136
+
137
+ def fetch_chip(name)
138
+ HDL::Loader.load(name)
139
+ end
140
+
141
+ end
@@ -0,0 +1,74 @@
1
+ class HDL::SchemaChip < HDL::Chip
2
+
3
+ def initialize(name, path, data)
4
+ super
5
+ @schema = data[:schema]
6
+ create_dependencies
7
+ load_dependencies(:dependents)
8
+ evaluate_some_expression
9
+ end
10
+
11
+ def internal
12
+ wiring_values - inputs - outputs - [true, false]
13
+ end
14
+
15
+ def components
16
+ chips = component_names.map do |name|
17
+ HDL::Loader.load(name)
18
+ end
19
+
20
+ freq = frequencies(chips)
21
+ hashes = [freq] + chips.map(&:components)
22
+
23
+ hashes.inject({}) do |acc, hash|
24
+ acc.merge(hash) { |_, a, b| a + b }
25
+ end
26
+ end
27
+
28
+ def primitive?
29
+ false
30
+ end
31
+
32
+ def evaluate(pins = {})
33
+ check_pins!(pins)
34
+ evaluator.evaluate(pins)
35
+ end
36
+
37
+ private
38
+ def create_dependencies
39
+ component_names.each do |dep|
40
+ HDL::Dependency.create(name, dep.to_s)
41
+ end
42
+ end
43
+
44
+ def component_names
45
+ @schema.map(&:keys).flatten
46
+ end
47
+
48
+ def wiring
49
+ @schema.map(&:values).flatten
50
+ end
51
+
52
+ def wiring_values
53
+ wiring.map(&:values).flatten.uniq
54
+ end
55
+
56
+ def evaluator
57
+ @evaluator ||= Evaluator.new(self, @schema)
58
+ end
59
+
60
+ def frequencies(array)
61
+ array.inject(Hash.new(0)) do |hash, element|
62
+ hash[element] += 1
63
+ hash
64
+ end
65
+ end
66
+
67
+ # Check upfront if there are circular problems
68
+ # by evaluating something on the chip.
69
+ def evaluate_some_expression
70
+ pins = Hash[inputs.zip(inputs.map { true })]
71
+ evaluate(pins)
72
+ end
73
+
74
+ end
@@ -0,0 +1,39 @@
1
+ class HDL::TableChip < HDL::Chip
2
+
3
+ def initialize(name, path, data)
4
+ super
5
+ @table = data[:table]
6
+ end
7
+
8
+ def internal
9
+ []
10
+ end
11
+
12
+ def components
13
+ {}
14
+ end
15
+
16
+ def primitive?
17
+ true
18
+ end
19
+
20
+ def evaluate(pins = {})
21
+ check_pins!(pins)
22
+
23
+ row = find_row(pins)
24
+ select_outputs(row)
25
+ end
26
+
27
+ private
28
+ def find_row(pins)
29
+ @table.detect do |row|
30
+ inputs.all? { |i| row[i] == pins[i] }
31
+ end
32
+ end
33
+
34
+ def select_outputs(row)
35
+ filtered = row.select { |p, _| outputs.include?(p) }
36
+ Hash[filtered]
37
+ end
38
+
39
+ end
data/lib/hdl/chip.rb ADDED
@@ -0,0 +1,41 @@
1
+ class HDL::Chip
2
+
3
+ attr_reader :name, :path, :inputs, :outputs
4
+
5
+ def initialize(name, path, data)
6
+ @name = name.to_s
7
+ @path = path
8
+ @inputs = data[:inputs]
9
+ @outputs = data[:outputs]
10
+ end
11
+
12
+ def inspect
13
+ "#<HDL::Chip #{name}>"
14
+ end
15
+
16
+ def primitives
17
+ dependents.select { |d| d.primitive? }
18
+ end
19
+
20
+ def dependents
21
+ load_dependencies(:dependents)
22
+ end
23
+
24
+ def dependees
25
+ load_dependencies(:dependees)
26
+ end
27
+
28
+ private
29
+ def load_dependencies(type)
30
+ deps = HDL::Dependency.send("#{type}_for", name)
31
+ deps.map { |d| HDL::Loader.load(d) }
32
+ end
33
+
34
+ def check_pins!(pins)
35
+ unless pins.keys.to_set == inputs.to_set
36
+ err = "Expecting inputs #{inputs.inspect}, given #{pins.keys.inspect}"
37
+ raise ArgumentError, err
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,70 @@
1
+ class HDL::Dependency
2
+ class << self
3
+
4
+ # dependee depends on dependent
5
+
6
+ def create(dependee, dependent)
7
+ check_for_cycles!(dependee, dependent)
8
+ set << { dependee => dependent }
9
+ end
10
+
11
+ def all
12
+ set
13
+ end
14
+
15
+ def where(filter = {})
16
+ scope = all
17
+
18
+ if (f = filter[:dependee])
19
+ scope = scope.select { |h| h.keys.first == f }
20
+ end
21
+
22
+ if (f = filter[:dependent])
23
+ scope = scope.select { |h| h.values.first == f }
24
+ end
25
+
26
+
27
+ scope
28
+ end
29
+
30
+ # What does the dependee depend on?
31
+ def dependents_for(dependee)
32
+ recursive_ancestors_for(dependee, :dependee, :values)
33
+ end
34
+
35
+ # What depends on the dependent?
36
+ def dependees_for(dependent)
37
+ recursive_ancestors_for(dependent, :dependent, :keys)
38
+ end
39
+
40
+ private
41
+ def set
42
+ @dependencies ||= Set.new
43
+ end
44
+
45
+ def check_for_cycles!(dependee, dependent)
46
+ if dependee == dependent
47
+ err = "'#{dependee}` cannot depend on itself"
48
+ elsif dependees_for(dependee).include?(dependent)
49
+ err = "'#{dependee}` and '#{dependent}` depend on each other"
50
+ end
51
+
52
+ raise CircularError, err if err
53
+ end
54
+
55
+ def recursive_ancestors_for(object, type, side_of_hash)
56
+ hashes = where(type => object)
57
+ children = hashes.map(&side_of_hash).flatten
58
+
59
+ ancestors = children.to_set
60
+ children.each do |c|
61
+ ancestors += recursive_ancestors_for(
62
+ c, type, side_of_hash
63
+ )
64
+ end
65
+ ancestors.to_a
66
+ end
67
+
68
+ class ::CircularError < StandardError; end
69
+ end
70
+ end
data/lib/hdl/loader.rb ADDED
@@ -0,0 +1,58 @@
1
+ class HDL::Loader
2
+ class << self
3
+
4
+ def path
5
+ @path ||= ["."]
6
+ end
7
+ attr_writer :path
8
+
9
+ def load(name, options = {})
10
+ memoize(name, options[:force]) do
11
+ name = name.to_s
12
+
13
+ if (d = options[:definition])
14
+ raw = d
15
+ path = nil
16
+ else
17
+ file = file_from_path(name)
18
+ raw = file.read
19
+ path = file.path
20
+ end
21
+
22
+ data = HDL::Parser.parse(raw)
23
+
24
+ if data[:table]
25
+ klass = HDL::TableChip
26
+ else
27
+ klass = HDL::SchemaChip
28
+ end
29
+
30
+ klass.new(name, path, data)
31
+ end
32
+ end
33
+
34
+ private
35
+ def memoize(key, override, &block)
36
+ @memo ||= {}
37
+
38
+ if @memo.has_key?(key) && !override
39
+ @memo[key]
40
+ else
41
+ @memo[key] = yield
42
+ end
43
+ end
44
+
45
+ def file_from_path(name)
46
+ path.each do |p|
47
+ q = File.join(p, name) + ".hdl"
48
+ return File.new(q) if File.exists?(q)
49
+ end
50
+
51
+ err = "Could not locate chip definition for `#{name}'"
52
+ raise FileNotFound, err
53
+ end
54
+
55
+ class ::FileNotFound < StandardError; end
56
+
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ grammar HDL
2
+ include Pins
3
+ include Schema
4
+ include Table
5
+
6
+ rule hdl
7
+ ws? pins ws body:(schema / table) ws? {
8
+ def structured
9
+ {
10
+ :inputs => pins.input_array,
11
+ :outputs => pins.output_array,
12
+ body.key => body.to_array
13
+ }
14
+ end
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ grammar Pins
2
+ include Vars
3
+
4
+ rule pins
5
+ inputs ws outputs {
6
+ def input_array
7
+ inputs.vars.to_array
8
+ end
9
+
10
+ def output_array
11
+ outputs.vars.to_array
12
+ end
13
+ }
14
+ end
15
+
16
+ rule inputs
17
+ "inputs" ws vars
18
+ end
19
+
20
+ rule outputs
21
+ "outputs" ws vars
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ grammar Schema
2
+ include Vars
3
+
4
+ rule schema
5
+ (ws? chip)+ {
6
+ def to_array
7
+ names = TreeWalker.walk(self, :name)
8
+ hashes = TreeWalker.walk(self, :to_hash)
9
+
10
+ names.zip(hashes).map { |n, h| { n => h } }
11
+ end
12
+
13
+ def key
14
+ :schema
15
+ end
16
+ }
17
+ end
18
+
19
+ rule chip
20
+ var "(" arguments ")" {
21
+ def name
22
+ var.to_sym
23
+ end
24
+ }
25
+ end
26
+
27
+ rule arguments
28
+ argument ("," arguments)* ws? {
29
+ def to_hash
30
+ lefts = TreeWalker.walk(self, :left)
31
+ rights = TreeWalker.walk(self, :right)
32
+
33
+ Hash[lefts.zip(rights)]
34
+ end
35
+ }
36
+ end
37
+
38
+ rule argument
39
+ ws? l:var ws? "=" ws? r:(var / boolean) {
40
+ def left
41
+ l.to_sym
42
+ end
43
+
44
+ def right
45
+ r.respond_to?(:to_sym) ? r.to_sym : r.to_bool
46
+ end
47
+ }
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ grammar Table
2
+ include Vars
3
+
4
+ rule table
5
+ header rows:(ws? row)+ {
6
+ def to_array
7
+ headers = header.header_cells.to_array
8
+ data = TreeWalker.walk(rows, :to_array)
9
+
10
+ data.map { |d| Hash[headers.zip(d)] }
11
+ end
12
+
13
+ def key
14
+ :table
15
+ end
16
+ }
17
+ end
18
+
19
+ rule header
20
+ "|" header_cells "|"
21
+ end
22
+
23
+ rule row
24
+ "|" row_cells "|"
25
+ end
26
+
27
+ rule header_cells
28
+ ws? var ws? ("|" header_cells)* {
29
+ def to_array
30
+ TreeWalker.walk(self, :to_sym)
31
+ end
32
+ }
33
+ end
34
+
35
+ rule row_cells
36
+ ws? boolean ws? ("|" row_cells)* {
37
+ def to_array
38
+ TreeWalker.walk(self, :to_bool)
39
+ end
40
+ }
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ grammar Vars
2
+ rule vars
3
+ var ("," ws? vars)* {
4
+ def to_array
5
+ TreeWalker.walk(self, :to_sym)
6
+ end
7
+ }
8
+ end
9
+
10
+ rule var
11
+ [a-z] [a-z0-9_]* {
12
+ def to_sym
13
+ text_value.to_sym
14
+ end
15
+ }
16
+ end
17
+
18
+ rule boolean
19
+ [01TF] {
20
+ def to_bool
21
+ ["1", "T"].include?(text_value)
22
+ end
23
+ }
24
+ end
25
+
26
+ rule ws
27
+ ([ \t\r\n] / comment)+
28
+ end
29
+
30
+ rule comment
31
+ "#" (!"\n" .)* "\n"
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ class TreeWalker
2
+ def self.walk(element, method)
3
+ array = []
4
+
5
+ if element.respond_to?(method)
6
+ array << element.send(method)
7
+ elsif !element.terminal?
8
+ array += element.elements.map { |e| walk(e, method) }.flatten(1)
9
+ end
10
+
11
+ array
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class HDL::Parser::Validator::InputValidator < HDL::Parser::Validator
2
+
3
+ def validate!
4
+ intersection = @hash[:inputs] & @hash[:outputs]
5
+ if intersection.size == 1
6
+ raise "`#{intersection.first}' is both an input and an output"
7
+ elsif intersection.size > 1
8
+ pins = intersection.map { |p| "`#{p}'" }.join(", ")
9
+ raise "These pins are both inputs and outputs: #{pins}"
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,44 @@
1
+ class HDL::Parser::Validator::SchemaValidator < HDL::Parser::Validator
2
+
3
+ def validate!
4
+ inputs!
5
+ outputs!
6
+ end
7
+
8
+ private
9
+ def inputs!
10
+ inputs = @hash[:inputs]
11
+ disconnected_inputs = inputs - connected_pins
12
+ disconnected_pins_error(disconnected_inputs, "input")
13
+ end
14
+
15
+ def outputs!
16
+ outputs = @hash[:outputs]
17
+ disconnected_outputs = outputs - connected_pins
18
+ disconnected_pins_error(disconnected_outputs, "output")
19
+
20
+ repetitions = outputs.select { |o| connected_pins.count(o) > 1 }
21
+ if repetitions.size == 1
22
+ raise "The output `#{repetitions.first}' is connected multiple times"
23
+ elsif repetitions.size > 1
24
+ list = repetitions.join(', ')
25
+ raise "These outputs are connected multiple times: #{list}"
26
+ end
27
+ end
28
+
29
+ def connected_pins
30
+ schema = @hash[:schema]
31
+ wiring = schema.map(&:values).flatten
32
+ wiring.map(&:values).flatten
33
+ end
34
+
35
+ def disconnected_pins_error(disconnected_pins, type)
36
+ if disconnected_pins.size == 1
37
+ raise "The #{type} `#{disconnected_pins.first}' is not connected"
38
+ elsif disconnected_pins.size > 1
39
+ list = disconnected_pins.join(", ")
40
+ raise "The following #{type}s are not connected: #{list}"
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,42 @@
1
+ class HDL::Parser::Validator::TableValidator < HDL::Parser::Validator
2
+
3
+ def validate!
4
+ headers!
5
+ combinations!
6
+ end
7
+
8
+ private
9
+ def headers!
10
+ external_pins = @hash[:inputs] + @hash[:outputs]
11
+ table_headers = @hash[:table].first.keys
12
+
13
+ unless external_pins.to_set == table_headers.to_set
14
+ raise "expecting table headers #{external_pins.join(",")}, got #{table_headers.join(",")}"
15
+ end
16
+ end
17
+
18
+ def combinations!
19
+ inputs = @hash[:inputs]
20
+
21
+ combi = @hash[:table].inject([]) do |acc, row|
22
+ only_inputs = row.select { |k, _| inputs.include?(k) }
23
+ acc + [Hash[only_inputs]]
24
+ end
25
+
26
+ repetitions = combi.select { |c| combi.count(c) > 1 }.uniq
27
+ if repetitions.size == 1
28
+ raise "The row for #{repetitions.first.inspect} appears multiple times"
29
+ elsif repetitions.size > 1
30
+ list = repetitions.map(&:inspect).join(', ')
31
+ raise "These rows appear multiple times: #{list}"
32
+ end
33
+
34
+ required_size = 2 ** inputs.size
35
+ given_size = combi.size
36
+ unless given_size == required_size
37
+ rows = given_size > 1 ? "rows were" : "row was"
38
+ raise "#{given_size} #{rows} given, but #{required_size} rows are required"
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,28 @@
1
+ class HDL::Parser::Validator
2
+
3
+ def self.validate!(hash)
4
+ new(hash).validate!
5
+ end
6
+
7
+ def initialize(hash)
8
+ @hash = hash
9
+ end
10
+
11
+ def validate!
12
+ InputValidator.validate!(@hash)
13
+
14
+ if @hash[:schema]
15
+ validator = SchemaValidator
16
+ else
17
+ validator = TableValidator
18
+ end
19
+
20
+ validator.validate!(@hash)
21
+ end
22
+
23
+ private
24
+ def raise(string)
25
+ super(ParseError.new(string))
26
+ end
27
+
28
+ end
data/lib/hdl/parser.rb ADDED
@@ -0,0 +1,17 @@
1
+ class HDL::Parser
2
+ def self.parse(definition)
3
+ parser = HDLParser.new
4
+
5
+ ast = parser.parse(definition)
6
+ if ast.nil?
7
+ raise ParseError, parser.failure_reason
8
+ end
9
+
10
+ structured_data = ast.structured
11
+ Validator.validate!(structured_data)
12
+
13
+ structured_data
14
+ end
15
+
16
+ class ::ParseError < StandardError; end
17
+ end
data/lib/hdl.rb ADDED
@@ -0,0 +1,21 @@
1
+ require "set"
2
+ require "polyglot"
3
+ require "treetop"
4
+ require "hdl/base"
5
+ require "hdl/loader"
6
+ require "hdl/dependency"
7
+ require "hdl/chip"
8
+ require "hdl/chip/schema_chip"
9
+ require "hdl/chip/schema_chip/evaluator"
10
+ require "hdl/chip/table_chip"
11
+ require "hdl/parser"
12
+ require "hdl/parser/validator"
13
+ require "hdl/parser/validator/input_validator"
14
+ require "hdl/parser/validator/schema_validator"
15
+ require "hdl/parser/validator/table_validator"
16
+ require "hdl/parser/tree_walker.rb"
17
+ require "hdl/parser/grammar/vars"
18
+ require "hdl/parser/grammar/pins"
19
+ require "hdl/parser/grammar/table"
20
+ require "hdl/parser/grammar/schema"
21
+ require "hdl/parser/grammar/hdl"
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hdl
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Chris Patuzzo
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-06-02 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: treetop
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: polyglot
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ description: A parser and emulator for a minimalist hardware description language.
64
+ email: chris@patuzzo.co.uk
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - README.md
73
+ - lib/hdl.rb
74
+ - lib/hdl/chip.rb
75
+ - lib/hdl/chip/schema_chip/evaluator.rb
76
+ - lib/hdl/chip/schema_chip.rb
77
+ - lib/hdl/chip/table_chip.rb
78
+ - lib/hdl/base.rb
79
+ - lib/hdl/dependency.rb
80
+ - lib/hdl/parser/validator.rb
81
+ - lib/hdl/parser/tree_walker.rb
82
+ - lib/hdl/parser/grammar/pins.treetop
83
+ - lib/hdl/parser/grammar/hdl.treetop
84
+ - lib/hdl/parser/grammar/schema.treetop
85
+ - lib/hdl/parser/grammar/table.treetop
86
+ - lib/hdl/parser/grammar/vars.treetop
87
+ - lib/hdl/parser/validator/table_validator.rb
88
+ - lib/hdl/parser/validator/input_validator.rb
89
+ - lib/hdl/parser/validator/schema_validator.rb
90
+ - lib/hdl/loader.rb
91
+ - lib/hdl/parser.rb
92
+ has_rdoc: true
93
+ homepage: https://github.com/cpatuzzo/hdl
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project:
122
+ rubygems_version: 1.6.2
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: HDL
126
+ test_files: []
127
+