Skip to content

Commit

Permalink
SketchCompiler does pre-processing for constants and creates the list…
Browse files Browse the repository at this point in the history
… of methods
  • Loading branch information
Greg Borenstein committed Aug 4, 2008
1 parent b0603cd commit b63bccb
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 88 deletions.
11 changes: 4 additions & 7 deletions lib/examples/basic_blink.rb
@@ -1,13 +1,10 @@
class BasicBlink < ArduinoSketch class BasicBlink < ArduinoSketch


# hello world (uncomment to run) # hello world (uncomment to run)


output_pin 13, :as => :led output_pin 13, :as => :led


def loop def loop
blink led, 100 blink led, 100
x = 4 x = 4
end end

end end
40 changes: 21 additions & 19 deletions lib/examples/external_variable_fu.rb
@@ -1,24 +1,26 @@
class ExternalVariableFu < ArduinoSketch class ExternalVariableFu < ArduinoSketch


@one = int @one = int
@two = long @two = long
@three = unsigned @three = unsigned
@four = short @four = short
@five = byte @five = byte
@six = 1 @six = 1
@seven = 1.2 @seven = 1.2
@eight = "0x00" @eight = "0x00"
@nine = "arduino" @nine = "arduino"
@ten = true @ten = true
@eleven = false @eleven = false
@twelve = "1, long" @twelve = "1, long"
@thirteen = "1, unsigned" @thirteen = "1, unsigned"
@fourteen = "1, byte" @fourteen = "1, byte"
@fifteen = HIGH
@sixteen = LOW
@seventeen = ON
@eighteen = OFF



def loop

delay @six
def loop end
delay @six
end


end end
3 changes: 0 additions & 3 deletions lib/examples/external_variables.rb
Expand Up @@ -14,10 +14,7 @@ class ExternalVariables < ArduinoSketch
@boom = "1, int" @boom = "1, int"
@rad = "1.00" @rad = "1.00"




def loop def loop

delay 1 delay 1
@foo = 2 @foo = 2
@foo = KOOL @foo = KOOL
Expand Down
6 changes: 3 additions & 3 deletions lib/examples/hello_world.rb
Expand Up @@ -2,9 +2,9 @@ class HelloWorld < ArduinoSketch


output_pin 13, :as => :led output_pin 13, :as => :led


def loop def loop
blink led, 100 blink led, 100
end end


end end


Expand Down
89 changes: 49 additions & 40 deletions lib/rad/arduino_sketch.rb
Expand Up @@ -186,11 +186,30 @@ def initialize #:nodoc:
@other_setup = [] # specifically, Serial.begin @other_setup = [] # specifically, Serial.begin
@assembler_declarations = [] @assembler_declarations = []
@accessors = [] @accessors = []
# @signatures = ["void blink();", "int main();", "void track_total_loop_time(void);", "unsigned long find_total_loop_time(void);"]
# @signatures = ["int main();", "void track_total_loop_time(void);", "unsigned long find_total_loop_time(void);"]
@signatures = ["int main();"] @signatures = ["int main();"]


helper_methods = [] helper_methods = []
# helper_methods << "void blink(int pin, int ms) {"
# helper_methods << "\tdigitalWrite( pin, HIGH );"
# helper_methods << "\tdelay( ms );"
# helper_methods << "\tdigitalWrite( pin, LOW );"
# helper_methods << "\tdelay( ms );"
# helper_methods << "}"
# helper_methods << "void track_total_loop_time(void)"
# helper_methods << "{"
# helper_methods << "\ttotal_loop_time = millis() - start_loop_time;"
# helper_methods << "\tstart_loop_time = millis();"
# helper_methods << "}"
# helper_methods << "unsigned long find_total_loop_time(void)"
# helper_methods << "{"
# helper_methods << "\nreturn total_loop_time;"
# helper_methods << "}"
@helper_methods = helper_methods.join( "\n" ) @helper_methods = helper_methods.join( "\n" )


# @declarations << "unsigned long start_loop_time = 0;"
# @declarations << "unsigned long total_loop_time = 0;"
end end


# Setup variables outside of the loop. Does some magic based on type of arguments. Subject to renaming. Use with caution. # Setup variables outside of the loop. Does some magic based on type of arguments. Subject to renaming. Use with caution.
Expand Down Expand Up @@ -548,23 +567,6 @@ def serial_begin(opts={})


end end


def formatted_print(opts={})

buffer_size = opts[:buffer_size] ? opts[:buffer_size] : 64

