move some unused parse related classes out of the way
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
require "asm/parser"
|
||||
require "asm/nodes"
|
||||
|
||||
module Asm
|
||||
module Arm
|
||||
|
@ -1,7 +1,7 @@
|
||||
require 'asm/arm/arm_assembler'
|
||||
require 'asm/arm/instruction'
|
||||
require 'asm/label_object'
|
||||
require 'asm/parser'
|
||||
require 'asm/nodes'
|
||||
require 'stream_reader'
|
||||
require 'stringio'
|
||||
|
||||
|
@ -1,149 +0,0 @@
|
||||
require_relative 'parser'
|
||||
require_relative 'assembler'
|
||||
require_relative 'objectwriter'
|
||||
require 'optparse'
|
||||
require 'ostruct'
|
||||
|
||||
module Asm
|
||||
class CommandLine
|
||||
def initialize
|
||||
options = OpenStruct.new
|
||||
options.output_file = "a.out"
|
||||
options.target = :arm
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: as [options] <input file>"
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Options:"
|
||||
|
||||
opts.on("-t", "--target TARGET",
|
||||
"Specify target architecture (arm [default], ttk91)") { |o|
|
||||
options.target = o.to_sym
|
||||
if (not [:arm, :ttk91].include?(options.target))
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
}
|
||||
|
||||
opts.on("-o", "--output FILENAME",
|
||||
"Specify output filename for object file") { |o|
|
||||
options.output_file = o
|
||||
}
|
||||
|
||||
opts.on("-s", "--show-ast",
|
||||
"Show parse tree") { |o|
|
||||
options.show_ast = true
|
||||
}
|
||||
|
||||
opts.on_tail("-h", "--help", "Show this message") {
|
||||
puts opts
|
||||
exit
|
||||
}
|
||||
end
|
||||
|
||||
opts.parse!(ARGV)
|
||||
|
||||
options.input_file = ARGV.shift
|
||||
if (not options.input_file)
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
||||
@options = options
|
||||
end
|
||||
attr_reader :options
|
||||
|
||||
def run
|
||||
begin
|
||||
if (options.input_file == '-')
|
||||
code = $stdin.read
|
||||
else
|
||||
code = File.read(options.input_file)
|
||||
end
|
||||
rescue => err
|
||||
puts 'as: could not read input file: ' + err.message
|
||||
exit 2
|
||||
end
|
||||
|
||||
begin
|
||||
ast = Asm::Parser.parse(code)
|
||||
rescue Asm::ParseError => err
|
||||
puts 'as: parse error on line %d, column %d' % [err.line+1, err.column+1]
|
||||
line = code.split("\n")[err.line]
|
||||
puts line.gsub(/\s/, ' ')
|
||||
puts ' ' * (err.column-1) + '^'
|
||||
puts ' ' + err.message
|
||||
exit 3
|
||||
end
|
||||
|
||||
if (options.show_ast)
|
||||
require 'pp'
|
||||
pp ast
|
||||
exit 0
|
||||
end
|
||||
|
||||
case options.target
|
||||
when :arm
|
||||
require_relative 'arm_assembler.rb'
|
||||
as_module = Asm::Arm
|
||||
as_target = Elf::Constants::TARGET_ARM
|
||||
when :ttk91
|
||||
require_relative 'ttk91_assembler.rb'
|
||||
as_module = Asm::TTK91
|
||||
as_target = Elf::Constants::TARGET_TTK91
|
||||
end
|
||||
|
||||
asm = Asm::AstAssembler.new(as_module)
|
||||
begin
|
||||
asm.load_ast ast
|
||||
data = StringIO.new
|
||||
asm.assemble(data)
|
||||
symbols = asm.symbols
|
||||
rescue Asm::AssemblyError => err
|
||||
if (err.node)
|
||||
puts 'as: assembly error on line %d, column %d' % [
|
||||
err.node.line+1, err.node.column+1]
|
||||
line = code.split("\n")[err.node.line]
|
||||
puts line.gsub(/\s/, ' ')
|
||||
puts ' ' * (err.node.column-1) + '^'
|
||||
puts ' ' + err.message
|
||||
else
|
||||
puts 'as: ' + err.message
|
||||
end
|
||||
exit 4
|
||||
end
|
||||
|
||||
writer = Asm::ObjectWriter.new(as_target)
|
||||
writer.set_text data.string
|
||||
|
||||
reloc_name_ref = {}
|
||||
|
||||
symbols.each { |symbol|
|
||||
label = symbol[:label]
|
||||
if (label.extern?)
|
||||
reloc_name_ref[label] = symbol[:name]
|
||||
writer.add_reloc_symbol symbol[:name]
|
||||
else
|
||||
writer.add_symbol symbol[:name], symbol[:label].address, symbol[:linkage]
|
||||
end
|
||||
}
|
||||
|
||||
asm.relocations.each { |reloc|
|
||||
writer.add_reloc reloc.position, reloc_name_ref[reloc.label], reloc.type
|
||||
}
|
||||
|
||||
begin
|
||||
writer.save(options.output_file)
|
||||
rescue => err
|
||||
puts 'as: cannot save output file: ' + err.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if (__FILE__ == $0)
|
||||
Asm::CommandLine.new.run
|
||||
end
|
@ -1,204 +0,0 @@
|
||||
require_relative 'str_scanner'
|
||||
require_relative 'nodes'
|
||||
|
||||
module Asm
|
||||
class Parser
|
||||
def initialize(str)
|
||||
scanner = Asm::Scanner.new(str)
|
||||
|
||||
@ast = parse_toplevel scanner
|
||||
end
|
||||
attr_reader :ast
|
||||
|
||||
def self.parse(str)
|
||||
new(str).ast
|
||||
end
|
||||
|
||||
def parse_toplevel(s)
|
||||
node = ToplevelNode.new(s)
|
||||
node.children = []
|
||||
while (not s.eos?)
|
||||
node.children << parse(s)
|
||||
end
|
||||
node
|
||||
end
|
||||
|
||||
def parse(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(comment directive label instruction).each { |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
}
|
||||
raise Asm::ParseError.new('could not parse element', s) unless node
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_comment(s)
|
||||
if (s.scan(/;.*?$/))
|
||||
CommentNode.new(s)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_directive(s)
|
||||
if (m = s.scan(/\.(\w+)(?:(?!$)\s+(.+)\s*?$)?/))
|
||||
DirectiveNode.new(s) { |n|
|
||||
n.name = m[0]
|
||||
n.value = m[1]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_label(s)
|
||||
if (m = s.scan(/(\/*\w+):/))
|
||||
LabelNode.new(s) { |n|
|
||||
n.name = m[0]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_instruction(s)
|
||||
if (m = s.scan(/(\w+)/))
|
||||
node = InstructionNode.new(s) { |n|
|
||||
n.opcode = m[0]
|
||||
n.args = []
|
||||
}
|
||||
if (not s.scan(/\s*($|;)/))
|
||||
loop {
|
||||
arg = parse_arg(s)
|
||||
node.args << arg
|
||||
break if not s.scan(/\s*,/)
|
||||
}
|
||||
end
|
||||
node
|
||||
end
|
||||
end
|
||||
|
||||
def parse_arg(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(reference register register_list num_literal label_ref).each { |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
}
|
||||
raise Asm::ParseError.new('expected argument but none found', s) unless node
|
||||
|
||||
if (node2 = parse_arg_op(s))
|
||||
node2.argument = node
|
||||
node = node2
|
||||
end
|
||||
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_arg_op(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(shift math).each do |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
end
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_shift(s)
|
||||
if (m = s.scan(/(lsl|lsr|asr|ror|rrx)\s+/i))
|
||||
op = m[0].downcase
|
||||
if (op == 'rrx' or arg = parse_arg(s))
|
||||
ShiftNode.new(s) { |n|
|
||||
n.type = m[0].downcase
|
||||
n.value = arg
|
||||
}
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_math(s)
|
||||
if (m = s.scan_str(/[\+\-]/))
|
||||
if (arg1 = parse_arg(s))
|
||||
MathNode.new(s) do |n|
|
||||
n.right = arg1
|
||||
n.op = m
|
||||
end
|
||||
else
|
||||
raise Asm::ParseError.new('expected right side for arithmetic op', s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
REGISTER_REGEXP = Regexp.union(*%w(r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12
|
||||
r13 r14 r15 a1 a2 a3 a4 v1 v2 v3 v4 v5 v6
|
||||
rfp sl fp ip sp lr pc
|
||||
))
|
||||
def parse_register(s)
|
||||
if (m = s.scan_str(REGISTER_REGEXP))
|
||||
RegisterArgNode.new(s) { |n|
|
||||
n.name = m
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_register_list(s)
|
||||
if (m = s.scan(/\{/))
|
||||
node = RegisterListArgNode.new(s) do |n|
|
||||
n.registers = []
|
||||
end
|
||||
loop do
|
||||
s.scan /\s*/
|
||||
reg = parse_register(s)
|
||||
if (not reg)
|
||||
return nil
|
||||
end
|
||||
s.scan /\s*,?/
|
||||
|
||||
node.registers << reg
|
||||
|
||||
if (s.scan(/\}/))
|
||||
break
|
||||
end
|
||||
end
|
||||
node
|
||||
end
|
||||
end
|
||||
|
||||
def parse_num_literal(s)
|
||||
if (m = s.scan(/(=?)#(-?(?:0x)?[0-9A-Fa-f]+)/))
|
||||
(m[0] == '=' ? NumEquivAddrArgNode : NumLiteralArgNode).new(s) { |n|
|
||||
n.value = Integer(m[1])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_label_ref(s)
|
||||
if (m = s.scan(/(=?)(\/*\w+)/))
|
||||
(m[0] == '=' ? LabelEquivAddrArgNode : LabelRefArgNode).new(s) { |n|
|
||||
n.label = m[1]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_reference(s)
|
||||
if (m = s.scan(/\[/))
|
||||
arg = parse_arg(s)
|
||||
if (arg and s.scan(/\]/))
|
||||
ReferenceArgNode.new(s) do |n|
|
||||
n.argument = arg
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if (__FILE__ == $0)
|
||||
p Asm::Parser.parse ARGV[0]
|
||||
end
|
@ -1,65 +0,0 @@
|
||||
module AS; end
|
||||
|
||||
if (not defined? RUBY_ENGINE or not RUBY_ENGINE == 'rbx')
|
||||
class Regexp
|
||||
def match_start(str, idx)
|
||||
Regexp.compile('\A(?:'+source+')').match(str[idx..-1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Asm::Scanner
|
||||
def initialize(str)
|
||||
@string = str
|
||||
@pos = 0
|
||||
@line = 0
|
||||
@column = 0
|
||||
end
|
||||
attr_accessor :string, :pos, :line, :column, :prev_line, :prev_column
|
||||
|
||||
def rest
|
||||
string[pos..-1]
|
||||
end
|
||||
|
||||
def advance_str(str)
|
||||
self.prev_line = line
|
||||
self.prev_column = column
|
||||
self.pos += str.length
|
||||
self.line += str.count("\n")
|
||||
if (str.include?("\n"))
|
||||
self.column = str.length - str.rindex("\n")
|
||||
else
|
||||
self.column += str.length
|
||||
end
|
||||
end
|
||||
|
||||
def scan(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
advance_str match.to_s
|
||||
match.captures
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def scan_str(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
advance_str match.to_s
|
||||
match.to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def lookahead(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def eos?
|
||||
pos == string.length
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user