hdl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+