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.intern] = name.intern 125: when /^rewrite_(.*)/ then 126: @rewriters[$1.intern] = name.intern 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 257 257: def assert_type(list, typ) 258: raise SexpTypeError, "Expected type #{typ.inspect} in #{list.inspect}" if 259: not Array === list or list.first != typ 260: end
Registers an error handler for node
# File lib/sexp_processor.rb, line 278 278: def on_error_in(node_type, &block) 279: @exceptions[node_type] = block 280: end
Default Sexp processor. Invokes process_
# File lib/sexp_processor.rb, line 163 163: def process(exp) 164: return nil if exp.nil? 165: exp = self.rewrite(exp) if self.context.empty? 166: 167: unless @unsupported_checked then 168: m = public_methods.grep(/^process_/) { |o| o.to_s.sub(/^process_/, '').intern } 169: supported = m - (m - @unsupported) 170: 171: raise UnsupportedNodeError, "#{supported.inspect} shouldn't be in @unsupported" unless supported.empty? 172: 173: @unsupported_checked = true 174: end 175: 176: result = self.expected.new 177: 178: type = exp.first 179: raise "type should be a Symbol, not: #{exp.first.inspect}" unless 180: Symbol === type 181: 182: self.context.unshift type 183: 184: if @debug.has_key? type then 185: str = exp.inspect 186: puts "// DEBUG: #{str}" if str =~ @debug[type] 187: end 188: 189: exp_orig = nil 190: exp_orig = exp.deep_clone if $DEBUG or 191: @debug.has_key? type or @exceptions.has_key?(type) 192: 193: raise UnsupportedNodeError, "'#{type}' is not a supported node type" if 194: @unsupported.include? type 195: 196: if @debug.has_key? type then 197: str = exp.inspect 198: puts "// DEBUG (rewritten): #{str}" if str =~ @debug[type] 199: end 200: 201: # now do a pass with the real processor (or generic) 202: meth = @processors[type] || @default_method 203: if meth then 204: 205: if @warn_on_default and meth == @default_method then 206: warn "WARNING: Using default method #{meth} for #{type}" 207: end 208: 209: exp.shift if @auto_shift_type and meth != @default_method 210: 211: result = error_handler(type, exp_orig) do 212: self.send(meth, exp) 213: end 214: 215: raise SexpTypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result 216: 217: self.assert_empty(meth, exp, exp_orig) if @require_empty 218: else 219: unless @strict then 220: until exp.empty? do 221: sub_exp = exp.shift 222: sub_result = nil 223: if Array === sub_exp then 224: sub_result = error_handler(type, exp_orig) do 225: process(sub_exp) 226: end 227: raise "Result is a bad type" unless Array === sub_exp 228: raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.first unless sub_exp.empty? 229: else 230: sub_result = sub_exp 231: end 232: result << sub_result 233: end 234: 235: # NOTE: this is costly, but we are in the generic processor 236: # so we shouldn't hit it too much with RubyToC stuff at least. 237: #if Sexp === exp and not exp.sexp_type.nil? then 238: begin 239: result.sexp_type = exp.sexp_type 240: rescue Exception 241: # nothing to do, on purpose 242: end 243: else 244: msg = "Bug! Unknown node-type #{type.inspect} to #{self.class}" 245: msg += " in #{exp_orig.inspect} from #{caller.inspect}" if $DEBUG 246: raise UnknownNodeError, msg 247: end 248: end 249: 250: self.context.shift 251: result 252: 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 293 293: def process_dummy(exp) 294: result = @expected.new(:dummy) rescue @expected.new 295: 296: until exp.empty? do 297: result << self.process(exp.shift) 298: end 299: 300: result 301: end
# File lib/sexp_processor.rb, line 139 139: def rewrite(exp) 140: type = exp.first 141: 142: self.context.unshift type 143: 144: exp.map! { |sub| Array === sub ? rewrite(sub) : sub } 145: 146: self.context.shift 147: 148: begin 149: meth = @rewriters[type] 150: exp = self.send(meth, exp) if meth 151: break unless Sexp === exp 152: old_type, type = type, exp.first 153: end until old_type == type 154: 155: exp 156: 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 322 322: def scope &block 323: env.scope(&block) 324: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.