rad 0.2.2 → 0.2.9
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.
- data/History.txt +34 -0
- data/Manifest.txt +113 -7
- data/{README.txt → README.rdoc} +17 -5
- data/Rakefile +3 -0
- data/bin/rad +106 -1
- data/lib/examples/add_hysteresis.rb +13 -0
- data/lib/examples/basic_blink.rb +10 -0
- data/lib/examples/blink_m_address_assignment.rb +104 -0
- data/lib/examples/blink_m_hello.rb +14 -0
- data/lib/examples/blink_m_multi.rb +61 -0
- data/lib/examples/blink_with_serial.rb +16 -0
- data/lib/examples/configure_pa_lcd_boot.rb +91 -0
- data/lib/examples/debounce_methods.rb +49 -0
- data/lib/examples/external_variable_fu.rb +26 -0
- data/lib/examples/external_variables.rb +32 -0
- data/lib/examples/first_sound.rb +23 -0
- data/lib/examples/frequency_generator.rb +30 -0
- data/lib/examples/hello_array.rb +48 -0
- data/lib/examples/hello_array2.rb +79 -0
- data/lib/examples/hello_array_eeprom.rb +59 -0
- data/lib/examples/hello_clock.rb +84 -0
- data/lib/examples/hello_eeprom.rb +51 -0
- data/lib/examples/hello_eeprom_lcdpa.rb +81 -0
- data/lib/examples/hello_format_print.rb +94 -0
- data/lib/examples/hello_lcd_charset.rb +75 -0
- data/lib/examples/hello_pa_lcd.rb +59 -0
- data/lib/examples/hello_servos.rb +88 -0
- data/lib/examples/hello_spectra_sound.rb +38 -0
- data/lib/examples/hello_world.rb +11 -0
- data/lib/examples/hello_xbee.rb +12 -0
- data/lib/examples/hysteresis_duel.rb +39 -0
- data/lib/examples/i2c_with_clock_chip.rb +124 -0
- data/lib/examples/midi_beat_box.rb +86 -0
- data/lib/examples/midi_scales.rb +94 -0
- data/lib/examples/motor_knob.rb +30 -0
- data/lib/examples/servo_buttons.rb +23 -0
- data/lib/examples/servo_calibrate_continuous.rb +92 -0
- data/lib/examples/servo_throttle.rb +40 -0
- data/lib/examples/sparkfun_lcd.rb +48 -0
- data/lib/examples/spectra_soft_pot.rb +34 -0
- data/lib/examples/times_method.rb +8 -0
- data/lib/examples/toggle.rb +10 -0
- data/lib/examples/twitter.rb +57 -0
- data/lib/examples/two_wire.rb +14 -0
- data/lib/libraries/AFSoftSerial/AFSoftSerial.cpp +321 -0
- data/lib/libraries/AFSoftSerial/AFSoftSerial.h +61 -0
- data/lib/libraries/AFSoftSerial/keywords.txt +18 -0
- data/lib/libraries/AF_XPort/AF_XPort.cpp +166 -0
- data/lib/libraries/AF_XPort/AF_XPort.h +48 -0
- data/lib/libraries/DS1307/DS1307.cpp +162 -0
- data/lib/libraries/DS1307/DS1307.h +66 -0
- data/lib/libraries/{SWSerLCDpa → DS1307}/keywords.txt +1 -1
- data/lib/libraries/FrequencyTimer2/FrequencyTimer2.cpp +144 -0
- data/lib/libraries/FrequencyTimer2/FrequencyTimer2.h +42 -0
- data/lib/libraries/FrequencyTimer2/keywords.txt +22 -0
- data/lib/libraries/I2CEEPROM/I2CEEPROM.cpp +120 -0
- data/lib/libraries/I2CEEPROM/I2CEEPROM.h +70 -0
- data/lib/libraries/I2CEEPROM/keywords.txt +21 -0
- data/lib/libraries/LoopTimer/LoopTimer.cpp +35 -0
- data/lib/libraries/LoopTimer/LoopTimer.h +34 -0
- data/lib/libraries/LoopTimer/keywords.txt +27 -0
- data/lib/libraries/OneWire/OneWire.cpp +194 -0
- data/lib/libraries/OneWire/OneWire.h +63 -0
- data/lib/libraries/OneWire/keywords.txt +35 -0
- data/lib/libraries/OneWire/readme.txt +13 -0
- data/lib/libraries/SWSerLCDpa/SWSerLCDpa.cpp +93 -47
- data/lib/libraries/SWSerLCDpa/SWSerLCDpa.h +16 -9
- data/lib/libraries/SWSerLCDsf/SWSerLCDsf.cpp +311 -0
- data/lib/libraries/SWSerLCDsf/SWSerLCDsf.h +67 -0
- data/lib/libraries/Servo/Servo.cpp +192 -0
- data/lib/libraries/Servo/Servo.h +61 -0
- data/lib/libraries/Stepper/Stepper.cpp +220 -0
- data/lib/libraries/Stepper/Stepper.h +86 -0
- data/lib/libraries/Stepper/keywords.txt +28 -0
- data/lib/libraries/Wire/Wire.cpp +262 -0
- data/lib/libraries/Wire/Wire.h +67 -0
- data/lib/libraries/Wire/keywords.txt +31 -0
- data/lib/libraries/Wire/twi.h +57 -0
- data/lib/libraries/Wire/utility/twi.c +449 -0
- data/lib/libraries/Wire/utility/twi.h +57 -0
- data/lib/plugins/bitwise_ops.rb +54 -0
- data/lib/plugins/blink.rb +25 -0
- data/lib/plugins/blink_m.rb +356 -0
- data/lib/plugins/debounce.rb +138 -0
- data/lib/plugins/debug_output_to_lcd.rb +71 -0
- data/lib/plugins/hysteresis.rb +52 -0
- data/lib/plugins/input_output_state.rb +84 -0
- data/lib/plugins/lcd_padding.rb +58 -0
- data/lib/plugins/mem_test.rb +37 -0
- data/lib/plugins/midi.rb +60 -0
- data/lib/plugins/parallax_ping.rb +50 -0
- data/lib/plugins/servo_pulse.rb +31 -0
- data/lib/plugins/servo_setup.rb +86 -0
- data/lib/plugins/smoother.rb +54 -0
- data/lib/plugins/spark_fun_serial_lcd.rb +100 -0
- data/lib/plugins/spectra_symbol.rb +79 -0
- data/lib/plugins/twitter_connect.rb +145 -0
- data/lib/rad/README.rdoc +5 -0
- data/lib/rad/arduino_plugin.rb +246 -0
- data/lib/rad/arduino_sketch.rb +351 -257
- data/lib/rad/generators/makefile/makefile.erb +1 -1
- data/lib/rad/generators/makefile/makefile.rb +9 -10
- data/lib/rad/hardware_library.rb +813 -0
- data/lib/rad/init.rb +3 -1
- data/lib/rad/rad_processor.rb +128 -0
- data/lib/rad/rad_rewriter.rb +94 -0
- data/lib/rad/rad_type_checker.rb +26 -0
- data/lib/rad/sim/arduino_sketch.rb +57 -0
- data/lib/rad/sketch_compiler.rb +47 -0
- data/lib/rad/tasks/build_and_make.rake +146 -24
- data/lib/rad/variable_processing.rb +153 -0
- data/lib/rad/version.rb +1 -1
- data/spec/examples/hello_world.rb +11 -0
- data/spec/examples/serial_motor.rb +12 -0
- data/spec/models/sketch_compiler_spec.rb +96 -0
- data/spec/sim/hello_world_spec.rb +42 -0
- data/test/test_array_processing.rb +179 -0
- data/test/test_plugin_loading.rb +151 -0
- data/test/test_translation_post_processing.rb +185 -0
- data/test/test_variable_processing.rb +238 -0
- data/website/index.html +22 -7
- data/website/stylesheets/screen.css +32 -1
- metadata +130 -13
data/lib/rad/init.rb
CHANGED
|
@@ -5,8 +5,10 @@ unless defined?(PROJECT_DIR_NAME)
|
|
|
5
5
|
PROJECT_DIR_NAME = a[a.length-1]
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
PLUGIN_C_VAR_TYPES = "int|void|unsigned|long|short|uint8_t|static|byte|char\\*|uint8_t"
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
%w(generators/makefile/makefile.rb rad_processor.rb rad_rewriter.rb rad_type_checker.rb variable_processing.rb arduino_sketch.rb arduino_plugin.rb hardware_library.rb tasks/rad.rb sketch_compiler.rb).each do |path|
|
|
10
12
|
require File.expand_path("#{RAD_ROOT}/vendor/rad/#{path}")
|
|
11
13
|
end
|
|
12
14
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'ruby_to_ansi_c'
|
|
3
|
+
|
|
4
|
+
class RADProcessor < RubyToAnsiC
|
|
5
|
+
|
|
6
|
+
def self.translator
|
|
7
|
+
unless defined? @translator then
|
|
8
|
+
@translator = CompositeSexpProcessor.new
|
|
9
|
+
@translator << RADRewriter.new
|
|
10
|
+
@translator << RADTypeChecker.new
|
|
11
|
+
@translator << R2CRewriter.new
|
|
12
|
+
@translator << self.new
|
|
13
|
+
@translator.on_error_in(:defn) do |processor, exp, err|
|
|
14
|
+
result = processor.expected.new
|
|
15
|
+
case result
|
|
16
|
+
when Array then
|
|
17
|
+
result << :error
|
|
18
|
+
end
|
|
19
|
+
msg = "// ERROR: #{err.class}: #{err}"
|
|
20
|
+
msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
|
|
21
|
+
msg += " from #{caller.join(', ')}" unless $TESTING
|
|
22
|
+
result << msg
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
@translator
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def process_iasgn(exp)
|
|
30
|
+
name = exp.shift
|
|
31
|
+
val = process exp.shift
|
|
32
|
+
"__#{name.to_s.sub(/^@/, '')} = #{val}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def process_ivar(exp)
|
|
36
|
+
name = exp.shift
|
|
37
|
+
"__#{name.to_s.sub(/^@/, '')}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def process_iter(exp)
|
|
41
|
+
# the array identifer may be in one of two locations
|
|
42
|
+
# when using the instance variable (ivar) style it is located at exp[0][1][1]
|
|
43
|
+
if exp[0][1][1]
|
|
44
|
+
enum = exp[0][1][1]
|
|
45
|
+
enum = "__#{enum.to_s.sub(/^@/, '')}" if enum.to_s =~ /^@/
|
|
46
|
+
# for local variables it is located at exp[0][1][2]
|
|
47
|
+
elsif exp[0][1][2]
|
|
48
|
+
enum = exp[0][1][2]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
out = []
|
|
52
|
+
# Only support enums in C-land # not sure if this comment if valid anymore
|
|
53
|
+
raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly
|
|
54
|
+
@env.scope do
|
|
55
|
+
|
|
56
|
+
call = process exp.shift
|
|
57
|
+
var = process(exp.shift).intern # semi-HACK-y
|
|
58
|
+
body = process exp.shift
|
|
59
|
+
|
|
60
|
+
# array types from varible_processing>post_process_arrays and arduino_sketch>array
|
|
61
|
+
$array_types.each do |k,v|
|
|
62
|
+
@array_type = v if k == enum.to_s.sub(/^__/,"")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
index_helper = $array_index_helpers.shift
|
|
66
|
+
index = "index_#{index_helper}" # solves redeclaration issue
|
|
67
|
+
|
|
68
|
+
body += ";" unless body =~ /[;}]\Z/
|
|
69
|
+
body.gsub!(/\n\n+/, "\n")
|
|
70
|
+
|
|
71
|
+
out << "unsigned int #{index};" # shouldn't need more than int
|
|
72
|
+
out << "for (#{index} = 0; #{index} < (int) (sizeof(#{enum}) / sizeof(#{enum}[0])); #{index}++) {"
|
|
73
|
+
out << "#{@array_type} #{var} = #{enum}[#{index}];"
|
|
74
|
+
out << body
|
|
75
|
+
out << "}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
return out.join("\n")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def process_lasgn(exp)
|
|
82
|
+
out = ""
|
|
83
|
+
|
|
84
|
+
var = exp.shift
|
|
85
|
+
value = exp.shift
|
|
86
|
+
# grab the size of the args, if any, before process converts to a string
|
|
87
|
+
arg_count = 0
|
|
88
|
+
arg_count = value.length - 1 if value.first == :array
|
|
89
|
+
args = value
|
|
90
|
+
|
|
91
|
+
exp_type = exp.sexp_type
|
|
92
|
+
@env.add var.to_sym, exp_type
|
|
93
|
+
var_type = self.class.c_type exp_type
|
|
94
|
+
|
|
95
|
+
if exp_type.list? then
|
|
96
|
+
assert_type args, :array
|
|
97
|
+
|
|
98
|
+
raise "array must be of one type" unless args.sexp_type == Type.homo
|
|
99
|
+
|
|
100
|
+
# HACK: until we figure out properly what to do w/ zarray
|
|
101
|
+
# before we know what its type is, we will default to long.
|
|
102
|
+
array_type = args.sexp_types.empty? ? 'void *' : self.class.c_type(args.sexp_types.first)
|
|
103
|
+
# we can fix array here..
|
|
104
|
+
args.shift # :arglist
|
|
105
|
+
out << "#{var} = (#{array_type}) malloc(sizeof(#{array_type}) * #{args.length});\n"
|
|
106
|
+
args.each_with_index do |o,i|
|
|
107
|
+
out << "#{var}[#{i}] = #{process o};\n"
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
out << "#{var} = #{process args}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
out.sub!(/;\n\Z/, '')
|
|
114
|
+
|
|
115
|
+
return out
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def process_str(exp)
|
|
119
|
+
s = exp.shift.gsub(/\n/, '\\n')
|
|
120
|
+
if s.size == 1
|
|
121
|
+
return "\'#{s}\'"
|
|
122
|
+
else
|
|
123
|
+
return "\"#{s}\""
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'ruby_to_ansi_c'
|
|
2
|
+
|
|
3
|
+
class RADRewriter < Rewriter
|
|
4
|
+
|
|
5
|
+
def process_iter(exp)
|
|
6
|
+
call = process exp.shift
|
|
7
|
+
var = process exp.shift
|
|
8
|
+
body = process exp.shift
|
|
9
|
+
|
|
10
|
+
var = s(:dasgn_curr, Unique.next) if var.nil?
|
|
11
|
+
|
|
12
|
+
assert_type call, :call
|
|
13
|
+
|
|
14
|
+
if call[2] != :each then # TODO: fix call[n] (api)
|
|
15
|
+
call.shift # :call
|
|
16
|
+
lhs = call.shift
|
|
17
|
+
method_name = call.shift
|
|
18
|
+
|
|
19
|
+
case method_name
|
|
20
|
+
when :downto then
|
|
21
|
+
var.shift #
|
|
22
|
+
start_value = lhs
|
|
23
|
+
finish_value = call.pop.pop # not sure about this
|
|
24
|
+
var_name = var.shift
|
|
25
|
+
body.find_and_replace_all(:dvar, :lvar)
|
|
26
|
+
result = s(:dummy,
|
|
27
|
+
s(:lasgn, var_name, start_value),
|
|
28
|
+
s(:while,
|
|
29
|
+
s(:call, s(:lvar, var_name), :>=,
|
|
30
|
+
s(:arglist, finish_value)),
|
|
31
|
+
s(:block,
|
|
32
|
+
body,
|
|
33
|
+
s(:lasgn, var_name,
|
|
34
|
+
s(:call, s(:lvar, var_name), :-,
|
|
35
|
+
s(:arglist, s(:lit, 1))))), true))
|
|
36
|
+
when :upto then
|
|
37
|
+
# REFACTOR: completely duped from above and direction changed
|
|
38
|
+
var.shift #
|
|
39
|
+
start_value = lhs
|
|
40
|
+
finish_value = call.pop.pop # not sure about this
|
|
41
|
+
var_name = var.shift
|
|
42
|
+
body.find_and_replace_all(:dvar, :lvar)
|
|
43
|
+
result = s(:dummy,
|
|
44
|
+
s(:lasgn, var_name, start_value),
|
|
45
|
+
s(:while,
|
|
46
|
+
s(:call, s(:lvar, var_name), :<=,
|
|
47
|
+
s(:arglist, finish_value)),
|
|
48
|
+
s(:block,
|
|
49
|
+
body,
|
|
50
|
+
s(:lasgn, var_name,
|
|
51
|
+
s(:call, s(:lvar, var_name), :+,
|
|
52
|
+
s(:arglist, s(:lit, 1))))), true))
|
|
53
|
+
when :times then
|
|
54
|
+
# REFACTOR: mostly duped from above and gave default start value of 0
|
|
55
|
+
# and a finish value that was the start value above
|
|
56
|
+
var.shift
|
|
57
|
+
start_value = s(:lit, 0)
|
|
58
|
+
finish_value = lhs
|
|
59
|
+
var_name = var.shift
|
|
60
|
+
body.find_and_replace_all(:dvar, :lvar)
|
|
61
|
+
result = s(:dummy,
|
|
62
|
+
s(:lasgn, var_name, start_value),
|
|
63
|
+
s(:while,
|
|
64
|
+
s(:call, s(:lvar, var_name), :<,
|
|
65
|
+
s(:arglist, finish_value)),
|
|
66
|
+
s(:block,
|
|
67
|
+
body,
|
|
68
|
+
s(:lasgn, var_name,
|
|
69
|
+
s(:call, s(:lvar, var_name), :+,
|
|
70
|
+
s(:arglist, s(:lit, 1))))), true))
|
|
71
|
+
when :define_method then
|
|
72
|
+
# BEFORE: [:iter, [:call, nil, :define_method, [:array, [:lit, :bmethod_added]]], [:dasgn_curr, :x], [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]
|
|
73
|
+
# we want to get it rewritten for the scope/block context, so:
|
|
74
|
+
# - throw call away
|
|
75
|
+
# - rewrite to args
|
|
76
|
+
# - plop body into a scope
|
|
77
|
+
# AFTER: [:block, [:args, :x], [:call, [:lvar, :x], :+, [:arglist, [:lit, 1]]]]
|
|
78
|
+
var.find_and_replace_all(:dasgn_curr, :args)
|
|
79
|
+
body.find_and_replace_all(:dvar, :lvar)
|
|
80
|
+
result = s(:block, var, body)
|
|
81
|
+
else
|
|
82
|
+
# HACK we butchered call up top
|
|
83
|
+
result = s(:iter, s(:call, lhs, method_name, call.shift), var, body)
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
if var.nil? then
|
|
87
|
+
var = s(:lvar, Unique.next)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
s(:iter, call, var, body)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'ruby_to_ansi_c'
|
|
2
|
+
|
|
3
|
+
class RADTypeChecker < TypeChecker
|
|
4
|
+
|
|
5
|
+
def process_const(exp)
|
|
6
|
+
c = exp.shift
|
|
7
|
+
if c.to_s =~ /^[A-Z]/ then
|
|
8
|
+
# TODO: validate that it really is a const?
|
|
9
|
+
# uber hackery
|
|
10
|
+
# since constants are defined in the arduino_sketch define method and
|
|
11
|
+
# we can't inject them into the methods
|
|
12
|
+
# transport them here with a $define_types hash
|
|
13
|
+
|
|
14
|
+
$define_types.each do |k,v|
|
|
15
|
+
if k == c.to_s
|
|
16
|
+
@const_type = eval "Type.#{v}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
return t(:const, c, @const_type)
|
|
20
|
+
else
|
|
21
|
+
raise "I don't know what to do with const #{c.inspect}. It doesn't look like a class."
|
|
22
|
+
end
|
|
23
|
+
raise "need to finish process_const in #{self.class}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
ON = true
|
|
2
|
+
OFF = !ON
|
|
3
|
+
HIGH = ON
|
|
4
|
+
LOW = !HIGH
|
|
5
|
+
|
|
6
|
+
class ArduinoSketch
|
|
7
|
+
attr_accessor :pins
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@pins = self.class.instance_variable_get("@pins")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.output_pin(num, opts)
|
|
14
|
+
module_eval "@pins ||= []"
|
|
15
|
+
module_eval do
|
|
16
|
+
@pins << Pin.new( num, :type => :output )
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if opts[:as]
|
|
20
|
+
module_eval <<-CODE
|
|
21
|
+
def #{opts[:as]}
|
|
22
|
+
pins.select{|p| p.num == #{num}}.first
|
|
23
|
+
end
|
|
24
|
+
CODE
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def loop
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def digitalWrite( pin, value )
|
|
32
|
+
to_change = pins.select{|p| p.num == pin.num}.first
|
|
33
|
+
to_change.value = value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delay( millis )
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# def serial_read
|
|
40
|
+
# end
|
|
41
|
+
|
|
42
|
+
# def serial_available
|
|
43
|
+
# end
|
|
44
|
+
|
|
45
|
+
# def blink
|
|
46
|
+
# end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Pin
|
|
50
|
+
attr_accessor :num, :type, :value
|
|
51
|
+
|
|
52
|
+
def initialize num, opts
|
|
53
|
+
@num = num
|
|
54
|
+
@type = opts[:type]
|
|
55
|
+
@value = opts[:value] || false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# TODO:
|
|
2
|
+
# compilation
|
|
3
|
+
# gather pieces of code we need as strings
|
|
4
|
+
# translate non-loop methods
|
|
5
|
+
# do plugin stuff
|
|
6
|
+
# deal with examples/ exception
|
|
7
|
+
# manage upload process
|
|
8
|
+
# compose_setup should move in here entirely
|
|
9
|
+
|
|
10
|
+
# require 'arduino_sketch'
|
|
11
|
+
|
|
12
|
+
class SketchCompiler
|
|
13
|
+
attr_accessor :path, :body, :klass, :target_dir, :name
|
|
14
|
+
|
|
15
|
+
def initialize path_to_sketch
|
|
16
|
+
@path = File.expand_path(path_to_sketch)
|
|
17
|
+
@body = open(@path).read
|
|
18
|
+
@name = @path.split("/").last.split(".").first
|
|
19
|
+
@klass = @name.split(".").first.split("_").collect{|c| c.capitalize}.join("")
|
|
20
|
+
@target_dir = parent_dir
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parent_dir
|
|
24
|
+
self.path.split("/")[0..@path.split("/").length-2].join("/")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def build_dir
|
|
28
|
+
"#{self.target_dir}/#{self.name}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_build_dir! optional_path_prefix=nil
|
|
32
|
+
self.target_dir = optional_path_prefix if optional_path_prefix
|
|
33
|
+
mkdir_p build_dir
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def process_constants
|
|
37
|
+
self.body.gsub!("HIGH", "1")
|
|
38
|
+
self.body.gsub!("LOW", "0")
|
|
39
|
+
self.body.gsub!("ON", "1")
|
|
40
|
+
self.body.gsub!("OFF", "0")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def sketch_methods
|
|
44
|
+
self.body.scan(/^\s*def\s.\w*/).collect{ |m| m.gsub(/\s*def\s*/, "") }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/../init.rb")
|
|
2
2
|
require 'ruby_to_ansi_c'
|
|
3
3
|
|
|
4
|
+
C_VAR_TYPES = "unsigned|int|long|double|str|char|byte|bool"
|
|
5
|
+
|
|
6
|
+
# incredibly primitive tests
|
|
7
|
+
# rake test:compile or rake test:upload
|
|
8
|
+
# runs through all sketches in the example directory
|
|
9
|
+
|
|
10
|
+
def run_tests(sketch, type)
|
|
11
|
+
sh %{rake make:#{type} sketch=examples/#{sketch}}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
namespace :test do
|
|
15
|
+
|
|
16
|
+
desc "iterate through all the sketches in the example directory"
|
|
17
|
+
task :upload => :gather do
|
|
18
|
+
@examples.each {|e| run_tests(e, "upload")}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
task :compile => :gather do
|
|
22
|
+
|
|
23
|
+
@examples.each {|e| run_tests(e, "compile")}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "gather all tests"
|
|
27
|
+
task :gather do # => "make:upload" do
|
|
28
|
+
@examples = []
|
|
29
|
+
if ENV['sketch']
|
|
30
|
+
@examples << ENV['sketch']
|
|
31
|
+
else
|
|
32
|
+
Dir.entries( File.expand_path("#{RAD_ROOT}/examples/") ).each do |f|
|
|
33
|
+
@examples << f.split('.').first if (f =~ /\.rb$/)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
4
41
|
namespace :make do
|
|
5
42
|
|
|
6
43
|
desc "compile the sketch and then upload it to your Arduino board"
|
|
@@ -9,70 +46,155 @@ namespace :make do
|
|
|
9
46
|
puts "Reset the Arduino and hit enter.\n==If your board doesn't need it, you can turn off this prompt in config/software.yml=="
|
|
10
47
|
STDIN.gets.chomp
|
|
11
48
|
end
|
|
12
|
-
sh %{cd #{
|
|
49
|
+
sh %{cd #{@sketch.build_dir}; make upload}
|
|
13
50
|
end
|
|
14
|
-
|
|
51
|
+
|
|
15
52
|
desc "generate a makefile and use it to compile the .cpp"
|
|
16
53
|
task :compile => [:clean_sketch_dir, "build:sketch"] do # should also depend on "build:sketch"
|
|
17
|
-
Makefile.compose_for_sketch( @
|
|
54
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
|
55
|
+
|
|
56
|
+
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
|
57
|
+
sh %{cd #{@sketch.build_dir}; make depend; make}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc "generate a makefile and use it to compile the .cpp using the current .cpp file"
|
|
61
|
+
task :compile_cpp => ["build:sketch_dir", "build:gather_required_plugins", "build:plugin_setup", "build:setup", :clean_sketch_dir] do # should also depend on "build:sketch"
|
|
62
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
|
18
63
|
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
|
19
|
-
sh %{cd #{
|
|
64
|
+
sh %{cd #{@sketch.build_dir}; make depend; make}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
desc "generate a makefile and use it to compile the .cpp and upload it using current .cpp file"
|
|
68
|
+
task :upload_cpp => ["build:sketch_dir", "build:gather_required_plugins", "build:plugin_setup", "build:setup", :clean_sketch_dir] do # should also depend on "build:sketch"
|
|
69
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
|
70
|
+
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
|
71
|
+
sh %{cd #{@sketch.build_dir}; make depend; make upload}
|
|
20
72
|
end
|
|
21
73
|
|
|
22
74
|
task :clean_sketch_dir => ["build:file_list", "build:sketch_dir"] do
|
|
23
|
-
@
|
|
24
|
-
|
|
25
|
-
FileList.new(Dir.entries("#{RAD_ROOT}/#{@sketch_name}")).exclude("#{@sketch_name}.cpp").exclude(/^\./).each do |f|
|
|
26
|
-
sh %{rm #{RAD_ROOT}/#{@sketch_name}/#{f}}
|
|
75
|
+
FileList.new(Dir.entries("#{@sketch.build_dir}")).exclude("#{@sketch.name}.cpp").exclude(/^\./).each do |f|
|
|
76
|
+
sh %{rm #{@sketch.build_dir}/#{f}}
|
|
27
77
|
end
|
|
28
78
|
end
|
|
79
|
+
|
|
29
80
|
end
|
|
30
81
|
|
|
31
82
|
namespace :build do
|
|
32
83
|
|
|
33
84
|
desc "actually build the sketch"
|
|
34
|
-
task :sketch => [:file_list, :sketch_dir, :setup] do
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
85
|
+
task :sketch => [:file_list, :sketch_dir, :gather_required_plugins, :plugin_setup, :setup] do
|
|
86
|
+
c_methods = []
|
|
87
|
+
sketch_signatures = []
|
|
88
|
+
# until we better understand RubyToC let's see what's happening on errors
|
|
89
|
+
@sketch.sketch_methods.each do |meth|
|
|
90
|
+
raw_rtc_meth = RADProcessor.translate(constantize(@sketch.klass), meth)
|
|
91
|
+
puts "Translator Error: #{raw_rtc_meth.inspect}" if raw_rtc_meth =~ /\/\/ ERROR:/
|
|
92
|
+
c_methods << raw_rtc_meth unless meth == "setup"
|
|
93
|
+
# treat the setup method differently
|
|
94
|
+
@additional_setup = [] if meth == "setup"
|
|
95
|
+
raw_rtc_meth.each {|m| @additional_setup << ArduinoSketch.add_to_setup(m) } if meth == "setup"
|
|
96
|
+
end
|
|
97
|
+
c_methods.each {|meth| sketch_signatures << "#{meth.scan(/^\w*\s?\*?\n.*\)/)[0].gsub("\n", " ")};" }
|
|
98
|
+
clean_c_methods = []
|
|
99
|
+
# remove external variables that were previously injected
|
|
100
|
+
c_methods.join("\n").each { |meth| clean_c_methods << ArduinoSketch.post_process_ruby_to_c_methods(meth) }
|
|
101
|
+
c_methods_with_timer = clean_c_methods.join.gsub(/loop\(\)\s\{/,"loop() {")
|
|
102
|
+
# last chance to add/change setup
|
|
103
|
+
@setup[2] << sketch_signatures.join("\n") unless sketch_signatures.empty?
|
|
104
|
+
# add special setup method to existing setup if present
|
|
105
|
+
if @additional_setup
|
|
106
|
+
@setup[2] << "void additional_setup();" # declaration
|
|
107
|
+
@setup[4] << "\tadditional_setup();" # call from setup
|
|
108
|
+
@setup[5] << @additional_setup.join("") #
|
|
109
|
+
end
|
|
110
|
+
result = "#{@setup.join( "\n" )}\n#{c_methods_with_timer}\n"
|
|
111
|
+
File.open("#{@sketch.build_dir}/#{@sketch.name}.cpp", "w"){|f| f << result}
|
|
41
112
|
end
|
|
42
113
|
|
|
43
114
|
# needs to write the library include and the method signatures
|
|
44
115
|
desc "build setup function"
|
|
45
116
|
task :setup do
|
|
46
|
-
|
|
47
|
-
eval "class #{klass} < ArduinoSketch; end;"
|
|
117
|
+
eval "class #{@sketch.klass} < ArduinoSketch; end;"
|
|
48
118
|
|
|
49
|
-
@@as =
|
|
119
|
+
@@as = HardwareLibrary.new
|
|
50
120
|
|
|
51
121
|
delegate_methods = @@as.methods - Object.new.methods
|
|
52
122
|
delegate_methods.reject!{|m| m == "compose_setup"}
|
|
53
123
|
|
|
54
124
|
delegate_methods.each do |meth|
|
|
55
|
-
constantize(klass).module_eval <<-CODE
|
|
125
|
+
constantize(@sketch.klass).module_eval <<-CODE
|
|
56
126
|
def self.#{meth}(*args)
|
|
57
127
|
@@as.#{meth}(*args)
|
|
58
128
|
end
|
|
59
129
|
CODE
|
|
60
130
|
end
|
|
131
|
+
# allow variable declaration without quotes: @foo = int
|
|
132
|
+
["long","unsigned","int","byte","short"].each do |type|
|
|
133
|
+
constantize(@sketch.klass).module_eval <<-CODE
|
|
134
|
+
def self.#{type}
|
|
135
|
+
return "#{type}"
|
|
136
|
+
end
|
|
137
|
+
CODE
|
|
138
|
+
end
|
|
61
139
|
|
|
62
|
-
|
|
140
|
+
@sketch.process_constants
|
|
141
|
+
|
|
142
|
+
eval ArduinoSketch.pre_process(@sketch.body)
|
|
143
|
+
@@as.process_external_vars(constantize(@sketch.klass))
|
|
63
144
|
@setup = @@as.compose_setup
|
|
64
145
|
end
|
|
65
146
|
|
|
147
|
+
desc "add plugin methods"
|
|
148
|
+
task :plugin_setup do
|
|
149
|
+
$plugins_to_load.each do |name|
|
|
150
|
+
klass = name.split(".").first.split("_").collect{|c| c.capitalize}.join("")
|
|
151
|
+
eval "class #{klass} < ArduinoPlugin; end;"
|
|
152
|
+
|
|
153
|
+
@@ps = ArduinoPlugin.new
|
|
154
|
+
plugin_delegate_methods = @@ps.methods - Object.new.methods
|
|
155
|
+
plugin_delegate_methods.reject!{|m| m == "compose_setup"}
|
|
156
|
+
|
|
157
|
+
plugin_delegate_methods.each do |meth|
|
|
158
|
+
constantize(klass).module_eval <<-CODE
|
|
159
|
+
def self.#{meth}(*args)
|
|
160
|
+
@@ps.#{meth}(*args)
|
|
161
|
+
end
|
|
162
|
+
CODE
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
eval ArduinoPlugin.process(File.read("vendor/plugins/#{name}"))
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
@@no_plugins = ArduinoPlugin.new if @plugin_names.empty?
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
desc "determine which plugins to load based on use of methods in sketch"
|
|
172
|
+
task :gather_required_plugins do
|
|
173
|
+
@plugin_names.each do |name|
|
|
174
|
+
ArduinoPlugin.check_for_plugin_use(@sketch.body, File.read("vendor/plugins/#{name}"), name )
|
|
175
|
+
end
|
|
176
|
+
puts "#{$plugins_to_load.length} of #{$plugin_methods_hash.length} plugins are being loaded: #{$plugins_to_load.join(", ")}"
|
|
177
|
+
end
|
|
178
|
+
|
|
66
179
|
desc "setup target directory named after your sketch class"
|
|
67
180
|
task :sketch_dir => [:file_list] do
|
|
68
|
-
|
|
181
|
+
@sketch.create_build_dir!
|
|
69
182
|
end
|
|
70
183
|
|
|
71
184
|
task :file_list do
|
|
72
|
-
|
|
73
|
-
|
|
185
|
+
# take another look at this, since if the root directory name is changed, everything breaks
|
|
186
|
+
# perhaps we generate a constant when the project is generated an pop it here or in the init file
|
|
187
|
+
if ENV['sketch']
|
|
188
|
+
@sketch = SketchCompiler.new File.expand_path("#{ENV['sketch']}.rb")
|
|
189
|
+
else
|
|
190
|
+
# assume the only .rb file in the sketch dir is the sketch:
|
|
191
|
+
@sketch = SketchCompiler.new Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/../../../*.rb").first
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
@plugin_names = []
|
|
195
|
+
Dir.entries( File.expand_path("#{RAD_ROOT}/vendor/plugins/") ).each do |f|
|
|
74
196
|
if (f =~ /\.rb$/)
|
|
75
|
-
@
|
|
197
|
+
@plugin_names << f
|
|
76
198
|
end
|
|
77
199
|
end
|
|
78
200
|
end
|