HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Debug statement remover

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
debugremoverstatement

Problem

I've been fiddling around with some programs, and since most of them are for fun, I haven't bothered with little things like making the output non-verbose until I, say, put it here, for review. I was idly wondering how I could make converting it easier, and naturally I thought of writing a program, because that's all I ever do. You could, of course, use a logging library and set the debug level, but that results in having lots of extra code everywhere that's never touched in production. With this, you can keep all that code, tag it, and when it's sent off to production

Here's how it works:

Read a line from standard input. If any state variables are nonzero, apply the action, decrement the variable, and move to the next line. If the line looks like a tag, set the appropriate state variable and go to the next line. If the line looks like it ends in a tag, apply the action from the tag to that line and go to the next. If it's still trying to figure out what to do, give up and output the line as-is.

debug_converter.rb

```
#USAGE: | ruby debug_converter.rb >

# Hurr hurr I'm funny
#DEBUG[2] - comment
#print 'Enter the comment separator: '
#ARGV = gets.split

DEFAULT_ACTION = 'delete'

raise "Wrong number of args: #{ARGV.inspect}" if ARGV.length != 1

COMMENT_SYMBOL = ARGV[0]

deleting = 0
commenting = 0
uncommenting = 0
until (line = STDIN.gets).nil?
line.chomp!
if deleting != 0
deleting -= 1
elsif commenting != 0
commenting -= 1
puts line.gsub(/^(\s*)/, "\\1#{COMMENT_SYMBOL}")
elsif uncommenting != 0
uncommenting -= 1
puts line.gsub(/^(\s*)#{COMMENT_SYMBOL}/, '\1')
else
#Helps to separate it conceptually from the bit that depends on the variables
case line
when /^[ \t]#{COMMENT_SYMBOL}\sDEBUG(?:\[(\d+)\])?\s(?:-\s([a-z]+))?/i
line.strip!
count, action = (Integer($1) rescue nil), $2 # For readability purposes.
count ||= 1
action ||= DEFAULT_ACTION
raise "Invalid count:

Solution

Regarding extensibility/repetition:

  • Instead of having a seperate count for each action, use a single variable for counting and another variable for the current action. (This also eliminates any possibility of illegal state, i.e. multiple counters greater than zero)



  • You can use a hash to map action strings to lambdas that accept a line and output to STDOUT.



  • You can avoid repetition by noticing that a line with an inline action (e.g. foo #DEBUG - comment) is equivalent to an action line (#DEBUG - comment) followed by another line (foo). Alternatively, you can think of an action-only line as an empty line with an inline action.



When writing regexes, use \A and \z (start and end of string) instead of ^ and $ (start and end of line). It doesn't actually matter in your code, but it's generally needed for correct handling of multiline strings.

Suggested solution:

#USAGE:  | ruby debug_converter.rb  > 

raise "Wrong number of args: #{ARGV.inspect}" if ARGV.length != 1

DEFAULT_ACTION = 'delete'
COMMENT_SYMBOL = ARGV[0]

$actions = {
  'delete' => ->(line) { },
  'comment' => ->(line) { puts line.gsub(/^(\s*)/, "\\1#{COMMENT_SYMBOL}") },
  'uncomment' => ->(line) { puts line.gsub(/^(\s*)#{COMMENT_SYMBOL}/, '\1') }
}

$current_action = nil
$remaining_count = 0

def parse_action_command(str)
  str =~ /\A(?:\[(\d+)\])?\s*(?:-\s*([a-z]+))?\z/i
  count = Integer($1) rescue 1
  action = $2 || DEFAULT_ACTION
  raise "Invalid count: #{count}" if count  0
end

def perform_current_action(line)
  action = $actions[$current_action]
  raise "Invalid action: #{$current_action}" unless action
  action.call(line)
  $remaining_count -= 1
end

LINE_WITH_ACTION_REGEX = /\A(.*)#{COMMENT_SYMBOL}\s*DEBUG((?:\[\d+\])?\s*(?:-\s*[a-z]+)?)\z/i
WHITESPACE_REGEX = /\A[ \t]*\z/

until (line = STDIN.gets).nil?
  line.chomp!
  if action_in_progress?
    perform_current_action(line)
  elsif line =~ LINE_WITH_ACTION_REGEX # split to code part and action part
    before_action, action = $1, $2
    parse_action_command(action)
    unless before_action =~ WHITESPACE_REGEX
      perform_current_action(before_action)
    end
  else
    puts line
  end
end

Code Snippets

#USAGE: <input source> | ruby debug_converter.rb <comment symbol> > <output file>

raise "Wrong number of args: #{ARGV.inspect}" if ARGV.length != 1

DEFAULT_ACTION = 'delete'
COMMENT_SYMBOL = ARGV[0]

$actions = {
  'delete' => ->(line) { },
  'comment' => ->(line) { puts line.gsub(/^(\s*)/, "\\1#{COMMENT_SYMBOL}") },
  'uncomment' => ->(line) { puts line.gsub(/^(\s*)#{COMMENT_SYMBOL}/, '\1') }
}

$current_action = nil
$remaining_count = 0

def parse_action_command(str)
  str =~ /\A(?:\[(\d+)\])?\s*(?:-\s*([a-z]+))?\z/i
  count = Integer($1) rescue 1
  action = $2 || DEFAULT_ACTION
  raise "Invalid count: #{count}" if count < 0
  $current_action = action
  $remaining_count = count.to_i
end

def action_in_progress?
  $remaining_count > 0
end

def perform_current_action(line)
  action = $actions[$current_action]
  raise "Invalid action: #{$current_action}" unless action
  action.call(line)
  $remaining_count -= 1
end

LINE_WITH_ACTION_REGEX = /\A(.*)#{COMMENT_SYMBOL}\s*DEBUG((?:\[\d+\])?\s*(?:-\s*[a-z]+)?)\z/i
WHITESPACE_REGEX = /\A[ \t]*\z/

until (line = STDIN.gets).nil?
  line.chomp!
  if action_in_progress?
    perform_current_action(line)
  elsif line =~ LINE_WITH_ACTION_REGEX # split to code part and action part
    before_action, action = $1, $2
    parse_action_command(action)
    unless before_action =~ WHITESPACE_REGEX
      perform_current_action(before_action)
    end
  else
    puts line
  end
end

Context

StackExchange Code Review Q#93683, answer score: 2

Revisions (0)

No revisions yet.