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
data/bin/ast.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ast
|
2
|
+
# A program consists of zero or more elements,
|
3
|
+
# each one of which can be a label, an action,
|
4
|
+
# a function definition, or a comment
|
5
|
+
class Program
|
6
|
+
def initialize elements
|
7
|
+
@elements = elements
|
8
|
+
end
|
9
|
+
attr_accessor :elements
|
10
|
+
end
|
11
|
+
|
12
|
+
# A comment contains text
|
13
|
+
class Comment
|
14
|
+
def initialize text
|
15
|
+
@text = text
|
16
|
+
end
|
17
|
+
attr_accessor :text
|
18
|
+
end
|
19
|
+
|
20
|
+
# A label contains text
|
21
|
+
class Label
|
22
|
+
def initialize text
|
23
|
+
@text = text
|
24
|
+
end
|
25
|
+
attr_accessor :text
|
26
|
+
end
|
27
|
+
|
28
|
+
# A function definition has an argument list and contains
|
29
|
+
# a number of actions and/or comments
|
30
|
+
class Function
|
31
|
+
def initialize args, actions
|
32
|
+
@args = args
|
33
|
+
@statements = actions
|
34
|
+
end
|
35
|
+
attr_accessor :args, :actions
|
36
|
+
end
|
37
|
+
|
38
|
+
# An action contains an operation and zero or more
|
39
|
+
# arguments
|
40
|
+
class Action
|
41
|
+
def initialize op, args
|
42
|
+
@op = op
|
43
|
+
@args = args
|
44
|
+
end
|
45
|
+
attr_accessor :op, :args
|
46
|
+
end
|
47
|
+
end
|
data/bin/voodooc
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'getoptlong'
|
4
|
+
|
5
|
+
require 'voodoo'
|
6
|
+
|
7
|
+
usage="USAGE: voodooc [options] <input file>"
|
8
|
+
help=<<EOT
|
9
|
+
Input file denotes the file to compile. The special name '-' causes
|
10
|
+
voodooc to read from standard input. In that case, the -o option is
|
11
|
+
mandatory.
|
12
|
+
|
13
|
+
Valid options are:
|
14
|
+
|
15
|
+
-a <architecture>
|
16
|
+
--arch <architecture>
|
17
|
+
--architecture <architecture>
|
18
|
+
Select target architecture. Use -a help to get a list of
|
19
|
+
supported architectures.
|
20
|
+
|
21
|
+
-f <format>
|
22
|
+
--format <format>
|
23
|
+
--output-format <format>
|
24
|
+
Select output format. Use -a <architecture> -f help to get a list of
|
25
|
+
supported output formats for architecture.
|
26
|
+
|
27
|
+
-o <output file>
|
28
|
+
--output <output file>
|
29
|
+
--output-file <output file>
|
30
|
+
Set output file name. The special name '-' causes voodooc to write
|
31
|
+
to standard output.
|
32
|
+
EOT
|
33
|
+
|
34
|
+
input_file = nil
|
35
|
+
output_file = nil
|
36
|
+
architecture = Voodoo::Config.default_architecture
|
37
|
+
output_format = Voodoo::Config.default_format
|
38
|
+
|
39
|
+
#
|
40
|
+
# Process command line
|
41
|
+
#
|
42
|
+
|
43
|
+
opts = GetoptLong.new(
|
44
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT ],
|
45
|
+
['--arch', '--architecture', '-a', GetoptLong::REQUIRED_ARGUMENT ],
|
46
|
+
['--output-file', '--output', '-o', GetoptLong::REQUIRED_ARGUMENT ],
|
47
|
+
['--output-format', '--format', '-f', GetoptLong::REQUIRED_ARGUMENT ]
|
48
|
+
)
|
49
|
+
|
50
|
+
# Process options
|
51
|
+
opts.each do |opt, arg|
|
52
|
+
case opt
|
53
|
+
when '--help'
|
54
|
+
puts usage
|
55
|
+
puts help
|
56
|
+
exit
|
57
|
+
when '--arch'
|
58
|
+
architecture = arg.to_sym
|
59
|
+
when '--output-file'
|
60
|
+
output_file = arg
|
61
|
+
when '--output-format'
|
62
|
+
output_format = arg.to_sym
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if architecture == :help
|
67
|
+
# List supported architectures
|
68
|
+
puts "Supported architectures:"
|
69
|
+
puts Voodoo::CodeGenerator.architectures
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
if output_format == :help
|
74
|
+
# List supported output formats
|
75
|
+
puts "Supported output formats for architecture #{architecture}:"
|
76
|
+
puts Voodoo::CodeGenerator.formats architecture
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get input file name
|
81
|
+
if ARGV.length == 1
|
82
|
+
input_file = ARGV[0]
|
83
|
+
else
|
84
|
+
if ARGV.length < 1
|
85
|
+
$stderr.puts "no input files"
|
86
|
+
else
|
87
|
+
$stderr.puts "Too many arguments"
|
88
|
+
end
|
89
|
+
$stderr.puts usage
|
90
|
+
exit 0x80
|
91
|
+
end
|
92
|
+
|
93
|
+
# Select code generator based on output format
|
94
|
+
begin
|
95
|
+
generator = Voodoo::CodeGenerator.get_generator :architecture => architecture,
|
96
|
+
:format => output_format
|
97
|
+
rescue NotImplementedError
|
98
|
+
if Voodoo::CodeGenerator.architecture_supported? architecture
|
99
|
+
$stderr.puts "Format #{output_format} is not supported for architecture #{architecture}"
|
100
|
+
$stderr.puts "Supported formats for #{architecture} are:"
|
101
|
+
$stderr.puts Voodoo::CodeGenerator.formats(architecture)
|
102
|
+
else
|
103
|
+
$stderr.puts "Architecture #{architecture} is not supported."
|
104
|
+
$stderr.puts "Supported architectures are:"
|
105
|
+
$stderr.puts Voodoo::CodeGenerator.architectures
|
106
|
+
end
|
107
|
+
exit 1
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
#
|
112
|
+
# Compile
|
113
|
+
#
|
114
|
+
|
115
|
+
if input_file == '-'
|
116
|
+
infile = $stdin
|
117
|
+
unless output_file
|
118
|
+
$stderr.puts "The -o option is mandatory when reading from standard input"
|
119
|
+
exit 0x80
|
120
|
+
end
|
121
|
+
else
|
122
|
+
infile = open input_file
|
123
|
+
# Set output_file if not already set
|
124
|
+
output_file = generator.output_file_name input_file unless output_file
|
125
|
+
end
|
126
|
+
|
127
|
+
if output_file == '-'
|
128
|
+
outfile = $stdout
|
129
|
+
else
|
130
|
+
outfile = open output_file, 'w'
|
131
|
+
end
|
132
|
+
|
133
|
+
begin
|
134
|
+
parser = Voodoo::Parser.new infile
|
135
|
+
compiler = Voodoo::Compiler.new parser, generator, outfile
|
136
|
+
|
137
|
+
compiler.compile
|
138
|
+
rescue
|
139
|
+
if output_file != '-'
|
140
|
+
File::unlink(output_file) if File::exists?(output_file)
|
141
|
+
end
|
142
|
+
raise
|
143
|
+
ensure
|
144
|
+
outfile.close
|
145
|
+
infile.close
|
146
|
+
end
|
data/lib/voodoo.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'voodoo/config'
|
2
|
+
require 'voodoo/code_generator'
|
3
|
+
require 'voodoo/compiler'
|
4
|
+
require 'voodoo/parser'
|
5
|
+
|
6
|
+
# = Voodoo - Code Generation for Multiple Target Platforms
|
7
|
+
#
|
8
|
+
# This module implements a compiler for the {Voodoo programming
|
9
|
+
# language}[http://inglorion.net/documents/designs/voodoo/], a simple
|
10
|
+
# programming language designed to be a thin abstraction of the CPU's
|
11
|
+
# native instruction set.
|
12
|
+
#
|
13
|
+
# The compiler consists of three parts:
|
14
|
+
#
|
15
|
+
# 1. The parser (Voodoo::Parser), which reads
|
16
|
+
# Voodoo[http://inglorion.net/documents/designs/voodoo/] source code and
|
17
|
+
# turns it into Ruby[http://www.ruby-lang.org/] objects.
|
18
|
+
#
|
19
|
+
# 2. The code generator (a class that implements the methods of
|
20
|
+
# Voodoo::CommonCodeGenerator), which provides methods that generate
|
21
|
+
# code for the target platform.
|
22
|
+
#
|
23
|
+
# 3. The compiler driver (Voodoo::Compiler), which reads from the parser
|
24
|
+
# and calls the appropriate methods of the code generator.
|
25
|
+
#
|
26
|
+
# Both the parser and the code generators can be used on their own. For
|
27
|
+
# example, you could use the code generator to generate native code for
|
28
|
+
# your own programming language without first creating a
|
29
|
+
# Voodoo[http://inglorion.net/documents/designs/voodoo/] program.
|
30
|
+
#
|
31
|
+
# Instead of instantiating a code generator directly, it is recommended
|
32
|
+
# that you use Voodoo::CodeGenerator.get_generator to obtain a suitable
|
33
|
+
# code generator for your target platform.
|
34
|
+
#
|
35
|
+
# A few examples to clarify the usage of the module:
|
36
|
+
#
|
37
|
+
# The following code compiles the source file <tt>test.voo</tt> to an ELF
|
38
|
+
# object file called <tt>test.o</tt> containing object code for i386:
|
39
|
+
#
|
40
|
+
# require 'voodoo'
|
41
|
+
#
|
42
|
+
# File.open('test.voo') do |infile|
|
43
|
+
# parser = Voodoo::Parser.new infile
|
44
|
+
# generator = Voodoo::CodeGenerator.get_generator :architecture => :i386,
|
45
|
+
# :format => :elf
|
46
|
+
# File.open('test.o', 'w') do |outfile|
|
47
|
+
# compiler = Voodoo::Compiler.new parser, generator, outfile
|
48
|
+
# compiler.compile
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The following code uses the code generator API to create an object file
|
53
|
+
# without the need to create a
|
54
|
+
# Voodoo[http://inglorion.net/documents/designs/voodoo/] program:
|
55
|
+
#
|
56
|
+
# require 'voodoo'
|
57
|
+
#
|
58
|
+
# generator = Voodoo::CodeGenerator.get_generator
|
59
|
+
#
|
60
|
+
# generator.add :functions, [:export, :fact], [:label, :fact]
|
61
|
+
# generator.add_function [:n],
|
62
|
+
# [:ifle, [:n, 1],
|
63
|
+
# # then
|
64
|
+
# [[:return, 1]],
|
65
|
+
# # else
|
66
|
+
# [[:let, :x, :sub, :n, 1],
|
67
|
+
# [:set, :x, :call, :fact, :x],
|
68
|
+
# [:return, :mul, :n, :x]]]
|
69
|
+
#
|
70
|
+
# File.open('fact.o', 'w') { |outfile| generator.write outfile }
|
71
|
+
#
|
72
|
+
|
73
|
+
module Voodoo
|
74
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'voodoo/config'
|
2
|
+
|
3
|
+
module Voodoo
|
4
|
+
# Module for selecting code generators.
|
5
|
+
#
|
6
|
+
# The code generation API is described in Voodoo::CommonCodeGenerator.
|
7
|
+
module CodeGenerator
|
8
|
+
# Hash using target architectures as keys.
|
9
|
+
# Each entry is a hash mapping output format to generator class.
|
10
|
+
@@generators = {}
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Register a code generator.
|
15
|
+
# Example:
|
16
|
+
# Voodoo::CodeGenerator.register_generator I386NasmGenerator,
|
17
|
+
# :architecture => :i386,
|
18
|
+
# :format => :nasm
|
19
|
+
def register_generator klass, params
|
20
|
+
if params.has_key?(:architecture) && params.has_key?(:format)
|
21
|
+
arch = params[:architecture].to_sym
|
22
|
+
format = params[:format].to_sym
|
23
|
+
format_hash = @@generators[arch] || {}
|
24
|
+
format_hash[format] = klass
|
25
|
+
@@generators[arch] = format_hash
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Need to specify :architecture and :format"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get a code generator for the specified parameters
|
32
|
+
def get_generator params = {}
|
33
|
+
params[:architecture] = Config.default_architecture \
|
34
|
+
unless params[:architecture]
|
35
|
+
params[:format] = Config.default_format unless params[:format]
|
36
|
+
arch = params[:architecture].to_sym
|
37
|
+
format = params[:format].to_sym
|
38
|
+
format_hash = @@generators[arch]
|
39
|
+
klass = format_hash ? format_hash[format] : nil
|
40
|
+
if klass
|
41
|
+
return klass.new params
|
42
|
+
else
|
43
|
+
raise NotImplementedError, "No code generator for architecture #{arch} and format #{format}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Tests if a given architecture is supported
|
48
|
+
def architecture_supported? arch
|
49
|
+
@@generators.has_key? arch.to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get an array of supported architectures
|
53
|
+
def architectures
|
54
|
+
@@generators.keys
|
55
|
+
end
|
56
|
+
|
57
|
+
# Tests if a given format is supported for a given architecture
|
58
|
+
def format_supported? arch, format
|
59
|
+
architecture_supported?(arch.to_sym) &&
|
60
|
+
@@generators[arch.to_sym].has_key?(format.to_sym)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get an array of supported formats for a given architecture
|
64
|
+
def formats architecture
|
65
|
+
@@generators[architecture.to_sym].keys
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Load generators
|
71
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'generators', '*.rb')).each do |file|
|
72
|
+
name = file.sub(/.*(voodoo\/generators\/.*)\.rb/, "\\1")
|
73
|
+
require name
|
74
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Voodoo
|
2
|
+
# Voodoo compiler driver.
|
3
|
+
# The compiler driver reads input from a parser (see
|
4
|
+
# Voodoo::Parser), feeds it to a code generator (see
|
5
|
+
# Voodoo::CommonCodeGenerator), and writes the generated code.
|
6
|
+
#
|
7
|
+
# An example of its usage can be found on the main page for the
|
8
|
+
# Voodoo module.
|
9
|
+
class Compiler
|
10
|
+
# Initialize a compiler.
|
11
|
+
#
|
12
|
+
# Parameters:
|
13
|
+
# [parser] the parser to be used (see Voodoo::Parser)
|
14
|
+
# [code_generator] the code generator to be used
|
15
|
+
# (see Voodoo::CommonCodeGenerator)
|
16
|
+
# [output] an IO object. The generated code will be written to it
|
17
|
+
def initialize parser, code_generator, output
|
18
|
+
@parser = parser
|
19
|
+
@generator = code_generator
|
20
|
+
@output = output
|
21
|
+
end
|
22
|
+
|
23
|
+
# Perform the compilation.
|
24
|
+
def compile
|
25
|
+
section = :code
|
26
|
+
while true
|
27
|
+
statement = @parser.parse_top_level
|
28
|
+
|
29
|
+
break if statement == nil
|
30
|
+
next if statement.empty?
|
31
|
+
|
32
|
+
case statement[0]
|
33
|
+
when :section
|
34
|
+
section = statement[1]
|
35
|
+
when :function
|
36
|
+
@generator.add section, [:function, statement[1]] + statement[2]
|
37
|
+
else
|
38
|
+
@generator.add section, statement
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@generator.write @output
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Voodoo
|
2
|
+
# Methods to get and set configuration parameters
|
3
|
+
module Config
|
4
|
+
# Class that holds configuration parameters
|
5
|
+
class Configuration
|
6
|
+
def initialize
|
7
|
+
@default_architecture = :amd64
|
8
|
+
@default_format = :elf
|
9
|
+
@nasm_command = "/usr/bin/nasm"
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :default_architecture,
|
13
|
+
:default_format,
|
14
|
+
:nasm_command
|
15
|
+
end
|
16
|
+
|
17
|
+
DEFAULT_CONFIGURATION = Configuration.new
|
18
|
+
|
19
|
+
module_function
|
20
|
+
|
21
|
+
def default_architecture
|
22
|
+
DEFAULT_CONFIGURATION.default_architecture
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_architecture= value
|
26
|
+
DEFAULT_CONFIGURATION.default_architecture = value
|
27
|
+
end
|
28
|
+
def default_format
|
29
|
+
DEFAULT_CONFIGURATION.default_format
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_format= value
|
33
|
+
DEFAULT_CONFIGURATION.default_format = value
|
34
|
+
end
|
35
|
+
def nasm_command
|
36
|
+
DEFAULT_CONFIGURATION.nasm_command
|
37
|
+
end
|
38
|
+
|
39
|
+
def nasm_command= value
|
40
|
+
DEFAULT_CONFIGURATION.nasm_command = value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'voodoo/generators/amd64_nasm_generator'
|
4
|
+
require 'voodoo/generators/nasm_elf_generator'
|
5
|
+
|
6
|
+
module Voodoo
|
7
|
+
# Generator that produces ELF objects for amd64
|
8
|
+
class AMD64ELFGenerator < DelegateClass(AMD64NasmGenerator)
|
9
|
+
def initialize params = {}
|
10
|
+
@nasmgenerator = AMD64NasmGenerator.new params
|
11
|
+
super(@nasmgenerator)
|
12
|
+
@elfgenerator = NasmELFGenerator.new @nasmgenerator, '-f elf64'
|
13
|
+
end
|
14
|
+
|
15
|
+
def output_file_name input_name
|
16
|
+
input_name.sub(/\.voo$/, '') + '.o'
|
17
|
+
end
|
18
|
+
|
19
|
+
def write io
|
20
|
+
@elfgenerator.write io
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Register class
|
25
|
+
Voodoo::CodeGenerator.register_generator AMD64ELFGenerator,
|
26
|
+
:architecture => :amd64,
|
27
|
+
:format => :elf
|
28
|
+
end
|