lib/ruby_less/processor.rb in rubyless-0.4.0 vs lib/ruby_less/processor.rb in rubyless-0.5.0
- old
+ new
@@ -10,10 +10,12 @@
PREFIX_OPERATOR = ['-@']
def self.translate(string, helper)
sexp = RubyParser.new.parse(string)
self.new(helper).process(sexp)
+ rescue Racc::ParseError => err
+ raise RubyLess::SyntaxError.new(err.message)
end
def initialize(helper)
super()
@helper = helper
@@ -84,12 +86,26 @@
end
code
end
def process_array(exp)
- res = process_arglist(exp)
- exp.size > 1 ? t("[#{res}]", res.opts) : res
+ literal = true
+ list = []
+ classes = []
+ while !exp.empty?
+ res = process(exp.shift)
+ content_class ||= res.opts[:class]
+ unless res.opts[:class] <= content_class
+ classes = list.map { content_class.name } + [res.opts[:class].name]
+ raise RubyLess::Error.new("Mixed Array not supported ([#{classes * ','}]).")
+ end
+ list << res
+ end
+
+ res.opts[:class] = Array
+ res.opts[:array_content_class] = content_class
+ t "[#{list * ','}]", res.opts.merge(:literal => nil)
end
def process_vcall(exp)
var_name = exp.shift
unless opts = get_method([var_name], @helper, false)
@@ -102,15 +118,16 @@
t method, opts
end
def process_lit(exp)
lit = exp.shift
- t lit.inspect, get_lit_class(lit.class)
+ t lit.inspect, get_lit_class(lit)
end
def process_str(exp)
- t exp.shift.inspect, String
+ lit = exp.shift
+ t lit.inspect, :class => String, :literal => lit
end
def process_dstr(exp)
t "\"#{parse_dstr(exp)}\"", String
end
@@ -118,31 +135,28 @@
def process_evstr(exp)
exp.empty? ? t('', String) : process(exp.shift)
end
def process_hash(exp)
- result = []
- klass = {}
+ result = t "", String
until exp.empty?
key = exp.shift
if [:lit, :str].include?(key.first)
key = key[1]
rhs = exp.shift
type = rhs.first
rhs = process rhs
#rhs = "(#{rhs})" unless [:lit, :str].include? type # TODO: verify better!
-
- result << "#{key.inspect} => #{rhs}"
- klass[key] = rhs.klass
+ result.set_hash(key, rhs)
else
# ERROR: invalid key
raise RubyLess::SyntaxError.new("Invalid key type for hash (should be a literal value, was #{key.first.inspect})")
end
end
-
- t "{#{result.join(', ')}}", :class => klass
+ result.rebuild_hash
+ result
end
def process_ivar(exp)
method_call(nil, exp)
end
@@ -196,34 +210,80 @@
signature = [method]
cond = []
end
if receiver
- if receiver.could_be_nil?
- cond += receiver.cond
- end
opts = get_method(receiver, signature)
+ method_call_with_receiver(receiver, args, opts, cond, signature)
+ else
+ opts = get_method(nil, signature)
method = opts[:method]
- if method == '/'
- t_if cond, "(#{receiver.raw}#{method}#{args.raw} rescue nil)", opts.merge(:nil => true)
- elsif INFIX_OPERATOR.include?(method)
- t_if cond, "(#{receiver.raw}#{method}#{args.raw})", opts
- elsif PREFIX_OPERATOR.include?(method)
- t_if cond, "#{method.to_s[0..0]}#{receiver.raw}", opts
- elsif method == '[]'
- t_if cond, "#{receiver.raw}[#{args.raw}]", opts
+ args = args_with_prepend(args, opts)
+
+ if (proc = opts[:pre_processor]) && !args.list.detect {|a| !a.literal}
+ if proc.kind_of?(Proc)
+ res = proc.call(*args.list.map(&:literal))
+ else
+ res = @helper.send(proc, *args.list.map(&:literal))
+ end
+
+ return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
+ end
+
+ if opts[:accept_nil]
+ method_call_accepting_nil(method, args, opts)
else
- args = args_with_prepend(args, opts)
args = "(#{args.raw})" if args
- t_if cond, "#{receiver.raw}.#{method}#{args}", opts
+ t_if cond, "#{method}#{args}", opts
end
+ end
+ end
+
+ def method_call_accepting_nil(method, args, opts)
+ if args
+ args = args.list.map do |arg|
+ if !arg.could_be_nil? || arg.raw == arg.cond.to_s
+ arg.raw
+ else
+ "(#{arg.cond} ? #{arg.raw} : nil)"
+ end
+ end.join(', ')
+
+ t "#{method}(#{args})", opts
else
- opts = get_method(nil, signature)
- method = opts[:method]
+ t method, opts
+ end
+ end
+
+ def method_call_with_receiver(receiver, args, opts, cond, signature)
+ method = opts[:method]
+ arg_list = args ? args.list : []
+
+ if receiver.could_be_nil? && opts != SafeClass.safe_method_type_for(NilClass, signature)
+ # Do not add a condition if the method applies on nil
+ cond += receiver.cond
+ elsif receiver.literal && (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
+ if proc.kind_of?(Proc)
+ res = proc.call([receiver.literal] + arg_list.map(&:literal))
+ else
+ res = receiver.literal.send(*([method] + arg_list.map(&:literal)))
+ end
+ return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
+ end
+
+ if method == '/'
+ t_if cond, "(#{receiver.raw}#{method}#{args.raw} rescue nil)", opts.merge(:nil => true)
+ elsif INFIX_OPERATOR.include?(method)
+ t_if cond, "(#{receiver.raw}#{method}#{args.raw})", opts
+ elsif PREFIX_OPERATOR.include?(method)
+ t_if cond, "#{method.to_s[0..0]}#{receiver.raw}", opts
+ elsif method == '[]'
+ t_if cond, "#{receiver.raw}[#{args.raw}]", opts
+ else
args = args_with_prepend(args, opts)
args = "(#{args.raw})" if args
- t_if cond, "#{method}#{args}", opts
+ t_if cond, "#{receiver.raw}.#{method}#{args}", opts
end
end
def parse_dstr(exp, in_regex = false)
res = escape_str(exp.shift, in_regex)
@@ -256,26 +316,38 @@
raise RubyLess::NoMethodError.new(receiver, klass, signature) if !type || type[:class].kind_of?(Symbol) # we cannot send: no object.
type[:class].kind_of?(Proc) ? type[:class].call(@helper, signature) : type
end
- def get_lit_class(klass)
- unless lit_class = RubyLess::SafeClass.literal_class_for(klass)
+ def get_lit_class(lit)
+ unless lit_class = RubyLess::SafeClass.literal_class_for(lit.class)
raise RubyLess::SyntaxError.new("#{klass} literal not supported by RubyLess.")
end
- lit_class
+ {:class => lit_class, :literal => lit}
end
def args_with_prepend(args, opts)
if prepend_args = opts[:prepend_args]
if args
prepend_args.append_argument(args)
- prepend_args
+ args = prepend_args
else
- prepend_args
+ args = prepend_args
end
- else
- args
end
+
+ if append_hash = opts[:append_hash]
+ last_arg = args.list.last
+ unless last_arg.klass.kind_of?(Hash)
+ last_arg = t "", String
+ args.append_argument(last_arg)
+ end
+ append_hash.each do |key, value|
+ last_arg.set_hash(key, value)
+ end
+ last_arg.rebuild_hash
+ args.rebuild_arguments
+ end
+ args
end
end
end