mirror of
https://github.com/kemko/liquid.git
synced 2026-01-02 08:15:41 +03:00
Compare commits
1 Commits
string-sli
...
var-c-ext-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc1a893ff |
@@ -12,4 +12,5 @@ void Init_liquid(void)
|
|||||||
|
|
||||||
init_liquid_tokenizer();
|
init_liquid_tokenizer();
|
||||||
init_liquid_block();
|
init_liquid_block();
|
||||||
|
init_liquid_variable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "block.h"
|
#include "block.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "variable.h"
|
||||||
|
|
||||||
extern VALUE mLiquid;
|
extern VALUE mLiquid;
|
||||||
extern VALUE cLiquidTemplate, cLiquidTag, cLiquidVariable;
|
extern VALUE cLiquidTemplate, cLiquidTag, cLiquidVariable;
|
||||||
|
|||||||
179
ext/liquid/variable.c
Normal file
179
ext/liquid/variable.c
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#include "liquid_ext.h"
|
||||||
|
|
||||||
|
VALUE cLiquidVariable;
|
||||||
|
extern VALUE mLiquid;
|
||||||
|
|
||||||
|
static void free_variable(void *ptr)
|
||||||
|
{
|
||||||
|
struct liquid_variable *variable = ptr;
|
||||||
|
xfree(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE rb_variable_allocate(VALUE klass)
|
||||||
|
{
|
||||||
|
VALUE obj;
|
||||||
|
struct liquid_variable *variable;
|
||||||
|
|
||||||
|
obj = Data_Make_Struct(klass, struct liquid_variable, NULL, free_variable, variable);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int skip_whitespace(char * str, int len)
|
||||||
|
{
|
||||||
|
int skipped = 0; char * ptr = str;
|
||||||
|
while (skipped < len && isspace(*ptr))
|
||||||
|
{skipped++; ptr++;}
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * get_quoted_fragment(char * str, int len, int * ret_size, int * end_offset, bool colon)
|
||||||
|
{
|
||||||
|
int p = 0; /* Current position in string */
|
||||||
|
int start = -1, end = -1; /* Start and end indices for the found string */
|
||||||
|
char quoted_by = -1; /* Is the current part of string quoted by a single or double quote? If so
|
||||||
|
ignore any special chars */
|
||||||
|
|
||||||
|
while (p < len) {
|
||||||
|
|
||||||
|
switch (str[p]) {
|
||||||
|
case '"':
|
||||||
|
if (start == -1) {start = p; quoted_by = '"';}
|
||||||
|
else if (str[start] == '"') {end = p; goto quoted_fragment_found;}
|
||||||
|
else if (quoted_by == -1) quoted_by = '"';
|
||||||
|
else if (quoted_by == '"') quoted_by = -1;
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
if (start == -1) {start = p; quoted_by = '\'';}
|
||||||
|
else if (str[start] == '\'') {end = p; goto quoted_fragment_found;}
|
||||||
|
else if (quoted_by == -1) quoted_by = '\'';
|
||||||
|
else if (quoted_by == '\'') quoted_by = -1;
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
if (colon)
|
||||||
|
if (start != -1 && quoted_by == -1) {end = p-1; goto quoted_fragment_found;}
|
||||||
|
else
|
||||||
|
if (start == -1) start = p;
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
case ',':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case '\f':
|
||||||
|
case '\t':
|
||||||
|
case '\v':
|
||||||
|
case ' ':
|
||||||
|
if (start != -1 && quoted_by == -1) {end = p-1; goto quoted_fragment_found;}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (start == -1) start = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (p == len && start != -1 && end == -1) end = len-1;
|
||||||
|
|
||||||
|
quoted_fragment_found:
|
||||||
|
if (end >= start) {
|
||||||
|
*ret_size = end-start+1;
|
||||||
|
*end_offset = end+1;
|
||||||
|
return &str[start];
|
||||||
|
} else {
|
||||||
|
*ret_size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE get_filters(char * str, int len, VALUE self) {
|
||||||
|
VALUE filters_arr = rb_ary_new();
|
||||||
|
|
||||||
|
int p = 0;
|
||||||
|
int ret_size, end_offset;
|
||||||
|
char * f;
|
||||||
|
|
||||||
|
while(p<len) {
|
||||||
|
if (str[p] == '|') {
|
||||||
|
VALUE filter = rb_ary_new();
|
||||||
|
VALUE f_args = rb_ary_new();
|
||||||
|
|
||||||
|
p += skip_whitespace(&str[p+1], len-p-1);
|
||||||
|
f = get_quoted_fragment(&str[p], len-p, &ret_size, &end_offset, true);
|
||||||
|
p += end_offset;
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
if (f[ret_size-1] == ':') ret_size--;
|
||||||
|
rb_ary_push(filter, rb_str_new(f, ret_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for filter arguments */
|
||||||
|
do {
|
||||||
|
if (p<len) {
|
||||||
|
p += skip_whitespace(&str[p], len-p);
|
||||||
|
|
||||||
|
// printf("\n1. %.*s\n", len-p, &str[p]);
|
||||||
|
|
||||||
|
if (str[p] != '|') {
|
||||||
|
f = get_quoted_fragment(&str[p], len-p, &ret_size, &end_offset, false);
|
||||||
|
|
||||||
|
// printf("\n2. %.*s\n", ret_size, f);
|
||||||
|
|
||||||
|
p += end_offset;
|
||||||
|
p += skip_whitespace(&str[p], len-p);
|
||||||
|
|
||||||
|
if (str[p] == '|') p--;
|
||||||
|
|
||||||
|
if (f) rb_ary_push(f_args, rb_str_new(f, ret_size));
|
||||||
|
|
||||||
|
} else p--;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (str[p] == ',' || str[p] == ':');
|
||||||
|
|
||||||
|
rb_ary_push(filter, f_args);
|
||||||
|
|
||||||
|
/* Add to filters_arr array */
|
||||||
|
rb_ary_push(filters_arr, filter);
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return filters_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE rb_variable_lax_parse(VALUE self, VALUE m)
|
||||||
|
{
|
||||||
|
char * markup = RSTRING_PTR(m);
|
||||||
|
int markup_len = RSTRING_LEN(m);
|
||||||
|
|
||||||
|
char * cursor = markup; int cursor_pos = 0;
|
||||||
|
VALUE filters_arr;
|
||||||
|
int size, end_offset;
|
||||||
|
|
||||||
|
/* Extract name */
|
||||||
|
cursor_pos += skip_whitespace(markup, markup_len);
|
||||||
|
cursor = markup + cursor_pos;
|
||||||
|
cursor = get_quoted_fragment(cursor, markup_len - cursor_pos, &size, &end_offset, false);
|
||||||
|
|
||||||
|
if (cursor == NULL) {
|
||||||
|
rb_iv_set(self, "@name", Qnil);
|
||||||
|
filters_arr = rb_ary_new();
|
||||||
|
rb_iv_set(self, "@filters", filters_arr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rb_iv_set(self, "@name", rb_str_new(cursor, size));
|
||||||
|
|
||||||
|
/* Extract filters */
|
||||||
|
if (end_offset < markup_len) {
|
||||||
|
cursor = &markup[end_offset];
|
||||||
|
filters_arr = get_filters(cursor, markup_len - end_offset, self);
|
||||||
|
rb_iv_set(self, "@filters", filters_arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filters_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_liquid_variable()
|
||||||
|
{
|
||||||
|
cLiquidVariable = rb_define_class_under(mLiquid, "Variable", rb_cObject);
|
||||||
|
rb_define_alloc_func(cLiquidVariable, rb_variable_allocate);
|
||||||
|
rb_define_method(cLiquidVariable, "lax_parse", rb_variable_lax_parse, 1);
|
||||||
|
}
|
||||||
13
ext/liquid/variable.h
Normal file
13
ext/liquid/variable.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef LIQUID_VARIABLE_H
|
||||||
|
#define LIQUID_VARIABLE_H
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
struct liquid_variable {
|
||||||
|
char *markup; long markup_len;
|
||||||
|
char *name; long name_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_liquid_variable();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -11,7 +11,6 @@ module Liquid
|
|||||||
# {{ user | link }}
|
# {{ user | link }}
|
||||||
#
|
#
|
||||||
class Variable
|
class Variable
|
||||||
FilterParser = /(?:#{FilterSeparator}|(?:\s*(?:#{QuotedFragment}|#{ArgumentSeparator})\s*)+)/o
|
|
||||||
EasyParse = /\A *(\w+(?:\.\w+)*) *\z/
|
EasyParse = /\A *(\w+(?:\.\w+)*) *\z/
|
||||||
attr_accessor :filters, :name, :warnings
|
attr_accessor :filters, :name, :warnings
|
||||||
|
|
||||||
@@ -35,22 +34,22 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def lax_parse(markup)
|
# def lax_parse(markup)
|
||||||
@filters = []
|
# @filters = []
|
||||||
if match = markup.match(/\s*(#{QuotedFragment})(.*)/o)
|
# if match = markup.match(/\s*(#{QuotedFragment})(.*)/o)
|
||||||
@name = match[1]
|
# @name = match[1]
|
||||||
if match[2].match(/#{FilterSeparator}\s*(.*)/o)
|
# if match[2].match(/#{FilterSeparator}\s*(.*)/o)
|
||||||
filters = Regexp.last_match(1).scan(FilterParser)
|
# filters = Regexp.last_match(1).scan(FilterParser)
|
||||||
filters.each do |f|
|
# filters.each do |f|
|
||||||
if matches = f.match(/\s*(\w+)/)
|
# if matches = f.match(/\s*(\w+)/)
|
||||||
filtername = matches[1]
|
# filtername = matches[1]
|
||||||
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
|
# filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
|
||||||
@filters << [filtername, filterargs]
|
# @filters << [filtername, filterargs]
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
||||||
def strict_parse(markup)
|
def strict_parse(markup)
|
||||||
# Very simple valid cases
|
# Very simple valid cases
|
||||||
|
|||||||
@@ -51,11 +51,9 @@ class VariableTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_filter_with_date_parameter
|
def test_filter_with_date_parameter
|
||||||
|
|
||||||
var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
|
var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
|
||||||
assert_equal "'2006-06-06'", var.name
|
assert_equal "'2006-06-06'", var.name
|
||||||
assert_equal [["date",["\"%m/%d/%Y\""]]], var.filters
|
assert_equal [["date",["\"%m/%d/%Y\""]]], var.filters
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_filters_without_whitespace
|
def test_filters_without_whitespace
|
||||||
@@ -73,7 +71,7 @@ class VariableTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_symbol
|
def test_symbol
|
||||||
var = Variable.new("http://disney.com/logo.gif | image: 'med' ", :error_mode => :lax)
|
var = Variable.new("http://disney.com/logo.gif | image: 'med'", :error_mode => :lax)
|
||||||
assert_equal "http://disney.com/logo.gif", var.name
|
assert_equal "http://disney.com/logo.gif", var.name
|
||||||
assert_equal [["image",["'med'"]]], var.filters
|
assert_equal [["image",["'med'"]]], var.filters
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user