idlc 0.1.1

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.
@@ -0,0 +1,22 @@
1
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ # Workaround for a tapioca/sorbet bug where, if AstNode#abstract! is defined only in
8
+ # ast.rb, tapioca reports that AstNode is declared abstract twice during RBI generation.
9
+ #
10
+ # This regression was observed immediately after upgrading tapioca from 0.16.11; keep
11
+ # this shim file until the duplicate-abstract error no longer occurs with the current
12
+ # tapioca/sorbet versions.
13
+
14
+ require "sorbet-runtime"
15
+
16
+ module Idl
17
+ class AstNode
18
+ extend T::Sig
19
+ extend T::Helpers
20
+ abstract!
21
+ end
22
+ end
data/lib/idlc/cli.rb ADDED
@@ -0,0 +1,232 @@
1
+ # typed: false
2
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
4
+
5
+ # frozen_string_literal: true
6
+
7
+ require "idlc"
8
+ # require "gli"
9
+ require "commander"
10
+ require "forwardable"
11
+ require "optparse"
12
+ require "yaml"
13
+
14
+ module Idl
15
+ class Cli
16
+ extend Forwardable
17
+ def_delegators :@runner,
18
+ :alias_command,
19
+ :always_trace!,
20
+ :command,
21
+ :default_command,
22
+ :global_option,
23
+ :never_trace!,
24
+ :program,
25
+ :run!
26
+
27
+ def initialize(args)
28
+ @defines = {}
29
+ @runner = Commander::Runner.new(args)
30
+ end
31
+
32
+ def add_defines(compiler, symtab)
33
+ @defines.each do |name, value_str|
34
+ expr_ast = compiler.compile_expression(value_str, symtab)
35
+ symtab.add!(name, Var.new(name, expr_ast.type(symtab), expr_ast.value(symtab)))
36
+ end
37
+ end
38
+
39
+ def do_eval(args, options)
40
+ if args.size != 1
41
+ if args.empty?
42
+ warn "Missing expression to evaluate"
43
+ else
44
+ warn "Unexpected arguments: #{args[1..]}"
45
+ end
46
+ @runner.commands["help"].run
47
+ exit 1
48
+ end
49
+
50
+ compiler = Compiler.new
51
+ symtab = SymbolTable.new
52
+
53
+ add_defines(compiler, symtab)
54
+ expr_ast = compiler.compile_expression(args[0], symtab)
55
+
56
+ case options.output
57
+ when "-"
58
+ $stdout.puts expr_ast.value(symtab)
59
+ else
60
+ f = File.open(options.output, "w")
61
+ f.puts expr_ast.value(symtab)
62
+ end
63
+ end
64
+
65
+ def do_compile(args, options)
66
+ if args.size != 1
67
+ if args.empty?
68
+ warn "Missing file to compile"
69
+ else
70
+ warn "Unexpected arguments: #{args[1..]}"
71
+ end
72
+ @runner.commands["help"].run
73
+ exit 1
74
+ end
75
+
76
+ compiler = Compiler.new
77
+
78
+ io =
79
+ if options.output == "-"
80
+ $stdout
81
+ else
82
+ File.open(options.output, "w")
83
+ end
84
+
85
+ compiler.parser.set_input_file(args[0], 0)
86
+ m = compiler.parser.parse(File.read(args[0]), root: options.root)
87
+ if m.nil?
88
+ raise SyntaxError, <<~MSG
89
+ While parsing #{args[0]}:#{compiler.parser.failure_line}
90
+
91
+ #{compiler.parser.failure_reason}
92
+ MSG
93
+ end
94
+
95
+ ast = m.to_ast
96
+ ast.set_input_file(args[0], 0)
97
+
98
+ if options.format == "yaml"
99
+ io.puts YAML.dump(ast.to_h)
100
+ else
101
+ raise "Unknown format: #{options.format}"
102
+ end
103
+ end
104
+
105
+ def do_tc_inst(args, options, vars)
106
+ compiler = Compiler.new
107
+ symtab = SymbolTable.new
108
+
109
+ add_defines(compiler, symtab)
110
+ symtab.push(nil)
111
+
112
+ vars.each do |name, width|
113
+ symtab.add!(name, Var.new(name, Type.new(:bits, width: width.to_i), decode_var: true))
114
+ end
115
+
116
+ io =
117
+ if args[0] == "-"
118
+ $stdin
119
+ else
120
+ File.open(args[0], "r")
121
+ end
122
+
123
+ idl =
124
+ if !options.key.nil?
125
+ yaml_contents = YAML.safe_load(io.read, permitted_classes: [String, Array, Hash], permitted_symbols: [])
126
+ raise "#{args[0]} has no key named '#{options.key}'" unless yaml_contents.key?(options.key)
127
+
128
+ yaml_contents[options.key]
129
+ else
130
+ io.read
131
+ end
132
+
133
+ ast = compiler.compile_inst_scope(idl, symtab:, input_file: args[0])
134
+ ast.type_check(symtab, strict: options.strict)
135
+ end
136
+
137
+ def run
138
+ default_command :help
139
+
140
+ program :name, "IDL Compiler"
141
+ program :version, Idl::Compiler.version
142
+ program :description, "Command line for the IDL reference compiler"
143
+
144
+ add_define_option = lambda do |c|
145
+ c.option "-D,--define PARM_NAME=PARAM_VALUE", (<<~DESC
146
+ Define a parameter (e.g., -DMXLEN=64).
147
+ PARAM_VALUE can be any IDL expression with a knowable value
148
+ DESC
149
+ ) do |varandval|
150
+ raise ArgumentError, "Define (#{varandval}) must be in the format VAR=VAL" unless varandval =~ /.+=.+/
151
+
152
+ var, val = varandval.split("=")
153
+ @defines[var] = val
154
+ end
155
+ end
156
+
157
+ command :compile do |c|
158
+ c.syntax = "idlc compile [options] PATH"
159
+ c.summary = "Compile idl, and return the result"
160
+ c.example "Convert to AST", "idlc compiler -f yaml PATH"
161
+ c.example "Convert operation() to AST", "idlc compiler -f yaml -r instruction_operation PATH"
162
+
163
+ c.option "--format FORMAT", String, "Output format"
164
+ c.option "--root ROOT_RULE", String, "Root rule to begin parsing"
165
+ c.option "--output OUTPUT", String, "Output file (- for STDOUT)"
166
+
167
+ c.action do |args, options|
168
+ options.default format: "yaml"
169
+ options.default root: "isa"
170
+ options.default output: "-"
171
+ do_compile(args, options)
172
+ end
173
+ end
174
+
175
+ command :eval do |c|
176
+ c.syntax = "idlc eval [options] EXPRESSION"
177
+ c.summary = "Evaluate an IDL expression"
178
+ c.example "Print '15'", "idlc eval -DA=5 -DB=10 A+B"
179
+
180
+ c.option "-o,--output FILE", String, "Output file (- for STDOUT)"
181
+ add_define_option.call(c)
182
+
183
+ c.action do |args, options|
184
+ options.default output: "-"
185
+ do_eval(args, options)
186
+ end
187
+ end
188
+
189
+ command "tc inst" do |c|
190
+ vars = {}
191
+ c.syntax = "idlc tc inst [options] FILE"
192
+ c.summary = "Type check an instruction 'operation()' block. Exits 0 if type checking succeeds, 1 otherwise."
193
+ c.example "Exit 0", "idlc tc inst -k 'operation()' -v xs1=5 -v xs2=5 -v xd=5 add.yaml"
194
+ c.example "Exit 1 (variables not defined)", "idlc tc inst -k 'operation()' add.yaml"
195
+ c.example "Exit 0", "echo 'X[2] = 15;' | idlc tc inst -"
196
+
197
+ add_define_option.call(c)
198
+ c.option "-k", "--key KEY", String, "When FILE is a YAML file, type check just the contents of KEY"
199
+ c.option "-s", "--strict", "Run strict type checking (only consider reachable code, and fail if using a variable/field is not known to be defined)"
200
+ c.option "-d", "--var NAME=WIDTH", (<<~DESC
201
+ Define decode variable, e.g., xs2=5
202
+ NAME is the name of the variable, and must be a valid IDL identifier
203
+ WIDTH is the bit width of the variable, and must be an integer
204
+ DESC
205
+ ) do |nameandwidth|
206
+ unless nameandwidth =~ /.+=.+/
207
+ raise ArgumentError, "Define (#{nameandwidth}) must be in the format NAME=WIDTH"
208
+ end
209
+
210
+ name, width = nameandwidth.split("=")
211
+ vars[name] = width.to_i
212
+ end
213
+
214
+ c.action do |args, options|
215
+ if args.size != 1
216
+ if args.empty?
217
+ warn "Missing file to type check"
218
+ else
219
+ warn "Unexpected arguments: #{args[1..]}"
220
+ end
221
+ @runner.commands["help"].run
222
+ exit 1
223
+ end
224
+
225
+ do_tc_inst(args, options, vars)
226
+ end
227
+ end
228
+
229
+ run!
230
+ end
231
+ end
232
+ end