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.
- data/bin/ast.rb +47 -0
- data/bin/voodooc +146 -0
- data/lib/voodoo.rb +74 -0
- data/lib/voodoo/code_generator.rb +74 -0
- data/lib/voodoo/compiler.rb +45 -0
- data/lib/voodoo/config.rb +43 -0
- data/lib/voodoo/generators/amd64_elf_generator.rb +28 -0
- data/lib/voodoo/generators/amd64_nasm_generator.rb +288 -0
- data/lib/voodoo/generators/command_postprocessor.rb +30 -0
- data/lib/voodoo/generators/common_code_generator.rb +238 -0
- data/lib/voodoo/generators/gas_generator.rb +91 -0
- data/lib/voodoo/generators/generator_api1.rb +95 -0
- data/lib/voodoo/generators/i386_elf_generator.rb +62 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +177 -0
- data/lib/voodoo/generators/mips_gas_generator.rb +148 -0
- data/lib/voodoo/generators/nasm_elf_generator.rb +55 -0
- data/lib/voodoo/generators/nasm_generator.rb +679 -0
- data/lib/voodoo/parser.rb +283 -0
- metadata +82 -0
@@ -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
|
+
|