class Cucumber::TagExpressions::Parser
Ruby tag expression parser
Public Class Methods
new()
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 7 def initialize @expressions = [] @operators = [] @operator_types = { 'or' => { type: :binary_operator, precedence: 0, assoc: :left }, 'and' => { type: :binary_operator, precedence: 1, assoc: :left }, 'not' => { type: :unary_operator, precedence: 2, assoc: :right }, ')' => { type: :close_paren, precedence: -1 }, '(' => { type: :open_paren, precedence: 1 } } end
Public Instance Methods
parse(infix_expression)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 20 def parse(infix_expression) expected_token_type = :operand tokens = tokenize(infix_expression) return True.new if tokens.empty? tokens.each do |token| if @operator_types[token] expected_token_type = send("handle_#{@operator_types[token][:type]}", infix_expression, token, expected_token_type) else expected_token_type = handle_literal(infix_expression, token, expected_token_type) end end while @operators.any? raise %Q{Tag expression "#{infix_expression}" could not be parsed because of syntax error: Unmatched (.} if @operators.last == '(' push_expression(pop(@operators)) end expression = pop(@expressions) @expressions.empty? ? expression : raise('Not empty') end
Private Instance Methods
assoc_of(token, value)
click to toggle source
Helpers
# File lib/cucumber/tag_expressions/parser.rb, line 47 def assoc_of(token, value) @operator_types[token][:assoc] == value end
check(infix_expression, expected_token_type, token_type)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 153 def check(infix_expression, expected_token_type, token_type) if expected_token_type != token_type raise %Q{Tag expression "#{infix_expression}" could not be parsed because of syntax error: Expected #{expected_token_type}.} end end
handle_binary_operator(infix_expression, token, expected_token_type)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 121 def handle_binary_operator(infix_expression, token, expected_token_type) check(infix_expression, expected_token_type, :operator) while @operators.any? && operator?(@operators.last) && lower_precedence?(token) push_expression(pop(@operators)) end @operators.push(token) :operand end
handle_close_paren(infix_expression, _token, expected_token_type)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 137 def handle_close_paren(infix_expression, _token, expected_token_type) check(infix_expression, expected_token_type, :operator) while @operators.any? && @operators.last != '(' push_expression(pop(@operators)) end raise %Q{Tag expression "#{infix_expression}" could not be parsed because of syntax error: Unmatched ).} if @operators.empty? pop(@operators) if @operators.last == '(' :operator end
handle_literal(infix_expression, token, expected_token_type)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 147 def handle_literal(infix_expression, token, expected_token_type) check(infix_expression, expected_token_type, :operand) push_expression(token) :operator end
handle_open_paren(infix_expression, token, expected_token_type)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 131 def handle_open_paren(infix_expression, token, expected_token_type) check(infix_expression, expected_token_type, :operand) @operators.push(token) :operand end
handle_unary_operator(infix_expression, token, expected_token_type)
click to toggle source
Handlers
# File lib/cucumber/tag_expressions/parser.rb, line 115 def handle_unary_operator(infix_expression, token, expected_token_type) check(infix_expression, expected_token_type, :operand) @operators.push(token) :operand end
lower_precedence?(operation)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 51 def lower_precedence?(operation) (assoc_of(operation, :left) && precedence(operation) <= precedence(@operators.last)) || (assoc_of(operation, :right) && precedence(operation) < precedence(@operators.last)) end
operator?(token)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 58 def operator?(token) @operator_types[token][:type] == :unary_operator || @operator_types[token][:type] == :binary_operator end
pop(array, n = 1)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 159 def pop(array, n = 1) result = array.pop(n) raise('Empty stack') if result.size != n n == 1 ? result.first : result end
precedence(token)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 63 def precedence(token) @operator_types[token][:precedence] end
push_expression(token)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 99 def push_expression(token) case token when 'and' @expressions.push(And.new(*pop(@expressions, 2))) when 'or' @expressions.push(Or.new(*pop(@expressions, 2))) when 'not' @expressions.push(Not.new(pop(@expressions))) else @expressions.push(Literal.new(token)) end end
tokenize(infix_expression)
click to toggle source
# File lib/cucumber/tag_expressions/parser.rb, line 67 def tokenize(infix_expression) tokens = [] escaped = false token = "" infix_expression.chars.each do | ch | if escaped if ch == '(' || ch == ')' || ch == '\\' || ch.match(/\s/) token += ch escaped = false else raise %Q{Tag expression "#{infix_expression}" could not be parsed because of syntax error: Illegal escape before "#{ch}".} end elsif ch == '\\' escaped = true elsif ch == '(' || ch == ')' || ch.match(/\s/) if token.length > 0 tokens.push(token) token = "" end if !ch.match(/\s/) tokens.push(ch) end else token += ch end end if token.length > 0 tokens.push(token) end tokens end