swift_generator 0.1.2

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.idea/.name +1 -0
  4. data/.idea/.rakeTasks +7 -0
  5. data/.idea/compiler.xml +22 -0
  6. data/.idea/copyright/profiles_settings.xml +3 -0
  7. data/.idea/dictionaries/ckornher.xml +7 -0
  8. data/.idea/encodings.xml +4 -0
  9. data/.idea/misc.xml +4 -0
  10. data/.idea/modules.xml +8 -0
  11. data/.idea/runConfigurations/Install_the_Gem.xml +26 -0
  12. data/.idea/runConfigurations/Ribosome.xml +26 -0
  13. data/.idea/runConfigurations/genswift_help.xml +26 -0
  14. data/.idea/runConfigurations/test1.xml +30 -0
  15. data/.idea/scopes/scope_settings.xml +5 -0
  16. data/.idea/swift_generator.iml +126 -0
  17. data/.idea/uiDesigner.xml +124 -0
  18. data/.idea/vcs.xml +6 -0
  19. data/.idea/workspace.xml +1356 -0
  20. data/.rspec +2 -0
  21. data/.travis.yml +3 -0
  22. data/CODE_OF_CONDUCT.md +13 -0
  23. data/Gemfile +7 -0
  24. data/LICENSE.txt +21 -0
  25. data/README.md +39 -0
  26. data/Rakefile +1 -0
  27. data/Tools/compile_templates.bash +3 -0
  28. data/Tools/help.rna +340 -0
  29. data/Tools/ribosome.rb +589 -0
  30. data/bin/console +14 -0
  31. data/bin/genswift +34 -0
  32. data/bin/setup +7 -0
  33. data/lib/swift_generator.rb +23 -0
  34. data/lib/swift_generator/code_generation/POSOTest.rb +8 -0
  35. data/lib/swift_generator/code_generation/SwiftFileTemplate.dna +157 -0
  36. data/lib/swift_generator/code_generation/ToRemove/generate.bash +41 -0
  37. data/lib/swift_generator/code_generation/code_generation_common.rb +6 -0
  38. data/lib/swift_generator/code_generation/swift_class_generation.rb +1589 -0
  39. data/lib/swift_generator/code_generation/swift_file_template.rb +499 -0
  40. data/lib/swift_generator/code_generation/swift_types.rb +106 -0
  41. data/lib/swift_generator/code_generation/usi_metaclasses.rb +23 -0
  42. data/lib/swift_generator/specfile_parser.rb +116 -0
  43. data/lib/swift_generator/version.rb +3 -0
  44. data/swift_generator.gemspec +31 -0
  45. metadata +118 -0
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in swift_generator.gemspec
4
+ gemspec
5
+
6
+ gem "json"
7
+ gem "commander"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Chris Kornher
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # SwiftGenerator
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/swift_generator`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'swift_generator'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install swift_generator
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it ( https://github.com/[my-github-username]/swift_generator/fork )
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ruby ribosome.rb --rna ../lib/swift_generator/code_generation/SwiftFileTemplate.dna > ../lib/swift_generator/code_generation/swift_file_template.rb
data/Tools/help.rna ADDED
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # The initial part of this file belongs to the ribosome project.
5
+ #
6
+ # Copyright (c) 2014 Martin Sustrik All rights reserved.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"),
10
+ # to deal in the Software without restriction, including without limitation
11
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
+ # and/or sell copies of the Software, and to permit persons to whom
13
+ # the Software is furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included
16
+ # in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
+ # IN THE SOFTWARE.
25
+ #
26
+
27
+ module Ribosome
28
+
29
+ # Class Block represents a rectangular area of text.
30
+ class Block
31
+
32
+ attr_accessor :text, :width
33
+
34
+ def initialize(s)
35
+ @text = []
36
+ @width = 0
37
+ return if s == nil
38
+
39
+ # Split the string into individual lines.
40
+ start = 0
41
+ loop do
42
+ i = s.index("\n", start) || s.size
43
+ @text << (i == start ? "" : s[start..i - 1])
44
+ @width = [@width, @text.last.size].max
45
+ start = i + 1
46
+ break if start > s.size
47
+ end
48
+ end
49
+
50
+ # Weld the supplied block to the right side of this block.
51
+ def add_right(block)
52
+
53
+ # Merge the blocks while taking care to add whitespace
54
+ # where they do not align properly.
55
+ i = 0
56
+ for l in block.text
57
+ if(@text[i])
58
+ @text[i] += (" " * (@width - @text[i].size)) + l
59
+ else
60
+ @text << (" " * @width) + l
61
+ end
62
+ i += 1
63
+ end
64
+
65
+ # Adjust the overall width of the block.
66
+ @width += block.width
67
+
68
+ end
69
+
70
+ # Weld the supplied block to the bottom side of this block.
71
+ def add_bottom(block)
72
+ @text += block.text
73
+ @width = [@width, block.width].max
74
+ end
75
+
76
+ # Trim the whitespace from the block.
77
+ def trim()
78
+
79
+ # Find the boundaries of the text.
80
+ top = -1
81
+ bottom = -1
82
+ left = -1
83
+ right = -1
84
+
85
+ i = 0
86
+ for l in @text
87
+ if(!l.lstrip().empty?)
88
+ top = i if top == -1
89
+ bottom = i;
90
+ if (left == -1)
91
+ left = l.size() - l.lstrip().size()
92
+ else
93
+ left = [left, l.size() - l.lstrip().size()].min
94
+ end
95
+ if (right == -1)
96
+ right = l.rstrip().size()
97
+ else
98
+ right = [right, l.rstrip().size()].max
99
+ end
100
+ end
101
+ i += 1
102
+ end
103
+
104
+ # The case of block with no text whatsoever.
105
+ if bottom == -1
106
+ @text = []
107
+ @width = 0
108
+ return
109
+ end
110
+
111
+ # Strip off the top and bottom whitespace.
112
+ @text = @text[top..bottom]
113
+
114
+ # Strip off the whitespace on the left and on the right.
115
+ for i in 0..@text.size() - 1
116
+ @text[i] = @text[i].rstrip()[left..right]
117
+ @text[i] = "" if @text[i] == nil
118
+ end
119
+
120
+ # Adjust the overall width of the block.
121
+ @width = (@text.max {|x,y| x.size <=> y.size} || "").size
122
+
123
+ end
124
+
125
+ def write(out, tabsize)
126
+ for l in @text
127
+
128
+ # If required, replace the initial whitespace by tabs.
129
+ if(tabsize > 0)
130
+ ws = l.size - l.lstrip.size
131
+ l = "\t" * (ws / tabsize) +
132
+ " " * (ws % tabsize) + l.lstrip
133
+ end
134
+
135
+ # Write an individual line to the output file.
136
+ out.write(l)
137
+ out.write("\n")
138
+ end
139
+ end
140
+
141
+ # Returns offset of the last line in the block.
142
+ def last_offset()
143
+ return 0 if @text.empty?
144
+ return @text.last.size - @text.last.lstrip.size
145
+ end
146
+
147
+ end
148
+
149
+ # Size of a tab. If set to zero, tabs are not generated.
150
+ @tabsize = 0
151
+
152
+ # The output file, or, alternativly, stdout.
153
+ @outisafile = false
154
+ @out = $stdout
155
+
156
+ # This is the ribosome call stack. At each level there is a list of
157
+ # text blocks generated up to that point.
158
+ @stack = [[]]
159
+
160
+ # Redirects output to the specified file.
161
+ def Ribosome.output(filename)
162
+ close()
163
+ @outisafile = true
164
+ @out = File.open(filename, "w")
165
+ end
166
+
167
+ # Redirects output to the specified file.
168
+ # New stuff is added to the existing content of the file.
169
+ def Ribosome.append(filename)
170
+ close()
171
+ @outisafile = true
172
+ @out = File.open(filename, "a")
173
+ end
174
+
175
+ # Redirects output to the stdout.
176
+ def Ribosome.stdout()
177
+ close()
178
+ @outisafile = false
179
+ @out = $stdout
180
+ end
181
+
182
+ # Sets the size of the tab.
183
+ def Ribosome.tabsize(size)
184
+ @tabsize = size
185
+ end
186
+
187
+ # Flush the data to the currently open file and close it.
188
+ def Ribosome.close()
189
+ for b in @stack.last
190
+ b.write(@out, @tabsize)
191
+ end
192
+ @stack = [[]]
193
+ @out.close() if @outisafile
194
+ end
195
+
196
+ # Adds one . line from the DNA file.
197
+ def Ribosome.add(line, bind)
198
+
199
+ # If there is no previous line, add one.
200
+ if(@stack.last.empty?)
201
+ @stack.last << Block.new("")
202
+ end
203
+
204
+ # In this block we will accumulate the expanded line.
205
+ block = @stack.last.last
206
+
207
+ # Traverse the line and convert it into a block.
208
+ i = 0
209
+ while true
210
+ j = line.index(/[@&][1-9]?\{/, i)
211
+ j = line.size if j == nil
212
+
213
+ # Process constant block of text.
214
+ if (i != j)
215
+ block.add_right(Block.new(line[i..j - 1]))
216
+ end
217
+
218
+ break if line.size == j
219
+
220
+ # Process an embedded expression.
221
+ i = j
222
+ j += 1
223
+ level = 0
224
+ if (line[j] >= ?1 && line[j] <= ?9)
225
+ level = line[j].to_i
226
+ j += 1
227
+ end
228
+
229
+ # Find corresponding }.
230
+ par = 0;
231
+ while true
232
+ if(line[j] == ?{)
233
+ par += 1
234
+ elsif(line[j] == ?})
235
+ par -= 1
236
+ end
237
+ break if par == 0
238
+ j += 1
239
+ if j >= line.size
240
+ raise SyntaxError.new("Unmatched {")
241
+ end
242
+ end
243
+
244
+ # Expression of higher indirection levels are simply brought
245
+ # down by one level.
246
+ if(level > 0)
247
+ if line [i + 1] == ?1
248
+ block.add_right(Block.new("@" + line[i + 2..j]))
249
+ else
250
+ line[i + 1] = (line [i + 1].to_i - 1).chr
251
+ block.add_right(Block.new(line[i..j]))
252
+ end
253
+ i = j + 1
254
+ next
255
+ end
256
+
257
+ # We are at the lowest level of embeddedness so we have to
258
+ # evaluate the embedded expression straight away.
259
+ expr = line[(level == 0 ? i + 2 : i + 3)..j - 1]
260
+ @stack.push([])
261
+ val = eval(expr, bind)
262
+ top = @stack.pop()
263
+ if(top.empty?)
264
+ val = Block.new(val.to_s)
265
+ else
266
+ val = Block.new("")
267
+ for b in top
268
+ val.add_bottom(b)
269
+ end
270
+ end
271
+ val.trim if line[i] == ?@
272
+ block.add_right(val)
273
+ i = j + 1
274
+ end
275
+ end
276
+
277
+ # Adds newline followed by one . line from the DNA file.
278
+ def Ribosome.dot(line, bind)
279
+ @stack.last << Block.new("")
280
+ add(line, bind)
281
+ end
282
+
283
+ # Adds newline followed by leading whitespace copied from the previous line
284
+ # and one line from the DNA file.
285
+ def Ribosome.align(line, bind)
286
+ if @stack.last.empty?
287
+ n = 0
288
+ else
289
+ n = @stack.last.last.last_offset
290
+ end
291
+ @stack.last << Block.new("")
292
+ add(" " * n, nil)
293
+ add(line, bind)
294
+ end
295
+
296
+ # Report an error that happened when executing RNA file.
297
+ def Ribosome.rethrow(e, rnafile, linemap)
298
+ i = 0
299
+ for i in 0..e.backtrace.size - 1
300
+ l = e.backtrace[i]
301
+ if l.start_with?(rnafile + ":")
302
+ stop = l.index(":", rnafile.size + 1) || l.size
303
+ num = l[rnafile.size + 1..stop - 1].to_i
304
+ for j in 0..linemap.size - 1
305
+ break if linemap[j][0] == nil || linemap[j][0] > num
306
+ end
307
+ j -= 1
308
+ num = num - linemap[j][0] + linemap[j][2]
309
+ l = "#{linemap[j][1]}:#{num}#{l[stop..-1]}"
310
+ e.backtrace[i] = l
311
+ end
312
+ end
313
+ raise e
314
+ end
315
+
316
+ end
317
+
318
+ # Escape function for @
319
+ def at()
320
+ return "@"
321
+ end
322
+
323
+ # Escape function for &
324
+ def amp()
325
+ return "&"
326
+ end
327
+
328
+ # Escape function for /
329
+ def slash()
330
+ return "/"
331
+ end
332
+
333
+ ################################################################################
334
+ # The code that belongs to the ribosome project ends at this point of the #
335
+ # RNA file and so does the associated license. What follows is the code #
336
+ # generated from the DNA file. #
337
+ ################################################################################
338
+
339
+ begin
340
+
data/Tools/ribosome.rb ADDED
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Copyright (c) 2014 Martin Sustrik All rights reserved.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"),
8
+ # to deal in the Software without restriction, including without limitation
9
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ # and/or sell copies of the Software, and to permit persons to whom
11
+ # the Software is furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included
14
+ # in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
+ # IN THE SOFTWARE.
23
+ #
24
+
25
+ ################################################################################
26
+ # RNA prologue code. #
27
+ ################################################################################
28
+
29
+ # In theory, helpers could be placed into a separate module file to keep
30
+ # the RNA lean and tidy, however, embedding the whole thing into each RNA
31
+ # file makes the ribosome dependencies and deployment much simpler.
32
+
33
+ PROLOGUE_LINE = __LINE__
34
+ PROLOGUE = '#!/usr/bin/env ruby
35
+
36
+ #
37
+ # The initial part of this file belongs to the ribosome project.
38
+ #
39
+ # Copyright (c) 2014 Martin Sustrik All rights reserved.
40
+ #
41
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
42
+ # of this software and associated documentation files (the "Software"),
43
+ # to deal in the Software without restriction, including without limitation
44
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
45
+ # and/or sell copies of the Software, and to permit persons to whom
46
+ # the Software is furnished to do so, subject to the following conditions:
47
+ #
48
+ # The above copyright notice and this permission notice shall be included
49
+ # in all copies or substantial portions of the Software.
50
+ #
51
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
54
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
56
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
57
+ # IN THE SOFTWARE.
58
+ #
59
+
60
+ module Ribosome
61
+
62
+ # Class Block represents a rectangular area of text.
63
+ class Block
64
+
65
+ attr_accessor :text, :width
66
+
67
+ def initialize(s)
68
+ @text = []
69
+ @width = 0
70
+ return if s == nil
71
+
72
+ # Split the string into individual lines.
73
+ start = 0
74
+ loop do
75
+ i = s.index("\n", start) || s.size
76
+ @text << (i == start ? "" : s[start..i - 1])
77
+ @width = [@width, @text.last.size].max
78
+ start = i + 1
79
+ break if start > s.size
80
+ end
81
+ end
82
+
83
+ # Weld the supplied block to the right side of this block.
84
+ def add_right(block)
85
+
86
+ # Merge the blocks while taking care to add whitespace
87
+ # where they do not align properly.
88
+ i = 0
89
+ for l in block.text
90
+ if(@text[i])
91
+ @text[i] += (" " * (@width - @text[i].size)) + l
92
+ else
93
+ @text << (" " * @width) + l
94
+ end
95
+ i += 1
96
+ end
97
+
98
+ # Adjust the overall width of the block.
99
+ @width += block.width
100
+
101
+ end
102
+
103
+ # Weld the supplied block to the bottom side of this block.
104
+ def add_bottom(block)
105
+ @text += block.text
106
+ @width = [@width, block.width].max
107
+ end
108
+
109
+ # Trim the whitespace from the block.
110
+ def trim()
111
+
112
+ # Find the boundaries of the text.
113
+ top = -1
114
+ bottom = -1
115
+ left = -1
116
+ right = -1
117
+
118
+ i = 0
119
+ for l in @text
120
+ if(!l.lstrip().empty?)
121
+ top = i if top == -1
122
+ bottom = i;
123
+ if (left == -1)
124
+ left = l.size() - l.lstrip().size()
125
+ else
126
+ left = [left, l.size() - l.lstrip().size()].min
127
+ end
128
+ if (right == -1)
129
+ right = l.rstrip().size()
130
+ else
131
+ right = [right, l.rstrip().size()].max
132
+ end
133
+ end
134
+ i += 1
135
+ end
136
+
137
+ # The case of block with no text whatsoever.
138
+ if bottom == -1
139
+ @text = []
140
+ @width = 0
141
+ return
142
+ end
143
+
144
+ # Strip off the top and bottom whitespace.
145
+ @text = @text[top..bottom]
146
+
147
+ # Strip off the whitespace on the left and on the right.
148
+ for i in 0..@text.size() - 1
149
+ @text[i] = @text[i].rstrip()[left..right]
150
+ @text[i] = "" if @text[i] == nil
151
+ end
152
+
153
+ # Adjust the overall width of the block.
154
+ @width = (@text.max {|x,y| x.size <=> y.size} || "").size
155
+
156
+ end
157
+
158
+ def write(out, tabsize)
159
+ for l in @text
160
+
161
+ # If required, replace the initial whitespace by tabs.
162
+ if(tabsize > 0)
163
+ ws = l.size - l.lstrip.size
164
+ l = "\t" * (ws / tabsize) +
165
+ " " * (ws % tabsize) + l.lstrip
166
+ end
167
+
168
+ # Write an individual line to the output file.
169
+ out.write(l)
170
+ out.write("\n")
171
+ end
172
+ end
173
+
174
+ # Returns offset of the last line in the block.
175
+ def last_offset()
176
+ return 0 if @text.empty?
177
+ return @text.last.size - @text.last.lstrip.size
178
+ end
179
+
180
+ end
181
+
182
+ # Size of a tab. If set to zero, tabs are not generated.
183
+ @tabsize = 0
184
+
185
+ # The output file, or, alternativly, stdout.
186
+ @outisafile = false
187
+ @out = $stdout
188
+
189
+ # This is the ribosome call stack. At each level there is a list of
190
+ # text blocks generated up to that point.
191
+ @stack = [[]]
192
+
193
+ # Redirects output to the specified file.
194
+ def Ribosome.output(filename)
195
+ close()
196
+ @outisafile = true
197
+ @out = File.open(filename, "w")
198
+ end
199
+
200
+ # Redirects output to the specified file.
201
+ # New stuff is added to the existing content of the file.
202
+ def Ribosome.append(filename)
203
+ close()
204
+ @outisafile = true
205
+ @out = File.open(filename, "a")
206
+ end
207
+
208
+ # Redirects output to the stdout.
209
+ def Ribosome.stdout()
210
+ close()
211
+ @outisafile = false
212
+ @out = $stdout
213
+ end
214
+
215
+ # Sets the size of the tab.
216
+ def Ribosome.tabsize(size)
217
+ @tabsize = size
218
+ end
219
+
220
+ # Flush the data to the currently open file and close it.
221
+ def Ribosome.close()
222
+ for b in @stack.last
223
+ b.write(@out, @tabsize)
224
+ end
225
+ @stack = [[]]
226
+ @out.close() if @outisafile
227
+ end
228
+
229
+ # Adds one . line from the DNA file.
230
+ def Ribosome.add(line, bind)
231
+
232
+ # If there is no previous line, add one.
233
+ if(@stack.last.empty?)
234
+ @stack.last << Block.new("")
235
+ end
236
+
237
+ # In this block we will accumulate the expanded line.
238
+ block = @stack.last.last
239
+
240
+ # Traverse the line and convert it into a block.
241
+ i = 0
242
+ while true
243
+ j = line.index(/[@&][1-9]?\{/, i)
244
+ j = line.size if j == nil
245
+
246
+ # Process constant block of text.
247
+ if (i != j)
248
+ block.add_right(Block.new(line[i..j - 1]))
249
+ end
250
+
251
+ break if line.size == j
252
+
253
+ # Process an embedded expression.
254
+ i = j
255
+ j += 1
256
+ level = 0
257
+ if (line[j] >= ?1 && line[j] <= ?9)
258
+ level = line[j].to_i
259
+ j += 1
260
+ end
261
+
262
+ # Find corresponding }.
263
+ par = 0;
264
+ while true
265
+ if(line[j] == ?{)
266
+ par += 1
267
+ elsif(line[j] == ?})
268
+ par -= 1
269
+ end
270
+ break if par == 0
271
+ j += 1
272
+ if j >= line.size
273
+ raise SyntaxError.new("Unmatched {")
274
+ end
275
+ end
276
+
277
+ # Expression of higher indirection levels are simply brought
278
+ # down by one level.
279
+ if(level > 0)
280
+ if line [i + 1] == ?1
281
+ block.add_right(Block.new("@" + line[i + 2..j]))
282
+ else
283
+ line[i + 1] = (line [i + 1].to_i - 1).chr
284
+ block.add_right(Block.new(line[i..j]))
285
+ end
286
+ i = j + 1
287
+ next
288
+ end
289
+
290
+ # We are at the lowest level of embeddedness so we have to
291
+ # evaluate the embedded expression straight away.
292
+ expr = line[(level == 0 ? i + 2 : i + 3)..j - 1]
293
+ @stack.push([])
294
+ val = eval(expr, bind)
295
+ top = @stack.pop()
296
+ if(top.empty?)
297
+ val = Block.new(val.to_s)
298
+ else
299
+ val = Block.new("")
300
+ for b in top
301
+ val.add_bottom(b)
302
+ end
303
+ end
304
+ val.trim if line[i] == ?@
305
+ block.add_right(val)
306
+ i = j + 1
307
+ end
308
+ end
309
+
310
+ # Adds newline followed by one . line from the DNA file.
311
+ def Ribosome.dot(line, bind)
312
+ @stack.last << Block.new("")
313
+ add(line, bind)
314
+ end
315
+
316
+ # Adds newline followed by leading whitespace copied from the previous line
317
+ # and one line from the DNA file.
318
+ def Ribosome.align(line, bind)
319
+ if @stack.last.empty?
320
+ n = 0
321
+ else
322
+ n = @stack.last.last.last_offset
323
+ end
324
+ @stack.last << Block.new("")
325
+ add(" " * n, nil)
326
+ add(line, bind)
327
+ end
328
+
329
+ # Report an error that happened when executing RNA file.
330
+ def Ribosome.rethrow(e, rnafile, linemap)
331
+ i = 0
332
+ for i in 0..e.backtrace.size - 1
333
+ l = e.backtrace[i]
334
+ if l.start_with?(rnafile + ":")
335
+ stop = l.index(":", rnafile.size + 1) || l.size
336
+ num = l[rnafile.size + 1..stop - 1].to_i
337
+ for j in 0..linemap.size - 1
338
+ break if linemap[j][0] == nil || linemap[j][0] > num
339
+ end
340
+ j -= 1
341
+ num = num - linemap[j][0] + linemap[j][2]
342
+ l = "#{linemap[j][1]}:#{num}#{l[stop..-1]}"
343
+ e.backtrace[i] = l
344
+ end
345
+ end
346
+ raise e
347
+ end
348
+
349
+ end
350
+
351
+ # Escape function for @
352
+ def at()
353
+ return "@"
354
+ end
355
+
356
+ # Escape function for &
357
+ def amp()
358
+ return "&"
359
+ end
360
+
361
+ # Escape function for /
362
+ def slash()
363
+ return "/"
364
+ end
365
+
366
+ ################################################################################
367
+ # The code that belongs to the ribosome project ends at this point of the #
368
+ # RNA file and so does the associated license. What follows is the code #
369
+ # generated from the DNA file. #
370
+ ################################################################################
371
+
372
+ '
373
+
374
+ ################################################################################
375
+ # DNA helper functions. #
376
+ ################################################################################
377
+
378
+ def usage()
379
+ $stderr.write "usage: ribosome.rb [options] <dna-file> <args-passed-to-dna-script>\n"
380
+ exit(1)
381
+ end
382
+
383
+ # Print out the error and terminate the generation.
384
+ def dnaerror(s)
385
+ $stderr.write("#{$dnastack.last[1]}:#{$dnastack.last[2]} - #{s}\n")
386
+ exit(1)
387
+ end
388
+
389
+ # Generate new line(s) into the RNA file.
390
+ def rnawrite(s)
391
+ $linemap << [$rnaln, $dnastack.last[1], $dnastack.last[2]]
392
+ $rna.write(s)
393
+ $rnaln += s.count("\n")
394
+ end
395
+
396
+ ################################################################################
397
+ # Main function. #
398
+ ################################################################################
399
+
400
+ # Parse the command line arguments.
401
+ if(ARGV.size() < 1 || ARGV[0] == "-h" || ARGV[0] == "--help")
402
+ usage()
403
+ end
404
+ if ARGV[0] == "-v" || ARGV[0] == "--version"
405
+ puts "ribosome code generator, version 1.14"
406
+ exit(1)
407
+ end
408
+ if ARGV[0] == "--rna"
409
+ if(ARGV.size() < 2)
410
+ usage()
411
+ end
412
+ rnaopt = true
413
+ dnafile = ARGV[1]
414
+ else
415
+ rnaopt = false
416
+ dnafile = ARGV[0]
417
+ end
418
+
419
+ # Given that we can 'require' other DNA files, we need to keep a stack of
420
+ # open DNA files. We also keep the name of the file and the line number to
421
+ # be able to report errors. We'll also keep track of the directory the DNA file
422
+ # is in to be able to correctly expand relative paths in /!include commands.
423
+ $dnastack = [[nil, "ribosome.rb", PROLOGUE_LINE + 1, Dir.pwd]]
424
+
425
+ if rnaopt
426
+ $rna = $stdout
427
+ else
428
+ # Create the RNA file.
429
+ if(dnafile[-4..-1] == ".dna")
430
+ $rnafile = dnafile[0..-5] + ".rna"
431
+ else
432
+ $rnafile = dnafile + ".rna"
433
+ end
434
+ $rna = File.open($rnafile, "w")
435
+ end
436
+ $rnaln = 1
437
+ $linemap = []
438
+
439
+ # Generate RNA prologue code.
440
+ rnawrite(PROLOGUE)
441
+ if (!rnaopt)
442
+ rnawrite("begin\n\n")
443
+ end
444
+
445
+ # Process the DNA file.
446
+ dirname = File.expand_path(File.dirname(dnafile))
447
+ $dnastack.push [File.open(dnafile, "r"), dnafile, 0, dirname]
448
+ loop do
449
+
450
+ # Get next line. Unwind the include stack as necessary.
451
+ line = nil
452
+ loop do
453
+ line = $dnastack.last[0].gets()
454
+ break if line != nil
455
+ $dnastack.pop[0].close()
456
+ break if $dnastack.size == 1
457
+ end
458
+ break if line == nil
459
+
460
+ # We are counting lines so that we can report line numbers in errors.
461
+ $dnastack.last[2] += 1
462
+
463
+ # All Ruby lines are copied to the RNA file verbatim.
464
+ if line.size == 0 || line[0] != ?\.
465
+ rnawrite(line)
466
+ next
467
+ end
468
+
469
+ # Removes dot from the beginning of the line and
470
+ # trailing $ sign, if present.
471
+ line = line[1..-2]
472
+ line = line[0..-2] if line[-1] == ?$
473
+
474
+ # Make sure there are no tabs in the line.
475
+ if(line.index(?\t) != nil)
476
+ dnaerror("tab found in the line, replace it by space")
477
+ end
478
+
479
+ # Find first two non-whitespace which can possibly form a command.
480
+ firsttwo = line.lstrip[0..1]
481
+
482
+ # /+ means that the line is appended to the previous line.
483
+ if(firsttwo == "/+")
484
+ rnawrite("Ribosome.add(#{line.lstrip[2..-1].inspect()}, binding)\n")
485
+ next
486
+ end
487
+
488
+ # /= means that the line is aligned with the previous line.
489
+ if(firsttwo == "/=")
490
+ rnawrite("Ribosome.align(#{line.lstrip[2..-1].inspect()}, binding)\n")
491
+ next
492
+ end
493
+
494
+ # /! means that a command follows.
495
+ if(firsttwo == "/!")
496
+ line = line.lstrip[2..-1]
497
+ match = /^[0-9A-Za-z_]+/.match(line)
498
+ if(match == nil)
499
+ dnaerror("/! should be followed by an identifier")
500
+ end
501
+ command = match[0]
502
+
503
+ # A subset of commands is simply translated to corresponding
504
+ # commands in the RNA file.
505
+ if (["output", "append", "stdout", "tabsize"].include?(command))
506
+ rnawrite("Ribosome.#{line}\n")
507
+ next
508
+ end
509
+
510
+ # The argument string will be added at the end of each interation of
511
+ # the following loop, except for the last one.
512
+ if(command == "separate")
513
+ def identity(x) x end
514
+ separator = eval("identity#{line[8..-1]}")
515
+ cname = "____separate_#{$rnaln}____"
516
+ rnawrite("#{cname} = true\n")
517
+ line = $dnastack.last[0].gets()
518
+ $dnastack.last[2] += 1
519
+ if(line == nil || line[0] == ?. ||
520
+ (!line.index('while') && !line.index('until') &&
521
+ !line.index('for') && !line.index('each') &&
522
+ !line.index('upto') && line.index('downto') &&
523
+ !line.index('times') && line.index('loop')))
524
+ dnaerror("'separate' command must be followed by a loop")
525
+ end
526
+ rnawrite(line)
527
+ rnawrite("if(#{cname})\n")
528
+ rnawrite(" #{cname} = false\n")
529
+ rnawrite("else\n")
530
+ rnawrite(" Ribosome.add(#{separator.inspect()}, binding)\n")
531
+ rnawrite("end\n")
532
+ next
533
+ end
534
+
535
+ # Open the file and put it on the top of the DNA stack. Relative paths
536
+ # are expanded using the directory the parent DNA file resides in as
537
+ # a starting point.
538
+ if(command == "include")
539
+ def identity(x) x end
540
+ filename = eval("identity#{line[7..-1]}")
541
+ filename = File.expand_path(filename, $dnastack.last[3])
542
+ dirname = File.dirname(filename)
543
+ $dnastack.push([File.open(filename, "r"), filename, 0, dirname])
544
+ next
545
+ end
546
+
547
+ dnaerror("unknown command '#{command}'")
548
+ end
549
+
550
+ # There's no command in the line. Process it in the standard way.
551
+ rnawrite("Ribosome.dot(#{line.inspect()}, binding)\n")
552
+
553
+ end
554
+
555
+ # Generate RNA epilogue code.
556
+ $dnastack = [[nil, "ribosome", __LINE__ + 1]]
557
+ $rna.write("\n")
558
+ if !rnaopt
559
+ $rna.write("rescue Exception =>e\n")
560
+ $rna.write(" LINEMAP = [\n");
561
+ last = nil
562
+ for le in $linemap
563
+ if last == nil || le[1] != last[1] || le[0] - last [0] != le[2] - last[2]
564
+ $rna.write(" [#{le[0]}, #{le[1].inspect}, #{le[2]}],\n")
565
+ last = le
566
+ end
567
+ end
568
+ $rna.write(" [nil]\n")
569
+ $rna.write(" ]\n");
570
+ $rna.write(" Ribosome.rethrow(e, " + $rnafile.inspect + ", LINEMAP)\n")
571
+ $rna.write("end\n")
572
+ $rna.write("\n")
573
+ end
574
+ $rna.write("Ribosome.close()\n")
575
+ $rna.write("\n")
576
+
577
+ # Flush the RNA file.
578
+ $rna.close()
579
+
580
+ if !rnaopt
581
+
582
+ # Execute the RNA file. Pass it any arguments not used by ribosome.
583
+ system("ruby #{$rnafile} " + ARGV[1..-1].join(' '))
584
+
585
+ # Delete the RNA file.
586
+ File.delete($rnafile)
587
+
588
+ end
589
+