Module: Ea::Ocl::Evaluator

Defined in:
lib/ea/ocl/evaluator.rb

Overview

Evaluates an AST produced by Parser against a context object (the model element under test). Dispatches on node class.

Defined Under Namespace

Classes: VarBinding

Class Method Summary collapse

Class Method Details

.attribute_value(context, name) ⇒ Object



31
32
33
34
35
# File 'lib/ea/ocl/evaluator.rb', line 31

def attribute_value(context, name)
  return nil unless context

  context.public_send(name)
end

.eval_binary(ast, context) ⇒ Object



98
99
100
101
102
103
104
105
106
# File 'lib/ea/ocl/evaluator.rb', line 98

def eval_binary(ast, context)
  left = evaluate(ast.left, context)
  right = evaluate(ast.right, context)
  case ast.op
  when :and then left && right
  when :or then left || right
  else raise UnsupportedError, "Unknown binary op: #{ast.op}"
  end
end

.eval_comparison(ast, context) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ea/ocl/evaluator.rb', line 78

def eval_comparison(ast, context)
  left = evaluate(ast.left, context)
  right = evaluate(ast.right, context)
  unless left.is_a?(Numeric) && right.is_a?(Numeric)
    raise UnsupportedError,
          "Comparison requires numeric operands: #{ast.op} on #{left.inspect}, #{right.inspect}"
  end

  case ast.op
  when ">"  then left > right
  when "<"  then left < right
  when ">=" then left >= right
  when "<=" then left <= right
  when "="  then left == right
  when "==" then left == right
  when "!=" then left != right
  else raise UnsupportedError, "Unknown comparison op: #{ast.op}"
  end
end

.eval_exists(ast, context) ⇒ Object



42
43
44
45
46
47
48
49
50
51
# File 'lib/ea/ocl/evaluator.rb', line 42

def eval_exists(ast, context)
  collection = evaluate(ast.collection, context)
  return false unless collection.is_a?(Array)

  var_name = ast.var
  predicate = ast.predicate
  collection.any? do |element|
    evaluate(predicate, var_binding(context, var_name, element))
  end
end

.eval_for_all(ast, context) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/ea/ocl/evaluator.rb', line 53

def eval_for_all(ast, context)
  collection = evaluate(ast.collection, context)
  return true unless collection.is_a?(Array)

  var_name = ast.var
  predicate = ast.predicate
  collection.all? do |element|
    evaluate(predicate, var_binding(context, var_name, element))
  end
end

.eval_is_empty(ast, context) ⇒ Object



71
72
73
74
75
76
# File 'lib/ea/ocl/evaluator.rb', line 71

def eval_is_empty(ast, context)
  collection = evaluate(ast.collection, context)
  return true unless collection.respond_to?(:empty?)

  collection.empty?
end

.eval_size(ast, context) ⇒ Object



64
65
66
67
68
69
# File 'lib/ea/ocl/evaluator.rb', line 64

def eval_size(ast, context)
  collection = evaluate(ast.collection, context)
  return 0 unless collection.respond_to?(:size)

  collection.size
end

.eval_string_matches(ast, context) ⇒ Object



37
38
39
40
# File 'lib/ea/ocl/evaluator.rb', line 37

def eval_string_matches(ast, context)
  target = evaluate(ast.string, context).to_s
  Regexp.new(ast.pattern).match?(target)
end

.eval_unary(ast, context) ⇒ Object



108
109
110
111
112
113
# File 'lib/ea/ocl/evaluator.rb', line 108

def eval_unary(ast, context)
  case ast.op
  when :not then !evaluate(ast.operand, context)
  else raise UnsupportedError, "Unknown unary op: #{ast.op}"
  end
end

.evaluate(ast, context) ⇒ Boolean, Object

Returns result of evaluation.

Parameters:

  • ast (Nodes::*)
  • context (Object)

    the model element (self)

Returns:

  • (Boolean, Object)

    result of evaluation



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/ea/ocl/evaluator.rb', line 13

def evaluate(ast, context)
  case ast
  when Nodes::Literal then ast.value
  when Nodes::AttributeAccess then attribute_value(context, ast.name)
  when Nodes::StringMatches then eval_string_matches(ast, context)
  when Nodes::CollectionExists then eval_exists(ast, context)
  when Nodes::CollectionForAll then eval_for_all(ast, context)
  when Nodes::CollectionSize then eval_size(ast, context)
  when Nodes::CollectionIsEmpty then eval_is_empty(ast, context)
  when Nodes::Comparison then eval_comparison(ast, context)
  when Nodes::BinaryOp then eval_binary(ast, context)
  when Nodes::UnaryOp then eval_unary(ast, context)
  else
    raise UnsupportedError,
          "Cannot evaluate AST node: #{ast.class}"
  end
end

.var_binding(context, var_name, value) ⇒ Object



126
127
128
# File 'lib/ea/ocl/evaluator.rb', line 126

def var_binding(context, var_name, value)
  VarBinding.new(context: context, var_name: var_name, value: value)
end