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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4465934e351aae8519533e572a4db1f636230a2d
4
+ data.tar.gz: 20b82528892f27d9c7dc428b3c744d973da93dce
5
+ SHA512:
6
+ metadata.gz: 6c9694603cc3dcd38c5a82df186c89573f586a0560b414f991771f4f7b638522b382e414b58febe950a210633c90e3a3ac5c43dd2e756997f0cbfc211a746b2a
7
+ data.tar.gz: 8bdf46becb20231885f57e47de38d0a8b5c347bb23dc5a1ae4f3788de98116894fb921d6ed89d5faedaaad508b8705e52a61cfba426fde5fb62e4ad8229528ed
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Evgeny Danilov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,176 @@
1
+ ### RubimC
2
+ It is a Ruby compiler and framework for microcontrollers. Full name RubimCode in Russian transcription heard as "cut down the code". Current version is working but realizes far not all the features of Ruby. All realized features you can find in folder "examples"
3
+
4
+ ### Description:
5
+ RubimC designed to simplify the process of programming microcontrollers, but can also be used as an clear С-code generator. The framework is a syntax-flavored Ruby combines the unique features of the Ruby, adding and expanding the functions required for a specific area. At the input generator takes the program to Ruby, and the output provides a pure C code, based on the user program and libraries that are connected to a select model of the microcontroller. All that is required to generate is installed ruby-interpreter. If it`s nessesary, for compile C-code you need installing compiler *gcc* or *avr-gcc* (for AVR microcontrollers)
6
+
7
+ ### Benefits of writing programs in RubymC
8
+ + increase development speed
9
+ + code readability and elegance inherent in the Ruby language
10
+ + an object-oriented approach
11
+ + the use of an interpreted language does not reduce the performance of the final program because there is **no virtual mashine**
12
+ + ability of hardware control IC and delivery of messages
13
+ + ability to get a list of the hardware for a particular version of the device, as well as a list of all methods and help them directly from the generator console, on the basis of libraries.
14
+
15
+ ### Why?
16
+ First of all for fan...I want to see at Ruby from other point, not only from famous framework Ruby On Rails. Of course, we have great project [mruby] (http://mruby.org/), that compile Ruby code, realized all common functions of Ruby and standart libraries, and supported by **Matz**. But...mruby generate a big-size code, and, as we know, microcontroller have very small memory. For example, for initialize only one array mruby generate binary file with size 1MB! At the other side RubimC generate code with minimal size, in most cases are not different from the similar size of, written on C. In addition, RubimC generator is clearness. You always can to see on generated C-code and to evaluate its performance and size.
17
+
18
+ ### How it`s work
19
+ Code generated in three stage:
20
+
21
+ 1. Preprocessing user programm, that replaced some Ruby keywords, operators and identificators
22
+ 2. Shell Ruby-code and generate C-code (use metaprograming of Ruby)
23
+ 3. Compile C-code (with gcc or avr-gcc).
24
+
25
+ ### Install
26
+ All you need to use **RubimC** gem is Ruby interpretator and gcc/avr-gcc compiler.
27
+
28
+ How to [install Ruby] (https://www.ruby-lang.org/en/documentation/installation/). For Ubuntu I recomended to use first chapter of this [manual] (https://gorails.com/setup/ubuntu).
29
+
30
+ Then you can install RubimC gem:
31
+ ```sh
32
+ gem install rubimc
33
+ ```
34
+
35
+ Compiler *gcc* provided by Linux. For others platforms use this [manual] (https://gcc.gnu.org/install/binaries.html).
36
+
37
+ To install *avr-gcc* use this [manual] (http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_GCC_Toolchain).
38
+
39
+
40
+ ### ToDo list (main of them):
41
+ 0. upload to rubygems
42
+ 0. Test core with command "rubimc test"
43
+ 1. Code generator:
44
+ + define user`s variables as local (now it defined in top-level instance)
45
+ + support all C types of variables (int ,float, unsigned int, u_int8 e.t.)
46
+ + support array, hash, string, range and constants
47
+ + support user`s methods and classes
48
+ + support threads
49
+ 2. Write libraries for microcontrollers (AVR, PIC, STM, e.t.)
50
+ 3. Fix a lot of possible bugs & features
51
+
52
+ ### What is done
53
+ 1. Init variables
54
+ 2. Support all arithmetic operation except ternary and logical operators (binary operators is done)
55
+ 3. Support conditions (if/unless and it`s modify version) and loops (while/until) except next/redo/break/retry keywords
56
+ 4. Support arrays init and arithmetic operation with it
57
+ 5. Realize example library for AVR AtTiny13 MCU with DigitalIO and ADC support
58
+
59
+ ### Example for AVR microcontroller:
60
+ Ruby programm (*"FirstController.rb"*):
61
+ ```ruby
62
+ require 'rubimc'
63
+
64
+ class FirstController < AVR_attiny13
65
+ def initialize
66
+ ANALOG_TO_DIGITAL.init(ref: "vcc", channel: ADC0)
67
+
68
+ ANALOG_TO_DIGITAL.interrupt(enabled: true) do |volts|
69
+ output :led, port: :B, pin: 3
70
+ led.off if volts < 30
71
+ led.on if volts >= 220
72
+ end
73
+ end
74
+
75
+ def main_loop # infinit loop, it stop only when MCU is reset
76
+ end
77
+ end
78
+ ```
79
+
80
+ To compile this code run in console:
81
+ ```sh
82
+ rubimc compile FirstController.rb
83
+ ```
84
+ or just
85
+ ```sh
86
+ rubimc compile --all
87
+ ```
88
+
89
+ It generate C-code placed in *"release/FirstController.c"* and hex-file for upload in MCU placed in *"release/FirstController.hex*
90
+ ```c
91
+ //=============================
92
+ #include <stdbool.h>
93
+
94
+ #define F_CPU 1000000UL
95
+ #include <avr/io.h>
96
+ #include <avr/iotn13.h>
97
+ #include <avr/interrupt.h>
98
+
99
+ int main()
100
+ {
101
+ // Init ADC
102
+ ADMUX = (0<<REFS0) | (0<<ADLAR) | MUX0;
103
+ ADCSRA = (1<<ADEN) | (0<<ADATE) | (1<<ADPS0) | (0<<ADIE);
104
+
105
+ ADMUX |= 1<<ADIE;
106
+ return 1;
107
+ }
108
+
109
+ // ADC Interrupt
110
+ ISR(ADC_vect)
111
+ {
112
+ int __rubim__volt = ADCL + ((ADCH&0b11) << 8);
113
+ DDRB |= 1<<(3);
114
+ if ((__rubim__volt<=(0))) {
115
+ PORTB &= 255 ^ (1<<(3));
116
+ }
117
+ if (!((__rubim__volt<(15)))) {
118
+ PORTB |= 1<<(3);
119
+ }
120
+ }
121
+ ```
122
+ *note: this is a valid C-code, but in real AVR-controllers it may not work, because avr-libraries are still in development*
123
+
124
+ ### Some rake helpers
125
+ For create new project RubimC gem support command "generate" (of just "g"). For example:
126
+ ```sh
127
+ rubimc generate mcu "BrainControll.rb" type:attiny13 # create template "BrainControll.rb" for AVR microcontroller 'attiny13'
128
+ rubimc g mcu FirstProg # create template "FirstProg.rb" for unknown microcontroller
129
+ rubimc g clearC Example # create template "Example.rb" for generate clear C code
130
+ ```
131
+
132
+ ### Some interesting idea
133
+ There is interesting idea for connect few microconrollers (IC) via some firmware interfaces, for example I2C or USB **(at this moment is not realized)**. This example will generate two binary files for each microcontroller.
134
+
135
+ ```ruby
136
+ class BrainController < AVR_atmega16
137
+ def initialize()
138
+ input :button, port: :A, pin: 6
139
+ # its a syntax surag of "@button = input name: 'button', port: 'A', pin: 6"
140
+ end
141
+
142
+ def main_loop() # infinit loop, it stop only when IC is reset
143
+ if want_to_say_hello?
144
+ LeftHandController.move_hand = "up" # transfer data to other controller
145
+ end
146
+ end
147
+
148
+ private # define user`s methods and classes
149
+ def want_to_say_hello?
150
+ @button.press?
151
+ end
152
+ end
153
+
154
+ class LeftHandController < AVR_attiny13
155
+ attr_accessor :move_hand, via: "I2C" # I2C - fireware data-transfer bus
156
+
157
+ def move_hand=(message) # execute when command is received
158
+ @led.toggle
159
+ if message=="up"
160
+ # ...run motor...
161
+ end
162
+ end
163
+
164
+ def initialize()
165
+ output :led, port: :B, pin: 3
166
+ end
167
+
168
+ def main_loop() # infinit loop, it stop only when IC is reset
169
+ end
170
+ end
171
+ ```
172
+
173
+ ### Help
174
+ If you interested the project and find some bugs, you may to write the tests and we try to fix it. Examples of tests is placed in folder *test*. To run tests use command *"rspec test/test_all.rb"*. Of course if you try to modify core and libraries it will be wonder.
175
+
176
+ Thank you!
data/bin/rubimc ADDED
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Example:
4
+ #
5
+ # rubimc generate mcu "Brain.rb" type:attiny13
6
+ # rubimc g mcu FirstProg
7
+ # rubimc g clearC Example
8
+ #
9
+ # rubimc compile "BrainControll.rb"
10
+ # rubimc compile BrainControll
11
+ # rubimc compile --all
12
+ #
13
+
14
+ require 'rake'
15
+
16
+ gem_dir = File.expand_path("..", File.dirname(__FILE__))
17
+ $LOAD_PATH.unshift gem_dir # Look in gem directory for resources first
18
+
19
+ exec_type = ARGV[0]
20
+ if exec_type == 'generate' or exec_type == 'g'
21
+ gen_type = ARGV[1]
22
+ if gen_type == 'mcu' # generate template for MCU
23
+ name = ARGV[2]
24
+ if name.nil?
25
+ puts "ERROR: you must define mcu name"
26
+ puts "for example: 'rubimc g mcu ExampleProg'"
27
+ exit 1
28
+ end
29
+
30
+ parent = "Unknown_MCU"
31
+ type_param = ARGV[3] # ToDo: parse params
32
+ unless type_param.nil?
33
+ require 'rubimc'
34
+ RubimCode::Printer.sandbox = true
35
+
36
+ type_param = type_param.gsub(/type:/, "")
37
+ mcu = Controllers.find_mcu("#{type_param}").first
38
+ if mcu.nil?
39
+ puts "ERROR: mcu type '#{type_param}'' not found in rubimc libraries"
40
+ exit 1
41
+ end
42
+ series, type = mcu::MCU_SERIES, mcu::MCU_NAME
43
+ parent = "#{series}_#{type}"
44
+ end
45
+
46
+ generated_text = "
47
+ require 'rubimc'
48
+
49
+ class #{name} < #{parent}
50
+ def initialize
51
+ end
52
+
53
+ def main_loop # infinit loop, it stop only when IC is reset
54
+ end
55
+ end"
56
+
57
+ elsif gen_type == 'clearC' # generate template for clear C code
58
+ name = ARGV[2]
59
+ if name.nil?
60
+ puts "ERROR: you must define file name"
61
+ puts "for example: 'rubimc g clearC ExampleProg'"
62
+ exit 1
63
+ end
64
+
65
+ generated_text = "
66
+ require 'rubimc'
67
+
68
+ def main(argv)
69
+ end"
70
+ else
71
+ puts "ERROR: unknown param '#{gen_type}'"
72
+ puts "Available params for generator: mcu; clearC"
73
+ exit 1
74
+ end
75
+
76
+ generated_text.gsub!(/\t\t\t/, "")
77
+ generated_text = generated_text[1..-1]
78
+ if File.exist?("#{name}.rb")
79
+ puts "WARNING: file '#{name}.rb' already exist. Overrire it (y/n)?"
80
+ answer = STDIN.gets
81
+ exit 1 if ["n\n", "N\n"].include? answer
82
+ end
83
+ File.open("#{name}.rb", 'w') {|file| file.puts(generated_text) }
84
+ # => end generate section
85
+
86
+ elsif exec_type == 'compile'
87
+
88
+ puts "\n === Run RubimC compiler === "
89
+
90
+ input_files = []
91
+ if ARGV[1] == '-all' or ARGV[1] == '--all'
92
+ Dir['*.rb'].each do |file|
93
+ input_files << file
94
+ end
95
+ else
96
+ ARGV[1] += '.rb' if File.extname(ARGV[1]).empty?
97
+ input_files << ARGV[1]
98
+ end
99
+ ARGV.clear
100
+
101
+ unless input_files.any?
102
+ puts 'No files to compile...'
103
+ exit 1
104
+ end
105
+
106
+ need_to_clear_release = true
107
+ input_files.each do |input_file|
108
+ # === check file exist ===
109
+ unless File.exist?(input_file)
110
+ puts "ERROR: File \"#{input_file}\" not found"
111
+ puts "Use 'rubimc compile --all' to compile all files in current directory"
112
+ puts ""
113
+ exit 1
114
+ end
115
+
116
+ # === check syntax of user program ===
117
+ print "Check syntax..."
118
+ sh "ruby -c '#{input_file}'", verbose: false do |ok, res|
119
+ exit 1 unless ok # check exit status after command runs
120
+ end
121
+
122
+ # === prepare file names ===
123
+ input_file = File.expand_path(input_file)
124
+ basename = File.basename(input_file, ".rb") # extract filename without path and extention
125
+ dirname = File.dirname(input_file)
126
+ release_folder = "#{dirname}/release/"
127
+ outfile = "#{dirname}/release/#{basename}"
128
+
129
+ # === clear directory "release" ===
130
+ if need_to_clear_release
131
+ need_to_clear_release = false
132
+ FileUtils.rm_rf(Dir.glob("#{release_folder}/*"))
133
+ end
134
+ Dir.mkdir("#{release_folder}/") unless Dir.exists?("#{release_folder}/")
135
+
136
+ # === preprocessing user`s program ===
137
+ print "preprocessing file \"#{basename}.rb\"..."
138
+ require 'rubimc/preprocessor'
139
+ PreProcessor.write_in_file(input_file, dirname, basename, "#{outfile}.rb")
140
+
141
+ # === check type: gcc/avr-gcc
142
+ require "#{outfile}.rb"
143
+ unless defined? RubimCode
144
+ puts "ERROR: file '#{outfile}.rb' is not rubimc program"
145
+ exit 1
146
+ end
147
+ RubimCode::Printer.sandbox = true
148
+ RubimCode::Printer.generate_cc
149
+ code_type = RubimCode::Printer.code_type
150
+ mcu_type = RubimCode::Printer.mcu_type
151
+
152
+ # === execute preprocessing program, generate C code ===
153
+ puts "generate C code"
154
+ sh "ruby '#{outfile}.rb' '#{outfile}'.c", verbose: false do |ok, res|
155
+ exit 1 unless ok # check exit status after command runs
156
+ end
157
+ puts "done"
158
+
159
+ # === compile C code to object-code and link to hex/exe ===
160
+ # ToDo: add DF_CPU (is it need?)
161
+ if code_type == "avr-gcc"
162
+ print "compile and link..."
163
+ sh "avr-gcc -std=c99 -Os -mmcu=#{mcu_type} -c '#{outfile}.c' -o '#{outfile}.o'", verbose: false do |ok, res|
164
+ exit 1 unless ok # check exit status after command runs
165
+ end
166
+ # generate hex for upload to MCU
167
+ sh "avr-objcopy -O ihex '#{outfile}.o' '#{outfile}.hex'", verbose: false do |ok, res|
168
+ exit 1 unless ok # check exit status after command runs
169
+ end
170
+ puts "done"
171
+
172
+ elsif code_type == "gcc"
173
+ print "compile and link..."
174
+ sh "gcc -std=c99 -o '#{outfile}.out' '#{outfile}.c'", verbose: false do |ok, res|
175
+ exit 1 unless ok # check exit status after command runs
176
+ end
177
+ puts "done"
178
+
179
+ print "run..."
180
+ sh "'#{outfile}.out'", verbose: false do |ok, res|
181
+ exit 1 unless ok # check exit status after command runs
182
+ end
183
+ end
184
+ end
185
+
186
+ else
187
+ puts "ERROR: unknown command for rubimc"
188
+ puts "Available commands: compile; generate;"
189
+ exit 1
190
+ end
@@ -0,0 +1,58 @@
1
+ class RubimCode
2
+ class << self
3
+
4
+ @@rubim_defined_values = []
5
+
6
+ def rubim_cond(cond, type="if", &block)
7
+ # ToDo: auto-define type of ret_value
8
+ # use:: __rubim__rval__int, __rubim__rval__float, e.t.
9
+
10
+ if @@rubim_defined_values.include? @level
11
+ pout "__rubim__rval#{@level} = 0;"
12
+ else
13
+ pout "int __rubim__rval#{@level} = 0;"
14
+ end
15
+ @@rubim_defined_values << @level
16
+
17
+ if type=="if"
18
+ pout "if (#{cond}) {"
19
+ elsif type=="unless"
20
+ pout "if (!(#{cond})) {"
21
+ end
22
+ @level += 1
23
+ ret_val = yield
24
+ pout "__rubim__rval#{@level-1} = #{ret_val};" if ret_val!="__rubim__noreturn"
25
+ pout "}"
26
+ @level -= 1
27
+ return "__rubim__rval#{@level}"
28
+ end
29
+
30
+ def rubim_cycle(type="while", cond="true", &block)
31
+ pout "#{type} (#{cond}) {"
32
+ @level+=1
33
+ yield
34
+ pout "}"
35
+ @level-=1
36
+ end
37
+
38
+ def rubim_if(cond, &block); rubim_cond(cond, "if", &block); end
39
+ def rubim_unless(cond, &block); rubim_cond(cond, "unless", &block); end
40
+ def rubim_while(cond, &block); rubim_cycle("while", cond, &block); end
41
+ def rubim_until(cond); rubim_cycle("until", cond, &block); end
42
+ def rubim_loop(&block); rubim_cycle("while", "true", &block); end
43
+
44
+ def rubim_whilemod(cond); pout "} while (#{cond});"; @level-=1; end
45
+ def rubim_untilmod(cond); pout "} until (#{cond});"; @level-=1; end
46
+
47
+ def rubim_ifmod(cond); pout "if (#{cond}) {"; @level+=1; true; end
48
+ def rubim_unlessmod(cond); pout "if (!(#{cond})) {"; @level+=1; true; end
49
+ def rubim_begin(); pout "{"; @level+=1; true; end
50
+ def rubim_end(); pout "}"; @level-=1; "__rubim__noreturn"; end
51
+ def rubim_tmpif(tmp); end
52
+
53
+ def rubim_else(); @level-=1; pout "} else {"; @level+=1; end
54
+ def rubim_elsif(cond); @level-=1; pout "} else if (#{cond}) {"; @level+=1; end
55
+ # ToDo: set return_val, like in rubim_if (need to change preprocessor)
56
+
57
+ end # class << self
58
+ end # RubimCode class