voodoo 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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