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.
@@ -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