Parse expressions in Liquid::Variable#parse.

This commit is contained in:
Dylan Thacker-Smith
2014-10-08 21:06:59 -04:00
parent 632b1fb702
commit f387508666
3 changed files with 107 additions and 74 deletions
+54 -29
View File
@@ -35,15 +35,16 @@ module Liquid
def lax_parse(markup)
@filters = []
if markup =~ /\s*(#{QuotedFragment})(.*)/om
@name = Regexp.last_match(1)
if Regexp.last_match(2) =~ /#{FilterSeparator}\s*(.*)/om
filters = Regexp.last_match(1).scan(FilterParser)
if markup =~ /(#{QuotedFragment})(.*)/om
name_markup, filter_markup = $1, $2
@name = Expression.parse(name_markup)
if filter_markup =~ /#{FilterSeparator}\s*(.*)/om
filters = $1.scan(FilterParser)
filters.each do |f|
if f =~ /\w+/
filtername = Regexp.last_match(0)
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
@filters << [filtername, filterargs]
@filters << parse_filter_expressions(filtername, filterargs)
end
end
end
@@ -53,7 +54,7 @@ module Liquid
def strict_parse(markup)
# Very simple valid cases
if markup =~ EasyParse
@name = $1
@name = Expression.parse($1)
@filters = []
return
end
@@ -61,11 +62,11 @@ module Liquid
@filters = []
p = Parser.new(markup)
# Could be just filters with no input
@name = p.look(:pipe) ? ''.freeze : p.expression
@name = p.look(:pipe) ? nil : Expression.parse(p.expression)
while p.consume?(:pipe)
filtername = p.consume(:id)
filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
@filters << [filtername, filterargs]
@filters << parse_filter_expressions(filtername, filterargs)
end
p.consume(:end_of_string)
end
@@ -81,28 +82,52 @@ module Liquid
end
def render(context)
return ''.freeze if @name.nil?
@filters.inject(context[@name]) do |output, filter|
filterargs = []
keyword_args = {}
filter[1].to_a.each do |a|
if matches = a.match(/\A#{TagAttributes}\z/o)
keyword_args[matches[1]] = context[matches[2]]
else
filterargs << context[a]
end
return ''.freeze unless @name
@filters.inject(context.evaluate(@name)) do |output, (filter_name, filter_args, filter_kwargs)|
filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
output = context.invoke(filter_name, output, *filter_args)
end.tap{ |obj| taint_check(obj) }
end
private
def parse_filter_expressions(filter_name, unparsed_args)
filter_args = []
keyword_args = {}
unparsed_args.each do |a|
if matches = a.match(/\A#{TagAttributes}\z/o)
keyword_args[matches[1]] = Expression.parse(matches[2])
else
filter_args << Expression.parse(a)
end
filterargs << keyword_args unless keyword_args.empty?
output = context.invoke(filter[0], output, *filterargs)
end.tap do |obj|
if obj.tainted?
case Template.taint_mode
when :warn
@warnings ||= []
@warnings << "variable '#{@name}' is tainted and was not escaped"
when :error
raise TaintedError, "Error - variable '#{@name}' is tainted and was not escaped"
end
end
result = [filter_name, filter_args]
result << keyword_args unless keyword_args.empty?
result
end
def evaluate_filter_expressions(context, filter_args, filter_kwargs)
parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
if filter_kwargs
parsed_kwargs = {}
filter_kwargs.each do |key, expr|
parsed_kwargs[key] = context.evaluate(expr)
end
parsed_args << parsed_kwargs
end
parsed_args
end
def taint_check(obj)
if obj.tainted?
@markup =~ QuotedFragment
name = Regexp.last_match(0)
case Template.taint_mode
when :warn
@warnings ||= []
@warnings << "variable '#{name}' is tainted and was not escaped"
when :error
raise TaintedError, "Error - variable '#{name}' is tainted and was not escaped"
end
end
end