SexpProcessor provides a uniform interface to process Sexps.
In order to create your own SexpProcessor subclass you’ll need to call super in the initialize method, then set any of the Sexp flags you want to be different from the defaults.
SexpProcessor uses a Sexp’s type to determine which process method to call in the subclass. For Sexp s(:lit, 1) SexpProcessor will call #, if it is defined.
You can also specify a default method to call for any Sexp types without a process_
Here is a simple example:
class MyProcessor < SexpProcessor def initialize super self.strict = false end def process_lit(exp) val = exp.shift return val end end
An array that specifies node types that are unsupported by this processor. SexpProcessor will raise UnsupportedNodeError if you try to process one of those node types.
Creates a new SexpProcessor. Use super to invoke this initializer from SexpProcessor subclasses, then use the attributes above to customize the functionality of the SexpProcessor
# File lib/sexp_processor.rb, line 102 102: def initialize 103: @default_method = nil 104: @warn_on_default = true 105: @auto_shift_type = false 106: @strict = false 107: @unsupported = [:alloca, :cfunc, :cref, :ifunc, :last, :memo, 108: :newline, :opt_n, :method] 109: @unsupported_checked = false 110: @debug = {} 111: @expected = Sexp 112: @require_empty = true 113: @exceptions = {} 114: 115: # we do this on an instance basis so we can subclass it for 116: # different processors. 117: @processors = {} 118: @rewriters = {} 119: @context = [] 120: 121: public_methods.each do |name| 122: case name 123: when /^process_(.*)/ then 124: @processors[$1.to_sym] = name.to_sym 125: when /^rewrite_(.*)/ then 126: @rewriters[$1.to_sym] = name.to_sym 127: end 128: end 129: end
# File lib/sexp_processor.rb, line 131 131: def assert_empty(meth, exp, exp_orig) 132: unless exp.empty? then 133: msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}" 134: msg += " from #{exp_orig.inspect}" if $DEBUG 135: raise NotEmptyError, msg 136: end 137: end
Raises unless the Sexp type for list matches typ
# File lib/sexp_processor.rb, line 269 269: def assert_type(list, typ) 270: raise SexpTypeError, "Expected type #{typ.inspect} in #{list.inspect}" if 271: not Array === list or list.first != typ 272: end
# File lib/sexp_processor.rb, line 338 338: def in_context type 339: self.context.unshift type 340: 341: yield 342: 343: self.context.shift 344: end
Registers an error handler for node
# File lib/sexp_processor.rb, line 290 290: def on_error_in(node_type, &block) 291: @exceptions[node_type] = block 292: end
Default Sexp processor. Invokes process_
# File lib/sexp_processor.rb, line 172 172: def process(exp) 173: return nil if exp.nil? 174: if self.context.empty? then 175: p :rewriting unless debug.empty? 176: exp = self.rewrite(exp) 177: p :done_rewriting unless debug.empty? 178: end 179: 180: unless @unsupported_checked then 181: m = public_methods.grep(/^process_/) { |o| o.to_s.sub(/^process_/, '').to_sym } 182: supported = m - (m - @unsupported) 183: 184: raise UnsupportedNodeError, "#{supported.inspect} shouldn't be in @unsupported" unless supported.empty? 185: 186: @unsupported_checked = true 187: end 188: 189: result = self.expected.new 190: 191: type = exp.first 192: raise "type should be a Symbol, not: #{exp.first.inspect}" unless 193: Symbol === type 194: 195: in_context type do 196: if @debug.has_key? type then 197: str = exp.inspect 198: puts "// DEBUG:(original ): #{str}" if str =~ @debug[type] 199: end 200: 201: exp_orig = nil 202: exp_orig = exp.deep_clone if $DEBUG or 203: @debug.has_key? type or @exceptions.has_key?(type) 204: 205: raise UnsupportedNodeError, "'#{type}' is not a supported node type" if 206: @unsupported.include? type 207: 208: # now do a pass with the real processor (or generic) 209: meth = @processors[type] || @default_method 210: if meth then 211: 212: if @warn_on_default and meth == @default_method then 213: warn "WARNING: Using default method #{meth} for #{type}" 214: end 215: 216: exp.shift if @auto_shift_type and meth != @default_method 217: 218: result = error_handler(type, exp_orig) do 219: self.send(meth, exp) 220: end 221: 222: if @debug.has_key? type then 223: str = exp.inspect 224: puts "// DEBUG (processed): #{str}" if str =~ @debug[type] 225: end 226: 227: raise SexpTypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result 228: 229: self.assert_empty(meth, exp, exp_orig) if @require_empty 230: else 231: unless @strict then 232: until exp.empty? do 233: sub_exp = exp.shift 234: sub_result = nil 235: if Array === sub_exp then 236: sub_result = error_handler(type, exp_orig) do 237: process(sub_exp) 238: end 239: raise "Result is a bad type" unless Array === sub_exp 240: raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.first unless sub_exp.empty? 241: else 242: sub_result = sub_exp 243: end 244: result << sub_result 245: end 246: 247: # NOTE: this is costly, but we are in the generic processor 248: # so we shouldn't hit it too much with RubyToC stuff at least. 249: #if Sexp === exp and not exp.sexp_type.nil? then 250: begin 251: result.sexp_type = exp.sexp_type 252: rescue Exception 253: # nothing to do, on purpose 254: end 255: else 256: msg = "Bug! Unknown node-type #{type.inspect} to #{self.class}" 257: msg += " in #{exp_orig.inspect} from #{caller.inspect}" if $DEBUG 258: raise UnknownNodeError, msg 259: end 260: end 261: end 262: 263: result 264: end
A fairly generic processor for a dummy node. Dummy nodes are used when your processor is doing a complicated rewrite that replaces the current sexp with multiple sexps.
Bogus Example:
def process_something(exp) return s(:dummy, process(exp), s(:extra, 42)) end
# File lib/sexp_processor.rb, line 305 305: def process_dummy(exp) 306: result = @expected.new(:dummy) rescue @expected.new 307: 308: until exp.empty? do 309: result << self.process(exp.shift) 310: end 311: 312: result 313: end
# File lib/sexp_processor.rb, line 139 139: def rewrite(exp) 140: type = exp.first 141: 142: if @debug.has_key? type then 143: str = exp.inspect 144: puts "// DEBUG (original ): #{str}" if str =~ @debug[type] 145: end 146: 147: in_context type do 148: exp.map! { |sub| Array === sub ? rewrite(sub) : sub } 149: end 150: 151: begin 152: meth = @rewriters[type] 153: exp = self.send(meth, exp) if meth 154: break unless Sexp === exp 155: 156: if @debug.has_key? type then 157: str = exp.inspect 158: puts "// DEBUG (rewritten): #{str}" if str =~ @debug[type] 159: end 160: 161: old_type, type = type, exp.first 162: end until old_type == type 163: 164: exp 165: end
Add a scope level to the current env. Eg:
def process_defn exp name = exp.shift args = process(exp.shift) scope do body = process(exp.shift) # ... end end env[:x] = 42 scope do env[:x] # => 42 env[:y] = 24 end env[:y] # => nil
# File lib/sexp_processor.rb, line 334 334: def scope &block 335: env.scope(&block) 336: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.