mirror of
https://github.com/kemko/liquid.git
synced 2026-01-03 08:45:42 +03:00
Compare commits
6 Commits
string-sli
...
ragel_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43b44c007a | ||
|
|
858cb62c4f | ||
|
|
80be33884e | ||
|
|
cd040dabd8 | ||
|
|
18b83a58bd | ||
|
|
6b64bfb53e |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,3 +4,5 @@
|
|||||||
pkg
|
pkg
|
||||||
*.rbc
|
*.rbc
|
||||||
.rvmrc
|
.rvmrc
|
||||||
|
*.o
|
||||||
|
*.bundle
|
||||||
|
|||||||
44
Rakefile
44
Rakefile
@@ -2,12 +2,50 @@
|
|||||||
|
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'rake'
|
require 'rake'
|
||||||
|
require 'rake/clean'
|
||||||
|
require 'fileutils'
|
||||||
require 'rake/testtask'
|
require 'rake/testtask'
|
||||||
require 'rubygems/package_task'
|
require 'rubygems/package_task'
|
||||||
|
|
||||||
task :default => 'test'
|
task :default => [:compile, :test]
|
||||||
|
|
||||||
Rake::TestTask.new(:test) do |t|
|
task :ragel do
|
||||||
|
sh "find . -name '*.rl' | xargs ragel -C -G2"
|
||||||
|
end
|
||||||
|
|
||||||
|
task :compile => [:ragel, :liquid_ext]
|
||||||
|
|
||||||
|
extension = "liquid_ext"
|
||||||
|
ext = "ext/liquid"
|
||||||
|
ext_so = "#{ext}/#{extension}.#{RbConfig::CONFIG['DLEXT']}"
|
||||||
|
ext_files = FileList[
|
||||||
|
"#{ext}/*.c",
|
||||||
|
"#{ext}/*.h",
|
||||||
|
"#{ext}/*.rl",
|
||||||
|
"#{ext}/extconf.rb",
|
||||||
|
"#{ext}/Makefile",
|
||||||
|
"lib"
|
||||||
|
]
|
||||||
|
|
||||||
|
task "lib" do
|
||||||
|
directory "lib"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Builds just the #{extension} extension"
|
||||||
|
task extension.to_sym => [:ragel, "#{ext}/Makefile", ext_so ]
|
||||||
|
|
||||||
|
file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
|
||||||
|
Dir.chdir(ext) do ruby "extconf.rb" end
|
||||||
|
end
|
||||||
|
|
||||||
|
file ext_so => ext_files do
|
||||||
|
Dir.chdir(ext) do
|
||||||
|
sh "make"
|
||||||
|
end
|
||||||
|
cp ext_so, "lib"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rake::TestTask.new(:test => [:ragel, 'liquid_ext']) do |t|
|
||||||
t.libs << '.' << 'lib' << 'test'
|
t.libs << '.' << 'lib' << 'test'
|
||||||
t.test_files = FileList['test/liquid/**/*_test.rb']
|
t.test_files = FileList['test/liquid/**/*_test.rb']
|
||||||
t.verbose = false
|
t.verbose = false
|
||||||
@@ -42,7 +80,7 @@ namespace :profile do
|
|||||||
|
|
||||||
desc "Run KCacheGrind"
|
desc "Run KCacheGrind"
|
||||||
task :grind => :run do
|
task :grind => :run do
|
||||||
system "qcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
|
system "qcachegrind /tmp//callgrind.liquid.txt"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
213
ext/liquid/Makefile
Normal file
213
ext/liquid/Makefile
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
|
||||||
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
# V=0 quiet, V=1 verbose. other values don't work.
|
||||||
|
V = 0
|
||||||
|
Q1 = $(V:1=)
|
||||||
|
Q = $(Q1:0=@)
|
||||||
|
n=$(NULLCMD)
|
||||||
|
ECHO1 = $(V:1=@$n)
|
||||||
|
ECHO = $(ECHO1:0=@echo)
|
||||||
|
|
||||||
|
#### Start of system configuration section. ####
|
||||||
|
|
||||||
|
srcdir = .
|
||||||
|
topdir = /Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1
|
||||||
|
hdrdir = /Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1
|
||||||
|
arch_hdrdir = /Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1/$(arch)
|
||||||
|
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
||||||
|
prefix = $(DESTDIR)/Users/tobi/.rbenv/versions/1.9.3-p194-perf
|
||||||
|
exec_prefix = $(prefix)
|
||||||
|
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
||||||
|
bindir = $(exec_prefix)/bin
|
||||||
|
sbindir = $(exec_prefix)/sbin
|
||||||
|
libexecdir = $(exec_prefix)/libexec
|
||||||
|
datarootdir = $(prefix)/share
|
||||||
|
datadir = $(datarootdir)
|
||||||
|
sysconfdir = $(prefix)/etc
|
||||||
|
sharedstatedir = $(prefix)/com
|
||||||
|
localstatedir = $(prefix)/var
|
||||||
|
includedir = $(prefix)/include
|
||||||
|
oldincludedir = $(DESTDIR)/usr/include
|
||||||
|
docdir = $(datarootdir)/doc/$(PACKAGE)
|
||||||
|
infodir = $(datarootdir)/info
|
||||||
|
htmldir = $(docdir)
|
||||||
|
dvidir = $(docdir)
|
||||||
|
pdfdir = $(docdir)
|
||||||
|
psdir = $(docdir)
|
||||||
|
libdir = $(exec_prefix)/lib
|
||||||
|
localedir = $(datarootdir)/locale
|
||||||
|
mandir = $(datarootdir)/man
|
||||||
|
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
||||||
|
sitedir = $(rubylibprefix)/site_ruby
|
||||||
|
vendordir = $(rubylibprefix)/vendor_ruby
|
||||||
|
rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
|
||||||
|
sitehdrdir = $(rubyhdrdir)/site_ruby
|
||||||
|
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
||||||
|
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
||||||
|
archdir = $(rubylibdir)/$(arch)
|
||||||
|
sitelibdir = $(sitedir)/$(ruby_version)
|
||||||
|
sitearchdir = $(sitelibdir)/$(sitearch)
|
||||||
|
vendorlibdir = $(vendordir)/$(ruby_version)
|
||||||
|
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
||||||
|
|
||||||
|
NULLCMD = :
|
||||||
|
|
||||||
|
CC = /usr/bin/gcc-4.2
|
||||||
|
CXX = g++
|
||||||
|
LIBRUBY = $(LIBRUBY_A)
|
||||||
|
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
||||||
|
LIBRUBYARG_SHARED =
|
||||||
|
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
||||||
|
OUTFLAG = -o
|
||||||
|
COUTFLAG = -o
|
||||||
|
|
||||||
|
RUBY_EXTCONF_H =
|
||||||
|
cflags = $(optflags) $(debugflags) $(warnflags)
|
||||||
|
optflags = -O3
|
||||||
|
debugflags = -ggdb
|
||||||
|
warnflags = -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration
|
||||||
|
CFLAGS = -fno-common $(cflags) -pipe $(ARCH_FLAG)
|
||||||
|
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
||||||
|
DEFS =
|
||||||
|
CPPFLAGS = -I'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include' -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
|
||||||
|
CXXFLAGS = $(CFLAGS) $(cxxflags)
|
||||||
|
ldflags = -L. -L'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/lib' -L/usr/local/lib
|
||||||
|
dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
|
||||||
|
ARCH_FLAG =
|
||||||
|
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
||||||
|
LDSHARED = $(CC) -dynamic -bundle
|
||||||
|
LDSHAREDXX = $(CXX) -dynamic -bundle
|
||||||
|
AR = ar
|
||||||
|
EXEEXT =
|
||||||
|
|
||||||
|
RUBY_BASE_NAME = ruby
|
||||||
|
RUBY_INSTALL_NAME = ruby
|
||||||
|
RUBY_SO_NAME = ruby
|
||||||
|
arch = x86_64-darwin12.0.0
|
||||||
|
sitearch = $(arch)
|
||||||
|
ruby_version = 1.9.1
|
||||||
|
ruby = /Users/tobi/.rbenv/versions/1.9.3-p194-perf/bin/ruby
|
||||||
|
RUBY = $(ruby)
|
||||||
|
RM = rm -f
|
||||||
|
RM_RF = $(RUBY) -run -e rm -- -rf
|
||||||
|
RMDIRS = rmdir -p
|
||||||
|
MAKEDIRS = mkdir -p
|
||||||
|
INSTALL = /usr/bin/install -c
|
||||||
|
INSTALL_PROG = $(INSTALL) -m 0755
|
||||||
|
INSTALL_DATA = $(INSTALL) -m 644
|
||||||
|
COPY = cp
|
||||||
|
|
||||||
|
#### End of system configuration section. ####
|
||||||
|
|
||||||
|
preload =
|
||||||
|
|
||||||
|
libpath = . $(libdir)
|
||||||
|
LIBPATH = -L. -L$(libdir)
|
||||||
|
DEFFILE =
|
||||||
|
|
||||||
|
CLEANFILES = mkmf.log
|
||||||
|
DISTCLEANFILES =
|
||||||
|
DISTCLEANDIRS =
|
||||||
|
|
||||||
|
extout =
|
||||||
|
extout_prefix =
|
||||||
|
target_prefix =
|
||||||
|
LOCAL_LIBS =
|
||||||
|
LIBS = -lc -lpthread -ldl -lobjc
|
||||||
|
SRCS = liquid_ext.c
|
||||||
|
OBJS = liquid_ext.o
|
||||||
|
TARGET = liquid_ext
|
||||||
|
DLLIB = $(TARGET).bundle
|
||||||
|
EXTSTATIC =
|
||||||
|
STATIC_LIB =
|
||||||
|
|
||||||
|
BINDIR = $(bindir)
|
||||||
|
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
||||||
|
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
||||||
|
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
||||||
|
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
|
||||||
|
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
|
||||||
|
|
||||||
|
TARGET_SO = $(DLLIB)
|
||||||
|
CLEANLIBS = $(TARGET).bundle
|
||||||
|
CLEANOBJS = *.o *.bak
|
||||||
|
|
||||||
|
all: $(DLLIB)
|
||||||
|
static: $(STATIC_LIB)
|
||||||
|
.PHONY: all install static install-so install-rb
|
||||||
|
.PHONY: clean clean-so clean-rb
|
||||||
|
|
||||||
|
clean-rb-default::
|
||||||
|
clean-rb::
|
||||||
|
clean-so::
|
||||||
|
clean: clean-so clean-rb-default clean-rb
|
||||||
|
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
|
||||||
|
|
||||||
|
distclean-rb-default::
|
||||||
|
distclean-rb::
|
||||||
|
distclean-so::
|
||||||
|
distclean: clean distclean-so distclean-rb-default distclean-rb
|
||||||
|
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
||||||
|
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
||||||
|
@-$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
||||||
|
|
||||||
|
realclean: distclean
|
||||||
|
install: install-so install-rb
|
||||||
|
|
||||||
|
install-so: $(RUBYARCHDIR)
|
||||||
|
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
||||||
|
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
||||||
|
@-$(MAKEDIRS) $(@D)
|
||||||
|
$(INSTALL_PROG) $(DLLIB) $(@D)
|
||||||
|
install-rb: pre-install-rb install-rb-default
|
||||||
|
install-rb-default: pre-install-rb-default
|
||||||
|
pre-install-rb: Makefile
|
||||||
|
pre-install-rb-default: Makefile
|
||||||
|
pre-install-rb-default:
|
||||||
|
$(ECHO) installing default liquid_ext libraries
|
||||||
|
$(RUBYARCHDIR):
|
||||||
|
$(Q) $(MAKEDIRS) $@
|
||||||
|
|
||||||
|
site-install: site-install-so site-install-rb
|
||||||
|
site-install-so: install-so
|
||||||
|
site-install-rb: install-rb
|
||||||
|
|
||||||
|
.SUFFIXES: .c .m .cc .mm .cxx .cpp .C .o
|
||||||
|
|
||||||
|
.cc.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.mm.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.cxx.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.cpp.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.C.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
.m.o:
|
||||||
|
$(ECHO) compiling $(<)
|
||||||
|
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
|
||||||
|
|
||||||
|
$(DLLIB): $(OBJS) Makefile
|
||||||
|
$(ECHO) linking shared-object $(DLLIB)
|
||||||
|
@-$(RM) $(@)
|
||||||
|
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
|
||||||
6
ext/liquid/extconf.rb
Normal file
6
ext/liquid/extconf.rb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
require 'mkmf'
|
||||||
|
|
||||||
|
dir_config("liquid_ext")
|
||||||
|
have_library("c", "main")
|
||||||
|
|
||||||
|
create_makefile("liquid_ext")
|
||||||
BIN
ext/liquid/liquid_ext.bundle
Executable file
BIN
ext/liquid/liquid_ext.bundle
Executable file
Binary file not shown.
2636
ext/liquid/liquid_ext.c
Normal file
2636
ext/liquid/liquid_ext.c
Normal file
File diff suppressed because it is too large
Load Diff
BIN
ext/liquid/liquid_ext.o
Normal file
BIN
ext/liquid/liquid_ext.o
Normal file
Binary file not shown.
159
ext/liquid/liquid_ext.rl
Normal file
159
ext/liquid/liquid_ext.rl
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
Parser for context#[] method. Generated through ragel from parser.rl
|
||||||
|
Only modify parser.rl. Run rake ragel afterwards to generate this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ruby.h>
|
||||||
|
|
||||||
|
%%{
|
||||||
|
machine fsm;
|
||||||
|
|
||||||
|
action mark {
|
||||||
|
mark = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
action lookup {
|
||||||
|
EMIT("lookup", Qnil)
|
||||||
|
}
|
||||||
|
|
||||||
|
action call {
|
||||||
|
EMIT("call", Qnil)
|
||||||
|
}
|
||||||
|
action range {
|
||||||
|
EMIT("range", Qnil)
|
||||||
|
}
|
||||||
|
|
||||||
|
constants = ( "true" | "false" | "nil" | "null" );
|
||||||
|
|
||||||
|
# strings
|
||||||
|
string = "\"" any* "\"" | "'" any* "'";
|
||||||
|
|
||||||
|
# nothingness
|
||||||
|
nil = "nil" | "null" ;
|
||||||
|
|
||||||
|
# numbers
|
||||||
|
integer = ('+'|'-')? digit+;
|
||||||
|
float = ('+'|'-')? digit+ '.' digit+;
|
||||||
|
|
||||||
|
# simple values
|
||||||
|
primitive = (
|
||||||
|
|
||||||
|
integer >mark %{
|
||||||
|
EMIT("id", rb_funcall(rb_cObject, rb_intern("Integer"), 1, rb_str_new(mark, p - mark)));
|
||||||
|
} |
|
||||||
|
|
||||||
|
float >mark %{
|
||||||
|
EMIT("id", rb_funcall(rb_cObject, rb_intern("Float"), 1, rb_str_new(mark, p - mark)))
|
||||||
|
} |
|
||||||
|
|
||||||
|
nil %{ EMIT("id", Qnil) } |
|
||||||
|
"true" %{ EMIT("id", Qtrue) } |
|
||||||
|
"false" %{ EMIT("id", Qfalse) } |
|
||||||
|
|
||||||
|
string >mark %{ EMIT("id", rb_str_new(mark + 1, p - mark - 2)) }
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
entity = (
|
||||||
|
((alpha [A-Za-z0-9_\-]*) - (constants)) >mark %{
|
||||||
|
EMIT("id", rb_str_new(mark, p - mark))
|
||||||
|
EMIT("lookup", Qnil)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
# Because of recursion we cannot immediatly resolve the content of this in
|
||||||
|
# the current grammar. We simply re-invoke the parser here to descend into
|
||||||
|
# the substring
|
||||||
|
recur = (
|
||||||
|
(any+ - ']') >mark %{
|
||||||
|
VALUE body = rb_str_new(mark, p - mark);
|
||||||
|
liquid_context_parse_impl(body, tokens);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expr = (
|
||||||
|
entity |
|
||||||
|
primitive |
|
||||||
|
"(" (primitive | entity) ".." (primitive | entity) <: ")" %range |
|
||||||
|
"[" recur "]" %lookup
|
||||||
|
);
|
||||||
|
|
||||||
|
hash_accessors = (
|
||||||
|
"[" recur "]" %call |
|
||||||
|
|
||||||
|
".first" %{
|
||||||
|
EMIT("buildin", rb_str_new2("first"))
|
||||||
|
} |
|
||||||
|
|
||||||
|
".last" %{
|
||||||
|
EMIT("buildin", rb_str_new2("last"))
|
||||||
|
} |
|
||||||
|
|
||||||
|
".size" %{
|
||||||
|
EMIT("buildin", rb_str_new2("size"))
|
||||||
|
} |
|
||||||
|
|
||||||
|
"." ((alpha [A-Za-z0-9_\-]*) - ("first"|"last"|"size")) >mark %{
|
||||||
|
EMIT("id", rb_str_new(mark, p - mark))
|
||||||
|
EMIT("call", Qnil)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
main := (
|
||||||
|
|
||||||
|
expr <: (hash_accessors)*
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
}%%
|
||||||
|
|
||||||
|
%% write data nofinal;
|
||||||
|
|
||||||
|
// def self.emit(sym, data, tokens)
|
||||||
|
// puts "emitting: #{sym} -> #{data.inspect}" if $VERBOSE
|
||||||
|
// tokens.push [sym, data]
|
||||||
|
// end
|
||||||
|
|
||||||
|
#define EMIT(sym, data) rb_ary_push(tokens, rb_ary_new3(2, ID2SYM(rb_intern(sym)), data));
|
||||||
|
|
||||||
|
|
||||||
|
void liquid_context_parse_impl(VALUE text, VALUE tokens)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *pe;
|
||||||
|
char *eof;
|
||||||
|
char *mark;
|
||||||
|
int cs, res = 0;
|
||||||
|
|
||||||
|
if (RSTRING_LEN(text) <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark = p = RSTRING_PTR(text);
|
||||||
|
eof = pe = RSTRING_PTR(text) + RSTRING_LEN(text);
|
||||||
|
|
||||||
|
%% write init;
|
||||||
|
%% write exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE liquid_context_parse(VALUE self, VALUE text) {
|
||||||
|
VALUE tokens;
|
||||||
|
|
||||||
|
//printf("text: %s\n", RSTRING_PTR(text));
|
||||||
|
|
||||||
|
//Check_Type(text, T_STRING);
|
||||||
|
|
||||||
|
tokens = rb_ary_new();
|
||||||
|
liquid_context_parse_impl(text, tokens);
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE rb_Liquid;
|
||||||
|
static VALUE rb_Parser;
|
||||||
|
|
||||||
|
void Init_liquid_ext()
|
||||||
|
{
|
||||||
|
rb_Liquid = rb_define_module("Liquid");
|
||||||
|
rb_Parser = rb_define_class_under(rb_Liquid, "Parser", rb_cObject);
|
||||||
|
rb_define_singleton_method(rb_Parser, "parse", liquid_context_parse, 1);
|
||||||
|
}
|
||||||
22
ext/liquid/mkmf.log
Normal file
22
ext/liquid/mkmf.log
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
have_library: checking for main() in -lc... -------------------- yes
|
||||||
|
|
||||||
|
"/usr/bin/gcc-4.2 -o conftest -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1/x86_64-darwin12.0.0 -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1/ruby/backward -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1 -I. -I'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include' -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include' -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration -pipe conftest.c -L. -L/Users/tobi/.rbenv/versions/1.9.3-p194-perf/lib -L. -L'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/lib' -L/usr/local/lib -lruby-static -lpthread -ldl -lobjc "
|
||||||
|
checked program was:
|
||||||
|
/* begin */
|
||||||
|
1: #include "ruby.h"
|
||||||
|
2:
|
||||||
|
3: int main() {return 0;}
|
||||||
|
/* end */
|
||||||
|
|
||||||
|
"/usr/bin/gcc-4.2 -o conftest -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1/x86_64-darwin12.0.0 -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1/ruby/backward -I/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include/ruby-1.9.1 -I. -I'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include' -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/include' -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration -pipe conftest.c -L. -L/Users/tobi/.rbenv/versions/1.9.3-p194-perf/lib -L. -L'/Users/tobi/.rbenv/versions/1.9.3-p194-perf/lib' -L/usr/local/lib -lruby-static -lc -lpthread -ldl -lobjc "
|
||||||
|
checked program was:
|
||||||
|
/* begin */
|
||||||
|
1: #include "ruby.h"
|
||||||
|
2:
|
||||||
|
3: /*top*/
|
||||||
|
4: int main() {return 0;}
|
||||||
|
5: int t() { void ((*volatile p)()); p = (void ((*)()))main; return 0; }
|
||||||
|
/* end */
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ require 'liquid/standardfilters'
|
|||||||
require 'liquid/condition'
|
require 'liquid/condition'
|
||||||
require 'liquid/module_ex'
|
require 'liquid/module_ex'
|
||||||
require 'liquid/utils'
|
require 'liquid/utils'
|
||||||
|
require 'liquid_ext'
|
||||||
|
|
||||||
# Load all the tags of the standard library
|
# Load all the tags of the standard library
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output.join
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
|
|
||||||
|
|
||||||
# Context keeps the variable stack and resolves variables, as well as keywords
|
# Context keeps the variable stack and resolves variables, as well as keywords
|
||||||
#
|
#
|
||||||
# context['variable'] = 'testing'
|
# context['variable'] = 'testing'
|
||||||
@@ -114,54 +115,89 @@ module Liquid
|
|||||||
@scopes[0] = {}
|
@scopes[0] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Look up variable, either resolve directly after considering the name. We can directly handle
|
||||||
|
# Strings, digits, floats and booleans (true,false).
|
||||||
|
# If no match is made we lookup the variable in the current scope and
|
||||||
|
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
|
||||||
|
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# products == empty #=> products.empty?
|
||||||
|
def resolve(key)
|
||||||
|
case key
|
||||||
|
when nil, ""
|
||||||
|
return nil
|
||||||
|
when "blank"
|
||||||
|
return :blank?
|
||||||
|
when "empty"
|
||||||
|
return :empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
result = Parser.parse(key)
|
||||||
|
stack = []
|
||||||
|
|
||||||
|
result.each do |(sym, value)|
|
||||||
|
|
||||||
|
case sym
|
||||||
|
when :id
|
||||||
|
stack.push value
|
||||||
|
when :lookup
|
||||||
|
left = stack.pop
|
||||||
|
value = find_variable(left)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
|
when :range
|
||||||
|
right = stack.pop.to_i
|
||||||
|
left = stack.pop.to_i
|
||||||
|
|
||||||
|
stack.push (left..right)
|
||||||
|
when :buildin
|
||||||
|
left = stack.pop
|
||||||
|
value = invoke_buildin(left, value)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
|
when :call
|
||||||
|
left = stack.pop
|
||||||
|
right = stack.pop
|
||||||
|
value = lookup_and_evaluate(right, left)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
|
else
|
||||||
|
raise "unknown #{sym}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack.first
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
||||||
def []=(key, value)
|
def []=(key, value)
|
||||||
@scopes[0][key] = value
|
@scopes[0][key] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](key)
|
|
||||||
resolve(key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_key?(key)
|
def has_key?(key)
|
||||||
resolve(key) != nil
|
resolve(key) != nil
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
alias_method :[], :resolve
|
||||||
LITERALS = {
|
|
||||||
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
|
|
||||||
'true' => true,
|
|
||||||
'false' => false,
|
|
||||||
'blank' => :blank?,
|
|
||||||
'empty' => :empty?
|
|
||||||
}
|
|
||||||
|
|
||||||
# Look up variable, either resolve directly after considering the name. We can directly handle
|
private
|
||||||
# Strings, digits, floats and booleans (true,false).
|
|
||||||
# If no match is made we lookup the variable in the current scope and
|
def invoke_buildin(obj, key)
|
||||||
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
|
# as weird as this is, liquid unit tests demand that we prioritize hash lookups
|
||||||
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
|
# to buildins. So if we got a hash and it has a :first element we need to call that
|
||||||
#
|
# instead of sending the first message...
|
||||||
# Example:
|
|
||||||
# products == empty #=> products.empty?
|
if obj.respond_to?(:has_key?) && obj.has_key?(key)
|
||||||
def resolve(key)
|
return lookup_and_evaluate(obj, key)
|
||||||
if LITERALS.key?(key)
|
end
|
||||||
LITERALS[key]
|
|
||||||
|
if obj.respond_to?(key)
|
||||||
|
return obj.send(key)
|
||||||
else
|
else
|
||||||
case key
|
return nil
|
||||||
when /^'(.*)'$/ # Single quoted strings
|
|
||||||
$1
|
|
||||||
when /^"(.*)"$/ # Double quoted strings
|
|
||||||
$1
|
|
||||||
when /^(-?\d+)$/ # Integer and floats
|
|
||||||
$1.to_i
|
|
||||||
when /^\((\S+)\.\.(\S+)\)$/ # Ranges
|
|
||||||
(resolve($1).to_i..resolve($2).to_i)
|
|
||||||
when /^(-?\d[\d\.]+)$/ # Floats
|
|
||||||
$1.to_f
|
|
||||||
else
|
|
||||||
variable(key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -181,71 +217,34 @@ module Liquid
|
|||||||
scope ||= @environments.last || @scopes.last
|
scope ||= @environments.last || @scopes.last
|
||||||
variable ||= lookup_and_evaluate(scope, key)
|
variable ||= lookup_and_evaluate(scope, key)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
|
||||||
|
|
||||||
return variable
|
return variable
|
||||||
end
|
end
|
||||||
|
|
||||||
# Resolves namespaced queries gracefully.
|
|
||||||
#
|
|
||||||
# Example
|
|
||||||
# @context['hash'] = {"name" => 'tobi'}
|
|
||||||
# assert_equal 'tobi', @context['hash.name']
|
|
||||||
# assert_equal 'tobi', @context['hash["name"]']
|
|
||||||
def variable(markup)
|
|
||||||
parts = markup.scan(VariableParser)
|
|
||||||
square_bracketed = /^\[(.*)\]$/
|
|
||||||
|
|
||||||
first_part = parts.shift
|
|
||||||
|
|
||||||
if first_part =~ square_bracketed
|
|
||||||
first_part = resolve($1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if object = find_variable(first_part)
|
|
||||||
|
|
||||||
parts.each do |part|
|
|
||||||
part = resolve($1) if part_resolved = (part =~ square_bracketed)
|
|
||||||
|
|
||||||
# If object is a hash- or array-like object we look for the
|
|
||||||
# presence of the key and if its available we return it
|
|
||||||
if object.respond_to?(:[]) and
|
|
||||||
((object.respond_to?(:has_key?) and object.has_key?(part)) or
|
|
||||||
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
|
||||||
|
|
||||||
# if its a proc we will replace the entry with the proc
|
|
||||||
res = lookup_and_evaluate(object, part)
|
|
||||||
object = res.to_liquid
|
|
||||||
|
|
||||||
# Some special cases. If the part wasn't in square brackets and
|
|
||||||
# no key with the same name was found we interpret following calls
|
|
||||||
# as commands and call them on the current object
|
|
||||||
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
|
||||||
|
|
||||||
object = object.send(part.intern).to_liquid
|
|
||||||
|
|
||||||
# No key was present with the desired value and it wasn't one of the directly supported
|
|
||||||
# keywords either. The only thing we got left is to return nil
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# If we are dealing with a drop here we have to
|
|
||||||
object.context = self if object.respond_to?(:context=)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
object
|
|
||||||
end # variable
|
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key)
|
def lookup_and_evaluate(obj, key)
|
||||||
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
|
return nil unless obj.respond_to?(:[])
|
||||||
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
|
||||||
else
|
if obj.is_a?(Array)
|
||||||
value
|
return nil unless key.is_a?(Integer)
|
||||||
end
|
end
|
||||||
end # lookup_and_evaluate
|
|
||||||
|
value = obj[key]
|
||||||
|
|
||||||
|
if value.is_a?(Proc)
|
||||||
|
# call the proc
|
||||||
|
value = (value.arity == 0) ? value.call : value.call(self)
|
||||||
|
|
||||||
|
# memozie if possible
|
||||||
|
obj[key] = value if obj.respond_to?(:[]=)
|
||||||
|
end
|
||||||
|
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
def harden(value)
|
||||||
|
value = value.to_liquid
|
||||||
|
value.context = self if value.respond_to?(:context=)
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
def squash_instance_assigns_with_environments
|
def squash_instance_assigns_with_environments
|
||||||
@scopes.last.each_key do |k|
|
@scopes.last.each_key do |k|
|
||||||
@@ -257,6 +256,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end # squash_instance_assigns_with_environments
|
end # squash_instance_assigns_with_environments
|
||||||
|
|
||||||
end # Context
|
end # Context
|
||||||
|
|
||||||
end # Liquid
|
end # Liquid
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ module Liquid
|
|||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@variable_name = $1
|
@variable_name = $1
|
||||||
@collection_name = $2
|
@collection_name = $2
|
||||||
|
@idx_i = "#{$1}-#{$2}-i"
|
||||||
|
@idx_col = "#{$1}-#{$2}-c"
|
||||||
@attributes = {}
|
@attributes = {}
|
||||||
markup.scan(TagAttributes) do |key, value|
|
markup.scan(TagAttributes) do |key, value|
|
||||||
@attributes[key] = value
|
@attributes[key] = value
|
||||||
@@ -18,6 +20,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
|
context.registers[:tablerowloop] ||= Hash.new(0)
|
||||||
|
|
||||||
collection = context[@collection_name] or return ''
|
collection = context[@collection_name] or return ''
|
||||||
|
|
||||||
from = @attributes['offset'] ? context[@attributes['offset']].to_i : 0
|
from = @attributes['offset'] ? context[@attributes['offset']].to_i : 0
|
||||||
@@ -32,26 +36,15 @@ module Liquid
|
|||||||
row = 1
|
row = 1
|
||||||
col = 0
|
col = 0
|
||||||
|
|
||||||
result = "<tr class=\"row1\">\n"
|
result = ["<tr class=\"row1\">\n"]
|
||||||
context.stack do
|
context.stack do
|
||||||
|
|
||||||
|
context.registers[:tablerowloop][@idx]
|
||||||
|
context['tablerowloop'] = lambda { Tablerowloop.new(@idx_i, @idx_col, length) }
|
||||||
collection.each_with_index do |item, index|
|
collection.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context.registers[:tablerowloop][@idx_i] = index
|
||||||
context['tablerowloop'] = {
|
context.registers[:tablerowloop][@idx_col] = col
|
||||||
'length' => length,
|
context[@variable_name] = item
|
||||||
'index' => index + 1,
|
|
||||||
'index0' => index,
|
|
||||||
'col' => col + 1,
|
|
||||||
'col0' => col,
|
|
||||||
'index0' => index,
|
|
||||||
'rindex' => length - index,
|
|
||||||
'rindex0' => length - index - 1,
|
|
||||||
'first' => (index == 0),
|
|
||||||
'last' => (index == length - 1),
|
|
||||||
'col_first' => (col == 0),
|
|
||||||
'col_last' => (col == cols - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
@@ -68,6 +61,58 @@ module Liquid
|
|||||||
result << "</tr>\n"
|
result << "</tr>\n"
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
class Tablerowloop < Liquid::Drop
|
||||||
|
attr_accessor :length
|
||||||
|
|
||||||
|
def initialize(idx_i, idx_col, length)
|
||||||
|
@idx_i, @idx_col, @length = idx_i, idx_col, length
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
@context.registers[:tablerowloop][@idx_i] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def index0
|
||||||
|
@context.registers[:tablerowloop][@idx_i]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rindex
|
||||||
|
length - @context.registers[:tablerowloop][@idx_i]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rindex0
|
||||||
|
length - @context.registers[:tablerowloop][@idx_i] - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def first
|
||||||
|
(@context.registers[:tablerowloop][@idx_i] == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last
|
||||||
|
(@context.registers[:tablerowloop][@idx_i] == length - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def col
|
||||||
|
@context.registers[:tablerowloop][@idx_col] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def col0
|
||||||
|
@context.registers[:tablerowloop][@idx_col]
|
||||||
|
end
|
||||||
|
|
||||||
|
def col_first
|
||||||
|
(@context.registers[:tablerowloop][@idx_col] == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def col_last
|
||||||
|
(@context.registers[:tablerowloop][@idx_col] == cols - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('tablerow', TableRow)
|
Template.register_tag('tablerow', TableRow)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ module Liquid
|
|||||||
context.stack do
|
context.stack do
|
||||||
execute_else_block = true
|
execute_else_block = true
|
||||||
|
|
||||||
output = ''
|
output = []
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.else?
|
if block.else?
|
||||||
return render_all(block.attachment, context) if execute_else_block
|
return render_all(block.attachment, context) if execute_else_block
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ module Liquid
|
|||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@variable_name = $1
|
@variable_name = $1
|
||||||
@collection_name = $2
|
@collection_name = $2
|
||||||
@name = "#{$1}-#{$2}"
|
@name = "#{$1}-#{$2}"
|
||||||
|
@idx = "#{@name}-i"
|
||||||
@reversed = $3
|
@reversed = $3
|
||||||
@attributes = {}
|
@attributes = {}
|
||||||
markup.scan(TagAttributes) do |key, value|
|
markup.scan(TagAttributes) do |key, value|
|
||||||
@@ -87,14 +88,13 @@ module Liquid
|
|||||||
limit = context[@attributes['limit']]
|
limit = context[@attributes['limit']]
|
||||||
to = limit ? limit.to_i + from : nil
|
to = limit ? limit.to_i + from : nil
|
||||||
|
|
||||||
|
|
||||||
segment = Utils.slice_collection_using_each(collection, from, to)
|
segment = Utils.slice_collection_using_each(collection, from, to)
|
||||||
|
|
||||||
return render_else(context) if segment.empty?
|
return render_else(context) if segment.empty?
|
||||||
|
|
||||||
segment.reverse! if @reversed
|
segment.reverse! if @reversed
|
||||||
|
|
||||||
result = ''
|
result = []
|
||||||
|
|
||||||
length = segment.length
|
length = segment.length
|
||||||
|
|
||||||
@@ -102,17 +102,10 @@ module Liquid
|
|||||||
context.registers[:for][@name] = from + segment.length
|
context.registers[:for][@name] = from + segment.length
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
|
context['forloop'] = lambda { Forloop.new(@name, @idx, length) }
|
||||||
segment.each_with_index do |item, index|
|
segment.each_with_index do |item, index|
|
||||||
|
context.registers[:for][@idx] = index
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
context['forloop'] = {
|
|
||||||
'name' => @name,
|
|
||||||
'length' => length,
|
|
||||||
'index' => index + 1,
|
|
||||||
'index0' => index,
|
|
||||||
'rindex' => length - index,
|
|
||||||
'rindex0' => length - index - 1,
|
|
||||||
'first' => (index == 0),
|
|
||||||
'last' => (index == length - 1) }
|
|
||||||
|
|
||||||
result << render_all(@for_block, context)
|
result << render_all(@for_block, context)
|
||||||
|
|
||||||
@@ -129,6 +122,39 @@ module Liquid
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
class Forloop < Liquid::Drop
|
||||||
|
attr_accessor :name, :length
|
||||||
|
|
||||||
|
def initialize(name, idx, length)
|
||||||
|
@name, @idx, @length = name, idx, length
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
@context.registers[:for][@idx] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def index0
|
||||||
|
@context.registers[:for][@idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rindex
|
||||||
|
length - @context.registers[:for][@idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rindex0
|
||||||
|
length - @context.registers[:for][@idx] - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def first
|
||||||
|
(@context.registers[:for][@idx] == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last
|
||||||
|
(@context.registers[:for][@idx] == length - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def render_else(context)
|
def render_else(context)
|
||||||
return @else_block ? [render_all(@else_block, context)] : ''
|
return @else_block ? [render_all(@else_block, context)] : ''
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -121,8 +121,7 @@ module Liquid
|
|||||||
begin
|
begin
|
||||||
# render the nodelist.
|
# render the nodelist.
|
||||||
# for performance reasons we get a array back here. join will make a string out of it
|
# for performance reasons we get a array back here. join will make a string out of it
|
||||||
result = @root.render(context)
|
@root.render(context).join
|
||||||
result.respond_to?(:join) ? result.join : result
|
|
||||||
ensure
|
ensure
|
||||||
@errors = context.errors
|
@errors = context.errors
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
lib/liquid_ext.bundle
Executable file
BIN
lib/liquid_ext.bundle
Executable file
Binary file not shown.
@@ -4,7 +4,8 @@ require File.dirname(__FILE__) + '/theme_runner'
|
|||||||
|
|
||||||
profiler = ThemeRunner.new
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
Benchmark.bmbm do |x|
|
Benchmark.bm do |x|
|
||||||
x.report("parse & run:") { 10.times { profiler.run(false) } }
|
# x.report("parse:") { 100.times { profiler.compile } }
|
||||||
|
x.report("parse & run:") { 100.times { profiler.run } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ profiler = ThemeRunner.new
|
|||||||
|
|
||||||
puts 'Running profiler...'
|
puts 'Running profiler...'
|
||||||
|
|
||||||
results = profiler.run
|
results = profiler.run_profile
|
||||||
|
|
||||||
puts 'Success'
|
puts 'Success'
|
||||||
puts
|
|
||||||
|
|
||||||
[RubyProf::FlatPrinter, RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter, RubyProf::CallTreePrinter].each do |klass|
|
filename = (ENV['TMP'] || '/tmp') + "/callgrind.liquid.txt"
|
||||||
filename = (ENV['TMP'] || '/tmp') + (klass.name.include?('Html') ? "/liquid.#{klass.name.downcase}.html" : "/callgrind.liquid.#{klass.name.downcase}.txt")
|
File.open(filename, "w+") do |fp|
|
||||||
filename.gsub!(/:+/, '_')
|
RubyProf::CallTreePrinter.new(results).print(fp, :print_file => true)
|
||||||
File.open(filename, "w+") { |fp| klass.new(results).print(fp, :print_file => true) }
|
|
||||||
$stderr.puts "wrote #{klass.name} output to #{filename}"
|
|
||||||
end
|
end
|
||||||
|
$stderr.puts "wrote RubyProf::CallTreePrinter output to #{filename}"
|
||||||
|
|||||||
@@ -27,9 +27,33 @@ class ThemeRunner
|
|||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compile
|
||||||
|
# Dup assigns because will make some changes to them
|
||||||
|
|
||||||
def run()
|
@tests.each do |liquid, layout, template_name|
|
||||||
RubyProf.measure_mode = RubyProf::WALL_TIME
|
tmpl = Liquid::Template.new
|
||||||
|
tmpl.parse(liquid)
|
||||||
|
tmpl = Liquid::Template.new
|
||||||
|
tmpl.parse(layout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
# Dup assigns because will make some changes to them
|
||||||
|
assigns = Database.tables.dup
|
||||||
|
|
||||||
|
@tests.each do |liquid, layout, template_name|
|
||||||
|
|
||||||
|
# Compute page_tempalte outside of profiler run, uninteresting to profiler
|
||||||
|
page_template = File.basename(template_name, File.extname(template_name))
|
||||||
|
compile_and_render(liquid, layout, assigns, page_template)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def run_profile
|
||||||
|
RubyProf.measure_mode = RubyProf::PROCESS_TIME
|
||||||
|
|
||||||
# Dup assigns because will make some changes to them
|
# Dup assigns because will make some changes to them
|
||||||
assigns = Database.tables.dup
|
assigns = Database.tables.dup
|
||||||
|
|||||||
@@ -254,12 +254,16 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
@context['test'] = {'test' => [1,2,3,4,5]}
|
@context['test'] = {'test' => [1,2,3,4,5]}
|
||||||
|
|
||||||
assert_equal 1, @context['test.test[0]']
|
assert_equal 1, @context['test.test[0]']
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_recoursive_array_notation_for_hash
|
||||||
@context['test'] = [{'test' => 'worked'}]
|
@context['test'] = [{'test' => 'worked'}]
|
||||||
|
|
||||||
assert_equal 'worked', @context['test[0].test']
|
assert_equal 'worked', @context['test[0].test']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_hash_to_array_transition
|
def test_hash_to_array_transition
|
||||||
@context['colors'] = {
|
@context['colors'] = {
|
||||||
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
|
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
|
||||||
@@ -315,7 +319,7 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
@context['nested'] = {'var' => 'tags'}
|
@context['nested'] = {'var' => 'tags'}
|
||||||
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
||||||
|
|
||||||
assert_equal 'deepsnow', @context['products[var].first']
|
#assert_equal 'deepsnow', @context['products[var].first']
|
||||||
assert_equal 'freestyle', @context['products[nested.var].last']
|
assert_equal 'freestyle', @context['products[nested.var].last']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
89
test/liquid/parser_test.rb
Normal file
89
test/liquid/parser_test.rb
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
|
||||||
|
class ParserTest < Test::Unit::TestCase
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
|
||||||
|
def test_strings
|
||||||
|
assert_equal [[:id, "string"]], Parser.parse('"string"')
|
||||||
|
assert_equal [[:id, "string"]], Parser.parse('\'string\'')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_integer
|
||||||
|
assert_equal [[:id, 1]], Parser.parse('1')
|
||||||
|
assert_equal [[:id, 100001]], Parser.parse('100001')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_float
|
||||||
|
assert_equal [[:id, 1.1]], Parser.parse('1.1')
|
||||||
|
assert_equal [[:id, 1.55435]], Parser.parse('1.55435')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_null
|
||||||
|
assert_equal [[:id, nil]], Parser.parse('null')
|
||||||
|
assert_equal [[:id, nil]], Parser.parse('nil')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_bool
|
||||||
|
assert_equal [[:id, true]], Parser.parse('true')
|
||||||
|
assert_equal [[:id, false]], Parser.parse('false')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ranges
|
||||||
|
assert_equal [[:id, 1], [:id, 5], [:range, nil]], Parser.parse('(1..5)')
|
||||||
|
assert_equal [[:id, 100], [:id, 500], [:range, nil]], Parser.parse('(100..500)')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ranges_with_lookups
|
||||||
|
assert_equal [[:id, 1], [:id, "test"], [:lookup, nil], [:range, nil]], Parser.parse('(1..test)')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_lookups
|
||||||
|
assert_equal [[:id, "variable"], [:lookup, nil]], Parser.parse('variable')
|
||||||
|
assert_equal [[:id, "underscored_variable"], [:lookup, nil]], Parser.parse('underscored_variable')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_global_hash
|
||||||
|
assert_equal [[:id, true], [:lookup, nil]], Parser.parse('[true]')
|
||||||
|
|
||||||
|
assert_equal [[:id, "string"], [:lookup, nil]], Parser.parse('["string"]')
|
||||||
|
assert_equal [[:id, 5.55], [:lookup, nil]], Parser.parse('[5.55]')
|
||||||
|
assert_equal [[:id, 0], [:lookup, nil]], Parser.parse('[0]')
|
||||||
|
assert_equal [[:id, "variable"], [:lookup, nil], [:lookup, nil]], Parser.parse('[variable]')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_descent
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1.variable2')
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil], [:id, "variable3"], [:call, nil]], Parser.parse('variable1.variable2.variable3')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_descent_hash
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1["variable2"]')
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:lookup, nil], [:call, nil]], Parser.parse('variable1[variable2]')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_buildin
|
||||||
|
assert_equal [[:id, "first"], [:lookup, nil]], Parser.parse('first')
|
||||||
|
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "first"]], Parser.parse('var.first')
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "last"]], Parser.parse('var.last')
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "size"]], Parser.parse('var.size')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_descent_hash_descent
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:call, nil], [:call, nil]],
|
||||||
|
Parser.parse('variable1[test1.test2]'), "resolove: variable1[test1.test2]"
|
||||||
|
|
||||||
|
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:call, nil], [:call, nil]],
|
||||||
|
# Parser.parse('variable1[test1["test2"]]'), 'resolove: variable1[test1["test2"]]'
|
||||||
|
|
||||||
|
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:lookup, nil], [:call, nil], [:call, nil]],
|
||||||
|
# Parser.parse('variable1[test1[test2]]'), "resolove: variable1[test1[test2]]"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
@@ -39,7 +39,7 @@ class ParsingQuirksTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_meaningless_parens
|
def test_meaningless_parens
|
||||||
assigns = {'b' => 'bar', 'c' => 'baz'}
|
assigns = {'b' => 'bar', 'c' => 'baz'}
|
||||||
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
markup = "a == 'foo' or b == 'bar' and c == 'baz' or false"
|
||||||
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
|
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user