Class: Platform::IEL::Evaluator
- Inherits:
-
Object
- Object
- Platform::IEL::Evaluator
- Defined in:
- lib/introhive_expression_language/iel/evaluator.rb
Class Method Summary collapse
- .apply_fn(node, ctx) ⇒ Object
- .deref_symbol(symbol_node, ctx) ⇒ Object
-
.eval_block(node, ctx) ⇒ Object
Evaluate each item inside this “block” of code (e.g. a list) and return the last evaluated result.
- .eval_block_expr(expr, ctx) ⇒ Object
- .eval_expr(expr, ctx) ⇒ Object
- .eval_list_node(node, ctx) ⇒ Object
- .eval_node(node, ctx) ⇒ Object
- .function_application?(node, ctx) ⇒ Boolean
-
.numeric_value_of(node) ⇒ Object
Returns the numeric value of this node.
-
.string_value_of(node) ⇒ Object
Returns the string value of this node.
-
.wrap_native_function(&fn) ⇒ Object
Wraps a native function so that it can be invoked in an expression.
Class Method Details
.apply_fn(node, ctx) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 54 def self.apply_fn(node, ctx) # Decode the function name. name_node = node.value.first raise EvaluationError.new('symbol expected', name_node) if name_node.nil? raise EvaluationError.new('function application requires symbol', name_node) if name_node.kind != :symbol symbol_name = name_node.value # Get the detail of this symbol from the provided context. detail_node = ctx[symbol_name] raise EvaluationError.new("function '#{symbol_name}' is not found", detail_node) if detail_node.nil? fn_proc = detail_node.value[:proc] arg_list = detail_node.value[:arg_list] # Create a new context overriding the provided context. this_ctx = EvaluationContext.new(ctx) # Decode the parameters and bind each to the new context. if arg_list.size == 1 && arg_list[0].start_with?('va_') # Varargs - build a list and bind as single arg this_ctx[arg_list[0]] = SexpParser::Node.list(node.value.drop(1).map do |arg_node| if arg_list[0].end_with?('__') arg_node else eval_node(arg_node, ctx) end end, node.source) else # Args are bound by placement and the number must match. raise EvaluationError.new("function '#{symbol_name}' requires #{arg_list.size} arguments but #{node.value.size - 1} are provided.", node) if arg_list.size != node.value.size - 1 node.value.drop(1).each_with_index do |arg_node, index| arg_name = arg_list[index] this_ctx[arg_name] = if arg_name.end_with?('__') arg_node else eval_node(arg_node, ctx) end end end # Invoke the function. rv = fn_proc.call(this_ctx) raise EvaluationError.new("function '#{symbol_name}' does not return a node", rv) unless rv.is_a?(SexpParser::Node) rv end |
.deref_symbol(symbol_node, ctx) ⇒ Object
48 49 50 51 52 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 48 def self.deref_symbol(symbol_node, ctx) target = ctx[symbol_node.value] raise EvaluationError.new("symbol '#{symbol_node.value}' at #{symbol_node.source} not found", target) if target.nil? target end |
.eval_block(node, ctx) ⇒ Object
Evaluate each item inside this “block” of code (e.g. a list) and return the last evaluated result. e.g. [
[puts "hello"]
[puts "world"]
123
] hello world
> 123
109 110 111 112 113 114 115 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 109 def self.eval_block(node, ctx) rv = nil node.value.each do |child_node| rv = Evaluator.eval_node(child_node, ctx) end rv end |
.eval_block_expr(expr, ctx) ⇒ Object
17 18 19 20 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 17 def self.eval_block_expr(expr, ctx) ast = Parser.parse(expr) eval_block(ast, ctx) end |
.eval_expr(expr, ctx) ⇒ Object
12 13 14 15 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 12 def self.eval_expr(expr, ctx) ast = Parser.parse(expr) eval_node(ast, ctx) end |
.eval_list_node(node, ctx) ⇒ Object
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 33 def self.eval_list_node(node, ctx) # First special form - function application if function_application?(node, ctx) apply_fn(node, ctx) else SexpParser::Node.list( node.value.map {|child_node| eval_node(child_node, ctx)}, node.source) end end |
.eval_node(node, ctx) ⇒ Object
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 22 def self.eval_node(node, ctx) case node.kind when :list eval_list_node(node, ctx) when :symbol deref_symbol(node, ctx) else node end end |
.function_application?(node, ctx) ⇒ Boolean
44 45 46 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 44 def self.function_application?(node, ctx) node.kind == :list && node.value.first && node.value.first.kind == :symbol && ctx[node.value.first.value] && ctx[node.value.first.value].kind == :native_function end |
.numeric_value_of(node) ⇒ Object
Returns the numeric value of this node. It does not evaluate the node first, just returns its numeric value if possible.
140 141 142 143 144 145 146 147 148 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 140 def self.numeric_value_of(node) if node.kind == :numeric_literal node.value elsif node.kind == :string_literal node.value.to_f else 0 end end |
.string_value_of(node) ⇒ Object
Returns the string value of this node. It does not evaluate the node first, just returns its string value if possible.
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 152 def self.string_value_of(node) if node.kind == :numeric_literal node.value.to_s elsif node.kind == :string_literal node.value elsif node.kind == :list node.value.map {|item| string_value_of(item)}.join else '' end end |
.wrap_native_function(&fn) ⇒ Object
Wraps a native function so that it can be invoked in an expression. The native function MUST accept the evaluation context as its first param. The remaining params are mapped as iel mandatory args.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/introhive_expression_language/iel/evaluator.rb', line 120 def self.wrap_native_function(&fn) arg_list = fn.parameters.drop(1).map {|_kind, name| name.to_s} wrapped_fn = lambda do |ctx| args = [ctx] + arg_list.map {|name| ctx[name]} fn.call(*args) end SexpParser::Node.new( :native_function, 0, { proc: wrapped_fn, arg_list: arg_list } ) end |