if opts[:as]
@@sprintf_inc ||= FALSE
if @@sprintf_inc == FALSE
@@sprintf_inc = TRUE
accessor = []
accessor << "\n#undef int\n#include <stdio.h>"
accessor << "#define write_line(...) sprintf(#{opts[:as]},__VA_ARGS__);"
@accessors << accessor.join( "\n" )
array("char #{opts[:as]}[#{buffer_size}]")
end
end
end

# Treat a pair of digital I/O pins as a serial line. See: http://www.arduino.cc/en/Tutorial/SoftwareSerial # Treat a pair of digital I/O pins as a serial line. See: http://www.arduino.cc/en/Tutorial/SoftwareSerial
def software_serial(rx, tx, opts={}) def software_serial(rx, tx, opts={})
raise ArgumentError, "can only define rx from Fixnum, got #{rx.class}" unless rx.is_a?(Fixnum) raise ArgumentError, "can only define rx from Fixnum, got #{rx.class}" unless rx.is_a?(Fixnum)
Expand Down Expand Up @@ -650,6 +652,34 @@ def swser_LCDpa(tx, opts={})
accessor << "void print( SWSerLCDpa& s, long i, int b ) {" accessor << "void print( SWSerLCDpa& s, long i, int b ) {"
accessor << "\treturn s.print( i, b );" accessor << "\treturn s.print( i, b );"
accessor << "}" accessor << "}"
# ------------------- print line generics -------------------------------
accessor << "void println(SWSerLCDpa& s) {"
accessor << "\treturn s.println();"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, uint8_t b ) {"
accessor << "\treturn s.println( b );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, char* str ) {"
accessor << "\treturn s.println( str );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, char c ) {"
accessor << "\treturn s.println( c );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, const char c[] ) {"
accessor << "\treturn s.println( c );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, int i ) {"
accessor << "\treturn s.println( i );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, long i ) {"
accessor << "\treturn s.println( i );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, unsigned long i ) {"
accessor << "\treturn s.println( i );"
accessor << "}"
accessor << "void println( SWSerLCDpa& s, long i, int b ) {"
accessor << "\treturn s.println( i, b );"
accessor << "}"
# ------------------ PA-LCD specific functions --------------------------------- # ------------------ PA-LCD specific functions ---------------------------------
accessor << "void clearscr(SWSerLCDpa& s) {" accessor << "void clearscr(SWSerLCDpa& s) {"
accessor << "\treturn s.clearscr();" accessor << "\treturn s.clearscr();"
Expand All @@ -660,9 +690,6 @@ def swser_LCDpa(tx, opts={})
accessor << "void clearscr(SWSerLCDpa& s, int n) {" accessor << "void clearscr(SWSerLCDpa& s, int n) {"
accessor << "\treturn s.clearscr(n);" accessor << "\treturn s.clearscr(n);"
accessor << "}" accessor << "}"
accessor << "void clearscr(SWSerLCDpa& s, long n, int b) {"
accessor << "\treturn s.clearscr(n, b);"
accessor << "}"
accessor << "void clearline(SWSerLCDpa& s, int line) {" accessor << "void clearline(SWSerLCDpa& s, int line) {"
accessor << "\treturn s.clearline( line );" accessor << "\treturn s.clearline( line );"
accessor << "}" accessor << "}"
Expand All @@ -672,9 +699,6 @@ def swser_LCDpa(tx, opts={})
accessor << "void clearline(SWSerLCDpa& s, int line, int n) {" accessor << "void clearline(SWSerLCDpa& s, int line, int n) {"
accessor << "\treturn s.clearline( line, n );" accessor << "\treturn s.clearline( line, n );"
accessor << "}" accessor << "}"
accessor << "void clearline(SWSerLCDpa& s, int line, long n, int b) {"
accessor << "\treturn s.clearline( line, n, b );"
accessor << "}"
accessor << "void home( SWSerLCDpa& s) {" accessor << "void home( SWSerLCDpa& s) {"
accessor << "\treturn s.home();" accessor << "\treturn s.home();"
accessor << "}" accessor << "}"
Expand All @@ -683,18 +707,12 @@ def swser_LCDpa(tx, opts={})
accessor << "}" accessor << "}"
accessor << "void home( SWSerLCDpa& s, int n) {" accessor << "void home( SWSerLCDpa& s, int n) {"
accessor << "\treturn s.home( n );" accessor << "\treturn s.home( n );"
accessor << "}"
accessor << "void home( SWSerLCDpa& s, long n, int b) {"
accessor << "\treturn s.home( n, b );"
accessor << "}" accessor << "}"
accessor << "void setxy( SWSerLCDpa& s, int x, int y) {" accessor << "void setxy( SWSerLCDpa& s, int x, int y) {"
accessor << "\treturn s.setxy( x, y );" accessor << "\treturn s.setxy( x, y );"
accessor << "}" accessor << "}"
accessor << "void setxy( SWSerLCDpa& s, int x, int y, const char *str) {" accessor << "void setxy( SWSerLCDpa& s, int x, int y, const char *str) {"
accessor << "\treturn s.setxy( x, y, str );" accessor << "\treturn s.setxy( x, y, str );"
accessor << "}"
accessor << "void setxy( SWSerLCDpa& s, int x, int y, long n, int b) {"
accessor << "\treturn s.setxy( x, y, n, b );"
accessor << "}" accessor << "}"
accessor << "void setxy( SWSerLCDpa& s, int x, int y, int n) {" accessor << "void setxy( SWSerLCDpa& s, int x, int y, int n) {"
accessor << "\treturn s.setxy( x, y, n );" accessor << "\treturn s.setxy( x, y, n );"
Expand Down Expand Up @@ -1359,18 +1377,9 @@ def assembler(name, signature, code)
end end


def self.pre_process(sketch_string) #:nodoc: def self.pre_process(sketch_string) #:nodoc:
result = sketch_string
# add external vars to each method (needed for better translation, will be removed in make:upload) # add external vars to each method (needed for better translation, will be removed in make:upload)
result.gsub!(/(^\s*def\s.\w*(\(.*\))?)/, '\1' + " \n #{$external_vars.join(" \n ")}" ) sketch_string.gsub!(/(^\s*def\s.\w*(\(.*\))?)/, '\1' + " \n #{$external_vars.join(" \n ")}" )
# gather method names sketch_string
sketch_methods = result.scan(/^\s*def\s.\w*/)
sketch_methods.each {|m| $sketch_methods << m.gsub(/\s*def\s*/, "") }

result.gsub!("HIGH", "1")
result.gsub!("LOW", "0")
result.gsub!("ON", "1")
result.gsub!("OFF", "0")
result
end end


def self.add_to_setup(meth) def self.add_to_setup(meth)
Expand Down
16 changes: 13 additions & 3 deletions lib/rad/sketch_compiler.rb
Expand Up @@ -21,17 +21,27 @@ def initialize path_to_sketch
end end


def parent_dir def parent_dir
@path.split("/")[0..@path.split("/").length-2].join("/") self.path.split("/")[0..@path.split("/").length-2].join("/")
end end


def build_dir def build_dir
"#{@target_dir}/#{@name}" "#{self.target_dir}/#{self.name}"
end end


def create_build_dir! optional_path_prefix=nil def create_build_dir! optional_path_prefix=nil
@target_dir = optional_path_prefix if optional_path_prefix self.target_dir = optional_path_prefix if optional_path_prefix
mkdir_p build_dir mkdir_p build_dir
end end


def process_constants
self.body.gsub!("HIGH", "1")
self.body.gsub!("LOW", "0")
self.body.gsub!("ON", "1")
self.body.gsub!("OFF", "0")
end

def sketch_methods
self.body.scan(/^\s*def\s.\w*/).collect{ |m| m.gsub(/\s*def\s*/, "") }
end


end end
26 changes: 13 additions & 13 deletions lib/rad/tasks/build_and_make.rake
Expand Up @@ -19,18 +19,20 @@ namespace :test do
end end


task :compile => :gather do task :compile => :gather do

@examples.each {|e| run_tests(e, "compile")} @examples.each {|e| run_tests(e, "compile")}
end
end end


desc "gather all tests" desc "gather all tests"
task :gather do # => "make:upload" do task :gather do # => "make:upload" do
@examples = [] @examples = []
@test_results = [] if ENV['sketch']
Dir.entries( File.expand_path("#{RAD_ROOT}/examples/") ).each do |f| @examples << ENV['sketch']
if (f =~ /\.rb$/) else
@examples << f.split('.').first Dir.entries( File.expand_path("#{RAD_ROOT}/examples/") ).each do |f|
@examples << f.split('.').first if (f =~ /\.rb$/)
end end
end
end end


end end
Expand All @@ -49,10 +51,8 @@ namespace :make do


desc "generate a makefile and use it to compile the .cpp" desc "generate a makefile and use it to compile the .cpp"
task :compile => [:clean_sketch_dir, "build:sketch"] do # should also depend on "build:sketch" task :compile => [:clean_sketch_dir, "build:sketch"] do # should also depend on "build:sketch"
puts; puts
puts @compiler.name
puts
Makefile.compose_for_sketch( @compiler.build_dir ) Makefile.compose_for_sketch( @compiler.build_dir )

# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH} # not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
sh %{cd #{@compiler.build_dir}; make depend; make} sh %{cd #{@compiler.build_dir}; make depend; make}
end end
Expand Down Expand Up @@ -86,7 +86,7 @@ namespace :build do
c_methods = [] c_methods = []
sketch_signatures = [] sketch_signatures = []
# until we better understand RubyToC let's see what's happening on errors # until we better understand RubyToC let's see what's happening on errors
$sketch_methods.each do |meth| @compiler.sketch_methods.each do |meth|
raw_rtc_meth = RADProcessor.translate(constantize(@compiler.klass), meth) raw_rtc_meth = RADProcessor.translate(constantize(@compiler.klass), meth)
puts "Translator Error: #{raw_rtc_meth.inspect}" if raw_rtc_meth[0..8] == "// ERROR:" puts "Translator Error: #{raw_rtc_meth.inspect}" if raw_rtc_meth[0..8] == "// ERROR:"
c_methods << raw_rtc_meth unless meth == "setup" c_methods << raw_rtc_meth unless meth == "setup"
Expand Down Expand Up @@ -137,6 +137,9 @@ namespace :build do
end end
CODE CODE
end end

@compiler.process_constants

eval ArduinoSketch.pre_process(@compiler.body) eval ArduinoSketch.pre_process(@compiler.body)
@@as.process_external_vars(constantize(@compiler.klass)) @@as.process_external_vars(constantize(@compiler.klass))
@setup = @@as.compose_setup @setup = @@as.compose_setup
Expand Down Expand Up @@ -177,18 +180,15 @@ namespace :build do
desc "setup target directory named after your sketch class" desc "setup target directory named after your sketch class"
task :sketch_dir => [:file_list] do task :sketch_dir => [:file_list] do
@compiler.create_build_dir! @compiler.create_build_dir!
# mkdir_p "#{@test_dir + @sketch_class.split(".").first}"
end end


task :file_list do task :file_list do
# take another look at this, since if the root directory name is changed, everything breaks # take another look at this, since if the root directory name is changed, everything breaks
# perhaps we generate a constant when the project is generated an pop it here or in the init file # perhaps we generate a constant when the project is generated an pop it here or in the init file
# assume the only .rb file in the sketch dir is the sketch:
if ENV['sketch'] if ENV['sketch']

@compiler = SketchCompiler.new File.expand_path("#{ENV['sketch']}.rb") @compiler = SketchCompiler.new File.expand_path("#{ENV['sketch']}.rb")

else else
# assume the only .rb file in the sketch dir is the sketch:
@compiler = SketchCompiler.new Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/../../../*.rb").first @compiler = SketchCompiler.new Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/../../../*.rb").first
end end


Expand Down
29 changes: 29 additions & 0 deletions spec/models/sketch_compiler_spec.rb
@@ -1,6 +1,35 @@
require File.dirname(__FILE__) + '/spec_helper.rb' require File.dirname(__FILE__) + '/spec_helper.rb'
require File.expand_path(File.dirname(__FILE__) + "/../../lib/rad/sketch_compiler.rb") require File.expand_path(File.dirname(__FILE__) + "/../../lib/rad/sketch_compiler.rb")


context "SketchCompiler#sketch_methods" do
before do
@as = File.expand_path(File.dirname(__FILE__)) + "/../../lib/examples/i2c_with_clock_chip.rb"
@sc = SketchCompiler.new @as
end

it "should locate all the methods defined in the sketch" do
@sc.sketch_methods.should include( "loop")
@sc.sketch_methods.should include( "printlz")
@sc.sketch_methods.should include( "print_hexbyte")
@sc.sketch_methods.should include( "clear_bottom_line")
end
end

context "SketchCompiler#process_constants" do
before do
@as = File.expand_path(File.dirname(__FILE__)) + "/../../lib/examples/external_variable_fu.rb"
@sc = SketchCompiler.new @as
end

it "should correctly process constants" do
@sc.process_constants
@sc.body.should_not match(/HIGH/)
@sc.body.should_not match(/LOW/)
@sc.body.should_not match(/ON/)
@sc.body.should_not match(/OFF/)
end
end

context "SketchCompiler.new" do context "SketchCompiler.new" do
before do before do
@as = File.expand_path(File.dirname(__FILE__)) + "/../../lib/examples/add_hysteresis.rb" @as = File.expand_path(File.dirname(__FILE__)) + "/../../lib/examples/add_hysteresis.rb"
Expand Down

0 comments on commit b63bccb

Please sign in to comment.