fwi 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/bin/fwi +3 -0
- data/fwi.gemspec +17 -0
- data/lib/fwi/compiler.rb +64 -0
- data/lib/fwi/cpp/compiler.rb +52 -0
- data/lib/fwi/cpp/gen.rb +329 -0
- data/lib/fwi/parser.rb +323 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87be340eab8e5bf9eb089e92cff12a5334be00e7
|
4
|
+
data.tar.gz: 688dc30f47412cabbd679f6d8a2571e9a71980b8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7df2c8cdffdee78b64599afbddf8d04efad76f37ed20f0b32de2051988de2ccaa6015d46aa65dcaf1ab75e95389e7d5fa074da9236145f75940ca69263ef6481
|
7
|
+
data.tar.gz: 3593cba598bc3cf4a2b552cca783a58f02ad8aa3dcb7c0a0deba5d9327b19160a5e2b9d2ffea3ed5ee00c51bc279dbdf0aa7ba989697f51fc8cc24315bf271ee
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Jaci R Brunning
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/bin/fwi
ADDED
data/fwi.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "fwi"
|
6
|
+
spec.version = "1.0.0"
|
7
|
+
spec.authors = ["Jaci Brunning"]
|
8
|
+
spec.email = ["jaci.brunning@gmail.com"]
|
9
|
+
|
10
|
+
spec.summary = %q{Fixed-Width Data Interchange Compiler}
|
11
|
+
spec.homepage = "http://github.com/JacisNonsense/Fixed-Width-Interchange"
|
12
|
+
|
13
|
+
spec.bindir = "bin"
|
14
|
+
spec.files = Dir.glob("lib/**/*") + ['fwi.gemspec', 'Gemfile', 'LICENSE']
|
15
|
+
spec.executables = ["fwi"]
|
16
|
+
spec.require_paths = ["lib"]
|
17
|
+
end
|
data/lib/fwi/compiler.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative 'cpp/compiler.rb'
|
2
|
+
require_relative 'parser.rb'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
COMPILERS = {
|
6
|
+
"cpp" => CPPCompiler.new
|
7
|
+
};
|
8
|
+
|
9
|
+
options = {
|
10
|
+
:source_root => "."
|
11
|
+
}
|
12
|
+
optp = OptionParser.new do |opts|
|
13
|
+
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Common options:"
|
16
|
+
|
17
|
+
opts.on("-h", "--help", "Show this message") do
|
18
|
+
puts opts
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-l", "--language LANGUAGE", COMPILERS.map { |n, v| n },
|
23
|
+
"Select the language to compile down to. One of [#{COMPILERS.keys.join(", ")}]") do |lang|
|
24
|
+
options[:language] = lang
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-d", "--source-directory DIRECTORY", "Set the source directory to pull files from") do |directory|
|
28
|
+
options[:source_root] = directory
|
29
|
+
end
|
30
|
+
|
31
|
+
COMPILERS.each do |n,x|
|
32
|
+
opts.separator ""
|
33
|
+
opts.separator n
|
34
|
+
x.populate_options opts
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
optp.parse!
|
42
|
+
mandatory = [:language]
|
43
|
+
missing = mandatory.select{ |param| options[param].nil? }
|
44
|
+
unless missing.empty?
|
45
|
+
raise OptionParser::MissingArgument.new(missing.join(', '))
|
46
|
+
end
|
47
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
48
|
+
puts $!.to_s
|
49
|
+
puts optp
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
|
53
|
+
source_files = ARGV
|
54
|
+
COMPILERS.each { |n,x| x.after_parse }
|
55
|
+
selected_compiler = COMPILERS[options[:language]]
|
56
|
+
|
57
|
+
p = FWI::Parser.new
|
58
|
+
|
59
|
+
selected_compiler.on_selected
|
60
|
+
source_files.each do |file|
|
61
|
+
lex = p.lex(file, options[:source_root])
|
62
|
+
bitmap = p.bitmap(lex)
|
63
|
+
selected_compiler.compile file, lex, bitmap, options
|
64
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'gen.rb'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class CPPCompiler
|
5
|
+
GEN = FWI::Generator::CPP.new
|
6
|
+
|
7
|
+
def populate_options o
|
8
|
+
@options = {
|
9
|
+
:hpp => ".", :cpp => ".",
|
10
|
+
:hpp_ext => ".hpp", :cpp_ext => ".cpp",
|
11
|
+
:hpp_only => false
|
12
|
+
};
|
13
|
+
o.on("--headers [DIRECTORY]", "Directory to put Generated C++ Header Files.") { |x| @options[:hpp] = x }
|
14
|
+
o.on("--sources [DIRECTORY]", "Directory to put Generated C++ Source Files.") { |x| @options[:cpp] = x }
|
15
|
+
o.separator ""
|
16
|
+
o.on("--header_ext [EXTENSION]", "Extension for Generated C++ Header Files. Default .hpp") { |x| @options[:hpp_ext] = x }
|
17
|
+
o.on("--source_ext [EXTENSION]", "Extension for Generated C++ Source Files. Default .cpp") { |x| @options[:cpp_ext] = x }
|
18
|
+
o.separator ""
|
19
|
+
o.on("--header-only", "Compile as header-only") { |x| @options[:hpp_only] = true }
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_parse
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_selected; end
|
26
|
+
|
27
|
+
def compile file, lex, bitmap, options
|
28
|
+
base = File.basename(file, File.extname(file))
|
29
|
+
|
30
|
+
hpp_ext = @options[:hpp_ext]
|
31
|
+
cpp_ext = @options[:cpp_ext]
|
32
|
+
|
33
|
+
compile_cpp = !@options[:hpp_only]
|
34
|
+
|
35
|
+
hpp = GEN.gen_hpp bitmap, hpp_ext, @options[:hpp_only]
|
36
|
+
hpp.each do |fn, contents|
|
37
|
+
f = File.join(@options[:hpp], fn)
|
38
|
+
FileUtils.mkdir_p File.expand_path("..", f)
|
39
|
+
File.write(f, contents)
|
40
|
+
end
|
41
|
+
|
42
|
+
if compile_cpp
|
43
|
+
cpp = GEN.gen_cpp bitmap, cpp_ext, hpp_ext
|
44
|
+
cpp.each do |fn, contents|
|
45
|
+
f = File.join(@options[:cpp], fn)
|
46
|
+
FileUtils.mkdir_p File.expand_path("..", f)
|
47
|
+
File.write(f, contents)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/fwi/cpp/gen.rb
ADDED
@@ -0,0 +1,329 @@
|
|
1
|
+
module FWI
|
2
|
+
module Generator
|
3
|
+
class CPP
|
4
|
+
TYPE_MAP = {
|
5
|
+
:float64 => "double",
|
6
|
+
:float32 => "float",
|
7
|
+
:u64 => "uint64_t",
|
8
|
+
:u32 => "uint32_t",
|
9
|
+
:u16 => "uint16_t",
|
10
|
+
:u8 => "uint8_t",
|
11
|
+
:i64 => "int64_t",
|
12
|
+
:i32 => "int32_t",
|
13
|
+
:i16 => "int16_t",
|
14
|
+
:i8 => "int8_t",
|
15
|
+
:string => "char *",
|
16
|
+
:bool => "bool"
|
17
|
+
};
|
18
|
+
|
19
|
+
def append buffer, indent, string
|
20
|
+
string.split(/\n/).each { |y| buffer << ("\t" * indent) + y + "\n" }
|
21
|
+
end
|
22
|
+
|
23
|
+
def _write_fwd_child name, child, indent, buffer
|
24
|
+
if child[:type] == :namespace
|
25
|
+
_write_fwd name, child, indent, buffer
|
26
|
+
elsif child[:type] == :block
|
27
|
+
append(buffer, indent, "struct #{name};")
|
28
|
+
elsif child[:type] == :enum
|
29
|
+
type = child[:reltype]
|
30
|
+
append(buffer, indent, "enum class #{name} {")
|
31
|
+
indent += 1
|
32
|
+
append(buffer, indent, child[:members].map { |n| "#{n[:name]} = #{n[:val]}" }.join(",\n"))
|
33
|
+
indent -= 1
|
34
|
+
append(buffer, indent, "};")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def _write_fwd name, namespace, indent, buffer
|
39
|
+
append(buffer, indent, "namespace #{name} {")
|
40
|
+
indent += 1
|
41
|
+
namespace[:members].each do |name, child|
|
42
|
+
_write_fwd_child name, child, indent, buffer
|
43
|
+
end
|
44
|
+
indent -= 1
|
45
|
+
append(buffer, indent, "}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def write_fwd_declarations str, root, indent
|
49
|
+
root[:members].each do |name, child|
|
50
|
+
_write_fwd_child name, child, indent, str
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def _func_util mode, name, member, truncate=true, relative_types=true, fulltype=""
|
55
|
+
type = member[:type]
|
56
|
+
type_sym = relative_types ? :reltype : :element_name
|
57
|
+
|
58
|
+
ctype = TYPE_MAP[type]
|
59
|
+
ctype = member[type_sym] + (member[:element_type] == :block ? " *" : "") if (member[:type] == :reference)
|
60
|
+
ctype = member[:attribute_map]["ctype"] unless member[:attribute_map]["ctype"].nil?
|
61
|
+
|
62
|
+
param = member[:array].nil? ? "" : "int index#{mode == :setter ? ', ' : ''}"
|
63
|
+
custom_getter = member[:attribute_map]["getter"]
|
64
|
+
custom_setter = member[:attribute_map]["setter"]
|
65
|
+
custom_length = member[:attribute_map]["length_func"]
|
66
|
+
prefix = relative_types ? "" : (fulltype + "::")
|
67
|
+
func_name = prefix
|
68
|
+
func_sig = ""
|
69
|
+
if mode == :setter
|
70
|
+
func_name += custom_setter.nil? ? "set_#{name}" : custom_setter
|
71
|
+
func_sig = "void #{func_name}(#{param}#{ctype} value)"
|
72
|
+
elsif mode == :getter
|
73
|
+
func_name += custom_getter.nil? ? "get_#{name}" : custom_getter
|
74
|
+
func_sig = "#{ctype} #{func_name}(#{param})"
|
75
|
+
elsif mode == :length
|
76
|
+
func_name += custom_length.nil? ? "#{name}_length" : custom_length
|
77
|
+
func_sig = "int #{func_name}(#{param})"
|
78
|
+
end
|
79
|
+
func_sig += truncate ? ";" : ' {'
|
80
|
+
index = member[:index]
|
81
|
+
index = "#{index} + (#{member[:typesize]} * index)" unless member[:array].nil?
|
82
|
+
{ :ctype => ctype, :prefix => prefix, :func_name => func_name, :func_sig => func_sig, :index => index }
|
83
|
+
end
|
84
|
+
|
85
|
+
def write_getter str, name, member, indent, truncate=true, relative_types=true, fulltype=""
|
86
|
+
return "" if member[:attributes].include? "no-getter"
|
87
|
+
util = _func_util :getter, name, member, truncate, relative_types, fulltype
|
88
|
+
|
89
|
+
append(str, indent, util[:func_sig])
|
90
|
+
unless truncate
|
91
|
+
type = member[:type]
|
92
|
+
ctype = util[:ctype]
|
93
|
+
|
94
|
+
indent += 1
|
95
|
+
if type == :reference
|
96
|
+
if member[:element_type] == :enum
|
97
|
+
append(str, indent, "return (#{ctype})(_store[#{util[:index]}]);")
|
98
|
+
else
|
99
|
+
if member[:array].nil?
|
100
|
+
append(str, indent, "return &_#{member[:name]};")
|
101
|
+
else
|
102
|
+
append(str, indent, "return &_#{member[:name]}[index];")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
elsif type == :string
|
106
|
+
append(str, indent, "return (_store + #{util[:index]});")
|
107
|
+
elsif type == :bool
|
108
|
+
if member[:array].nil?
|
109
|
+
append(str, indent, "return FWI_IS_BIT_SET(_store[#{util[:index]}], #{member[:bit_index]});")
|
110
|
+
else
|
111
|
+
append(str, indent, "return FWI_IS_BIT_SET(_store[#{member[:index]} + index / 8], index % 8);")
|
112
|
+
end
|
113
|
+
else
|
114
|
+
append(str, indent, "return FWI_MEM_VAL(#{util[:ctype]}, _store, #{util[:index]});")
|
115
|
+
end
|
116
|
+
indent -= 1
|
117
|
+
append(str, indent, "}")
|
118
|
+
end
|
119
|
+
str
|
120
|
+
end
|
121
|
+
|
122
|
+
def write_setter str, name, member, indent, truncate=true, relative_types=true, fulltype=""
|
123
|
+
return "" if member[:attributes].include? "no-setter"
|
124
|
+
util = _func_util :setter, name, member, truncate, relative_types, fulltype
|
125
|
+
|
126
|
+
append(str, indent, util[:func_sig])
|
127
|
+
unless truncate
|
128
|
+
type = member[:type]
|
129
|
+
ctype = util[:ctype]
|
130
|
+
|
131
|
+
indent += 1
|
132
|
+
if type == :reference
|
133
|
+
append(str, indent, "_store[#{util[:index]}] = (char)value;")
|
134
|
+
elsif type == :bool
|
135
|
+
if member[:array].nil?
|
136
|
+
append(str, indent, "FWI_SET_BIT_TO(_store[#{util[:index]}], #{member[:bit_index]}, value ? 1 : 0);")
|
137
|
+
else
|
138
|
+
append(str, indent, "FWI_SET_BIT_TO(_store[#{member[:index]} + index / 8], index % 8, value ? 1 : 0);")
|
139
|
+
end
|
140
|
+
else
|
141
|
+
append(str, indent, "FWI_MEM_VAL(#{ctype}, _store, #{util[:index]}) = value;")
|
142
|
+
end
|
143
|
+
indent -= 1
|
144
|
+
append(str, indent, "}")
|
145
|
+
end
|
146
|
+
str
|
147
|
+
end
|
148
|
+
|
149
|
+
def write_length str, name, member, indent, truncate=true, relative_types=true, fulltype=""
|
150
|
+
return "" if member[:attributes].include? "no-len"
|
151
|
+
util = _func_util :length, name, member, truncate, relative_types, fulltype
|
152
|
+
|
153
|
+
append(str, indent, util[:func_sig])
|
154
|
+
unless truncate
|
155
|
+
indent += 1
|
156
|
+
append(str, indent, "return #{member[:size]};")
|
157
|
+
indent -= 1
|
158
|
+
append(str, indent, "}")
|
159
|
+
end
|
160
|
+
str
|
161
|
+
end
|
162
|
+
|
163
|
+
def write_ptr_update buffer, b_refs, indent, truncate=true, prefix=""
|
164
|
+
unless b_refs.empty?
|
165
|
+
virt = prefix.empty? ? "virtual " : ""
|
166
|
+
if truncate
|
167
|
+
append(buffer, indent, "#{virt}void #{prefix}_update_ptr();")
|
168
|
+
else
|
169
|
+
append(buffer, indent, "#{virt}void #{prefix}_update_ptr() {")
|
170
|
+
indent += 1
|
171
|
+
i_def = false
|
172
|
+
b_refs.each do |ref|
|
173
|
+
if ref[:array].nil?
|
174
|
+
append(buffer, indent, "_#{ref[:name]}.map_to(_store + #{ref[:index]});")
|
175
|
+
else
|
176
|
+
unless i_def
|
177
|
+
i_def = true
|
178
|
+
append(buffer, indent, "int i;")
|
179
|
+
end
|
180
|
+
append(buffer, indent, "for (i = 0; i < #{ref[:array]}; i++) {")
|
181
|
+
indent += 1
|
182
|
+
append(buffer, indent, "_#{ref[:name]}[i].map_to(_store + #{ref[:index]} + (#{ref[:typesize]} * i));")
|
183
|
+
indent -= 1
|
184
|
+
append(buffer, indent, "}")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
indent -= 1
|
188
|
+
append(buffer, indent, "}")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def _get_for_file bitmap, file
|
194
|
+
map = { :members => {}, :type => :namespace }
|
195
|
+
bitmap[:members].each do |name, member|
|
196
|
+
if member[:type] == :namespace
|
197
|
+
ret = _get_for_file member, file
|
198
|
+
map[:members][name] = ret unless ret[:members].empty?
|
199
|
+
else
|
200
|
+
map[:members][name] = member if member[:file] == file
|
201
|
+
end
|
202
|
+
end
|
203
|
+
map
|
204
|
+
end
|
205
|
+
|
206
|
+
def gen_hpp_for buffer, indent, filtered, hpp_only
|
207
|
+
namespace, object = filtered[:members].partition { |n,x| x[:type] == :namespace }
|
208
|
+
|
209
|
+
object.select { |n,x| x[:type] == :block }.each do |name, block|
|
210
|
+
append(buffer, indent, "struct #{name} : public FWI::Block {")
|
211
|
+
indent += 1
|
212
|
+
append(buffer, indent, "static const int SIZE = #{block[:size]};")
|
213
|
+
b_refs, mems = block[:members].partition { |x| x[:type] == :reference && x[:element_type] == :block }
|
214
|
+
buffer << "\n"
|
215
|
+
|
216
|
+
trunc = !hpp_only
|
217
|
+
|
218
|
+
b_refs.each do |block_ref|
|
219
|
+
write_getter buffer, block_ref[:name], block_ref, indent, trunc, true
|
220
|
+
buffer << "\n"
|
221
|
+
end
|
222
|
+
|
223
|
+
mems.each do |member_ref|
|
224
|
+
write_getter buffer, member_ref[:name], member_ref, indent, trunc, true
|
225
|
+
if member_ref[:type] == :string
|
226
|
+
write_length buffer, member_ref[:name], member_ref, indent, trunc, true
|
227
|
+
else
|
228
|
+
write_setter buffer, member_ref[:name], member_ref, indent, trunc, true
|
229
|
+
end
|
230
|
+
buffer << "\n"
|
231
|
+
end
|
232
|
+
|
233
|
+
b_refs.each do |block_ref|
|
234
|
+
if block_ref[:array].nil?
|
235
|
+
append(buffer, indent, "#{block_ref[:reltype]} _#{block_ref[:name]};")
|
236
|
+
else
|
237
|
+
append(buffer, indent, "#{block_ref[:reltype]} _#{block_ref[:name]}[#{block_ref[:array]}];")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
buffer << "\n"
|
242
|
+
|
243
|
+
write_ptr_update buffer, b_refs, indent, trunc
|
244
|
+
|
245
|
+
indent -= 1
|
246
|
+
append(buffer, indent, "}; // struct: #{name}")
|
247
|
+
end
|
248
|
+
|
249
|
+
namespace.each do |name, ns|
|
250
|
+
append(buffer, indent, "namespace #{name} {")
|
251
|
+
gen_hpp_for buffer, indent + 1, ns, hpp_only
|
252
|
+
append(buffer, indent, "} // namespace: #{name}")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def gen_cpp_for buffer, indent, blocks
|
257
|
+
blocks.each do |obj|
|
258
|
+
name = obj[:name]
|
259
|
+
block = obj[:block]
|
260
|
+
|
261
|
+
b_refs, mems = block[:members].partition { |x| x[:type] == :reference && x[:element_type] == :block }
|
262
|
+
|
263
|
+
b_refs.each do |block_ref|
|
264
|
+
write_getter buffer, block_ref[:name], block_ref, indent, false, false, block[:fulltype]
|
265
|
+
buffer << "\n"
|
266
|
+
end
|
267
|
+
|
268
|
+
mems.each do |member_ref|
|
269
|
+
write_getter buffer, member_ref[:name], member_ref, indent, false, false, block[:fulltype]
|
270
|
+
if member_ref[:type] == :string
|
271
|
+
write_length buffer, member_ref[:name], member_ref, indent, false, false, block[:fulltype]
|
272
|
+
else
|
273
|
+
write_setter buffer, member_ref[:name], member_ref, indent, false, false, block[:fulltype]
|
274
|
+
end
|
275
|
+
buffer << "\n"
|
276
|
+
end
|
277
|
+
|
278
|
+
buffer << "\n"
|
279
|
+
|
280
|
+
write_ptr_update(buffer, b_refs, indent, false, (block[:fulltype] + "::"))
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def gen_hpp bitmap, hpp_ext, hpp_only
|
285
|
+
map = {}
|
286
|
+
bitmap[:files].each do |name, file|
|
287
|
+
filename = name.sub ".fwi", hpp_ext
|
288
|
+
contents = "#pragma once\n\n#include \"fwi.hpp\"\n"
|
289
|
+
file[:imports].each do |import|
|
290
|
+
append(contents, 0, "#include \"#{import.sub '.fwi', hpp_ext}\"")
|
291
|
+
end
|
292
|
+
contents << "\n" unless file[:imports].empty?
|
293
|
+
|
294
|
+
filtered_bitmap = _get_for_file(bitmap, name)
|
295
|
+
|
296
|
+
write_fwd_declarations contents, filtered_bitmap, 0
|
297
|
+
contents << "\n"
|
298
|
+
gen_hpp_for contents, 0, filtered_bitmap, hpp_only
|
299
|
+
|
300
|
+
map[filename] = contents
|
301
|
+
end
|
302
|
+
map
|
303
|
+
end
|
304
|
+
|
305
|
+
def gen_cpp bitmap, cpp_ext, hpp_ext
|
306
|
+
map = {}
|
307
|
+
bitmap[:files].each do |name, file|
|
308
|
+
filename = name.sub ".fwi", cpp_ext
|
309
|
+
contents = "#include \"#{name.sub '.fwi', hpp_ext}\"\n\n"
|
310
|
+
|
311
|
+
blocks = file[:blocks].map do |b|
|
312
|
+
ref = bitmap
|
313
|
+
name = ""
|
314
|
+
b.split("::").each do |c|
|
315
|
+
ref = ref[:members][c]
|
316
|
+
name = c
|
317
|
+
end
|
318
|
+
{ :name => name, :block => ref }
|
319
|
+
end
|
320
|
+
|
321
|
+
gen_cpp_for contents, 0, blocks
|
322
|
+
|
323
|
+
map[filename] = contents
|
324
|
+
end
|
325
|
+
map
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
data/lib/fwi/parser.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
class ::Hash
|
2
|
+
def deep_merge(second)
|
3
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
|
4
|
+
self.merge(second.to_h, &merger)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module FWI
|
9
|
+
class Parser
|
10
|
+
TYPE_MAP = {
|
11
|
+
"float64" => :float64,
|
12
|
+
"float32" => :float32,
|
13
|
+
"u64" => :u64,
|
14
|
+
"u32" => :u32,
|
15
|
+
"u16" => :u16,
|
16
|
+
"u8" => :u8,
|
17
|
+
"i64" => :i64,
|
18
|
+
"i32" => :i32,
|
19
|
+
"i16" => :i16,
|
20
|
+
"i8" => :i8,
|
21
|
+
"string" => :string,
|
22
|
+
"bool" => :bool,
|
23
|
+
|
24
|
+
"blank" => :blank,
|
25
|
+
|
26
|
+
"float" => :float32,
|
27
|
+
"double" => :float64,
|
28
|
+
"ulong" => :u64,
|
29
|
+
"long" => :i64,
|
30
|
+
"uint" => :u32,
|
31
|
+
"int" => :i32,
|
32
|
+
"ushort" => :u16,
|
33
|
+
"short" => :i16,
|
34
|
+
"char" => :i8,
|
35
|
+
"uchar" => :u8,
|
36
|
+
"byte" => :i8,
|
37
|
+
"ubyte" => :u8,
|
38
|
+
"boolean" => :bool,
|
39
|
+
"bit" => :bool
|
40
|
+
};
|
41
|
+
|
42
|
+
TYPE_SIZES = {
|
43
|
+
:float64 => 8,
|
44
|
+
:u64 => 8,
|
45
|
+
:i64 => 8,
|
46
|
+
|
47
|
+
:float32 => 4,
|
48
|
+
:u32 => 4,
|
49
|
+
:i32 => 4,
|
50
|
+
|
51
|
+
:u16 => 2,
|
52
|
+
:i16 => 2,
|
53
|
+
|
54
|
+
:u8 => 1,
|
55
|
+
:i8 => 1
|
56
|
+
};
|
57
|
+
|
58
|
+
# Do the initial parsing on the file
|
59
|
+
def lex src_file, src_dir
|
60
|
+
src = File.read(File.join(src_dir, src_file))
|
61
|
+
hash = {
|
62
|
+
:members => {},
|
63
|
+
:files => {
|
64
|
+
src_file => {
|
65
|
+
:imports => [],
|
66
|
+
:enums => [],
|
67
|
+
:blocks => []
|
68
|
+
}
|
69
|
+
},
|
70
|
+
:block_order => []
|
71
|
+
}
|
72
|
+
file = hash[:files][src_file]
|
73
|
+
|
74
|
+
_blevel = []
|
75
|
+
hashobj = lambda do
|
76
|
+
hr = hash
|
77
|
+
_blevel.select { |n| n[:type] == :namespace }.each do |ns|
|
78
|
+
hr = hr[:members][ns[:name]]
|
79
|
+
end
|
80
|
+
hr
|
81
|
+
end
|
82
|
+
|
83
|
+
namespacedive = lambda do |fqn|
|
84
|
+
hr = hash
|
85
|
+
comp = fqn.split("::")
|
86
|
+
_blevel.select { |x| x[:type] == :namespace }.each do |b|
|
87
|
+
return nil if hr.nil?
|
88
|
+
hr = hr[:members][b[:name]]
|
89
|
+
end
|
90
|
+
comp.each do |c|
|
91
|
+
return nil if hr.nil?
|
92
|
+
hr = hr[:members][c]
|
93
|
+
end
|
94
|
+
hr
|
95
|
+
end
|
96
|
+
|
97
|
+
rootdive = lambda do |fqn|
|
98
|
+
hr = hash
|
99
|
+
comp = fqn.split("::")
|
100
|
+
comp.each do |a|
|
101
|
+
return nil if hr.nil?
|
102
|
+
hr = hr[:members][a]
|
103
|
+
end
|
104
|
+
hr
|
105
|
+
end
|
106
|
+
|
107
|
+
current_fqn = lambda do
|
108
|
+
_blevel.select { |n| n[:type] == :namespace }.map { |n| n[:name] }.join("::")
|
109
|
+
end
|
110
|
+
|
111
|
+
findtype = lambda do |tn|
|
112
|
+
return tn unless rootdive[tn].nil?
|
113
|
+
unless namespacedive[tn].nil?
|
114
|
+
return [current_fqn[], tn].join("::")
|
115
|
+
end
|
116
|
+
usings = _blevel.each_index.map { |x| Hash[x, _blevel[x]] if _blevel[x][:type] == :using }.reject(&:nil?)
|
117
|
+
usings.size.times do |time|
|
118
|
+
afqn = [usings.first(time + 1).map { |x| x.values[0][:name] }, tn].join("::")
|
119
|
+
return afqn unless rootdive[afqn].nil?
|
120
|
+
return [current_fqn[], afqn].join("::") unless namespacedive[afqn].nil?
|
121
|
+
end
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
_enumidx = 0
|
126
|
+
|
127
|
+
src.each_line.map(&:strip).reject { |l| l.start_with?("//") || l.start_with?("#") }.each do |line|
|
128
|
+
tok = line.split(/\s+/).reject(&:empty?)
|
129
|
+
unless _blevel.empty? || tok[0] == "}"
|
130
|
+
cur = _blevel.last
|
131
|
+
b_name = cur[:name]
|
132
|
+
b_type = cur[:type]
|
133
|
+
h = hashobj[][:members][b_name]
|
134
|
+
|
135
|
+
if b_type == :block
|
136
|
+
type, vals = line.split(/\s+/, 2)
|
137
|
+
|
138
|
+
next if vals.nil?
|
139
|
+
vals.split(/\s*,\s*/).each do |splitcomma|
|
140
|
+
name, *attrib = splitcomma.split(/\s+/).reject(&:empty?)
|
141
|
+
array_match = /(.+)\[([0-9]+)\]/.match(name)
|
142
|
+
arrlen = 1
|
143
|
+
|
144
|
+
unless array_match.nil?
|
145
|
+
name = array_match[1]
|
146
|
+
arrlen = array_match[2].to_i
|
147
|
+
end
|
148
|
+
|
149
|
+
type_s = TYPE_MAP[type]
|
150
|
+
m = { :type => type_s, :name => name, :original_type => type }
|
151
|
+
m[:attributes] = attrib
|
152
|
+
m[:attribute_map] = Hash[attrib.select { |x| x.is_a?(String) && x.include?("=") }.map { |x| x.split(/\s*=\s*/, 2) }]
|
153
|
+
|
154
|
+
if m[:type].nil?
|
155
|
+
fqn = findtype[type]
|
156
|
+
throw "Can't find type: #{type}" if fqn.nil?
|
157
|
+
m[:type] = :reference
|
158
|
+
m[:element_name] = fqn
|
159
|
+
m[:element_type] = rootdive[fqn][:type]
|
160
|
+
|
161
|
+
spl = fqn.split("::")
|
162
|
+
spl_cu = current_fqn[].split("::")
|
163
|
+
|
164
|
+
min = spl - spl_cu
|
165
|
+
if min.empty?
|
166
|
+
m[:reltype] = fqn
|
167
|
+
else
|
168
|
+
m[:reltype] = min.join("::")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
unless arrlen == 1
|
173
|
+
m[:array] = arrlen
|
174
|
+
end
|
175
|
+
h[:members] << m
|
176
|
+
end
|
177
|
+
elsif b_type == :enum
|
178
|
+
line.split(",").each do |ln|
|
179
|
+
a, b, c = ln.split(/\s+/).reject(&:empty?)
|
180
|
+
|
181
|
+
if !b.nil? && b == "="
|
182
|
+
_enumidx = c.to_i
|
183
|
+
end
|
184
|
+
h[:members] << { :name => a, :val => _enumidx}
|
185
|
+
_enumidx = _enumidx + 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if tok[0] == "}"
|
191
|
+
_blevel.pop
|
192
|
+
elsif tok[0] == "namespace"
|
193
|
+
if tok[2] == "{"
|
194
|
+
hashobj[][:members][tok[1]] = { :type => :namespace, :members => {} }
|
195
|
+
_blevel << { :type => :namespace, :name => tok[1] }
|
196
|
+
else
|
197
|
+
tok[1].split("::").each do |ns|
|
198
|
+
hashobj[][:members][ns] = { :type => :namespace, :members => {} }
|
199
|
+
_blevel << { :type => :namespace, :name => ns }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
elsif tok[0] == "block"
|
203
|
+
cur = current_fqn[]
|
204
|
+
fulltype = tok[1]
|
205
|
+
fulltype = cur + "::" + tok[1] unless cur.empty?
|
206
|
+
|
207
|
+
hashobj[][:members][tok[1]] = {
|
208
|
+
:type => :block,
|
209
|
+
:members => [],
|
210
|
+
:file => src_file,
|
211
|
+
:fulltype => fulltype
|
212
|
+
}
|
213
|
+
|
214
|
+
# Block Order is used by the bitmap generator to decide what order to process block sizes
|
215
|
+
# and references in. This means that declaration order matters
|
216
|
+
hash[:block_order] << fulltype
|
217
|
+
file[:blocks] << fulltype
|
218
|
+
|
219
|
+
_blevel << { :type => :block, :name => tok[1] }
|
220
|
+
elsif tok[0] == "enum"
|
221
|
+
cur = current_fqn[]
|
222
|
+
fulltype = tok[1]
|
223
|
+
fulltype = cur + "::" + tok[1] unless cur.empty?
|
224
|
+
|
225
|
+
hashobj[][:members][tok[1]] = {
|
226
|
+
:type => :enum,
|
227
|
+
:members => [],
|
228
|
+
:file => src_file,
|
229
|
+
:fulltype => fulltype
|
230
|
+
}
|
231
|
+
_enumidx = 0
|
232
|
+
_blevel << { :type => :enum, :name => tok[1] }
|
233
|
+
|
234
|
+
file[:enums] << fulltype
|
235
|
+
elsif tok[0] == "using" && tok[1] == "namespace"
|
236
|
+
if tok[3] == "{"
|
237
|
+
_blevel << { :type => :using, :name => tok[2] }
|
238
|
+
else
|
239
|
+
tok[2].split("::").each do |ns|
|
240
|
+
_blevel << { :type => :using, :name => ns }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
elsif tok[0] == "import"
|
244
|
+
file[:imports] << tok[1]
|
245
|
+
hash = hash.deep_merge(lex(tok[1], src_dir))
|
246
|
+
end
|
247
|
+
end
|
248
|
+
hash
|
249
|
+
end
|
250
|
+
|
251
|
+
def bitmap lex
|
252
|
+
lex[:block_order].each do |block_name|
|
253
|
+
block = lex
|
254
|
+
block_name.split("::").each { |x| block = block[:members][x] }
|
255
|
+
|
256
|
+
bools, nonbools = block[:members].partition { |el| el[:type] == :bool && el[:array].nil? }
|
257
|
+
idx = 0
|
258
|
+
|
259
|
+
bools.each_slice(8) do |byte|
|
260
|
+
byte.each_with_index do |bitvalue, i|
|
261
|
+
bitvalue[:bit_index] = i
|
262
|
+
bitvalue[:index] = idx
|
263
|
+
end
|
264
|
+
idx += 1
|
265
|
+
end
|
266
|
+
|
267
|
+
nonbools.each do |member|
|
268
|
+
t = member[:type]
|
269
|
+
n = member[:name]
|
270
|
+
|
271
|
+
arraysize = 1
|
272
|
+
arraysize = member[:array] unless member[:array].nil?
|
273
|
+
|
274
|
+
if t == :bool
|
275
|
+
# It must be a boolean array.
|
276
|
+
total_size = (arraysize / 8.to_f).ceil
|
277
|
+
member[:index] = idx
|
278
|
+
member[:size] = total_size
|
279
|
+
idx += total_size
|
280
|
+
elsif t == :blank
|
281
|
+
idx += arraysize
|
282
|
+
elsif t == :string
|
283
|
+
typesize = member[:attributes][0].to_i
|
284
|
+
len = typesize * arraysize
|
285
|
+
member[:index] = idx
|
286
|
+
member[:size] = len
|
287
|
+
member[:typesize] = typesize
|
288
|
+
idx += len
|
289
|
+
elsif t == :reference
|
290
|
+
if member[:element_type] == :block
|
291
|
+
referenced = lex
|
292
|
+
member[:element_name].split("::").each { |a| referenced = referenced[:members][a] }
|
293
|
+
|
294
|
+
size = referenced[:size] * arraysize
|
295
|
+
member[:size] = size
|
296
|
+
member[:index] = idx
|
297
|
+
member[:typesize] = referenced[:size]
|
298
|
+
idx += size
|
299
|
+
elsif member[:element_type] == :enum
|
300
|
+
member[:size] = arraysize
|
301
|
+
member[:typesize] = 1
|
302
|
+
member[:index] = idx
|
303
|
+
idx += arraysize
|
304
|
+
end
|
305
|
+
else
|
306
|
+
size = TYPE_SIZES[t] * arraysize
|
307
|
+
member[:size] = size
|
308
|
+
member[:index] = idx
|
309
|
+
member[:typesize] = TYPE_SIZES[t]
|
310
|
+
idx += TYPE_SIZES[t]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Remove :blank types
|
315
|
+
block[:members].each_index.select { |i| block[:members][i][:type] == :blank }.each { |x| block[:members].delete_at(x) }
|
316
|
+
|
317
|
+
# Final Size
|
318
|
+
block[:size] = idx
|
319
|
+
end
|
320
|
+
lex
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fwi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jaci Brunning
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- jaci.brunning@gmail.com
|
16
|
+
executables:
|
17
|
+
- fwi
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- Gemfile
|
22
|
+
- LICENSE
|
23
|
+
- bin/fwi
|
24
|
+
- fwi.gemspec
|
25
|
+
- lib/fwi/compiler.rb
|
26
|
+
- lib/fwi/cpp/compiler.rb
|
27
|
+
- lib/fwi/cpp/gen.rb
|
28
|
+
- lib/fwi/parser.rb
|
29
|
+
homepage: http://github.com/JacisNonsense/Fixed-Width-Interchange
|
30
|
+
licenses: []
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.4.5
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Fixed-Width Data Interchange Compiler
|
52
|
+
test_files: []
|