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,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