introhive_expression_language 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +80 -0
  3. data/.github/pull_request_template.md +17 -0
  4. data/.github/workflows/ci.yml +31 -0
  5. data/.gitignore +15 -0
  6. data/.qlty/qlty.toml +10 -0
  7. data/.ruby-version +1 -0
  8. data/CLAUDE.md +169 -0
  9. data/Gemfile +4 -0
  10. data/Gemfile.lock +87 -0
  11. data/README.md +55 -0
  12. data/Rakefile +10 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/introhive_expression_language.gemspec +54 -0
  16. data/lib/introhive_expression_language/iel/evaluation_context.rb +35 -0
  17. data/lib/introhive_expression_language/iel/evaluation_error.rb +9 -0
  18. data/lib/introhive_expression_language/iel/evaluator.rb +166 -0
  19. data/lib/introhive_expression_language/iel/node_util.rb +65 -0
  20. data/lib/introhive_expression_language/iel/parser.rb +30 -0
  21. data/lib/introhive_expression_language/iel/sexp_parser.rb +339 -0
  22. data/lib/introhive_expression_language/iel/std_lib.rb +43 -0
  23. data/lib/introhive_expression_language/iel/std_lib_assoc.rb +84 -0
  24. data/lib/introhive_expression_language/iel/std_lib_control.rb +81 -0
  25. data/lib/introhive_expression_language/iel/std_lib_enum.rb +30 -0
  26. data/lib/introhive_expression_language/iel/std_lib_existence.rb +19 -0
  27. data/lib/introhive_expression_language/iel/std_lib_json.rb +38 -0
  28. data/lib/introhive_expression_language/iel/std_lib_kind.rb +88 -0
  29. data/lib/introhive_expression_language/iel/std_lib_let.rb +22 -0
  30. data/lib/introhive_expression_language/iel/std_lib_list.rb +85 -0
  31. data/lib/introhive_expression_language/iel/std_lib_logic.rb +52 -0
  32. data/lib/introhive_expression_language/iel/std_lib_math.rb +75 -0
  33. data/lib/introhive_expression_language/iel/std_lib_number.rb +28 -0
  34. data/lib/introhive_expression_language/iel/std_lib_regexp.rb +30 -0
  35. data/lib/introhive_expression_language/iel/std_lib_string.rb +79 -0
  36. data/lib/introhive_expression_language/iel/symbol_detail.rb +16 -0
  37. data/lib/introhive_expression_language/iel.rb +2 -0
  38. data/lib/introhive_expression_language/version.rb +3 -0
  39. data/lib/introhive_expression_language.rb +9 -0
  40. metadata +305 -0
