class Rox::Core::Parser

Attributes

global_context[RW]

Public Class Methods

new(user_unhandled_error_invoker) click to toggle source
# File lib/rox/core/roxx/parser.rb, line 17
def initialize(user_unhandled_error_invoker)
  @operators_map = {}
  @global_context = nil
  set_basic_operators
  @user_unhandled_error_invoker = user_unhandled_error_invoker
  ValueCompareExtensions.new(self).extend
  RegularExpressionExtensions.new(self).extend
end

Public Instance Methods

add_operator(operator, &block) click to toggle source
# File lib/rox/core/roxx/parser.rb, line 26
def add_operator(operator, &block)
  @operators_map[operator] = block
end
evaluate_expression(expression, context = nil) click to toggle source
# File lib/rox/core/roxx/parser.rb, line 30
def evaluate_expression(expression, context = nil)
  stack = CoreStack.new
  tokens = TokenizedExpression.new(expression, @operators_map.keys).tokens
  reverse_tokens = tokens.reverse
  begin
    reverse_tokens.each do |token|
      node = token
      case node.type
      when NodeTypes::RAND
        stack.push(node.value)
      when NodeTypes::RATOR
        handler = @operators_map[node.value]
        handler&.call(self, stack, context)
      else
        return EvaluationResult.new(nil)
      end
    end

    result = stack.pop
    result = nil if result == TokenType::UNDEFINED

    EvaluationResult.new(result)
  rescue Rox::Core::UserspaceHandlerException => e
    @user_unhandled_error_invoker.invoke(e.exception_source, e.exception_trigger, e.exception)
    Logging.logger.warn("Roxx Exception: Failed evaluate expression, user unhandled expression: #{e}")
    EvaluationResult.new(nil)
  rescue StandardError => e
    Logging.logger.warn("Roxx Exception: Failed evaluate expression: #{e}")
    EvaluationResult.new(nil)
  end
end
set_basic_operators() click to toggle source
# File lib/rox/core/roxx/parser.rb, line 62
def set_basic_operators
  add_operator('isUndefined') do |_parser, stack, _context|
    op1 = stack.pop
    if op1.is_a?(TokenType)
      stack.push(op1 == TokenType::UNDEFINED)
    else
      stack.push(false)
    end
  end

  add_operator('now') do |_parser, stack, _context|
    stack.push((Time.now.to_f * 1000).to_i)
  end

  add_operator('and') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    op1 = false if op1 == TokenType::UNDEFINED
    op2 = false if op2 == TokenType::UNDEFINED
    raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1) && Utils.boolean?(op2)

    stack.push(op1 && op2)
  end

  add_operator('or') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    op1 = false if op1 == TokenType::UNDEFINED
    op2 = false if op2 == TokenType::UNDEFINED
    raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1) && Utils.boolean?(op2)

    stack.push(op1 || op2)
  end

  add_operator('ne') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    op1 = false if op1 == TokenType::UNDEFINED
    op2 = false if op2 == TokenType::UNDEFINED
    stack.push(op1 != op2)
  end

  add_operator('eq') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    op1 = false if op1 == TokenType::UNDEFINED
    op2 = false if op2 == TokenType::UNDEFINED
    stack.push(op1 == op2)
  end

  add_operator('not') do |_parser, stack, _context|
    op1 = stack.pop
    op1 = false if op1 == TokenType::UNDEFINED
    raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1)

    stack.push(!op1)
  end

  add_operator('ifThen') do |_parser, stack, _context|
    condition_expression = stack.pop
    true_expression = stack.pop
    false_expression = stack.pop
    raise ArgumentError, 'should be boolean' unless Utils.boolean?(condition_expression)

    if condition_expression
      stack.push(true_expression)
    else
      stack.push(false_expression)
    end
  end

  add_operator('inArray') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    if op2.is_a?(Array)
      stack.push(op2.include?(op1))
    else
      stack.push(false)
    end
  end

  add_operator('md5') do |_parser, stack, _context|
    op1 = stack.pop
    if op1.is_a?(String)
      stack.push(Digest::MD5.hexdigest(op1))
    else
      stack.push(TokenType::UNDEFINED)
    end
  end

  add_operator('concat') do |_parser, stack, _context|
    op1 = stack.pop
    op2 = stack.pop
    if op1.is_a?(String) && op2.is_a?(String)
      stack.push("#{op1}#{op2}")
    else
      stack.push(TokenType::UNDEFINED)
    end
  end

  add_operator('b64d') do |_parser, stack, _context|
    op1 = stack.pop
    if op1.is_a?(String)
      decoded = Base64.decode64(op1)
      decoded = decoded.force_encoding('UTF-8')
      stack.push(decoded)
    else
      stack.push(TokenType::UNDEFINED)
    end
  end
end