rubimc 0.2.1
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.
- checksums.yaml +7 -0
- data/LICENSE.md +21 -0
- data/README.md +176 -0
- data/bin/rubimc +190 -0
- data/lib/rubimc/control_structures.rb +58 -0
- data/lib/rubimc/controllers.rb +46 -0
- data/lib/rubimc/init_var.rb +99 -0
- data/lib/rubimc/io_ports.rb +66 -0
- data/lib/rubimc/mcu/avr/attiny13.rb +176 -0
- data/lib/rubimc/mcu/avr/avr_controller.rb +16 -0
- data/lib/rubimc/preprocessor.rb +354 -0
- data/lib/rubimc/printer.rb +143 -0
- data/lib/rubimc/ruby_classes.rb +54 -0
- data/lib/rubimc.rb +234 -0
- data/lib/version.rb +3 -0
- metadata +60 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# ToDo: replace Controllers to MCUs
|
|
2
|
+
class Controllers
|
|
3
|
+
def self.all # list of USER`s microcontrolles
|
|
4
|
+
@@controllers_array
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
@@controllers_array = []
|
|
8
|
+
def self.inherited(child_class) # hook when define class
|
|
9
|
+
@@controllers_array << child_class if child_class.is_real_controller?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.is_real_controller?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.print_cc_layout(position)
|
|
17
|
+
if position == :before_main
|
|
18
|
+
RubimCode.pout "/**************************************************************"
|
|
19
|
+
RubimCode.pout " * This code was generated by RubimC micro-framework"
|
|
20
|
+
RubimCode.pout " * RubimC version: #{RubimCode::VERSION}"
|
|
21
|
+
RubimCode.pout " * Author: Evgeny Danilov"
|
|
22
|
+
RubimCode.pout " * File created at #{Time.now}"
|
|
23
|
+
RubimCode.pout " **************************************************************/"
|
|
24
|
+
RubimCode.pout
|
|
25
|
+
RubimCode.pout "#include <stdbool.h>"
|
|
26
|
+
RubimCode.pout "#include <stdio.h>"
|
|
27
|
+
RubimCode.pout
|
|
28
|
+
yield if block_given?
|
|
29
|
+
RubimCode.pout
|
|
30
|
+
RubimCode.pout "int main(int argc, char *argv[]) {"
|
|
31
|
+
RubimCode.level += 1
|
|
32
|
+
else
|
|
33
|
+
RubimCode.pout
|
|
34
|
+
RubimCode.pout "return 1;"
|
|
35
|
+
RubimCode.level -= 1
|
|
36
|
+
RubimCode.pout "}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.find_mcu(name)
|
|
41
|
+
series_array = Controllers.descendants
|
|
42
|
+
real_mcu_array = []
|
|
43
|
+
series_array.each {|series| real_mcu_array += series.descendants}
|
|
44
|
+
return real_mcu_array.select {|mcu| mcu.is_real_controller? and mcu::MCU_NAME == name}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#####################################################################
|
|
2
|
+
# Initialize user`s variables #
|
|
3
|
+
#####################################################################
|
|
4
|
+
|
|
5
|
+
class << RubimCode
|
|
6
|
+
attr_accessor :binding
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def integer(*varies) # varies - набор инициализируемых переменных
|
|
10
|
+
str_cc = "int "
|
|
11
|
+
ret_var = []
|
|
12
|
+
|
|
13
|
+
varies.each {|var|
|
|
14
|
+
# ToDo - поиск уже объявленных переменных и выдача предупреждений
|
|
15
|
+
|
|
16
|
+
if var.is_a? Hash # ToDo
|
|
17
|
+
RubimCode.perror "Ошибка. В бета-версии нельзя назначать переменным значения при объявлении"
|
|
18
|
+
# key = var.keys.first
|
|
19
|
+
# instance_variable_set("@#{key.to_s}" , UserVariable.new("#{key.to_s}"))
|
|
20
|
+
# str_cc += "#{key.to_s}=#{var[key]}, "
|
|
21
|
+
else
|
|
22
|
+
if var.to_s[0] == '@'
|
|
23
|
+
# ToDo:
|
|
24
|
+
RubimCode.perror "ToDo:"
|
|
25
|
+
elsif var.to_s[0] == '$'
|
|
26
|
+
# ToDo or delete
|
|
27
|
+
RubimCode.perror "Ошибка. В текущей версии нельзя инициализировать глобальные переменные"
|
|
28
|
+
else
|
|
29
|
+
str_cc += "#{var}, "
|
|
30
|
+
ret_var << RubimCode::UserVariable.new("#{var}", 'int')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# if self.ancestors.include? RubimCode or self == TestController
|
|
34
|
+
|
|
35
|
+
# str_cc = "int " if str_cc.nil?
|
|
36
|
+
# str_cc += "#{var}, "
|
|
37
|
+
|
|
38
|
+
# eval ("
|
|
39
|
+
# def #{var}
|
|
40
|
+
# @users__var__#{var}
|
|
41
|
+
# end
|
|
42
|
+
# @users__var__#{var} = RubimCode::UserVariable.new(\"#{var}\", 'int')
|
|
43
|
+
# ")
|
|
44
|
+
|
|
45
|
+
# # Альтернативная реализация
|
|
46
|
+
# var_method = Proc.new {instance_variable_get("@users__var__#{var}")}
|
|
47
|
+
# self.class.send(:define_method, var.to_sym, var_method)
|
|
48
|
+
# instance_variable_set("@users__var__#{var}" , UserVariable.new("#{var}", 'int'))
|
|
49
|
+
# else
|
|
50
|
+
# add_var_array(self.name, UserVariable.new("#{var}", 'int'))
|
|
51
|
+
# eval ("
|
|
52
|
+
# def #{var}=(value)
|
|
53
|
+
# pout \"\#{self.name}.#{var} = \#{value};\"
|
|
54
|
+
# end
|
|
55
|
+
# def #{var}
|
|
56
|
+
# UserVariable.new(\"\#{self.name}.#{var}\", 'int')
|
|
57
|
+
# end
|
|
58
|
+
# ")
|
|
59
|
+
# end
|
|
60
|
+
end
|
|
61
|
+
}
|
|
62
|
+
if ret_var.empty?
|
|
63
|
+
RubimCode.perror "no variables to init"
|
|
64
|
+
end
|
|
65
|
+
RubimCode.pout ("#{str_cc.chomp(', ')};")
|
|
66
|
+
|
|
67
|
+
if ret_var.count == 1
|
|
68
|
+
return ret_var[0]
|
|
69
|
+
else
|
|
70
|
+
return ret_var
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def array_of_integer(var, size: nil)
|
|
75
|
+
array(var, with: {type: :integer, size: size})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def array(var, with: {type: 'UserVariable', size: nil})
|
|
79
|
+
with[:size] = with[:size].to_i
|
|
80
|
+
with[:type] = with[:type].to_s
|
|
81
|
+
if with[:size].nil? or with[:type].nil?
|
|
82
|
+
RubimCode.perror "Необходимо указать параметры массива (напр.: with: {type: :float, size: n, ...})"
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
user_class = with[:type]
|
|
87
|
+
with[:type] = 'int' if with[:type] == 'integer'
|
|
88
|
+
if (with[:type].in? ['bool','int','float','double','string'])
|
|
89
|
+
user_class = "UserVariable"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
arr = with[:size].times.map do |i|
|
|
93
|
+
eval("RubimCode::#{user_class}.new('#{var}[#{i}]', '#{with[:type]}')")
|
|
94
|
+
end
|
|
95
|
+
instance_variable_set("@#{var}", RubimCode::UserArray.new(arr))
|
|
96
|
+
eval ("@#{var}.name = '#{var}'")
|
|
97
|
+
eval ("@#{var}.type = \"#{with[:type]}\"")
|
|
98
|
+
RubimCode.pout "#{with[:type]} #{var}[#{with[:size]}];"
|
|
99
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
def output (var, port: nil, pin: nil, type: "normal")
|
|
2
|
+
if port.nil? or pin.nil?
|
|
3
|
+
RubimCode.perror "Необходимо указать порт и пин для выхода #{var}"
|
|
4
|
+
elsif not self.class::PORTS.include? port
|
|
5
|
+
RubimCode.perror "У микроконтроллера #{self.class::MCU_NAME} нет порта #{port}"
|
|
6
|
+
elsif not self.class::PORTS[port].include? pin.to_i
|
|
7
|
+
RubimCode.perror "У микроконтроллера #{self.class::MCU_NAME} нет порта пина #{pin} для порта #{port}"
|
|
8
|
+
|
|
9
|
+
elsif type == "normal"
|
|
10
|
+
# define_method(var.to_sym) do
|
|
11
|
+
# instance_variable_get("@users__var__#{var}")
|
|
12
|
+
# end
|
|
13
|
+
# instance_variable_set("@users__var__#{var}" , UserOutput.new("#{var}", port: port, pin: pin, type: type))
|
|
14
|
+
|
|
15
|
+
eval ("
|
|
16
|
+
def #{var}
|
|
17
|
+
@users__var__#{var}
|
|
18
|
+
end
|
|
19
|
+
@users__var__#{var} = RubimCode::UserOutput.new(\"#{var}\", port: port, pin: pin, type: type)
|
|
20
|
+
")
|
|
21
|
+
|
|
22
|
+
elsif type == "tri-state"
|
|
23
|
+
RubimCode.perror "В данный момент тип выхода 'z-state' не реализован"
|
|
24
|
+
|
|
25
|
+
else
|
|
26
|
+
RubimCode.perror "Неизвестный тип выхода '#{type}'"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# ToDo: realize this method
|
|
31
|
+
def input (var, port: nil, pin: nil)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RubimCode
|
|
36
|
+
class << self
|
|
37
|
+
def rubim_sbit(var, bit); "#{var} |= 1<<#{bit};"; end
|
|
38
|
+
def rubim_cbit(var, bit); "#{var} &= ~(1<<#{bit});"; end
|
|
39
|
+
def rubim_tbit(var, bit); "#{var} ^= 1<<#{bit};"; end
|
|
40
|
+
end # class << self
|
|
41
|
+
|
|
42
|
+
class UserOutput
|
|
43
|
+
attr_accessor :name, :port, :pin, :type
|
|
44
|
+
|
|
45
|
+
def initialize(name, port: nil, pin: nil, type: "normal")
|
|
46
|
+
@name = name.to_s
|
|
47
|
+
@port = port.to_s
|
|
48
|
+
@pin = pin.to_s
|
|
49
|
+
@type = type.to_s
|
|
50
|
+
RubimCode.pout (RubimCode.rubim_sbit("DDR#{port}", "#{pin}"))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def on
|
|
54
|
+
RubimCode.pout (RubimCode.rubim_sbit("PORT#{port}", "#{pin}"))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def off
|
|
58
|
+
RubimCode.pout (RubimCode.rubim_cbit("PORT#{port}", "#{pin}"))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def toggle
|
|
62
|
+
RubimCode.pout (RubimCode.rubim_tbit("PORT#{port}", "#{pin}"))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end # RubimCode class
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#####################################################################
|
|
2
|
+
# ATMEL AVR - attiny13 #
|
|
3
|
+
#####################################################################
|
|
4
|
+
|
|
5
|
+
class AVR_attiny13 < AVRController
|
|
6
|
+
class << self
|
|
7
|
+
def is_real_controller?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def micro_layout
|
|
12
|
+
# ToDo: set F_CPU from user programm
|
|
13
|
+
RubimCode.pout "#define __AVR_ATtiny13__ 1"
|
|
14
|
+
RubimCode.pout "#define F_CPU 1000000UL" # Microcontroller frequency (Hz)
|
|
15
|
+
RubimCode.pout "#include <avr/io.h>"
|
|
16
|
+
RubimCode.pout "#include <avr/iotn13.h>"
|
|
17
|
+
RubimCode.pout "#include <avr/interrupt.h>"
|
|
18
|
+
end
|
|
19
|
+
end # class << self
|
|
20
|
+
|
|
21
|
+
MCU_NAME = "attiny13"
|
|
22
|
+
PORTS = {B: (0...5)}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
RESET_PIN = {port: :B, pin: 5}
|
|
26
|
+
|
|
27
|
+
ADC_CHANNEL = [{port: :B,pin: 5}, {port: :B, pin: 2}, {port: :B, pin: 4}, {port: :B, pin: 3}]
|
|
28
|
+
AN_COMPARATOR = [{positive: {port: :B, pin: 0}, negative: {port: :B, pin: 1}}]
|
|
29
|
+
ADC0="(0b00<<MUX0)"; ADC1="(0b01<<MUX0)"; ADC2="(0b10<<MUX0)"; ADC3="(0b11<<MUX0)";
|
|
30
|
+
|
|
31
|
+
INTERRUPT_SOURCE = 5.times.map {|i| {port: :B, pin: i} }
|
|
32
|
+
|
|
33
|
+
TIMER_CLOCK_SOURCE = [{port: :B, pin: 2}]
|
|
34
|
+
TIMER_COMPARE_OUTPUT = [{matchA: {port: :B, pin: 0}, matchB: {port: :B, pin: 1}}]
|
|
35
|
+
|
|
36
|
+
SPI_PIN = { master: {
|
|
37
|
+
input: {port: :B, pin: 1},
|
|
38
|
+
output: {port: :B, pin: 0}
|
|
39
|
+
},
|
|
40
|
+
slave: {
|
|
41
|
+
input: {port: :B, pin: 0},
|
|
42
|
+
output: {port: :B, pin: 1}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class ANALOG_TO_DIGITAL
|
|
47
|
+
def self.init(**options)
|
|
48
|
+
options.permit_and_default!(ref: "vcc", channel: "MUX0",
|
|
49
|
+
prescale: 2, auto_triggering: "disable", interrupt_enabled: false,
|
|
50
|
+
digital_inputs: false)
|
|
51
|
+
|
|
52
|
+
RubimCode.pout
|
|
53
|
+
RubimCode.pout "// Init ADC"
|
|
54
|
+
|
|
55
|
+
# === ADMUX ===
|
|
56
|
+
refs0 = case options[:ref] # источник опорного напряжения
|
|
57
|
+
when "vcc" then 0
|
|
58
|
+
when "internal" then 1
|
|
59
|
+
else perror "Undefined value for option :ref in method '#{__method__}'"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
adlar = case "right" # options[:result_adjust] # выравнивание результата вычислений всегда по правому краю
|
|
63
|
+
when "left" then 1
|
|
64
|
+
when "right" then 0
|
|
65
|
+
else perror "Undefined value for option :result_adjust in method '#{__method__}'"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
channel = options[:channel]
|
|
69
|
+
unless channel.in? [ADC0, ADC1, ADC2, ADC3]
|
|
70
|
+
RubimCode.pout channel
|
|
71
|
+
perror "Undefined value for option :channel in method '#{__method__}'"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
RubimCode.pout("ADMUX = (#{refs0}<<REFS0) | (#{adlar}<<ADLAR) | #{channel};")
|
|
75
|
+
|
|
76
|
+
# === ADCSRA ===
|
|
77
|
+
adate = case options[:auto_triggering] # одиночное преобразование или множественное
|
|
78
|
+
when "enable" then 1
|
|
79
|
+
when "disable" then 0
|
|
80
|
+
else perror "Undefined value for option :auto_triggering in method '#{__method__}'"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# adif = ? # ToDo: ADC Interrupt Flag
|
|
84
|
+
|
|
85
|
+
adie = 0 # interrupt enable (для установки этого значения есть отдельная функция)
|
|
86
|
+
|
|
87
|
+
adps = case options[:prescale] # prescale
|
|
88
|
+
when 2 then 1
|
|
89
|
+
when 4 then 2
|
|
90
|
+
when 8 then 3
|
|
91
|
+
when 16 then 4
|
|
92
|
+
when 32 then 5
|
|
93
|
+
when 64 then 6
|
|
94
|
+
when 128 then 7
|
|
95
|
+
else perror "Undefined value for option :prescale in method '#{__method__}'"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
RubimCode.pout ("ADCSRA = (1<<ADEN) | (#{adate}<<ADATE) | (#{adps}<<ADPS0) | (#{adie}<<ADIE);")
|
|
99
|
+
|
|
100
|
+
# ToDo: config ports as input (is it need???)
|
|
101
|
+
# ToDo: config DIDR0
|
|
102
|
+
RubimCode.pout
|
|
103
|
+
end # init method
|
|
104
|
+
|
|
105
|
+
def self.convert(channel: nil)
|
|
106
|
+
# ToDo: должен возвращать значение, реализовать как С-функцию
|
|
107
|
+
# ToDo: можно использовать ленивую загрузку Ruby - autoload
|
|
108
|
+
unless channel.in? [ADC0, ADC1, ADC2, ADC3, nil]
|
|
109
|
+
perror "Undefined value for option :channel in method '#{__method__}'"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
unless channel.nil?
|
|
113
|
+
RubimCode.pout ("ADMUX = (ADMUX & 0b11111100) | channel;") # ToDo: replace 0b11111110 to ARV-const
|
|
114
|
+
RubimCode.pout ("_delay_us(1);") # Для стабилизации входного напряжения # ToDo: replace to rubim's delay(1.miliseconds)
|
|
115
|
+
end
|
|
116
|
+
RubimCode.pout ("ADCSRA |= (1 << ADSC);") # Старт преобразования
|
|
117
|
+
RubimCode.pout ("while (ADCSRA & (1 << ADSC));") # Ожидание завершения преобразования
|
|
118
|
+
|
|
119
|
+
# ToDo - надо точно разобраться с выравниванием
|
|
120
|
+
# (и поправить аналогичный код в interrupt)
|
|
121
|
+
RubimCode.pout ("ADCL + ((ADCH&0b11) << 8);") # выравнивание результата вычислений всегда по правому краю (см. adlar)
|
|
122
|
+
end # convert method
|
|
123
|
+
|
|
124
|
+
def set_freq(freq) # установка частоты АЦП (с помощью установки предделителя)
|
|
125
|
+
# ToDo: реализовать метод
|
|
126
|
+
# ToDo: в методе инициализации добавить опцию frequency: nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.interrupt(**options, &block)
|
|
130
|
+
options.permit_and_default!(enabled: false)
|
|
131
|
+
# if is_enabled && block.nil? # ToDo: проверить на наличие блока если прерывание активно
|
|
132
|
+
# perror "method #{__method__} mast have a block"
|
|
133
|
+
# end
|
|
134
|
+
|
|
135
|
+
adie = case options[:enabled] # ADC Interrupt Enable
|
|
136
|
+
when true then 1
|
|
137
|
+
when false then 0
|
|
138
|
+
else perror "Undefined value for option :is_enabled in method '#{__method__}'"
|
|
139
|
+
end
|
|
140
|
+
if adie == 0 # if interrupt disabled
|
|
141
|
+
RubimCode.pout "Start continuously ADC convert"
|
|
142
|
+
RubimCode.pout (RubimCode.rubim_cbit("ADMUX", "ADIE"))
|
|
143
|
+
# RubimCode.pout ("cli();") # automatically set (is it?)
|
|
144
|
+
else
|
|
145
|
+
RubimCode.pout (RubimCode.rubim_sbit("ADMUX", "ADIE"))
|
|
146
|
+
# RubimCode.pout ("sei();") # automatically set (is it?)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Genetare Interrupt code
|
|
150
|
+
if block_given?
|
|
151
|
+
interrupt_code = "" # Write code in variable "interrupt_code"
|
|
152
|
+
RubimCode.pout_destination = interrupt_code
|
|
153
|
+
old_level = RubimCode.level
|
|
154
|
+
RubimCode.level = 0
|
|
155
|
+
|
|
156
|
+
RubimCode.pout
|
|
157
|
+
RubimCode.pout "// ADC Interrupt"
|
|
158
|
+
RubimCode.pout ("ISR(ADC_vect) {")
|
|
159
|
+
RubimCode.level += 1
|
|
160
|
+
# ToDo - надо точно разобраться с выравниванием
|
|
161
|
+
# (see above in convert method)
|
|
162
|
+
RubimCode.pout "int __rubim__volt = ADCL + ((ADCH&0b11) << 8);"
|
|
163
|
+
yield (RubimCode::UserVariable.new("__rubim__volt", "int"))
|
|
164
|
+
RubimCode.level -= 1
|
|
165
|
+
RubimCode.pout ("}")
|
|
166
|
+
|
|
167
|
+
RubimCode.level = old_level
|
|
168
|
+
RubimCode.pout_destination = :default
|
|
169
|
+
RubimCode::Interrupts.add(interrupt_code)
|
|
170
|
+
end
|
|
171
|
+
end # interrupt method
|
|
172
|
+
|
|
173
|
+
end # ANALOG_TO_DIGITAL Class
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
end # RubimCode class
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class AVRController < Controllers
|
|
2
|
+
MCU_SERIES = "AVR"
|
|
3
|
+
|
|
4
|
+
def self.print_layout(position)
|
|
5
|
+
if position == :before_main
|
|
6
|
+
print_cc_layout(:before_main) do
|
|
7
|
+
micro_layout # prints includes for current microcontroller
|
|
8
|
+
end
|
|
9
|
+
elsif position == :after_main
|
|
10
|
+
print_cc_layout(:after_main)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ToDO: add all folder 'avr'
|
|
16
|
+
require 'rubimc/mcu/avr/attiny13'
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
ЗАДАЧА ПРЕПРОЦЕССОРА:
|
|
4
|
+
1. (готово) замена оператора присваивания: "=" на ".c_assign="
|
|
5
|
+
2. (готово )замена всех цифр(целые, дробные, отриц.)
|
|
6
|
+
3a. (не готово) Замена условий (find_far_pos - возвращать позицию с учетом длины идентификатора)
|
|
7
|
+
3b. (готово) Замена циклов
|
|
8
|
+
3c. Замена управляющих структур: break, next, redo, retry
|
|
9
|
+
4. замена строк "123" => UserVariable.new("123")
|
|
10
|
+
- строки "это_строка" и 'это_строка'
|
|
11
|
+
5. Поиск необъявленных переменных и выдача ошибок
|
|
12
|
+
6. Предупреждение об использовании зарезервированных переменных и методов
|
|
13
|
+
все они начинаются на "__rubim__"
|
|
14
|
+
(напр. __rubim__times или __rubim__classparams)
|
|
15
|
+
7. Предупреждение об использовании зарезервированных классов
|
|
16
|
+
UserClass, UserArray (лучше заменить на RubimClass, RubimArray)
|
|
17
|
+
8. Добавление предка к пользовательским классам
|
|
18
|
+
9. Замена return на __rubim_return
|
|
19
|
+
10. Сохранить все пользовательские комментарии в генерируемом коде
|
|
20
|
+
11. Цикл for - хз-чо делать...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
=end
|
|
24
|
+
|
|
25
|
+
class Object
|
|
26
|
+
def in?(array)
|
|
27
|
+
array.each {|x| return true if x == self}
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
require 'ripper'
|
|
33
|
+
class RubimRipper
|
|
34
|
+
# 1. (готово) замена оператора присваивания: "=" на ".c_assign="
|
|
35
|
+
def self.replace_assing_operators(source)
|
|
36
|
+
lexs = Ripper.lex(source)
|
|
37
|
+
lexs.reverse_each do |lex|
|
|
38
|
+
pos, ident, symb = lex
|
|
39
|
+
if (ident == :on_op and symb == "=")
|
|
40
|
+
source = replace_words(source, symb, ".c_assign=", pos)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
return source
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# 2. (готово )замена всех цифр(целые, дробные, отриц.)
|
|
47
|
+
def self.replace_all_numeric(source)
|
|
48
|
+
lexs = Ripper.lex(source)
|
|
49
|
+
lexs.reverse_each do |lex|
|
|
50
|
+
pos, ident, symb = lex
|
|
51
|
+
if (ident.in? [:on_int, :on_float])
|
|
52
|
+
bug_plus = (symb[0]=="+" ? "+" : "")
|
|
53
|
+
# Note: space before "UserVariable" is neсessary
|
|
54
|
+
source = replace_words(source, symb, "#{bug_plus} RubimCode::UserVariable.new(#{symb})", pos)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
return source
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.replace_modify_express(source, kw_str = "if")
|
|
61
|
+
kw_sym = "#{kw_str}_mod".to_sym
|
|
62
|
+
|
|
63
|
+
parse_array = Ripper.sexp(source)
|
|
64
|
+
mod_expresses = find_rec(parse_array, kw_sym)
|
|
65
|
+
|
|
66
|
+
mod_expresses.reverse_each do |record|
|
|
67
|
+
kw_pos = find_keyword_pos(source, record, kw_str) # 1. find modify 'if/unless' keywords
|
|
68
|
+
cond_pos = find_express_end(source, record[1]) # 2. find end of 'if/unless' condition
|
|
69
|
+
|
|
70
|
+
case kw_str
|
|
71
|
+
when "if"
|
|
72
|
+
source = paste_before_pos(source, "; RubimCode.rubim_end;", cond_pos) # 3. paste "rubim_end" at the end of condition
|
|
73
|
+
source = replace_words(source, kw_str, ";RubimCode.rubim_tmpif RubimCode.rubim_ifmod", kw_pos) # 4. replace "if/unless" keywords to "rubim_ifmod/__rubim_unlessmod"
|
|
74
|
+
when "unless"
|
|
75
|
+
source = paste_before_pos(source, "; RubimCode.rubim_end;", cond_pos) # 3. paste "rubim_end" at the end of condition
|
|
76
|
+
source = replace_words(source, kw_str, ";RubimCode.rubim_tmpif RubimCode.rubim_unlessmod", kw_pos) # 4. replace "if/unless" keywords to "rubim_ifmod/__rubim_unlessmod"
|
|
77
|
+
when "while"
|
|
78
|
+
source = replace_words(source, kw_str, ";RubimCode.rubim_tmpif RubimCode.rubim_begin; RubimCode.rubim_whilemod", kw_pos) # 4. replace "while/until" keywords
|
|
79
|
+
when "until"
|
|
80
|
+
source = replace_words(source, kw_str, ";RubimCode.rubim_tmpif RubimCode.rubim_begin; RubimCode.rubim_untilmod", kw_pos) # 4. replace "while/until" keywords
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
return source
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.replace_flat_express(source, kw_str = "if")
|
|
87
|
+
source = replace_keywords(source, "then", ";")
|
|
88
|
+
kw_sym = "#{kw_str}".to_sym
|
|
89
|
+
|
|
90
|
+
parse_array = Ripper.sexp(source)
|
|
91
|
+
flat_expresses = find_rec(parse_array, kw_sym)
|
|
92
|
+
|
|
93
|
+
flat_expresses.reverse_each do |record|
|
|
94
|
+
kw_pos = find_keyword_pos(source, record, kw_str) # 1. find modify 'if/unless' keywords
|
|
95
|
+
cond_pos = find_express_end(source, record[1]) # 2. find end of 'if/unless' condition
|
|
96
|
+
|
|
97
|
+
lineno, charno = cond_pos
|
|
98
|
+
if (source.lines[lineno-1][charno..charno+1] != "do")
|
|
99
|
+
source = paste_before_pos(source, " do", cond_pos) # 4. paste "str" at the end of condition
|
|
100
|
+
end
|
|
101
|
+
source = replace_words(source, kw_str, "RubimCode.rubim_#{kw_str}", kw_pos)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
return source
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.replace_loop(source)
|
|
108
|
+
lexs = Ripper.lex(source)
|
|
109
|
+
lexs.reverse_each do |lex|
|
|
110
|
+
kw_pos, ident, symb = lex
|
|
111
|
+
if (ident == :on_ident and symb == "loop")
|
|
112
|
+
source = replace_words(source, symb, "RubimCode.rubim_loop", kw_pos)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
return source
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.replace_then_else_elsif_kw(source)
|
|
119
|
+
source = replace_keywords(source, "then", ";")
|
|
120
|
+
source = replace_keywords(source, "else", "RubimCode.rubim_else")
|
|
121
|
+
source = replace_keywords(source, "elsif", "RubimCode.rubim_elsif")
|
|
122
|
+
return source
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.replace_rubim_tmpif(source)
|
|
126
|
+
source.gsub(/;RubimCode.rubim_tmpif/, 'if')
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.replace_boolean_kw(source)
|
|
130
|
+
source = replace_keywords(source, "true", "UserVariable.new(true)")
|
|
131
|
+
source = replace_keywords(source, "false", "UserVariable.new(false)")
|
|
132
|
+
return source
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.add_binding_to_init(source) # for initialize variables with methods 'integer', 'float', e.t.
|
|
136
|
+
sexp = Ripper.sexp(source)
|
|
137
|
+
command_array = find_rec(sexp, :command)
|
|
138
|
+
command_array.reverse_each do |elem|
|
|
139
|
+
varies_name = []
|
|
140
|
+
symb, helper_name, helper_pos = elem[1]
|
|
141
|
+
if helper_name.in? ["integer", "float", "string"] # if one of helper methods
|
|
142
|
+
args_add_block = find_rec(elem, :args_add_block)[0][1]
|
|
143
|
+
args_add_block.each do |arg|
|
|
144
|
+
if arg[0] == :symbol_literal
|
|
145
|
+
tmp_res = []
|
|
146
|
+
tmp_res << find_rec(arg, :@ident)[0]
|
|
147
|
+
tmp_res << find_rec(arg, :@ivar)[0]
|
|
148
|
+
tmp_res << find_rec(arg, :@gvar)[0]
|
|
149
|
+
varies_name << tmp_res.compact.map {|el| el[1]}
|
|
150
|
+
elsif arg[0] == :bare_assoc_hash
|
|
151
|
+
bare_array = find_rec(arg, :@label)
|
|
152
|
+
bare_array.each do |bare|
|
|
153
|
+
varies_name << bare[1][0..-2]
|
|
154
|
+
end
|
|
155
|
+
break
|
|
156
|
+
else
|
|
157
|
+
raise ArgumentError.new("Wrong arguments for helper '#{helper_name}'")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
var_str = varies_name.join(', ') # list of vars, defined in helper method
|
|
161
|
+
source = paste_before_pos(source, "#{var_str} = ", helper_pos) # paste vars before helper method
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
return source
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
#########################
|
|
168
|
+
# === PRIVATE SECTION ===
|
|
169
|
+
#########################
|
|
170
|
+
private
|
|
171
|
+
# Поиск вложенного массива (sexp) с ключевым символом find_sym (рекурсивный вызов)
|
|
172
|
+
def self.find_rec(array, find_sym)
|
|
173
|
+
return [] unless array.is_a? Array
|
|
174
|
+
result = []
|
|
175
|
+
array.each do |elem|
|
|
176
|
+
result << array if elem == find_sym
|
|
177
|
+
result += find_rec(elem, find_sym)
|
|
178
|
+
end
|
|
179
|
+
return result
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Замена слов
|
|
183
|
+
def self.replace_words(source, dstr, rstr, pos) # источник, исходное слово, конечное слово, начальная позиция
|
|
184
|
+
output = ""
|
|
185
|
+
lineno, charno = pos
|
|
186
|
+
source.lines.each_with_index do |line, index|
|
|
187
|
+
if index == lineno-1
|
|
188
|
+
line[charno .. charno+dstr.length-1] = rstr
|
|
189
|
+
end
|
|
190
|
+
output += line
|
|
191
|
+
end
|
|
192
|
+
return output
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Вставка слова до указанной позиции
|
|
196
|
+
def self.paste_before_pos(source, rstr, pos)
|
|
197
|
+
output = ""
|
|
198
|
+
lineno, charno = pos
|
|
199
|
+
source.lines.each_with_index do |line, index|
|
|
200
|
+
if index == lineno-1
|
|
201
|
+
line.insert(charno, rstr)
|
|
202
|
+
end
|
|
203
|
+
output += line
|
|
204
|
+
end
|
|
205
|
+
return output
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Замена ключевых слов
|
|
209
|
+
def self.replace_keywords(source, kw, repl_kw)
|
|
210
|
+
lexs = Ripper.lex(source)
|
|
211
|
+
lexs.reverse_each do |lex|
|
|
212
|
+
kw_pos, ident, symb = lex
|
|
213
|
+
if (ident == :on_kw and symb == kw)
|
|
214
|
+
source = replace_words(source, symb, repl_kw, kw_pos)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
return source
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Поиск позиции наиболее близкого идентификатора справа
|
|
221
|
+
def self.find_near_pos(array)
|
|
222
|
+
array.each do |el|
|
|
223
|
+
if el.is_a? Array # если найден идентификатор, указывающий на позицию
|
|
224
|
+
if (el[0].is_a? Symbol and el.size==3 and /\A@/===el[0].to_s)
|
|
225
|
+
return el[2]
|
|
226
|
+
else
|
|
227
|
+
res = find_near_pos(el)
|
|
228
|
+
return res unless res.nil?
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
raise "can not find near position in array #{array}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Поиск позиции наиболее дальнего идентификатора справа
|
|
236
|
+
def self.find_far_pos(array)
|
|
237
|
+
array.reverse_each do |el|
|
|
238
|
+
if el.is_a? Array # если найден идентификатор, указывающий на позицию
|
|
239
|
+
if (el[0].is_a? Symbol and el.size==3 and /\A@/===el[0].to_s)
|
|
240
|
+
return el[2]
|
|
241
|
+
else
|
|
242
|
+
res = find_far_pos(el)
|
|
243
|
+
return res unless res.nil?
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
raise "can not find far position in array #{array}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Поиск ключевого слова "kw_str" начиная с позиции near_pos и левее
|
|
251
|
+
def self.find_keyword_pos(source, array, kw_str)
|
|
252
|
+
near_pos = find_near_pos(array)
|
|
253
|
+
lineno, charno = near_pos
|
|
254
|
+
curline = source.lines[lineno-1]
|
|
255
|
+
charno.downto(0) do |index|
|
|
256
|
+
return [lineno, index-1] if /#{kw_str} / === curline[index-1..-1]
|
|
257
|
+
end
|
|
258
|
+
raise "can not find keyword '#{kw_str}' if array #{array}"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Поиск окончания выражения
|
|
262
|
+
def self.find_express_end(source, express_array)
|
|
263
|
+
start_pos = find_far_pos(express_array)
|
|
264
|
+
lineno, charno = start_pos
|
|
265
|
+
lexs = Ripper.lex(source)
|
|
266
|
+
lexs.each do |lex|
|
|
267
|
+
lex_pos, ident, symb = lex
|
|
268
|
+
if ((lex_pos <=> start_pos) >= 0) then
|
|
269
|
+
if ((ident==:on_nl && symb=="\n") or
|
|
270
|
+
(ident==:on_semicolon && symb==";") or
|
|
271
|
+
(ident==:on_kw && symb=="do") or
|
|
272
|
+
(ident==:on_kw && symb=="end")) then
|
|
273
|
+
return lex_pos
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
lcount = source.lines.count
|
|
278
|
+
return [lcount, source.lines[lcount-1].length]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
end # class RubimRipper
|
|
282
|
+
|
|
283
|
+
class PreProcessor
|
|
284
|
+
@@program = ""
|
|
285
|
+
|
|
286
|
+
def self.program; @@program; end
|
|
287
|
+
def self.program=(str); @@program = str; end
|
|
288
|
+
|
|
289
|
+
def self.execute(str)
|
|
290
|
+
@@program = str
|
|
291
|
+
@@program = RubimRipper.replace_assing_operators(@@program)
|
|
292
|
+
@@program = RubimRipper.replace_all_numeric(@@program)
|
|
293
|
+
|
|
294
|
+
# Последовательность очень важна - не нарушать!
|
|
295
|
+
@@program = RubimRipper.replace_then_else_elsif_kw(@@program)
|
|
296
|
+
|
|
297
|
+
@@program = RubimRipper.replace_modify_express(@@program, "if")
|
|
298
|
+
@@program = RubimRipper.replace_modify_express(@@program, "unless")
|
|
299
|
+
@@program = RubimRipper.replace_modify_express(@@program, "while")
|
|
300
|
+
@@program = RubimRipper.replace_modify_express(@@program, "until")
|
|
301
|
+
|
|
302
|
+
@@program = RubimRipper.replace_flat_express(@@program, "if")
|
|
303
|
+
@@program = RubimRipper.replace_flat_express(@@program, "unless")
|
|
304
|
+
@@program = RubimRipper.replace_flat_express(@@program, "while")
|
|
305
|
+
@@program = RubimRipper.replace_flat_express(@@program, "until")
|
|
306
|
+
|
|
307
|
+
@@program = RubimRipper.replace_loop(@@program)
|
|
308
|
+
@@program = RubimRipper.replace_rubim_tmpif(@@program)
|
|
309
|
+
|
|
310
|
+
@@program = RubimRipper.add_binding_to_init(@@program)
|
|
311
|
+
|
|
312
|
+
# @@program = RubimRipper.replace_boolean_kw(@@program)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# See
|
|
316
|
+
# p defined?(x = 1) # => "assignment"
|
|
317
|
+
# p defined?(x[5] = 1) # => "method"
|
|
318
|
+
# p defined?(x[5] += 1) # => "method"
|
|
319
|
+
|
|
320
|
+
# --- OLD VERSION, BASED ON REGEXP ---
|
|
321
|
+
# убрать пробелы между всеми односимвольными операторами (кроме оператора ":")
|
|
322
|
+
# operators = "\\+\\-\\*\\/\\^\\!\\=\\~\\?\\:\\%\\|\\&"
|
|
323
|
+
# @@program.gsub!(/\ *?([#{operators}&&[^\:]])\ ?/, '\1')
|
|
324
|
+
|
|
325
|
+
# замена оператора "=" на ".c_assign=" (только перед переменными)
|
|
326
|
+
# ch = "a-zA-Z"
|
|
327
|
+
# @@program.gsub!(/(^|[^#{ch}\.])([#{ch}\d]+)=([^\=\~])/, '\1\2.c_assign=\3')
|
|
328
|
+
|
|
329
|
+
# замена всех цифр(Fixnum), после которых идут операторы, на UserVariable.new()
|
|
330
|
+
# @@program.gsub!(/(^|[^\w])([-+]?\d*\.?\d+)([#{operators}&&[^\=]])/, '\1UserVariable.new(\2)\3')
|
|
331
|
+
# замена всех цифр(Fixnum), после которых идет указание метода, на UserVariable.new()
|
|
332
|
+
# @@program.gsub!(/(^|[^\w])(\d+)(\.[\w&&[^\d]])/, '\1UserVariable.new(\2)\3')
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# write preprocessing program in file
|
|
337
|
+
def self.write_in_file(input_file, dirname, basename, outfile)
|
|
338
|
+
# basename = File.basename(input_file)
|
|
339
|
+
# dirname = File.dirname(input_file)
|
|
340
|
+
# outfile = "#{dirname}/release"
|
|
341
|
+
|
|
342
|
+
# # clear directory "release"
|
|
343
|
+
# Dir.mkdir(outfile) unless Dir.exists?(outfile)
|
|
344
|
+
# Dir.foreach(outfile) do |file|
|
|
345
|
+
# File.delete("#{outfile}/#{file}") if (file!='.' && file!='..')
|
|
346
|
+
# end
|
|
347
|
+
|
|
348
|
+
PreProcessor.execute( File.read(input_file) )
|
|
349
|
+
File.write("#{outfile}", PreProcessor.program)
|
|
350
|
+
|
|
351
|
+
print "done\n"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
end
|