voodoo 0.5.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.
@@ -0,0 +1,283 @@
1
+ module Voodoo
2
+ # Voodoo parser.
3
+ # The parser reads Voodoo[http://inglorion.net/documents/designs/voodoo/]
4
+ # source code and turns it into Ruby[http://www.ruby-lang.org/] objects.
5
+ #
6
+ # The public interface to Parser consists of the methods #new and
7
+ # #parse_top_level
8
+ #
9
+ # Example usage:
10
+ #
11
+ # require 'voodoo/parser'
12
+ #
13
+ # File.open('example.voo') do |infile|
14
+ # parser = Voodoo::Parser.new infile
15
+ #
16
+ # while (element = parser.parse_top_level)
17
+ # puts element.inspect
18
+ # end
19
+ # end
20
+ class Parser
21
+ # Creates a parser using the specified object as input.
22
+ # The input object must support a method +getc+, which must
23
+ # return the next character of the input, or +nil+ to indicate
24
+ # the end of the input has been reached.
25
+ def initialize input
26
+ @input = input
27
+ @line = 1
28
+ @char = 0
29
+ @lookahead = nil
30
+ end
31
+
32
+ # Consumes the current lookahead character
33
+ def consume
34
+ old = @lookahead
35
+ if old == 10
36
+ @line = @line.succ
37
+ @char = 0
38
+ end
39
+ @lookahead = @input.getc
40
+ @lookahead = :eof if @lookahead == nil
41
+ @char = @char.succ unless @lookahead == :eof
42
+ old
43
+ end
44
+
45
+ # Returns the current lookahead character,
46
+ # or +nil+ when the end of the input has been reached.
47
+ def lookahead
48
+ if @lookahead == nil
49
+ @lookahead = @input.getc
50
+ @char = @char.succ
51
+ end
52
+ case @lookahead
53
+ when :eof
54
+ :eof
55
+ else
56
+ @lookahead.chr
57
+ end
58
+ end
59
+
60
+ # Parses a top-level element.
61
+ # Returns an array containing the parts of the element.
62
+ # Eeach element of the array is a Symbol, a String, or an
63
+ # Integer.
64
+ #
65
+ # For a label, returns:
66
+ # [:label, label_name]
67
+ #
68
+ # For a function definition, returns:
69
+ # [:function, [formala, formalb, ...], [statementa, statementb, ...]]
70
+ #
71
+ # For a conditional, returns:
72
+ # [condition, expression, [truea, trueb, ...], [falsea, falseb, ...]]
73
+ #
74
+ def parse_top_level
75
+ words = []
76
+ word = ""
77
+
78
+ while true
79
+ case lookahead
80
+ when :eof
81
+ # End of input
82
+ break
83
+ when "\n"
84
+ # Newline
85
+ consume
86
+ # Exit the loop, but only if the line wasn't empty
87
+ break unless words.empty?
88
+ when "#"
89
+ # Skip comment
90
+ while lookahead != :eof && lookahead != "\n"
91
+ word << lookahead
92
+ consume
93
+ end
94
+ when /\d|-/
95
+ # Digit; parse number
96
+ words << parse_number
97
+ when /\w|\\/
98
+ # Letter, underscore, or backslash; parse symbol
99
+ # Note: \w matches digites, too, so keep this case after \d
100
+ words << parse_symbol
101
+ if words.length == 1 && is_label?(words[-1])
102
+ # We have a label; return it
103
+ return [:label, words[-1].to_s[0..-2].to_sym]
104
+ end
105
+ when "\""
106
+ # Double quote; parse string
107
+ words << parse_string
108
+ when /\s/
109
+ # Skip whitespace
110
+ consume
111
+ else
112
+ raise "Invalid character (#{@lookahead}) at #{@line}:#{@char}"
113
+ end
114
+ end
115
+
116
+ # We have a line of input. Conditionals and function declarations
117
+ # must be handled specially, because they consist of more than one
118
+ # line.
119
+ if words.empty?
120
+ # Nothing to parse; return nil
121
+ nil
122
+ elsif words[0] == :function
123
+ # Function declaration. Parse function body
124
+ body = parse_body :function
125
+ [:function, words[1..-1], body]
126
+ elsif is_conditional?(words[0])
127
+ # Conditional. Parse true part
128
+ true_body = parse_body :conditional
129
+ # Parse false part, if present
130
+ if true_body[-1] == :else
131
+ true_body = true_body[0..-2]
132
+ false_body = parse_body :conditional
133
+ else
134
+ false_body = []
135
+ end
136
+ [words[0], words[1..-1], true_body, false_body]
137
+ else
138
+ # Statement or data declaration; simply return it
139
+ words
140
+ end
141
+ end
142
+
143
+ # Parses a body for a function or a conditional
144
+ def parse_body kind
145
+ body = []
146
+ case kind
147
+ when :function
148
+ kind_text = 'function definition'
149
+ else
150
+ kind_text = kind.to_s
151
+ end
152
+ while true
153
+ statement = parse_top_level
154
+ if statement == nil
155
+ raise "End of input while inside #{kind_text}"
156
+ elsif statement[0] == :end
157
+ # Done parsing body
158
+ break
159
+ elsif kind == :conditional && statement[0] == :else
160
+ # Done parsing body, but there is another one coming up
161
+ body << :else
162
+ break
163
+ else
164
+ # Parsed a statement. Add it to body.
165
+ body << statement
166
+ end
167
+ end
168
+ body
169
+ end
170
+
171
+ # Parses an escape sequence.
172
+ # This method should be called while the lookahead is the escape
173
+ # character (backslash). It decodes the escape sequence and returns
174
+ # the result as a string.
175
+ def parse_escape
176
+ result = nil
177
+ consume
178
+ case lookahead
179
+ when :eof
180
+ raise "Unexpected end of input in escape sequence"
181
+ when "\\", "\"", " "
182
+ result = lookahead
183
+ consume
184
+ when "n"
185
+ # \n is newline
186
+ consume
187
+ result = "\n"
188
+ when "r"
189
+ # \r is carriage return
190
+ consume
191
+ result = "\r"
192
+ when "x"
193
+ # \xXX is byte with hex value XX
194
+ code = @input.read 2
195
+ @char = @char + 2
196
+ consume
197
+ result = [code].pack('H2')
198
+ when "\n"
199
+ # \<newline> is line continuation character
200
+ consume
201
+ # Skip indentation of next line
202
+ while lookahead =~ /\s/
203
+ consume
204
+ end
205
+ result = ''
206
+ else
207
+ # Default to just passing on next character
208
+ result = lookahead
209
+ consume
210
+ end
211
+ result
212
+ end
213
+
214
+ # Parses a number.
215
+ # This method should be called while the lookahead is the first
216
+ # character of the number.
217
+ def parse_number
218
+ text = lookahead
219
+ consume
220
+ while lookahead =~ /\d/
221
+ text << lookahead
222
+ consume
223
+ end
224
+ text.to_i
225
+ end
226
+
227
+ # Parses a string.
228
+ # This method should be called while the lookahead is the opening
229
+ # double quote.
230
+ def parse_string
231
+ consume
232
+ result = ''
233
+ while true
234
+ case lookahead
235
+ when "\""
236
+ consume
237
+ break
238
+ when "\\"
239
+ result << parse_escape
240
+ else
241
+ result << lookahead
242
+ consume
243
+ end
244
+ end
245
+ result
246
+ end
247
+
248
+ # Parses a symbol.
249
+ # This method should be called while the lookahead is the first
250
+ # character of the symbol name.
251
+ def parse_symbol
252
+ name = ''
253
+ while true
254
+ case lookahead
255
+ when "\\"
256
+ name << parse_escape
257
+ when /\w|-/
258
+ name << lookahead
259
+ consume
260
+ when ':'
261
+ # Colon parsed as last character of the symbol name
262
+ name << lookahead
263
+ consume
264
+ break
265
+ else
266
+ break
267
+ end
268
+ end
269
+ name.to_sym
270
+ end
271
+
272
+ # Tests if a symbol is a label
273
+ def is_label? symbol
274
+ symbol.to_s[-1] == ?:
275
+ end
276
+
277
+ # Tests if a symbol is a conditional starter
278
+ def is_conditional? symbol
279
+ [:ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne].member? symbol
280
+ end
281
+ end
282
+
283
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: voodoo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Robbert Haarman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-10 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: |
17
+ The Voodoo compiler is an implementation of the Voodoo programming
18
+ language, a simple low-level programming language.
19
+
20
+ The compiler can be used both as a stand-alone program to
21
+ compile Voodoo source code and as a Ruby module to generate code on
22
+ the fly. Multiple target platforms are supported.
23
+
24
+ email: voodoo@inglorion.net
25
+ executables:
26
+ - voodooc
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - bin/ast.rb
33
+ - bin/voodooc
34
+ - lib/voodoo.rb
35
+ - lib/voodoo/parser.rb
36
+ - lib/voodoo/code_generator.rb
37
+ - lib/voodoo/compiler.rb
38
+ - lib/voodoo/config.rb
39
+ - lib/voodoo/generators/amd64_elf_generator.rb
40
+ - lib/voodoo/generators/common_code_generator.rb
41
+ - lib/voodoo/generators/nasm_generator.rb
42
+ - lib/voodoo/generators/amd64_nasm_generator.rb
43
+ - lib/voodoo/generators/mips_gas_generator.rb
44
+ - lib/voodoo/generators/generator_api1.rb
45
+ - lib/voodoo/generators/i386_nasm_generator.rb
46
+ - lib/voodoo/generators/command_postprocessor.rb
47
+ - lib/voodoo/generators/gas_generator.rb
48
+ - lib/voodoo/generators/nasm_elf_generator.rb
49
+ - lib/voodoo/generators/i386_elf_generator.rb
50
+ has_rdoc: true
51
+ homepage: http://inglorion.net/software/voodoo/
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - -m
57
+ - Voodoo
58
+ - -t
59
+ - Voodoo Code Generator API Documentation
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: voodoo
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Compiler for the Voodoo programming language
81
+ test_files: []
82
+