Class: NNQ::CLI::ExpressionEvaluator
- Inherits:
-
Object
- Object
- NNQ::CLI::ExpressionEvaluator
- Defined in:
- lib/nnq/cli/expression_evaluator.rb
Overview
Compiles and evaluates a single Ruby expression string for use in –recv-eval / –send-eval. Handles BEGIN{}/END{} block extraction, proc compilation, and result normalisation.
One instance per direction (send or recv).
nnq has no multipart, so ‘$F` is always a 1-element array and `$_` is the body string.
Constant Summary collapse
- SENT =
Sentinel: eval proc returned the context object, meaning it already sent the reply itself.
Object.new.freeze
Instance Attribute Summary collapse
-
#begin_proc ⇒ Object
readonly
Returns the value of attribute begin_proc.
-
#end_proc ⇒ Object
readonly
Returns the value of attribute end_proc.
-
#eval_proc ⇒ Object
readonly
Returns the value of attribute eval_proc.
Class Method Summary collapse
-
.compile_inside_ractor(src) ⇒ Object
Compiles begin/end/eval procs inside a Ractor from a raw expression string.
-
.normalize_result(result) ⇒ Object
Normalises an eval result to nil (skip) or a 1-element Array of strings.
Instance Method Summary collapse
-
#call(msg, context) ⇒ Object
Runs the eval proc against
msgusingcontextas self. -
#initialize(src, format:, fallback_proc: nil) ⇒ ExpressionEvaluator
constructor
A new instance of ExpressionEvaluator.
Constructor Details
#initialize(src, format:, fallback_proc: nil) ⇒ ExpressionEvaluator
Returns a new instance of ExpressionEvaluator.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 26 def initialize(src, format:, fallback_proc: nil) @format = format if src expr, begin_body, end_body = extract_blocks(src) @begin_proc = eval("proc { #{begin_body} }") if begin_body # rubocop:disable Security/Eval @end_proc = eval("proc { #{end_body} }") if end_body # rubocop:disable Security/Eval if expr && !expr.strip.empty? @eval_proc = eval("proc { $_ = $F&.first; #{expr} }") # rubocop:disable Security/Eval end elsif fallback_proc @eval_proc = proc { |msg| body = msg&.first $_ = body fallback_proc.call(body) } end end |
Instance Attribute Details
#begin_proc ⇒ Object (readonly)
Returns the value of attribute begin_proc.
15 16 17 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 15 def begin_proc @begin_proc end |
#end_proc ⇒ Object (readonly)
Returns the value of attribute end_proc.
15 16 17 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 15 def end_proc @end_proc end |
#eval_proc ⇒ Object (readonly)
Returns the value of attribute eval_proc.
15 16 17 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 15 def eval_proc @eval_proc end |
Class Method Details
.compile_inside_ractor(src) ⇒ Object
Compiles begin/end/eval procs inside a Ractor from a raw expression string. Returns [begin_proc, end_proc, eval_proc], any may be nil. Must be called inside the Ractor block.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 77 def self.compile_inside_ractor(src) return [nil, nil, nil] unless src extract = ->(expr, kw) { s = expr.index(/#{kw}\s*\{/) return [expr, nil] unless s ci = expr.index("{", s) depth = 1 j = ci + 1 while j < expr.length && depth > 0 depth += 1 if expr[j] == "{" depth -= 1 if expr[j] == "}" j += 1 end [expr[0...s] + expr[j..], expr[(ci + 1)..(j - 2)]] } expr, begin_body = extract.(src, "BEGIN") expr, end_body = extract.(expr, "END") begin_proc = eval("proc { #{begin_body} }") if begin_body # rubocop:disable Security/Eval end_proc = eval("proc { #{end_body} }") if end_body # rubocop:disable Security/Eval eval_proc = nil if expr && !expr.strip.empty? ractor_expr = expr.gsub(/\$F\b/, "__F") eval_proc = eval("proc { |__F| $_ = __F&.first; #{ractor_expr} }") # rubocop:disable Security/Eval end [begin_proc, end_proc, eval_proc] end |
.normalize_result(result) ⇒ Object
Normalises an eval result to nil (skip) or a 1-element Array of strings. Used inside Ractor worker blocks.
67 68 69 70 71 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 67 def self.normalize_result(result) return nil if result.nil? result = result.is_a?(Array) ? result.first(1) : [result] result.map!(&:to_s) end |
Instance Method Details
#call(msg, context) ⇒ Object
Runs the eval proc against msg using context as self. Returns the normalised result Array, nil (filter/skip), or SENT.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/nnq/cli/expression_evaluator.rb', line 48 def call(msg, context) return msg unless @eval_proc $F = msg result = context.instance_exec(msg, &@eval_proc) return nil if result.nil? return SENT if result.equal?(context) return [result] if @format == :marshal result = result.is_a?(Array) ? result.first(1) : [result] result.map!(&:to_s) rescue => e $stderr.puts "nnq: eval error: #{e.} (#{e.class})" exit 3 end |