<commit_before>require "./blocks"

module Liquid
  class RenderVisitor < Visitor
    @data : Context
    @io : IO
    @template_path : String?

    def initialize
      @data = Context.new
      @io = IO::Memory.new
    end

    def initialize(@data : Context)
      @io = IO::Memory.new
    end

    def initialize(@data : Context, @io : IO)
    end

    def initialize(@data : Context, @io : IO, @template_path : String?)
    end

    def output
      @io.close
      @io.to_s
    end

    def visit(node : If)
      if node.if_expression.not_nil!.eval(@data).raw
        node.children.each &.accept(self)
      else
        found = false
        if elsif_arr = node.elsif
          elsif_arr.each do |alt|
            if alt.eval(@data).raw
              found = true
              alt.accept self
              break
            end
          end
        end
        if !found && (else_alt = node.else)
          else_alt.accept self
        end
      end
    end

    def visit(node : Node)
      node.children.each &.accept(self)
    end

    def visit(node : Assign)
      @data.set node.varname, node.value.eval(@data)
    end

    def visit(node : Raw)
      @io << node.content
    end

    def visit(node : Capture)
      io = IO::Memory.new
      visitor = RenderVisitor.new @data, io
      node.children.each &.accept visitor
      io.close
      @data.set node.var_name, io.to_s
    end

    def visit(node : Increment)
      var = @data.get node.var_name
      if var && (num = var.as_i?)
        @data.set node.var_name, num + 1
      else
        @data.set node.var_name, 1
      end
    end

    def visit(node : Expression)
      if node.children.empty?
        @io << node.eval(@data)
      else
        node.children.each &.accept(self)
      end
    end

    def visit(node : Filtered)
      matches = node.raw.scan GFILTER
      if matches.first["filter"] == node.first.var ||
         "\"#{matches.first["filter"]}\"" == node.first.var
        matches.shift
      end
      matches.each do |fm|
        if filter = Filters::FilterRegister.get fm["filter"]
          args : Array(Expression)?
          args = nil
          if (margs = fm["args"]?)
            args = Array(Expression).new
            splitted = margs.split(',').map &.strip
            splitted.each { |m| args << Expression.new(m) }
          end
          node.filters << {filter, args}
        else
          raise InvalidExpression.new "Filter #{fm["filter"]} is not registered."
        end
      end

      result : Any
      result = node.first.eval @data
      node.filters.each do |tuple|
        args = tuple[1].not_nil!.map &.eval(@data) if tuple[1]
        result = tuple[0].filter(result, args)
      end
      @io << result
    end

    def visit(node : Boolean)
      @io << (node.inner ? "true" : "false")
    end

    def visit(node : For)
      data = @data.dup
      if node.begin && node.end
        render_with_range node, data
      elsif node.loop_over
        render_with_var node, data
      else
        raise InvalidStatement.new "Can't iterate over #{node.loop_over}"
      end
    end

    def visit(node : Include)
      filename = if @template_path != nil
          File.join(@template_path.not_nil!, node.template_name)
        else
          node.template_name
        end

      template_content = File.read filename
      template = Template.parse template_content
      @io << template.render(@data)
    end

    private def render_with_range(node : For, data : Context)
      i = 0
      visitor = RenderVisitor.new data, @io
      start = node.begin.as(Int32)
      stop = node.end.as(Int32)
      start.upto stop do |x|
        data.set node.loop_var, x
        data.set "loop.index", i + 1
        data.set "loop.index0", i
        data.set "loop.revindex", stop - start - i + 1
        data.set "loop.revindex0", stop - start - i
        data.set "loop.first", x == start<commit_msg>add default extension for include file<commit_after>require "./blocks"

module Liquid
  class RenderVisitor < Visitor
    @data : Context
    @io : IO
    @template_path : String?

    def initialize
      @data = Context.new
      @io = IO::Memory.new
    end

    def initialize(@data : Context)
      @io = IO::Memory.new
    end

    def initialize(@data : Context, @io : IO)
    end

    def initialize(@data : Context, @io : IO, @template_path : String?)
    end

    def output
      @io.close
      @io.to_s
    end

    def visit(node : If)
      if node.if_expression.not_nil!.eval(@data).raw
        node.children.each &.accept(self)
      else
        found = false
        if elsif_arr = node.elsif
          elsif_arr.each do |alt|
            if alt.eval(@data).raw
              found = true
              alt.accept self
              break
            end
          end
        end
        if !found && (else_alt = node.else)
          else_alt.accept self
        end
      end
    end

    def visit(node : Node)
      node.children.each &.accept(self)
    end

    def visit(node : Assign)
      @data.set node.varname, node.value.eval(@data)
    end

    def visit(node : Raw)
      @io << node.content
    end

    def visit(node : Capture)
      io = IO::Memory.new
      visitor = RenderVisitor.new @data, io
      node.children.each &.accept visitor
      io.close
      @data.set node.var_name, io.to_s
    end

    def visit(node : Increment)
      var = @data.get node.var_name
      if var && (num = var.as_i?)
        @data.set node.var_name, num + 1
      else
        @data.set node.var_name, 1
      end
    end

    def visit(node : Expression)
      if node.children.empty?
        @io << node.eval(@data)
      else
        node.children.each &.accept(self)
      end
    end

    def visit(node : Filtered)
      matches = node.raw.scan GFILTER
      if matches.first["filter"] == node.first.var ||
         "\"#{matches.first["filter"]}\"" == node.first.var
        matches.shift
      end
      matches.each do |fm|
        if filter = Filters::FilterRegister.get fm["filter"]
          args : Array(Expression)?
          args = nil
          if (margs = fm["args"]?)
            args = Array(Expression).new
            splitted = margs.split(',').map &.strip
            splitted.each { |m| args << Expression.new(m) }
          end
          node.filters << {filter, args}
        else
          raise InvalidExpression.new "Filter #{fm["filter"]} is not registered."
        end
      end

      result : Any
      result = node.first.eval @data
      node.filters.each do |tuple|
        args = tuple[1].not_nil!.map &.eval(@data) if tuple[1]
        result = tuple[0].filter(result, args)
      end
      @io << result
    end

    def visit(node : Boolean)
      @io << (node.inner ? "true" : "false")
    end

    def visit(node : For)
      data = @data.dup
      if node.begin && node.end
        render_with_range node, data
      elsif node.loop_over
        render_with_var node, data
      else
        raise InvalidStatement.new "Can't iterate over #{node.loop_over}"
      end
    end

    def visit(node : Include)
      filename = if @template_path != nil
          File.join(@template_path.not_nil!, node.template_name)
        else
          node.template_name
        end
      filename += ".liquid" if File.extname(filename).empty?

      template_content = File.read filename
      template = Template.parse template_content
      @io << template.render(@data)
    end

    private def render_with_range(node : For, data : Context)
      i = 0
      visitor = RenderVisitor.new data, @io
      start = node.begin.as(Int32)
      stop = node.end.as(Int32)
      start.upto stop do |x|
        data.set node.loop_var, x
        data.set "loop.index", i + 1
        data.set "loop.index0", i
        data.set "loop.revindex", stop - start - i + 1
        data.set "loop.revindex0", stop - start - i
        data.set "loop.first", x == start
{'fraction_non_alphanumeric': 0.11192521475492673, 'fraction_numerical': 0.0035371399696816574, 'mean_word_length': 1.8014861995753715, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1335168', 'n_tokens_mistral': 2485, 'n_tokens_neox': 2424, 'n_words': 642}