systemrdl 0.1.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +127 -0
  4. data/lib/systemrdl/error.rb +55 -0
  5. data/lib/systemrdl/evaluator/address_allocation.rb +126 -0
  6. data/lib/systemrdl/evaluator/address_operation.rb +103 -0
  7. data/lib/systemrdl/evaluator/addrmap.rb +67 -0
  8. data/lib/systemrdl/evaluator/array_component.rb +42 -0
  9. data/lib/systemrdl/evaluator/builtin_properties.rb +400 -0
  10. data/lib/systemrdl/evaluator/common.rb +22 -0
  11. data/lib/systemrdl/evaluator/component_definition.rb +185 -0
  12. data/lib/systemrdl/evaluator/component_insts.rb +106 -0
  13. data/lib/systemrdl/evaluator/container_component.rb +16 -0
  14. data/lib/systemrdl/evaluator/field.rb +245 -0
  15. data/lib/systemrdl/evaluator/instance.rb +120 -0
  16. data/lib/systemrdl/evaluator/list.rb +26 -0
  17. data/lib/systemrdl/evaluator/literal.rb +173 -0
  18. data/lib/systemrdl/evaluator/operation.rb +241 -0
  19. data/lib/systemrdl/evaluator/processor.rb +166 -0
  20. data/lib/systemrdl/evaluator/property.rb +116 -0
  21. data/lib/systemrdl/evaluator/property_assignment.rb +220 -0
  22. data/lib/systemrdl/evaluator/reference.rb +135 -0
  23. data/lib/systemrdl/evaluator/reg.rb +156 -0
  24. data/lib/systemrdl/evaluator/regfile.rb +33 -0
  25. data/lib/systemrdl/evaluator/root.rb +39 -0
  26. data/lib/systemrdl/evaluator/value.rb +13 -0
  27. data/lib/systemrdl/evaluator.rb +14 -0
  28. data/lib/systemrdl/model/instance.rb +194 -0
  29. data/lib/systemrdl/model/value.rb +54 -0
  30. data/lib/systemrdl/model.rb +11 -0
  31. data/lib/systemrdl/parser/generated_parser.rb +2808 -0
  32. data/lib/systemrdl/parser/node.rb +17 -0
  33. data/lib/systemrdl/parser/parser.rb +86 -0
  34. data/lib/systemrdl/parser/scanner.rb +251 -0
  35. data/lib/systemrdl/parser/systemrdl.y +448 -0
  36. data/lib/systemrdl/parser/token.rb +58 -0
  37. data/lib/systemrdl/parser.rb +11 -0
  38. data/lib/systemrdl/version.rb +5 -0
  39. data/lib/systemrdl.rb +90 -0
  40. metadata +96 -0
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemRDL
4
+ module Parser
5
+ class Node < AST::Node
6
+ attr_reader :token_range
7
+
8
+ def replace_type(type)
9
+ updated(type, nil, nil)
10
+ end
11
+
12
+ def replace_token_range(token_range)
13
+ updated(nil, nil, { token_range: token_range })
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemRDL
4
+ module Parser
5
+ class Parser < GeneratedParser
6
+ include RaiseParseError
7
+
8
+ def initialize(scanner, debug: false)
9
+ @scanner = scanner
10
+ @yydebug = debug
11
+ super()
12
+ end
13
+
14
+ def parse
15
+ do_parse
16
+ end
17
+
18
+ private
19
+
20
+ def next_token
21
+ @scanner.next_token
22
+ end
23
+
24
+ def on_error(_token_id, value, _value_stack)
25
+ parse_error(value)
26
+ end
27
+
28
+ def parse_error(value)
29
+ message = "syntax error on value '#{value.text}' (#{value.kind})"
30
+ raise_parse_error message, value.position
31
+ end
32
+
33
+ def to_list(values, include_separator:)
34
+ if include_separator
35
+ [values[0], *values[1]&.map { |_, value| value }]
36
+ else
37
+ [values[0], *values[1]]
38
+ end
39
+ end
40
+
41
+ def node(kind, children, values)
42
+ token_range = to_token_range(values)
43
+ Node.new(kind, children, { token_range: token_range })
44
+ end
45
+
46
+ def array_node(values)
47
+ values = values.flatten
48
+ children = values.filter_map.with_index { |v, i| i % 3 == 1 ? v : nil }
49
+ node(:array, children, values)
50
+ end
51
+
52
+ def component_insts_node(type, insts)
53
+ case type&.kind
54
+ when :EXTERNAL then insts.replace_type(:external_component_insts)
55
+ when :INTERNAL then insts.replace_type(:internal_component_insts)
56
+ end
57
+ end
58
+
59
+ def component_inst_node(values)
60
+ array, range = values[1]
61
+ node(:component_inst, [values[0], array, range, *values[2..]], values)
62
+ end
63
+
64
+ def uop_node(values)
65
+ node(:unary_operation, values, values)
66
+ end
67
+
68
+ def bop_node(values)
69
+ node(:binary_operation, [values[1], values[0], values[2]], values)
70
+ end
71
+
72
+ def to_token_range(values)
73
+ values = values.compact
74
+ head = values.first
75
+ tail = values.last
76
+ if values.size == 1 && head.is_a?(Node)
77
+ head.token_range
78
+ else
79
+ head_token = (head.is_a?(Node) && head.token_range.head) || head
80
+ tail_token = (tail.is_a?(Node) && tail.token_range.tail) || tail
81
+ TokenRange.new(head_token, tail_token)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemRDL
4
+ module Parser
5
+ class Scanner
6
+ include RaiseParseError
7
+
8
+ class << self
9
+ private
10
+
11
+ def keyword_patterns
12
+ [
13
+ 'abstract', 'accesstype', 'addressingtype', 'addrmap', 'alias',
14
+ 'all', 'bit', 'boolean', 'bothedge', 'compact',
15
+ 'component', 'componentwidth', 'constraint', 'default', 'encode',
16
+ 'enum', 'external', 'false', 'field', 'fullalign',
17
+ 'hw', 'inside', 'internal', 'level', 'longint',
18
+ 'mem', 'na', 'negedge', 'nonsticky', 'number',
19
+ 'onreadtype', 'onwritetype', 'posedge', 'property', 'r',
20
+ 'rclr', 'ref', 'reg', 'regalign', 'regfile',
21
+ 'rset', 'ruser', 'rw', 'rw1', 'signal',
22
+ 'string', 'struct', 'sw', 'this', 'true',
23
+ 'type', 'unsigned', 'w', 'w1', 'wclr',
24
+ 'woclr', 'woset', 'wot', 'wr', 'wset',
25
+ 'wuser', 'wzc', 'wzs', 'wzt'
26
+ ].to_h { |kw| [kw, kw.upcase.to_sym] }.freeze
27
+ end
28
+
29
+ def symbol_patterns
30
+ patterns = [
31
+ '[', ']', '(', ')', '{', '}',
32
+ '!', '&&', '||', '<', '>', '<=', '>=', '==', '!=', '>>', '<<',
33
+ '~', '&', '~&', '|', '~|', '^', '~^', '^~', '*', '/', '%', '+', '-', '**',
34
+ '?', ':', '->', '.', ',', "'", ';', '=', '@', '+=', '%='
35
+ ]
36
+ patterns
37
+ .sort_by(&:size)
38
+ .reverse
39
+ .to_h { |pattern| [Regexp.new(Regexp.escape(pattern)), pattern] }
40
+ .freeze
41
+ end
42
+ end
43
+
44
+ WHITE_SPACES = /[ \t\n\r]+/
45
+
46
+ LINE_COMMENT = %r{//.*$}
47
+ BLOCK_COMMENT = %r{/\*(?:(?!\*/).)*\*/}m
48
+ UNTERMINATED_BLOCK_COMMENT = %r{/\*(?:(?!\*/).)*}m
49
+
50
+ KEYWORDS = keyword_patterns
51
+
52
+ SYMBOLS = symbol_patterns
53
+
54
+ VERILOG_BIN_NUMBER = /(\d+)'[bB]([01][01_]*)/
55
+ VERILOG_DEC_NUMBER = /(\d+)'[dD](\d[\d_]*)/
56
+ VERILOG_HEX_NUMBER = /(\d+)'[hH](\h[\h_]*)/
57
+
58
+ DEC_NUMBER = /\d[\d_]*/
59
+ HEX_NUMBER = /0[xX]\h[\h_]*/
60
+
61
+ NUMBERS = {
62
+ VERILOG_HEX_NUMBER => :VERILOG_NUMBER,
63
+ VERILOG_DEC_NUMBER => :VERILOG_NUMBER,
64
+ VERILOG_BIN_NUMBER => :VERILOG_NUMBER,
65
+ HEX_NUMBER => :NUMBER,
66
+ DEC_NUMBER => :NUMBER
67
+ }.freeze
68
+
69
+ def initialize(code, filename, test)
70
+ @ss = StringScanner.new(code)
71
+ @filename = filename
72
+ @line = 1
73
+ @column = 1
74
+ @control_tokens = []
75
+ @control_tokens << create_test_token(test) if test
76
+ end
77
+
78
+ def next_token
79
+ if @control_tokens.empty?
80
+ skip_blank
81
+ push_eos_tokens if eos?
82
+ end
83
+
84
+ token =
85
+ if @control_tokens.empty?
86
+ scan_next_token
87
+ else
88
+ @control_tokens.shift
89
+ end
90
+ token && [token.kind, token]
91
+ end
92
+
93
+ private
94
+
95
+ def eos?
96
+ @ss.eos?
97
+ end
98
+
99
+ def skip_blank
100
+ loop do
101
+ next if scan(WHITE_SPACES)
102
+ next if scan(LINE_COMMENT)
103
+ next if scan_block_comment
104
+
105
+ break
106
+ end
107
+ end
108
+
109
+ def scan_block_comment
110
+ comment = scan(BLOCK_COMMENT)
111
+ return comment if comment
112
+
113
+ if (_, line, column = scan(UNTERMINATED_BLOCK_COMMENT))
114
+ pos = Position.new(@filename, line, column)
115
+ raise_parse_error 'unterminated block comment', pos
116
+ end
117
+ end
118
+
119
+ def push_eos_tokens
120
+ @control_tokens << create_control_token(:EOS)
121
+ @control_tokens << nil
122
+ end
123
+
124
+ def scan(pattern)
125
+ text = @ss.scan(pattern)
126
+ return unless text
127
+
128
+ line = @line
129
+ column = @column
130
+ update_state(text)
131
+
132
+ [text, line, column]
133
+ end
134
+
135
+ def scan_token(kind, pattern)
136
+ text, line, column = scan(pattern)
137
+ text && create_token(kind, text, line, column)
138
+ end
139
+
140
+ def peek(pattern)
141
+ @ss.check(pattern)
142
+ end
143
+
144
+ def peek_char
145
+ peek(/./m)
146
+ end
147
+
148
+ def advance(text)
149
+ @ss.pos += text.bytesize
150
+ update_state(text)
151
+ end
152
+
153
+ def update_state(text)
154
+ return if text.empty?
155
+
156
+ n_newline = text.count("\n")
157
+ @line += n_newline
158
+
159
+ @column =
160
+ if text[-1] == "\n"
161
+ 1
162
+ elsif n_newline > 0
163
+ last = text.lines.last
164
+ last.length
165
+ else
166
+ @column + text.length
167
+ end
168
+ end
169
+
170
+ def create_token(kind, text, line, column)
171
+ position = Position.new(@filename, line, column)
172
+ Token.new(text, kind, position)
173
+ end
174
+
175
+ def create_control_token(kind)
176
+ create_token(kind, '', @line, @column)
177
+ end
178
+
179
+ def create_test_token(test)
180
+ kind = :"__test_#{test}__".upcase
181
+ create_control_token(kind)
182
+ end
183
+
184
+ def current_position
185
+ Position.new(@filename, @line, @column)
186
+ end
187
+
188
+ def scan_next_token
189
+ token =
190
+ scan_string || scan_number || scan_symbol || scan_word
191
+ return token if token
192
+
193
+ char = peek_char
194
+ raise_parse_error "illegal character `#{char}`", current_position
195
+ end
196
+
197
+ def scan_string
198
+ return unless peek(/"/)
199
+
200
+ line = @line
201
+ column = @column
202
+
203
+ buffer = []
204
+ while (char = peek_char)
205
+ advance(char)
206
+ if char == '\\' && ((next_char, _, _) = scan(/"/))
207
+ buffer << next_char
208
+ else
209
+ buffer << char
210
+ break if buffer.size >= 2 && buffer.last == '"'
211
+ end
212
+ end
213
+
214
+ text = buffer.join
215
+ create_token(:STRING, text, line, column)
216
+ end
217
+
218
+ def scan_number
219
+ NUMBERS.each do |pattern, kind|
220
+ token = scan_token(kind, pattern)
221
+ return token if token
222
+ end
223
+
224
+ nil
225
+ end
226
+
227
+ def scan_symbol
228
+ SYMBOLS.each do |pattern, kind|
229
+ token = scan_token(kind, pattern)
230
+ return token if token
231
+ end
232
+
233
+ nil
234
+ end
235
+
236
+ def scan_word
237
+ text, line, column = scan(/[_a-zA-Z]\w*/)
238
+ return unless text
239
+
240
+ KEYWORDS.each do |pattern, kind|
241
+ next if text != pattern
242
+
243
+ token = create_token(kind, text, line, column)
244
+ return token
245
+ end
246
+
247
+ create_token(:SIMPLE_ID, text, line, column)
248
+ end
249
+ end
250
+ end
251
+ end