From: Graham Nicholls (spam_filtered) Date: 2004-08-23T22:45:43+09:00 Subject: Re: How to check syntax Andreas Schwarz wrote: > Graham Nicholls wrote: > >> OK, so its not a syntax error. Pedants :-) There ought to be a utility to >> look for variable references, etc. > > This is almost impossible in dynamic languages like ruby. Variables can > be defined everywhere, in eval(), conditional code, etc. You don't find > out until you execute it. I had a feeling that this was the case. > > > Now, I realize that this may be hard as >> its presumably akin to part of writing a compile. What I'd like to know, >> then is how to avoid the above - its a real nuisance in "scripting >> languages" (Oh and if you don't like that term, get over it - you know >> what I mean, python, perl(ugh!) Ruby & the like) > > Use unit tests. Could you elucidate. Typically this is pretty small stuff - my larger projects tend to be coded in C, although unit testing can presumably be applied there. Heres a slice of code: ,----[ /home/graham/src/hsb/add_logo ] | #!/usr/bin/env ruby | # | =begin | Parse an input file (which is intended to be an Informix -produced text | file containing PCL escape sequences) for a pattern which will introduce a | logo. The pattern looks like this: | | .LOGO filename x,y | | where .LOGO (uppercase) is the special string, filename is the full path | to the logo file (/usr/local/logos?) and x and y are optional coordinates | for the logo which will otherwise be placed at the current file position. | | | Skeleton taken from ttp_merge. | The -c(onvert_euros) option from ttp_merge is kept, as when we move to | CUPS,this is a process which will need to be applied, as there is no | longer an interface file where we can do this. | | G.Nicholls : 23 Aug 2004 | | =end | | | # globals: | $debug=false | $verbose=false | $convert_euros=false | $progname=$0 | $ver=0.1 | | # Constants | USAGE_ERR=1 | BAD_FILE=2 | BAD_ARGS=3 | MISC_ERROR=255 | | # "main" program | | | def main(argv) | output_fname = fname = nil | | $progname.gsub!(/^.*\//,'') # Get rid of everything in path up to last / | | argv.each do |arg| | case arg | when /-h(elp)?/ | usage() | exit (0) | when /^-c(onvert)?(_euros)?/ | $convert_euros=true | when /^-f(orce)?/ | $force=true | when /^-v(erbose)?/ | $verbose=true | printf("%s version %s\n",$0.sub(/^.*\//,""),$ver) | when /^-d/ | print("DEBUG: Version #{$ver}\n") | $debug=true | when /^-o(\S+)/ | output_fname=Regexp.last_match[1] | else | if fname != nil | usage("Sorry, you can only specify one file to merge") | exit(BAD_ARGS) | end | fname=arg | end | end | | if fname == nil | usage("What file do you want me to logoise?\n") | exit(USAGE_ERR) | end | | | if output_fname == nil | output_fname=fname+".out" | end | if not $force | if FileTest::exists?(output_fname) | abort("Output file #{output_fname} exists already - use -f or -force to | overwrite it\n") end | end | | if $verbose | print("#{$progname} : version #{$version} logoising file #{fname}\n") | print("Writing output to #{output_fname}\n") | if $force | print("which will be overwritten if it exists\n") | end | end | | # OK process the file | begin | fdata=IO.readlines(fname) | rescue => err | abort("Error reading file #{fname} : #{err}\n",BAD_FILE) | end | # Match the pattern & an optional x,y part | logo_patt=Regexp.compile(/^\s*\.LOGO\s+(\S+)(\s+([0-9]+)\s* \s*([0-9]+))?.*$/) | if $convert_euros | euro_patt=Regexp.compile(/([\$\~][\w\d]+):/) | end | | line_no=0 | fdata.each do |tline| | line_no+=1 | if $debug | printf("Line %d: \n[%s]\n",line_no,tline) | end | # Before searching for a logo,FIRST replace euros if relevant, | # as there could be the euro char in the logo, where we do NOT want | # to replace it as it simply binary data: | if $convert_euros | line.tr!(\174,\244) | else | line.tr!(\234,\273) | end | | | | # | # Search for the .LOGO lines | # | if (refs=logo_patt.match(tline)) != nil | logo_file=Regexp.last_match[1] | if $debug | printf("Found a logo : including [%s]\n",logo_file) | end | begin | # Insert the data from the include file | logo_data=IO.readlines(logo_file) | fdata=fdata[0..line_no-2] + logo_data + fdata[line_no..fdata.size()-1] | rescue =>err | printf("Error processing include file %s : %s\n",logo_file,err) | end | end | end | | # OK, we now have an array with the merged data - write it: | | opfile=File.new(output_fname,"w") | fdata.each do |tline| | opfile.puts("#{tline}") | end | opfile.close | end | | def abort(message=nil,errcode=MISC_ERROR) | print message if message != nil | exit(errcode) | end | | def usage(message=nil) | print message if message != nil | print("Usage: #{$progname} -v -d -h(elp) -o[output_fname] [filename]\n") | print("If output filename is not given, a suitable one will be | constructed\n") end | | ########################################################################################## | | # OK, call main: | main(ARGV) `---- As you can see, theres not much to it - an hours work or so - I'd rather not have to spend much time writing testing - ISTM its easier to fix a bug when it rears than spend ages trying to eliminate all possible bugs Thanks Graham -- The answer's always "Yes". Now, what's the question?