Class: Rips::Assembler

Inherits:
Object
  • Object
show all
Defined in:
lib/rips/assembler.rb

Instance Method Summary collapse

Constructor Details

#initialize(debug) ⇒ Assembler

@debug: switch to show trace in console @input: array with assembly instructions @output: array with coded instructions @cmd: line split on tokens (name + arguments) @instruction: instruction instance @instructions: array with line for each instruction @labels: name and number line of label @line: number of file’s line



14
15
16
17
18
19
20
21
22
23
# File 'lib/rips/assembler.rb', line 14

def initialize (debug)
  @debug = debug
  @input = []
  @output = []
  @cmd = {}
  @instruction
  @instructions = [] 
  @labels = {}
  @line = 1  
end

Instance Method Details

#<<(value) ⇒ Object

Stores each new line of file



54
55
56
# File 'lib/rips/assembler.rb', line 54

def << (value)
  @input << value.strip
end

#argument_sizeObject

Check number of arguments given with expected by instruction



165
166
167
168
169
170
171
172
173
# File 'lib/rips/assembler.rb', line 165

def argument_size
  if @cmd[:arguments].size != @instruction.args_number
    Error::message( 5, 
                    @line, 
                    @cmd[:name], 
                    @cmd[:arguments].size, 
                    @instruction.args_number )
  end
end

#argument_syntaxObject

Check if arguments are the same variable type of instruction



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/rips/assembler.rb', line 176

def argument_syntax
  @instruction.variables.each_with_index do |var,i|
    if var.syntax? @cmd[:arguments][i]
      @cmd[:arguments][i] = var.to_i(@cmd[:arguments][i])
    else
      Error::message( 6,
                      @line,
                      var.error(@cmd[:arguments][i]) )     
    end
  end
end

#exists_instructionObject

Exists instruction in Instruction Set?



156
157
158
159
160
161
162
# File 'lib/rips/assembler.rb', line 156

def exists_instruction
  if !Instructions::SET.include? (@cmd[:name])
    Error::message( 4, 
                    @line, 
                    @cmd[:name] )
  end
end

#find_instructionsObject

Store number line for each instruction



44
45
46
47
48
49
50
51
# File 'lib/rips/assembler.rb', line 44

def find_instructions

  @input.each_with_index do |line,i|
    if (!line.empty?) && (line.scan(/\w+:/).empty?) && (line[0] != "#")
      @instructions << i+1
    end
  end
end

#find_labelsObject

Store labels and number line



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rips/assembler.rb', line 26

def find_labels

  @input.each_with_index do |line, i|
    if !line.empty?
      label = line.scan(/\w+:/)
      if (label.size == 1)
        
        if !@labels.include?(label[0].to_s.split(":").first)
          @labels[label[0].to_s.split(":").first] = [*@instructions.each_with_index].bsearch{|x, _| x >= i}.last
        else
          Error::message(7, i+1, label[0].to_s.split(":").first) 
        end
      end
    end
  end
end

#generateObject

Generate output in “progfile.dat”



111
112
113
114
115
116
117
# File 'lib/rips/assembler.rb', line 111

def generate
  File.open("progfile.dat", "w") do |f|
    @output.each do |line|
      f.puts line
    end
  end        
end

#get_instructionObject

Obtain instruction’s instance object



151
152
153
# File 'lib/rips/assembler.rb', line 151

def get_instruction
  Object.const_get("Rips::Instructions::#{@cmd[:name].capitalize}").new
end

#parse_input(line) ⇒ Object

Split on tokens



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rips/assembler.rb', line 120

def parse_input (line)

  if line[0] == "#"
    @cmd[:comments] = line
  else
    @cmd[:name] = line.split("#").first.split(" ").first.downcase
    @cmd[:arguments] = line.split("#").first.split("#{@cmd[:name]} ")
    if !@cmd[:arguments].empty?
      @cmd[:arguments] = @cmd[:arguments].pop.split("#").first.del(/\s+|\t+/).split(",")
    end
    if @cmd[:arguments].first == "jr" ||
       @cmd[:arguments].first == "nop"
       @cmd[:arguments] = []
    end
    @cmd[:comments] = line.split("#").slice(1..-1).join
    @cmd[:comments].insert(0,"#") if !@cmd[:comments].empty?
  end
end

#parse_labelObject

Translate label’s name to instruction’s number



140
141
142
143
144
145
146
147
148
# File 'lib/rips/assembler.rb', line 140

def parse_label
  if (@instruction.is_a? Rips::Instructions::Beqz) ||
     (@instruction.is_a? Rips::Instructions::Bnez) ||
     (@instruction.is_a? Rips::Instructions::J) ||
     (@instruction.is_a? Rips::Instructions::Jal)

      @cmd[:arguments] = [@labels[@cmd[:arguments].first].to_s]
  end
end

#runObject

Analyze and translate each instruction



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rips/assembler.rb', line 59

def run

  find_instructions
  find_labels

  @input.each do |line|

    # If line is empty -> next line
    # Or if not is a label
    if (!line.empty?) && (line.scan(/\w+:/).empty?)

      parse_input(line)
      @instruction = nil

      # If it's a comment -> show but not work with it
      if line[0] != "#"

        exists_instruction
        @instruction = get_instruction

        parse_label
        
        argument_size
        argument_syntax

        @instruction.set_arguments(@cmd[:arguments])
        @output << @instruction.code
      end
      show if @debug
    end        
    @line += 1
  end
  
  generate
end

#showObject

Codification log of instruction



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/rips/assembler.rb', line 96

def show

  # If line was a comment -> @instruction should be nil
  if @instruction.nil?
    codification = ""
  else
    codification = @instruction.code.scan(/.{4}|.+/).join("_")
  end

  puts  "@#{@line}:" \
        "\t#{codification}" \
        "\t#{@cmd[:comments]}"
end