From bd33df09de271541b4186caa1e84863e436a18a1 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Mon, 31 Aug 2020 17:28:26 -0400 Subject: [PATCH 1/4] Provide Block#new_body so that liquid-c can override it This way liquid-c can return a body of a different class that wraps a C implementation. --- lib/liquid/block.rb | 10 ++++++++-- lib/liquid/tags/case.rb | 6 +++--- lib/liquid/tags/for.rb | 4 ++-- lib/liquid/tags/if.rb | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/liquid/block.rb b/lib/liquid/block.rb index 9d7b752..b3a982e 100644 --- a/lib/liquid/block.rb +++ b/lib/liquid/block.rb @@ -10,7 +10,7 @@ module Liquid end def parse(tokens) - @body = BlockBody.new + @body = new_body while parse_body(@body, tokens) end end @@ -55,8 +55,14 @@ module Liquid @block_delimiter ||= "end#{block_name}" end - protected + private + # @api public + def new_body + BlockBody.new + end + + # @api public def parse_body(body, tokens) if parse_context.depth >= MAX_DEPTH raise StackLevelError, "Nesting too deep" diff --git a/lib/liquid/tags/case.rb b/lib/liquid/tags/case.rb index f6e0e70..0b48bf1 100644 --- a/lib/liquid/tags/case.rb +++ b/lib/liquid/tags/case.rb @@ -19,7 +19,7 @@ module Liquid end def parse(tokens) - body = BlockBody.new + body = new_body body = @blocks.last.attachment while parse_body(body, tokens) if blank? @blocks.each { |condition| condition.attachment.remove_blank_strings } @@ -59,7 +59,7 @@ module Liquid private def record_when_condition(markup) - body = BlockBody.new + body = new_body while markup unless markup =~ WhenSyntax @@ -80,7 +80,7 @@ module Liquid end block = ElseCondition.new - block.attach(BlockBody.new) + block.attach(new_body) @blocks << block end diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb index bb03392..6f66ebd 100644 --- a/lib/liquid/tags/for.rb +++ b/lib/liquid/tags/for.rb @@ -54,7 +54,7 @@ module Liquid super @from = @limit = nil parse_with_selected_parser(markup) - @for_block = BlockBody.new + @for_block = new_body @else_block = nil end @@ -74,7 +74,7 @@ module Liquid def unknown_tag(tag, markup, tokens) return super unless tag == 'else' - @else_block = BlockBody.new + @else_block = new_body end def render_to_output_buffer(context, output) diff --git a/lib/liquid/tags/if.rb b/lib/liquid/tags/if.rb index 43c8b0a..1b4de55 100644 --- a/lib/liquid/tags/if.rb +++ b/lib/liquid/tags/if.rb @@ -64,7 +64,7 @@ module Liquid end @blocks.push(block) - block.attach(BlockBody.new) + block.attach(new_body) end def lax_parse(markup) From 037b6036031802cf38f7360dda9071666c5a8474 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Tue, 1 Sep 2020 14:38:40 -0400 Subject: [PATCH 2/4] Turn some Liquid::BlockBody methods into class methods for liquid-c So they can be used from a Liquid::C::BlockBody --- lib/liquid/block_body.rb | 41 ++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 18b3062..e2ebf7a 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -58,6 +58,28 @@ module Liquid Block.raise_unknown_tag(tag, 'liquid', '%}', parse_context) end + # @api private + def self.raise_missing_tag_terminator(token, parse_context) + raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect) + end + + # @api private + def self.raise_missing_variable_terminator(token, parse_context) + raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect) + end + + # @api private + def self.render_node(context, output, node) + node.render_to_output_buffer(context, output) + rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e + context.handle_error(e, node.line_number) + rescue MemoryError + raise + rescue ::StandardError => e + line_number = node.is_a?(String) ? nil : node.line_number + output << context.handle_error(e, line_number) + end + private def parse_liquid_tag(markup, parse_context) liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true) parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup| @@ -74,7 +96,7 @@ module Liquid when token.start_with?(TAGSTART) whitespace_handler(token, parse_context) unless token =~ FullToken - raise_missing_tag_terminator(token, parse_context) + BlockBody.raise_missing_tag_terminator(token, parse_context) end tag_name = Regexp.last_match(2) markup = Regexp.last_match(4) @@ -179,14 +201,7 @@ module Liquid private def render_node(context, output, node) - node.render_to_output_buffer(context, output) - rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e - context.handle_error(e, node.line_number) - rescue MemoryError - raise - rescue ::StandardError => e - line_number = node.is_a?(String) ? nil : node.line_number - output << context.handle_error(e, line_number) + BlockBody.render_node(context, output, node) end def create_variable(token, parse_context) @@ -194,15 +209,17 @@ module Liquid markup = content.first return Variable.new(markup, parse_context) end - raise_missing_variable_terminator(token, parse_context) + BlockBody.raise_missing_variable_terminator(token, parse_context) end + # @deprecated Use {.raise_missing_tag_terminator} instead def raise_missing_tag_terminator(token, parse_context) - raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect) + BlockBody.raise_missing_tag_terminator(token, parse_context) end + # @deprecated Use {.raise_missing_variable_terminator} instead def raise_missing_variable_terminator(token, parse_context) - raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect) + BlockBody.raise_missing_variable_terminator(token, parse_context) end def registered_tags From dfbbf87ba9f02c0ca439a79f6eb7e475cd9bf84e Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Wed, 2 Sep 2020 09:32:19 -0400 Subject: [PATCH 3/4] Use BlockBody from Document using composition rather than inheritence This way liquid-c can more cleanly use a Liquid::C::BlockBody object for the block body by overriding Liquid::Document#new_body. --- lib/liquid/document.rb | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/liquid/document.rb b/lib/liquid/document.rb index e160886..8338b06 100644 --- a/lib/liquid/document.rb +++ b/lib/liquid/document.rb @@ -1,15 +1,26 @@ # frozen_string_literal: true module Liquid - class Document < BlockBody + class Document def self.parse(tokens, parse_context) - doc = new + doc = new(parse_context) doc.parse(tokens, parse_context) doc end + attr_reader :parse_context, :body + + def initialize(parse_context) + @parse_context = parse_context + @body = new_body + end + + def nodelist + @body.nodelist + end + def parse(tokens, parse_context) - super do |end_tag_name, _end_tag_params| + @body.parse(tokens, parse_context) do |end_tag_name, _end_tag_params| unknown_tag(end_tag_name, parse_context) if end_tag_name end rescue SyntaxError => e @@ -25,5 +36,19 @@ module Liquid raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag) end end + + def render_to_output_buffer(context, output) + @body.render_to_output_buffer(context, output) + end + + def render(context) + @body.render(context) + end + + private + + def new_body + Liquid::BlockBody.new + end end end From bbc56f35ecd6e68f6d6a31246ea59ded5028c4a0 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Wed, 9 Sep 2020 12:25:35 -0400 Subject: [PATCH 4/4] Add ParseContext#new_block_body to centralize the liquid-c override point --- lib/liquid/block.rb | 2 +- lib/liquid/document.rb | 2 +- lib/liquid/parse_context.rb | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/liquid/block.rb b/lib/liquid/block.rb index b3a982e..e0ad332 100644 --- a/lib/liquid/block.rb +++ b/lib/liquid/block.rb @@ -59,7 +59,7 @@ module Liquid # @api public def new_body - BlockBody.new + parse_context.new_block_body end # @api public diff --git a/lib/liquid/document.rb b/lib/liquid/document.rb index 8338b06..e0818ec 100644 --- a/lib/liquid/document.rb +++ b/lib/liquid/document.rb @@ -48,7 +48,7 @@ module Liquid private def new_body - Liquid::BlockBody.new + parse_context.new_block_body end end end diff --git a/lib/liquid/parse_context.rb b/lib/liquid/parse_context.rb index 83d4e0f..4724ec2 100644 --- a/lib/liquid/parse_context.rb +++ b/lib/liquid/parse_context.rb @@ -19,6 +19,10 @@ module Liquid @options[option_key] end + def new_block_body + Liquid::BlockBody.new + end + def partial=(value) @partial = value @options = value ? partial_options : @template_options