Class: Token

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

Overview

A raka expression is a list of linked tokens. The Token class store current token, info of previous tokens, and context. It plays rule of both token and expr

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(compiler, context, chain, inline_scope) ⇒ Token

Returns a new instance of Token.



32
33
34
35
36
37
# File 'lib/token.rb', line 32

def initialize(compiler, context, chain, inline_scope)
  @compiler = compiler
  @context = context
  @chain = chain
  @inline_scope = inline_scope
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args) ⇒ Object

rubocop:disable Style/MissingRespondToMissing # for DSL not essential



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/token.rb', line 81

def method_missing(sym, *args)
  # if ends with '=' then is to compile;
  # if not but has a arg then it is template token, push template;
  # else is inconclusive so just push symbol
  super if internal(sym)

  if sym.to_s.end_with? '='
    @compiler.compile(_attach_(sym.to_s.chomp('=')), args.first)
  elsif !args.empty?
    _attach_ args.first.to_s
  else
    _attach_ sym.to_s
  end
end

Instance Attribute Details

#chainObject (readonly)

Returns the value of attribute chain.



30
31
32
# File 'lib/token.rb', line 30

def chain
  @chain
end

Instance Method Details

#[](pattern) ⇒ Object

These two methods indicate that this is a pattern token



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/token.rb', line 146

def [](pattern)
  symbol = @chain.pop.to_s
  # if the pattern contains child pattern like percent_(\d+), we change the capture to
  # named capture so that it can be captured later. The name is symbol with the index, like func0
  pattern = pattern.gsub(/\(\S+?\)/).with_index { |m, i| "(?<#{symbol}#{i}>#{m})" }

  # if the symbol is _, \S+ will be put in chain, it indicates not to capture,
  # so just replace it with the refined pattern
  if symbol == Pattern::ANY # match-everything and not bound
    @chain.push pattern.to_s
  else
    @chain.push "(?<#{symbol}>(#{pattern}\\w*))"
  end
  self
end

#[]=(pattern, value) ⇒ Object



162
163
164
# File 'lib/token.rb', line 162

def []=(pattern, value)
  @compiler.compile(self[pattern], value)
end

#_(*args) ⇒ Object

non capture matching anything



98
99
100
101
102
103
104
# File 'lib/token.rb', line 98

def _(*args)
  if !args.empty?
    _attach_ args.first.to_s
  else
    _attach_ Pattern::ANY
  end
end

#_=(rhs) ⇒ Object



106
107
108
# File 'lib/token.rb', line 106

def _=(rhs)
  @compiler.compile(_attach_(Pattern::ANY), rhs)
end

#_attach_(item) ⇒ Object

attach a new item to the chain



76
77
78
# File 'lib/token.rb', line 76

def _attach_(item)
  Token.new(@compiler, @context, @chain + [item], @inline_scope)
end

#_captures_(target) ⇒ Object



39
40
41
42
43
# File 'lib/token.rb', line 39

def _captures_(target)
  matched = _pattern_.match(target)
  keys = matched.names.map(&:to_sym)
  Hash[keys.zip(matched.captures)]
end

#_input_?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/token.rb', line 110

def _input_?
  @chain.length > 1
end

#_inputs_(output, ext) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/token.rb', line 114

def _inputs_(output, ext)
  # no input
  return [] if @chain.length == 1

  # match the body part besides the scope (if not scoped), leading xxx__ and .ext of output
  info = _parse_output_(output)
  input_stem = /^\S+?__(\S+)$/.match(info.stem)[1]
  auto_input = "#{input_stem}.#{ext}"
  auto_input = "#{info.target_scope}/" + auto_input if info.target_scope
  auto_input = "#{info.scope}/" + auto_input if info.scope
  [auto_input]
end

#_parse_output_(output) ⇒ Object

rubocop:disable Style/MethodLength # long but straightforward



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
# File 'lib/token.rb', line 46

def _parse_output_(output)
  # xxx? is for minimal match
  out_pattern = %r{^((?<scope>\S+)/)?}.source
  out_pattern += %r{(?<target_scope>#{@inline_scope})/}.source unless @inline_scope.nil?
  out_pattern += /(?<stem>(\S+))(?<ext>\.[^\.]+)$/.source
  info = Regexp.new(out_pattern).match(output)
  res = Hash[info.names.zip(info.captures)]
  unless info[:scope].nil?
    rule_scopes = Regexp.new(_scope_pattern_).match(info[:scope]).captures
    res[:rule_scopes] = rule_scopes[1..-1].reverse
  end
  if !@inline_scope.nil? && !info[:target_scope].nil?
    segs = Regexp.new(@inline_scope).match(info[:target_scope]).captures
    res[:target_scope_captures] = segs
  end
  name_details = /^(\S+?)__(\S+)$/.match(info[:stem])
  res = if name_details
          res.merge(func: name_details[1], input_stem: name_details[2])
        else
          res.merge(func: nil, input_stem: nil)
        end
  res = res.merge(captures: OpenStruct.new(_captures_(output)))
  res[:name] = output
  res[:output] = output
  res[:output_stem] = info[:stem]
  OpenStruct.new res
end

#_pattern_Object



131
132
133
134
135
136
137
# File 'lib/token.rb', line 131

def _pattern_
  # scopes as leading
  leading = !@context.scopes.empty? ? _scope_pattern_ + '/' : _scope_pattern_
  leading += "(#{@inline_scope})/" unless @inline_scope.nil?
  body = @chain.reverse.map { |s| "(#{s})" }.join('__')
  Regexp.new('^' + leading + body + '\.' + @context.ext.to_s + '$')
end

#_scope_pattern_Object



127
128
129
# File 'lib/token.rb', line 127

def _scope_pattern_
  '((?:(\S+)/)?' + (@context.scopes.map { |layer| "(#{layer.join('|')})" }).join('/') + ')'
end

#_template_(scope = nil) ⇒ Object



139
140
141
142
143
# File 'lib/token.rb', line 139

def _template_(scope = nil)
  (scope.nil? ? '' : scope + '/') + (@inline_scope.nil? ? '' : @inline_scope + '/') +
    @chain.reverse.join('__') + '.' +
    @context.ext.to_s
end