@@ -0,0 +1,84 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibAssoc
4
+ class << self
5
+
6
+ def declare(defining_context)
7
+ StdLibList.declare(defining_context)
8
+ declare_assoc(defining_context)
9
+ declare_assoc_get(defining_context)
10
+ # declare_assoc_let(defining_context) # This needs to be reimplemented to ensure immutability of data structures
11
+ end
12
+
13
+ def assoc_get(associative_array, keys)
14
+ match = nil
15
+ # Still have to support single keys - Strings and symbols. Convert them to a list so we can go through logic normally
16
+ keys = SexpParser::Node.list([keys]) unless keys.kind == :list
17
+
18
+ keys.value.each do |k|
19
+ match = associative_array.value.find do |kv_node|
20
+ kv_node.kind == :list && kv_node.value.size >= 2 && kv_node.value.first == k
21
+ end
22
+ break if match.nil?
23
+ associative_array = match.value[1]
24
+ end
25
+ if match
26
+ match.value[1]
27
+ else
28
+ SexpParser::Node.nil
29
+ end
30
+ end
31
+
32
+ # TODO this can be reused as a defining_context with tests
33
+ def assoc_contains(associative_array, key)
34
+ match = associative_array.value.find do |kv_node|
35
+ kv_node.kind == :list && kv_node.value.size >= 2 && kv_node.value.first == key
36
+ end
37
+ if match
38
+ SexpParser::Node.boolean(true)
39
+ else
40
+ SexpParser::Node.boolean(false)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def declare_assoc(defining_context)
47
+ Evaluator.eval_expr(%q{
48
+ [def assoc [va_pairs]
49
+ /* Build an assoc from the arguments.
50
+ Allows this expression to produce an assoc:
51
+ [assoc [a 1] [b 2]] */
52
+ [
53
+ [list-prepend va_pairs 'assoc]
54
+ ]
55
+ ]
56
+ }, defining_context)
57
+ end
58
+
59
+ def declare_assoc_get(defining_context)
60
+ defining_context['assoc-get'] ||= Evaluator.wrap_native_function do |_calling_ctx, associative_array, key|
61
+ raise EvaluationError.new("associative_array argument to function 'assoc-get' must be of type list but it is #{associative_array.kind}", associative_array) unless associative_array.kind == :list
62
+ assoc_get(associative_array, key)
63
+ end
64
+ end
65
+
66
+ def declare_assoc_let(defining_context)
67
+ defining_context['assoc-let'] ||= Evaluator.wrap_native_function do |_calling_ctx, associative_array, key, value|
68
+ raise EvaluationError.new("associative_array argument to function 'assoc-let' must be of type list but it is #{associative_array.kind}", associative_array) unless associative_array.kind == :list
69
+ match = associative_array.value.find do |kv_node|
70
+ kv_node.kind == :list && kv_node.value.size >= 2 && kv_node.value.first == key
71
+ end
72
+ if match
73
+ match.value[1] = value
74
+ else
75
+ associative_array.value << SexpParser::Node.list([key, value], key.source, _calling_ctx)
76
+ end
77
+ value
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,81 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibControl
4
+
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ declare_def(defining_context)
9
+ declare_if(defining_context)
10
+ declare_quote(defining_context)
11
+ declare_block(defining_context)
12
+ declare_eval(defining_context)
13
+ end
14
+
15
+ private
16
+
17
+ def declare_def(defining_context)
18
+ defining_context['def'] ||= Evaluator.wrap_native_function do |calling_ctx, name__, arg_list__, body__|
19
+ raise EvaluationError.new("name argument to function 'def' must be of type symbol but it is #{name__.kind}", name__) unless name__.kind == :symbol
20
+ function_name = name__.value
21
+ arg_list = arg_list__.value.map do |n|
22
+ raise EvaluationError.new("function arg list member '#{n.inspect}' must be a symbol", n) unless n.kind == :symbol
23
+ n.value
24
+ end
25
+
26
+ raise EvaluationError.new("function body must be a list", body__) if body__.kind != :list
27
+
28
+ calling_ctx.enclosing_context[function_name] = SexpParser::Node.new(
29
+ :native_function,
30
+ body__.source,
31
+ {
32
+ proc: Proc.new do |inner_ctx|
33
+ Evaluator.eval_block(body__, inner_ctx)
34
+ end,
35
+ arg_list: arg_list
36
+ },
37
+ body__.context
38
+ )
39
+ end
40
+ end
41
+
42
+ def declare_if(defining_context)
43
+ defining_context['if'] ||= Evaluator.wrap_native_function do |calling_ctx, condition_node, true_body__, false_body__|
44
+ raise EvaluationError.new("Condition argument to function 'if' must be of type boolean but it is #{condition_node.kind}", condition_node) unless condition_node.kind == :boolean
45
+ if condition_node.value
46
+ Evaluator.eval_block(true_body__, calling_ctx)
47
+ else
48
+ Evaluator.eval_block(false_body__, calling_ctx)
49
+ end
50
+ end
51
+ end
52
+
53
+ def declare_quote(defining_context)
54
+ defining_context['quote'] ||= Evaluator.wrap_native_function do |_calling_ctx, arg__|
55
+ arg__
56
+ end
57
+ end
58
+
59
+ def declare_block(defining_context)
60
+ defining_context['block'] ||= Evaluator.wrap_native_function do |_calling_ctx, va_list_of_exprs__|
61
+ raise EvaluationError.new("list_of_exprs argument to function 'block' must be of type list but it is #{va_list_of_exprs__.kind}", va_list_of_exprs__) unless va_list_of_exprs__.kind == :list
62
+ rv = SexpParser::Node.nil(va_list_of_exprs__.source)
63
+ va_list_of_exprs__.value.each do |expr|
64
+ rv = Evaluator.eval_node(expr, _calling_ctx)
65
+ end
66
+ rv
67
+ end
68
+ end
69
+
70
+ def declare_eval(defining_context)
71
+ defining_context['eval'] ||= Evaluator.wrap_native_function do |calling_ctx, node__|
72
+ actual_code = calling_ctx[node__.value]
73
+ Evaluator.eval_node(actual_code, calling_ctx)
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,30 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibEnum
4
+
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ declare_map(defining_context)
9
+ end
10
+
11
+ private
12
+
13
+ def declare_map(defining_context)
14
+ defining_context['map'] ||= Evaluator.wrap_native_function do |calling_ctx, list, var_symbol__, code_block__|
15
+ raise EvaluationError.new("list argument to function 'map' must be of type list but it is #{list.kind}", list) unless list.kind == :list
16
+ raise EvaluationError.new("var_symbol argument to function 'map' must be of type symbol but it is #{var_symbol__.kind}", var_symbol__) unless var_symbol__.kind == :symbol
17
+ raise EvaluationError.new("code_block argument to function 'map' must be of type list but it is #{code_block__.kind}", code_block__) unless code_block__.kind == :list
18
+ SexpParser::Node.list(list.value.map do |item|
19
+ instance_context = EvaluationContext.new(calling_ctx)
20
+ instance_context[var_symbol__.value] = item
21
+ Evaluator.eval_block(code_block__, instance_context)
22
+ end, list.source)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibExistence
4
+ class << self
5
+ def declare(defining_context)
6
+ declare_present(defining_context)
7
+ end
8
+
9
+ private
10
+
11
+ def declare_present(defining_context)
12
+ defining_context['present?'] ||= Evaluator.wrap_native_function do |calling_ctx, expr|
13
+ Platform::IEL::SexpParser::Node.boolean(expr.value.present?, expr.source, calling_ctx)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibJson
4
+ class << self
5
+
6
+ def declare(defining_context)
7
+ declare_json_build(defining_context)
8
+ declare_json_load(defining_context)
9
+ end
10
+
11
+ private
12
+
13
+ def declare_json_build(defining_context)
14
+ defining_context['json-build'] ||= Evaluator.wrap_native_function do |_calling_ctx, item|
15
+ struct = NodeUtil.to_native(item)
16
+ SexpParser::Node.string_literal(JSON.dump(struct), item.source, _calling_ctx)
17
+ end
18
+ end
19
+
20
+ def declare_json_load(defining_context)
21
+ defining_context['json-load'] ||= Evaluator.wrap_native_function do |_calling_ctx, json_string|
22
+ raise EvaluationError.new("json_string argument to function 'json-load' must be of type string_literal but it is #{json_string.kind}", json_string) unless json_string.kind == :string_literal
23
+ begin
24
+ doc = JSON.parse(json_string.value)
25
+ NodeUtil.from_native(doc)
26
+ rescue JSON::ParserError => e
27
+ _calling_ctx.root_context['json-last-error'] = SexpParser::Node.list([
28
+ SexpParser::Node.string_literal(e.message.gsub(/\d{3}: /, ''))
29
+ ])
30
+ SexpParser::Node.nil
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,88 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibKind
4
+
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ KIND_TEST_FUNCTIONS.keys.each do |function_name|
9
+ send("declare_#{function_name}", defining_context)
10
+ end
11
+ declare_sym(defining_context)
12
+ declare_eq(defining_context)
13
+ declare_float(defining_context)
14
+ declare_int(defining_context)
15
+ declare_str(defining_context)
16
+ end
17
+
18
+ private
19
+
20
+ KIND_TEST_FUNCTIONS = {
21
+ 'nil?' => :nil,
22
+ 'list?' => :list,
23
+ 'num?' => :numeric_literal,
24
+ 'str?' => :string_literal,
25
+ 'func?' => :native_function,
26
+ 'nan?' => :nan,
27
+ 'sym?' => :symbol
28
+ }.freeze
29
+ KIND_TEST_FUNCTIONS.each do |function_name, node_kind|
30
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
31
+ def declare_#{function_name}(defining_context)
32
+ defining_context['#{function_name}'] = Evaluator.wrap_native_function do |calling_ctx, expr|
33
+ Platform::IEL::SexpParser::Node.boolean(expr.kind == :#{node_kind}, expr.source)
34
+ end
35
+ end
36
+ RUBY
37
+ end
38
+
39
+ def declare_sym(defining_context)
40
+ defining_context['sym'] ||= Evaluator.wrap_native_function do |_calling_ctx, value|
41
+ raise EvaluationError.new("value argument to function 'sym' must be of type symbol or string but it is #{value.kind}", value) unless [:symbol, :string_literal].include?(value.kind)
42
+ Platform::IEL::SexpParser::Node.symbol(value.value, value.source)
43
+ end
44
+ end
45
+
46
+ def declare_eq(defining_context)
47
+ defining_context['='] ||= Evaluator.wrap_native_function do |_calling_ctx, left, right|
48
+ Platform::IEL::SexpParser::Node.boolean(left.value == right.value)
49
+ end
50
+ end
51
+
52
+ def declare_cast(defining_context, cast_name, allowed_kinds, cast_function)
53
+ defining_context[cast_name] ||= Evaluator.wrap_native_function do |_calling_ctx, operand|
54
+ raise EvaluationError.new("value argument to function '#{cast_name}' must be of type #{allowed_kinds.join(' or ')} but it is #{operand.kind}", operand) unless allowed_kinds.include? operand.kind
55
+ NodeUtil.from_native(operand.value.send(cast_function))
56
+ end
57
+ end
58
+
59
+ def declare_float(defining_context)
60
+ declare_cast(defining_context, 'float', [:numeric_literal, :string_literal, :nil], :to_f)
61
+ end
62
+
63
+ def declare_int(defining_context)
64
+ declare_cast(defining_context, 'int', [:numeric_literal, :string_literal, :nil], :to_i)
65
+ end
66
+
67
+ def declare_str(defining_context)
68
+ declare_cast(defining_context, 'str', [:symbol, :string_literal, :numeric_literal, :nan, :list, :nil, :boolean], :to_s)
69
+ end
70
+
71
+ # def declare_def_nn(defining_context)
72
+ # Evaluator.eval_expr(%{
73
+ # [def def-nn [function_name] [
74
+ # [def [sym [join function_name "-nn"]]
75
+ # [args function_name]
76
+ # [
77
+ # [function_name
78
+ # ]
79
+ # ]
80
+ # ]]
81
+ # }, defining_context)
82
+ # end
83
+
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,22 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibLet
4
+ class << self
5
+
6
+ def declare(defining_context)
7
+ declare_let(defining_context)
8
+ end
9
+
10
+ private
11
+
12
+ def declare_let(defining_context)
13
+ defining_context['let'] ||= Evaluator.wrap_native_function do |_calling_ctx, symbol__, expr|
14
+ raise EvaluationError.new("symbol argument to function 'let' must be of type symbol but it is #{symbol__.kind}", symbol__) unless symbol__.kind == :symbol
15
+ _calling_ctx.enclosing_context[symbol__.value] = expr
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibList
4
+ class << self
5
+
6
+ def declare(defining_context)
7
+ declare_list_elem(defining_context)
8
+ declare_list_join(defining_context)
9
+ declare_list_append(defining_context)
10
+ declare_list_prepend(defining_context)
11
+ declare_list_len(defining_context)
12
+ declare_list_any(defining_context)
13
+ end
14
+
15
+ # Return an element from the list at its ordinal position. List indexes are zero based.
16
+ # If a negative index is provided then it specifies items from the end of the list.
17
+ # e.g. 0 is the first item in the list
18
+ # -1 is the last item in the list
19
+ # -2 is the second-to-last item in the list
20
+ def list_elem(list_node, index_node)
21
+ raise EvaluationError.new("list argument to function 'list-elem' must be of type list but it is #{list_node.kind}", list_node) unless list_node.kind == :list
22
+ raise EvaluationError.new("index argument to function 'list-elem' must be of type numeric_literal but it is #{index_node.kind}", index_node) unless index_node.kind == :numeric_literal
23
+ index = index_node.value
24
+ if index >= list_node.value.size
25
+ SexpParser::Node.nil
26
+ elsif index < 0
27
+ index_from_end = list_node.value.size + index
28
+ if index_from_end < 0
29
+ SexpParser::Node.nil
30
+ else
31
+ list_node.value[index_from_end]
32
+ end
33
+ else
34
+ list_node.value[index]
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def declare_list_elem(defining_context)
41
+ defining_context['list-elem'] ||= Evaluator.wrap_native_function do |_calling_ctx, list, index|
42
+ list_elem(list, index)
43
+ end
44
+ end
45
+
46
+ def declare_list_join(defining_context)
47
+ defining_context['list-join'] ||= Evaluator.wrap_native_function do |_calling_ctx, va_lists|
48
+ combined_list = []
49
+ va_lists.value.each_with_index do |arg, index|
50
+ raise EvaluationError.new("argument #{index+1} to function 'list-join' must be of type list but it is #{arg.kind}", arg) unless arg.kind == :list
51
+ combined_list.concat(arg.value)
52
+ end
53
+ SexpParser::Node.list(combined_list, va_lists.source)
54
+ end
55
+ end
56
+
57
+ def declare_list_append(defining_context)
58
+ defining_context['list-append'] ||= Evaluator.wrap_native_function do |_calling_ctx, list, item|
59
+ raise EvaluationError.new("argument list to function 'list-append' must be of type list but it is #{list.kind}", list) unless list.kind == :list
60
+ SexpParser::Node.list(list.value + [item], list.source)
61
+ end
62
+ end
63
+
64
+ def declare_list_prepend(defining_context)
65
+ defining_context['list-prepend'] ||= Evaluator.wrap_native_function do |_calling_ctx, list, item|
66
+ raise EvaluationError.new("argument list to function 'list-prepend' must be of type list but it is #{list.kind}", list) unless list.kind == :list
67
+ SexpParser::Node.list([item] + list.value, list.source)
68
+ end
69
+ end
70
+ def declare_list_len(defining_context)
71
+ defining_context['list-len'] ||= Evaluator.wrap_native_function do |_calling_ctx, list|
72
+ raise EvaluationError.new("argument list to function 'list-len' must be of type list but it is #{list.kind}", list) unless list.kind == :list
73
+ SexpParser::Node.numeric_literal(list.value.length)
74
+ end
75
+ end
76
+ def declare_list_any(defining_context)
77
+ defining_context['list-any?'] ||= Evaluator.wrap_native_function do |_calling_ctx, list, value|
78
+ raise EvaluationError.new("argument list to function 'list-any?' must be of type list but it is #{list.kind}", list) unless list.kind == :list
79
+ SexpParser::Node.boolean(list.value.include?(value))
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,52 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibLogic
4
+
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ declare_and(defining_context)
9
+ declare_or(defining_context)
10
+ declare_not(defining_context)
11
+ end
12
+
13
+ private
14
+
15
+ def declare_or(defining_context)
16
+ defining_context['or'] ||= Evaluator.wrap_native_function do |calling_ctx, va_conditions__|
17
+ raise EvaluationError.new("conditions argument to function 'or' must be of type list but it is #{va_conditions__.kind}", va_conditions__) unless va_conditions__.kind == :list
18
+ raise EvaluationError.new("function 'or' requires at least 2 arguments but #{va_conditions__.value.size} are provided", va_conditions__) if va_conditions__.value.size < 2
19
+
20
+ result = va_conditions__.value.each_with_index.any? do |condition, index|
21
+ condition = Evaluator.eval_node(condition, calling_ctx)
22
+ raise EvaluationError.new("Condition argument #{index} to function 'or' must be of type boolean but it is #{condition.kind}", condition) unless condition.kind == :boolean
23
+ condition.value
24
+ end
25
+ Platform::IEL::SexpParser::Node.boolean(result)
26
+ end
27
+ end
28
+
29
+ def declare_and(defining_context)
30
+ defining_context['and'] ||= Evaluator.wrap_native_function do |calling_ctx, va_conditions__|
31
+ raise EvaluationError.new("conditions argument to function 'and' must be of type list but it is #{va_conditions__.kind}", va_conditions__) unless va_conditions__.kind == :list
32
+ raise EvaluationError.new("function 'and' requires at least 2 arguments but #{va_conditions__.value.size} are provided", va_conditions__) if va_conditions__.value.size < 2
33
+
34
+ result = va_conditions__.value.each_with_index.all? do |condition, index|
35
+ condition = Evaluator.eval_node(condition, calling_ctx)
36
+ raise EvaluationError.new("Condition argument #{index} to function 'and' must be of type boolean but it is #{condition.kind}", condition) unless condition.kind == :boolean
37
+ condition.value
38
+ end
39
+ Platform::IEL::SexpParser::Node.boolean(result)
40
+ end
41
+ end
42
+
43
+ def declare_not(defining_context)
44
+ defining_context['not'] ||= Evaluator.wrap_native_function do |__calling_ctx, condition_node|
45
+ raise EvaluationError.new("Condition argument to function 'not' must be of type boolean but it is #{condition_node.kind}", condition_node) unless condition_node.kind == :boolean
46
+ Platform::IEL::SexpParser::Node.boolean(!condition_node.value)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,75 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibMath
4
+
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ declare_add(defining_context)
9
+ declare_subtract(defining_context)
10
+ declare_multiply(defining_context)
11
+ declare_divide(defining_context)
12
+ declare_greater_than(defining_context)
13
+ declare_less_than(defining_context)
14
+ declare_greater_than_or_equal(defining_context)
15
+ declare_less_than_or_equal(defining_context)
16
+ end
17
+
18
+ private
19
+
20
+ def declare_add(defining_context)
21
+ defining_context['+'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
22
+ SexpParser::Node.numeric_literal(Evaluator.numeric_value_of(operand1) + Evaluator.numeric_value_of(operand2), operand1.source)
23
+ end
24
+ end
25
+
26
+ def declare_subtract(defining_context)
27
+ defining_context['-'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
28
+ SexpParser::Node.numeric_literal(Evaluator.numeric_value_of(operand1) - Evaluator.numeric_value_of(operand2), operand1.source)
29
+ end
30
+ end
31
+
32
+ def declare_multiply(defining_context)
33
+ defining_context['*'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
34
+ SexpParser::Node.numeric_literal(Evaluator.numeric_value_of(operand1) * Evaluator.numeric_value_of(operand2), operand1.source)
35
+ end
36
+ end
37
+
38
+ def declare_divide(defining_context)
39
+ defining_context['/'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
40
+ divisor = Evaluator.numeric_value_of(operand2)
41
+ if divisor == 0
42
+ SexpParser::Node.nan
43
+ else
44
+ SexpParser::Node.numeric_literal(Evaluator.numeric_value_of(operand1) / divisor, operand1.source)
45
+ end
46
+ end
47
+ end
48
+
49
+ def declare_greater_than(defining_context)
50
+ defining_context['>'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
51
+ SexpParser::Node.boolean(Evaluator.numeric_value_of(operand1) > Evaluator.numeric_value_of(operand2))
52
+ end
53
+ end
54
+
55
+ def declare_less_than(defining_context)
56
+ defining_context['<'] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
57
+ SexpParser::Node.boolean(Evaluator.numeric_value_of(operand1) < Evaluator.numeric_value_of(operand2))
58
+ end
59
+ end
60
+
61
+ def declare_greater_than_or_equal(defining_context)
62
+ defining_context['>='] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
63
+ SexpParser::Node.boolean(Evaluator.numeric_value_of(operand1) >= Evaluator.numeric_value_of(operand2))
64
+ end
65
+ end
66
+
67
+ def declare_less_than_or_equal(defining_context)
68
+ defining_context['<='] ||= Evaluator.wrap_native_function do |_calling_ctx, operand1, operand2|
69
+ SexpParser::Node.boolean(Evaluator.numeric_value_of(operand1) <= Evaluator.numeric_value_of(operand2))
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,28 @@
1
+ require 'active_support/number_helper'
2
+ module Platform
3
+ module IEL
4
+ class StdLibNumber
5
+ class << self
6
+
7
+ def declare(defining_context)
8
+ StdLibAssoc.declare(defining_context)
9
+ declare_money_humanize(defining_context)
10
+ end
11
+
12
+ private
13
+
14
+ def declare_money_humanize(defining_context)
15
+ defining_context['number-humanize'] ||= Evaluator.wrap_native_function do |_calling_ctx, number, units|
16
+ raise EvaluationError.new("Argument 'number' of function 'number-humanize' must be a numeric_literal", number) unless number.kind == :numeric_literal
17
+ raise EvaluationError.new("Argument 'units' of function 'number-humanize' must be a list or nil", units) unless units.kind == :list || units.kind == :nil
18
+
19
+ SexpParser::Node.string_literal(ActiveSupport::NumberHelper.number_to_human(number.value, units: Platform::IEL::NodeUtil.to_native(units)))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+
@@ -0,0 +1,30 @@
1
+ module Platform
2
+ module IEL
3
+ class StdLibRegexp
4
+ class << self
5
+ def declare(defining_context)
6
+ declare_regexp_match?(defining_context)
7
+ declare_regexp_match(defining_context)
8
+ end
9
+
10
+ private
11
+
12
+ def declare_regexp_match?(defining_context)
13
+ defining_context['regexp-match?'] ||= Evaluator.wrap_native_function do |context, regexp, string|
14
+ raise EvaluationError.new("Argument 'regexp' of function 'regexp-match?' must be a string_literal but it is #{regexp.kind}", regexp) unless regexp.kind == :string_literal
15
+ raise EvaluationError.new("Argument 'string' of function 'regexp-match?' must be a string_literal but it is #{string.kind}", string) unless string.kind == :string_literal
16
+ SexpParser::Node.boolean(Regexp.new(regexp.value).match?(string.value), regexp.source, context)
17
+ end
18
+ end
19
+
20
+ def declare_regexp_match(defining_context)
21
+ defining_context['regexp-match'] ||= Evaluator.wrap_native_function do |context, regexp, string|
22
+ raise EvaluationError.new("Argument 'regexp' of function 'regexp-match' must be a string_literal but it is #{regexp.kind}", regexp) unless regexp.kind == :string_literal
23
+ raise EvaluationError.new("Argument 'string' of function 'regexp-match' must be a string_literal but it is #{string.kind}", string) unless string.kind == :string_literal
24
+ Platform::IEL::NodeUtil.from_native(Regexp.new(regexp.value).match(string.value).to_a)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end