require_relative 'str_scanner' require_relative 'nodes' class NumEquivAddrNode < NumLiteralNode end class LabelEquivAddrNode < LabelRefNode end class ToplevelNode < Node attr_accessor :children end class DirectiveNode < Node attr_accessor :name, :value end class LabelNode < Node attr_accessor :name end class MathNode < Node attr_accessor :left, :right, :op alias_method :argument, :left alias_method :argument=, :left= end 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)) RegisterNode.new(s) { |n| n.name = m } end end def parse_register_list(s) if (m = s.scan(/\{/)) node = RegisterListNode.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] == '=' ? NumEquivAddrNode : NumLiteralNode).new(s) { |n| n.value = Integer(m[1]) } end end def parse_label_ref(s) if (m = s.scan(/(=?)(\/*\w+)/)) (m[0] == '=' ? LabelEquivAddrNode : LabelRefNode).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(/\]/)) ReferenceNode.new(s) do |n| n.argument = arg end end end end end end if (__FILE__ == $0) p Asm::Parser.parse ARGV[0] end