# encoding: UTF-8 class TextNlp class Pattern attr_reader :root def initialize(root_or_string = nil) if (root_or_string.is_a?(String)) @root = parse(root_or_string) else @root = root_or_string end end def <<(node) @root << node end def match?(text) @root.evaluate(text) end private def parse(expr) operators = ['||','&&'] current_expression, node, opened, closed = '', nil, 0, 0 expr.chars.each_with_index do |char,i| if (char == '(') opened += 1 current_expression << char if ((opened - closed) > 1) elsif (char == ')') closed += 1 current_expression << char if ((opened - closed) > 0) elsif ((opened == closed) && (operators.include?(expr[i-1..i]))) node = operator_node(expr[i-1..i]) node << parse(current_expression[0..-2]) node << parse(expr[i+1..-1]) break; else current_expression << char end end unless node if (current_expression.match(/\|{2}|&{2}/)) node = parse(current_expression) else node = current_expression[0..0] == '!' ? Not.new(current_expression[1..-1]) : Unary.new(current_expression) end end node end def operator_node(operator) node = case operator when '||' then Or.new when '&&' then And.new end node end class Composite attr_reader :nodes def initialize(*nodes) @nodes = nodes || [] end def <<(node) @nodes << node end def values @nodes.map { |node| node.values }.flatten end end class And < Composite def evaluate(expr) @nodes.each do |node| return false unless node.evaluate(expr) end return true end end class Or < Composite def evaluate(expr) @nodes.each do |node| return true if node.evaluate(expr) end return false end end class Unary attr_reader :value def initialize(value) @value = value @expressions = Expressions.new([@value]) end def evaluate(expr) @expressions.any?(expr) end def values [value] end end class Not < Unary def evaluate(expr) !super(expr) end def values [] end end end end