Class: Mxrb::Runtime::Native::Expression

Inherits:
Object
  • Object
show all
Defined in:
lib/mxrb/runtime/native.rb

Overview

Deliberately small Mendix-expression evaluator. Unsupported syntax is rejected rather than guessed, preserving deterministic test semantics.

Defined Under Namespace

Classes: Lexer, Parser

Instance Method Summary collapse

Constructor Details

#initializeExpression

Returns a new instance of Expression.



168
169
170
# File 'lib/mxrb/runtime/native.rb', line 168

def initialize
  @token_cache = {}
end

Instance Method Details

#evaluate(source, variables, node: nil) ⇒ Object



172
173
174
175
176
177
178
179
180
# File 'lib/mxrb/runtime/native.rb', line 172

def evaluate(source, variables, node: nil)
  text = unwrap(source).strip
  return nil if text.empty?

  tokens = (@token_cache[text] ||= Lexer.new(text).tokens.freeze)
  Parser.new(tokens, self, variables, node).parse
rescue ArgumentError, TypeError, KeyError
  raise NativeRuntimeError, "unsupported Mendix expression: #{source.inspect}"
end

#invoke(name, arguments) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/mxrb/runtime/native.rb', line 209

def invoke(name, arguments)
  case name.downcase
  when 'tostring' then mendix_string(arguments.fetch(0))
  when 'parseinteger' then Integer(arguments.fetch(0))
  when 'parsedecimal' then Float(arguments.fetch(0))
  when 'round' then arguments.fetch(0).round
  when 'random' then Random.rand
  when 'substring' then substring(*arguments)
  when 'find' then arguments.fetch(0).to_s.index(arguments.fetch(1).to_s) || -1
  when 'formatdatetime' then format_datetime(*arguments)
  when 'contains' then arguments.fetch(0).to_s.include?(arguments.fetch(1).to_s)
  when 'startswith' then arguments.fetch(0).to_s.start_with?(arguments.fetch(1).to_s)
  when 'endswith' then arguments.fetch(0).to_s.end_with?(arguments.fetch(1).to_s)
  else raise ArgumentError, name
  end
end

#resolve_identifier(text, node) ⇒ Object

Raises:

  • (ArgumentError)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/mxrb/runtime/native.rb', line 194

def resolve_identifier(text, node)
  return true if text.casecmp?('true')
  return false if text.casecmp?('false')
  return nil if text.casecmp?('empty')

  if node && bare_reference?(text)
    member = text.split('/').last.split('.').last
    return node.members[member] if node.members.key?(member)
  end
  return text if text.match?(/\A[A-Za-z_]\w*\.[A-Za-z_]\w*\.[A-Za-z_]\w*\z/)
  return node_member(text, node) if node && bare_reference?(text)

  raise ArgumentError, text
end

#resolve_variable(text, variables) ⇒ Object

Raises:



182
183
184
185
186
187
188
189
190
191
192
# File 'lib/mxrb/runtime/native.rb', line 182

def resolve_variable(text, variables)
  name, member = text.delete_prefix('$').split('/', 2)
  value = variables.fetch(name) do
    raise NativeRuntimeError, "unknown variable $#{name}"
  end
  return value unless member

  raise NativeRuntimeError, "$#{name} is not an object" unless value.is_a?(ObjectValue)

  value.members[member.split('.').last]
end