mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Superfluid -- initial hack
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -16,6 +16,7 @@ end
|
||||
|
||||
group :test do
|
||||
gem 'rubocop', '~> 0.53.0'
|
||||
gem 'awesome_print'
|
||||
|
||||
platform :mri do
|
||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
|
||||
|
||||
249
lab.rb
Normal file
249
lab.rb
Normal file
@@ -0,0 +1,249 @@
|
||||
require 'liquid'
|
||||
require 'ap'
|
||||
|
||||
AwesomePrint.defaults = {
|
||||
raw: true
|
||||
}
|
||||
|
||||
source = <<~LIQUID
|
||||
|
||||
{% assign prime_max = 10000 %}
|
||||
|
||||
{% for candidate in (3..prime_max) %}{% assign prime = 1 %}{% assign less = candidate | minus: 1 %}{% for divisor in (2..less) %}{% assign rem = candidate | modulo: divisor %}{% if rem == 0 %}{% assign prime = 0 %}{% break %}{% endif %}{% endfor %}{% if prime == 1 %}{{ candidate }}, {% endif %}{% endfor %}
|
||||
LIQUID
|
||||
|
||||
template = Liquid::Template.parse(source)
|
||||
|
||||
def yell!(message)
|
||||
system("toilet --gay #{message}")
|
||||
end
|
||||
|
||||
def derp(message, expr)
|
||||
yell! message
|
||||
ap expr
|
||||
exit
|
||||
end
|
||||
|
||||
class Output
|
||||
attr_reader :string
|
||||
|
||||
def initialize(initial_indent)
|
||||
@string = ''.dup
|
||||
@indent_level = initial_indent
|
||||
@indent_str = " " * initial_indent * 2
|
||||
end
|
||||
|
||||
def <<(line)
|
||||
@string << @indent_str << line << "\n"
|
||||
end
|
||||
|
||||
def indent
|
||||
@indent_level += 1
|
||||
@indent_str = " " * @indent_level * 2
|
||||
yield
|
||||
@indent_level -= 1
|
||||
@indent_str = " " * @indent_level * 2
|
||||
end
|
||||
end
|
||||
|
||||
class Compiler
|
||||
class << self
|
||||
def compile(template)
|
||||
compiler = new
|
||||
compiler.compile(template)
|
||||
compiler.ruby
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@output = Output.new(2)
|
||||
end
|
||||
|
||||
def ruby
|
||||
[
|
||||
header,
|
||||
@output.string,
|
||||
trailer
|
||||
].join("\n")
|
||||
end
|
||||
|
||||
def compile(node)
|
||||
case node
|
||||
when Liquid::Document, Liquid::BlockBody
|
||||
node.nodelist.collect(&method(:compile))
|
||||
when Liquid::Variable
|
||||
compile_variable(node)
|
||||
when Liquid::For
|
||||
compile_for(node)
|
||||
when Liquid::If
|
||||
compile_if(node)
|
||||
when Liquid::Template
|
||||
compile(node.root)
|
||||
when Liquid::Assign
|
||||
compile_assign(node)
|
||||
when String
|
||||
compile_echo_literal(node)
|
||||
when Liquid::Break
|
||||
line "break"
|
||||
else
|
||||
yell! "Weird node"
|
||||
ap node
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
def header
|
||||
<<~RUBY
|
||||
class NilUndefinedMethod
|
||||
def method_missing(*)
|
||||
nil
|
||||
end
|
||||
|
||||
def run(liquid_out, strainer)
|
||||
RUBY
|
||||
end
|
||||
|
||||
def trailer
|
||||
<<~RUBY
|
||||
end
|
||||
end
|
||||
NilUndefinedMethod.new.method(:run)
|
||||
RUBY
|
||||
end
|
||||
|
||||
def compile_for(node)
|
||||
variable_name = node.variable_name
|
||||
|
||||
collection_name = node.collection_name
|
||||
iter_target_expr = case collection_name
|
||||
when Liquid::VariableLookup
|
||||
var(collection_name.name)
|
||||
when Range
|
||||
collection_name
|
||||
when Liquid::RangeLookup
|
||||
start_expr = make_variable_expr(collection_name.start_obj)
|
||||
end_expr = make_variable_expr(collection_name.end_obj)
|
||||
"#{start_expr}..#{end_expr}"
|
||||
else
|
||||
derp "Weird iter!", collection_name
|
||||
end
|
||||
|
||||
line "(#{iter_target_expr}).each do |#{var(variable_name)}|"
|
||||
indent { compile(node.for_block) }
|
||||
line "end"
|
||||
end
|
||||
|
||||
def compile_if(node)
|
||||
condition = node.blocks.first
|
||||
left = make_variable_expr(condition.left)
|
||||
right = make_variable_expr(condition.right)
|
||||
line "if #{left} #{condition.operator} #{right}"
|
||||
indent { condition.attachment.nodelist.each(&method(:compile)) }
|
||||
line "end"
|
||||
end
|
||||
|
||||
def compile_echo_literal(node)
|
||||
echo node.inspect
|
||||
end
|
||||
|
||||
def compile_variable(variable)
|
||||
echo make_variable_expr(variable)
|
||||
end
|
||||
|
||||
def compile_assign(node)
|
||||
from_expr = case node.from
|
||||
when Liquid::Variable
|
||||
make_variable_expr(node.from)
|
||||
else
|
||||
derp "Weird assign", node
|
||||
end
|
||||
|
||||
line "#{var(node.to)} = #{from_expr}"
|
||||
end
|
||||
|
||||
def make_variable_expr(variable)
|
||||
case variable
|
||||
when Liquid::Variable
|
||||
base_expression = case variable.name
|
||||
when Integer
|
||||
variable.name
|
||||
when String
|
||||
variable.name.inspect
|
||||
when Liquid::VariableLookup
|
||||
var(variable.name.name)
|
||||
else
|
||||
derp "Bad var name", variable.name
|
||||
end
|
||||
variable.filters.inject(base_expression) do |inner, filter|
|
||||
filter_name = filter.first
|
||||
args = filter.last.map(&method(:make_variable_expr))
|
||||
"strainer.invoke(#{filter_name.inspect}, #{inner}, *[#{args.join(', ')}])"
|
||||
end
|
||||
when Liquid::VariableLookup
|
||||
var(variable.name)
|
||||
else
|
||||
variable.inspect
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def line(string)
|
||||
output << string
|
||||
end
|
||||
|
||||
def echo(string)
|
||||
output << "liquid_out.write(#{string})"
|
||||
end
|
||||
|
||||
def indent(&block)
|
||||
output.indent(&block)
|
||||
end
|
||||
|
||||
def var(name)
|
||||
"__liquid_#{name}"
|
||||
end
|
||||
|
||||
attr_reader :output
|
||||
end
|
||||
|
||||
ap template
|
||||
|
||||
context = Liquid::Context.new
|
||||
strainer = Liquid::Strainer.create(context)
|
||||
|
||||
system('hr -')
|
||||
|
||||
template = Liquid::Template.parse(source)
|
||||
ruby = Compiler.compile(template)
|
||||
puts ruby
|
||||
|
||||
system('hr / ')
|
||||
|
||||
puts
|
||||
puts "Compose + parse + render"
|
||||
system('hr - ')
|
||||
|
||||
start = Time.now
|
||||
template = Liquid::Template.parse(source)
|
||||
ruby = Compiler.compile(template)
|
||||
instructions = RubyVM::InstructionSequence.compile(ruby)
|
||||
instructions.eval.call(STDOUT, strainer)
|
||||
puts Time.now - start
|
||||
|
||||
puts
|
||||
puts "Render cached"
|
||||
system('hr - ')
|
||||
|
||||
start = Time.now
|
||||
instructions.eval.call(STDOUT, strainer)
|
||||
puts Time.now - start
|
||||
|
||||
puts
|
||||
puts "Legacy rendering"
|
||||
system('hr - ')
|
||||
|
||||
start = Time.now
|
||||
template = Liquid::Template.parse(source)
|
||||
puts template.render
|
||||
puts Time.now - start
|
||||
@@ -15,6 +15,8 @@ module Liquid
|
||||
@end_obj = end_obj
|
||||
end
|
||||
|
||||
attr_reader :start_obj, :end_obj
|
||||
|
||||
def evaluate(context)
|
||||
start_int = to_integer(context.evaluate(@start_obj))
|
||||
end_int = to_integer(context.evaluate(@end_obj))
|
||||
|
||||
@@ -46,7 +46,7 @@ module Liquid
|
||||
class For < Block
|
||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||
|
||||
attr_reader :collection_name, :variable_name, :limit, :from
|
||||
attr_reader :collection_name, :variable_name, :limit, :from, :for_block
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
Reference in New Issue
Block a user