Class: Twig::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/twig/parser.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(environment) ⇒ Parser

Returns a new instance of Parser.

Parameters:



10
11
12
# File 'lib/twig/parser.rb', line 10

def initialize(environment)
  @environment = environment
end

Instance Attribute Details

#block_stackObject (readonly)

Returns the value of attribute block_stack.



7
8
9
# File 'lib/twig/parser.rb', line 7

def block_stack
  @block_stack
end

#streamTokenStream (readonly)

Returns:



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/twig/parser.rb', line 6

class Parser
  attr_reader :stream, :block_stack

  # @param [Environment] environment
  def initialize(environment)
    @environment = environment
  end

  # @param [TokenStream] stream
  def parse(stream, test = nil, drop_needle: false)
    @stream = stream
    @parent = nil
    @blocks = {}
    @block_stack = []
    @imported_symbols = [{}]

    body = subparse(test, drop_needle:)

    Node::Module.new(body, @parent, Node::Nodes.new(@blocks), stream.source)
  end

  # @param [Proc] test
  # @return [Node::Base]
  def subparse(test, drop_needle: false)
    lineno = current_token.lineno
    rv = AutoHash.new

    until stream.eof?
      case current_token.type
      when Token::TEXT_TYPE
        token = stream.next
        rv.add(Node::Text.new(token.value, token.lineno))
      when Token::VAR_START_TYPE
        token = stream.next
        expr = expression_parser.parse_expression
        stream.expect(Token::VAR_END_TYPE)

        rv.add(Node::Print.new(expr, token.lineno))
      when Token::BLOCK_START_TYPE
        stream.next
        token = current_token

        unless token.type == Token::NAME_TYPE
          raise Error::Syntax.new('A block must start with a tag name.', token.lineno, stream.source)
        end

        if test&.call(token)
          stream.next if drop_needle
          return rv.values.first if rv.length == 1

          return Node::Nodes.new(rv, lineno)
        end

        # @todo Check that there is a token parser for this token value
        subparser = @environment.token_parser(token.value)

        unless subparser
          raise Error::Syntax.new("Unexpected '#{token.value}' tag.", token.lineno, stream.source)
        end

        stream.next
        subparser.parser = self
        node = subparser.parse(token)

        raise 'Cannot return nil from TokenParser' unless node

        node.tag = subparser.tag

        rv.add(node)
      else
        raise "Unable to parse token of type #{current_token.type}"
      end
    end

    Node::Nodes.new(rv)
  end

  # @return [Token]
  def current_token
    stream.current
  end

  # @return [ExpressionParser]
  def expression_parser
    @expression_parser ||= ExpressionParser.new(self, @environment)
  end

  def push_local_scope
    @imported_symbols.unshift({})
  end

  def pop_local_scope
    @imported_symbols.shift
  end

  def peek_block_stack
    @block_stack[-1]
  end

  def pop_block_stack
    @block_stack.pop
  end

  def push_block_stack(name)
    @block_stack << name
  end

  def block?(name)
    @blocks.key?(name)
  end

  # @todo type value as BlockNode and also set it to a BodyNode
  def set_block(name, value)
    @blocks[name] = value
  end

  # @param [Node::Base] parent
  def parent=(parent)
    if @parent
      raise Error::Syntax.new('Cannot extends twice', parent.lineno, parent.source_context)
    end

    @parent = parent
  end
end

Instance Method Details

#block?(name) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/twig/parser.rb', line 113

def block?(name)
  @blocks.key?(name)
end

#current_tokenToken

Returns:



84
85
86
# File 'lib/twig/parser.rb', line 84

def current_token
  stream.current
end

#expression_parserExpressionParser

Returns:



89
90
91
# File 'lib/twig/parser.rb', line 89

def expression_parser
  @expression_parser ||= ExpressionParser.new(self, @environment)
end

#parent=(parent) ⇒ Object

Parameters:



123
124
125
126
127
128
129
# File 'lib/twig/parser.rb', line 123

def parent=(parent)
  if @parent
    raise Error::Syntax.new('Cannot extends twice', parent.lineno, parent.source_context)
  end

  @parent = parent
end

#parse(stream, test = nil, drop_needle: false) ⇒ Object

Parameters:



15
16
17
18
19
20
21
22
23
24
25
# File 'lib/twig/parser.rb', line 15

def parse(stream, test = nil, drop_needle: false)
  @stream = stream
  @parent = nil
  @blocks = {}
  @block_stack = []
  @imported_symbols = [{}]

  body = subparse(test, drop_needle:)

  Node::Module.new(body, @parent, Node::Nodes.new(@blocks), stream.source)
end

#peek_block_stackObject



101
102
103
# File 'lib/twig/parser.rb', line 101

def peek_block_stack
  @block_stack[-1]
end

#pop_block_stackObject



105
106
107
# File 'lib/twig/parser.rb', line 105

def pop_block_stack
  @block_stack.pop
end

#pop_local_scopeObject



97
98
99
# File 'lib/twig/parser.rb', line 97

def pop_local_scope
  @imported_symbols.shift
end

#push_block_stack(name) ⇒ Object



109
110
111
# File 'lib/twig/parser.rb', line 109

def push_block_stack(name)
  @block_stack << name
end

#push_local_scopeObject



93
94
95
# File 'lib/twig/parser.rb', line 93

def push_local_scope
  @imported_symbols.unshift({})
end

#set_block(name, value) ⇒ Object



118
119
120
# File 'lib/twig/parser.rb', line 118

def set_block(name, value)
  @blocks[name] = value
end

#subparse(test, drop_needle: false) ⇒ Node::Base

Parameters:

  • test (Proc)

Returns:



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/twig/parser.rb', line 29

def subparse(test, drop_needle: false)
  lineno = current_token.lineno
  rv = AutoHash.new

  until stream.eof?
    case current_token.type
    when Token::TEXT_TYPE
      token = stream.next
      rv.add(Node::Text.new(token.value, token.lineno))
    when Token::VAR_START_TYPE
      token = stream.next
      expr = expression_parser.parse_expression
      stream.expect(Token::VAR_END_TYPE)

      rv.add(Node::Print.new(expr, token.lineno))
    when Token::BLOCK_START_TYPE
      stream.next
      token = current_token

      unless token.type == Token::NAME_TYPE
        raise Error::Syntax.new('A block must start with a tag name.', token.lineno, stream.source)
      end

      if test&.call(token)
        stream.next if drop_needle
        return rv.values.first if rv.length == 1

        return Node::Nodes.new(rv, lineno)
      end

      # @todo Check that there is a token parser for this token value
      subparser = @environment.token_parser(token.value)

      unless subparser
        raise Error::Syntax.new("Unexpected '#{token.value}' tag.", token.lineno, stream.source)
      end

      stream.next
      subparser.parser = self
      node = subparser.parse(token)

      raise 'Cannot return nil from TokenParser' unless node

      node.tag = subparser.tag

      rv.add(node)
    else
      raise "Unable to parse token of type #{current_token.type}"
    end
  end

  Node::Nodes.new(rv)
end