class TestML::Runtime

Attributes

base[RW]
bridge[RW]
compiler[RW]
error[RW]
function[RW]
global[RW]
library[RW]
skip[RW]
testml[RW]

Public Class Methods

new(attributes={}) click to toggle source
# File lib/testml/runtime.rb, line 15
def initialize(attributes={})
  attributes.each { |k,v| self.send "#{k}=", v }
  $TestMLRuntimeSingleton = self
  @base ||= 'test'
end

Public Instance Methods

apply_signature(function, args) click to toggle source
# File lib/testml/runtime.rb, line 45
def apply_signature(function, args)
  signature = function.signature || []

  fail "Function received #{args.size} args but expected #{signature.size}" \
    if ! signature.empty? and args.size != signature.size

  @function.setvar('Self', function)
  signature.each_with_index do |sig_elem, i|
    arg = args[i]
    arg = run_expression(arg) \
      if arg.kind_of? TestML::Expression
    function.setvar(sig_elem, arg)
  end
end
compile_testml() click to toggle source
# File lib/testml/runtime.rb, line 198
def compile_testml
  fail "'testml' document required but not found" \
    unless @testml
  if @testml !~ /\n/
    @testml =~ /(.*)\/(.*)/ or fail
    testml = $2
    @base = @base + '/' + $1
    @testml = read_testml_file testml
  end
  @function = @compiler.new.compile(@testml)
end
get_label() click to toggle source
# File lib/testml/runtime.rb, line 231
def get_label
  label = @function.getvar('Label') or return
  label = label.value
  label.gsub(/\$(\w+)/) {|m| replace_label($1)}
end
get_point(name) click to toggle source
# File lib/testml/runtime.rb, line 173
def get_point(name)
  value = @function.getvar('Block').points[name] or return
  if value.sub!(/\n+\z/, "\n") and value == "\n"
    value = ''
  end
  return TestML::Str.new(value)
end
initialize_runtime() click to toggle source
# File lib/testml/runtime.rb, line 210
def initialize_runtime
  @global = @function.outer

  @global.setvar('Block', TestML::Block.new)
  @global.setvar('Label', TestML::Str.new('$BlockLabel'))
  @global.setvar('True', TestML::Constant::True)
  @global.setvar('False', TestML::Constant::False)
  @global.setvar('None', TestML::Constant::None)
  @global.setvar('TestNumber', TestML::Num.new(0))
  @global.setvar('Library', TestML::List.new)

  library = @function.getvar('Library')
  [@bridge, @library].each do |lib|
    if lib.kind_of? Array
      lib.each {|l| library.push(l.new)}
    else
      library.push(lib.new)
    end
  end
end
lookup_callable(name) click to toggle source
# File lib/testml/runtime.rb, line 159
def lookup_callable(name)
  @function.getvar('Library').value.each do |library|
    if library.respond_to?(name)
      function = lambda do |*args|
        library.method(name).call(*args)
      end
      callable = TestML::Callable.new(function)
      @function.setvar(name, callable)
      return callable
    end
  end
  return nil
end
read_testml_file(file) click to toggle source
# File lib/testml/runtime.rb, line 249
def read_testml_file file
  path = @base + '/' + file
  File.read(path)
end
replace_label(var) click to toggle source
# File lib/testml/runtime.rb, line 237
def replace_label(var)
  block = @function.getvar('Block')
  if var == 'BlockLabel'
    block.label
  elsif v = block.points[var]
    v.sub!(/\n.*/m, '')
    v.strip
  elsif v = function.getvar(var)
    v.value
  end
end
run() click to toggle source
# File lib/testml/runtime.rb, line 21
def run
  compile_testml
  initialize_runtime
  run_function(@function, [])
end
run_assertion(left, assert) click to toggle source
# File lib/testml/runtime.rb, line 79
def run_assertion left, assert
  method_ = method(('assert_' + assert.name).to_sym)

  @function.getvar('TestNumber').value += 1

  if assert.expr
    method_.call(left, run_expression(assert.expr))
  else
    method_.call(left)
  end
end
run_assignment(assignment) click to toggle source
# File lib/testml/runtime.rb, line 72
def run_assignment(assignment)
  @function.setvar(
    assignment.name,
    run_expression(assignment.expr)
  )
end
run_call(call, context=nil) click to toggle source
# File lib/testml/runtime.rb, line 115
  def run_call call, context=nil
    if call.kind_of? TestML::Object
      return call
    end
    if call.kind_of? TestML::Function
      return call
    end
    if call.kind_of? TestML::Point
      return get_point(call.name)
    end
    if call.kind_of? TestML::Call
      name = call.name
      callable =
        @function.getvar(name) ||
        get_point(name) ||
        lookup_callable(name) ||
          fail("Can't locate '#{name}' callable")
      if callable.kind_of? TestML::Object
        return callable
      end
      return callable unless call.args or !context.nil?
      call.args ||= []
      args = call.args.map{|arg| run_expression(arg)}
      args.unshift context if !context.nil?
      if callable.kind_of? TestML::Callable
        begin
          value = callable.value.call(*args)
        rescue
          @error = $!.message
#           @error = "#{$!.class}: #{$!.message}\n at #{$!.backtrace[0]}"
          return TestML::Error.new(@error)
        end
        fail "'#{name}' did not return a TestML::Object object" \
          unless value.kind_of? TestML::Object
        return value
      end
      if callable.kind_of? TestML::Function
        return run_function(callable, args)
      end
      fail
    end
    fail
  end
run_expression(expr) click to toggle source
# File lib/testml/runtime.rb, line 91
def run_expression(expr)
  context = nil
  @error = nil
  if expr.kind_of? TestML::Expression
    calls = expr.calls.clone
    fail if calls.size <= 1
    context = run_call(calls.shift)
    calls.each do |call|
      if @error
        next unless
          call.kind_of?(TestML::Call) and
          call.name == 'Catch'
      end
      context = run_call(call, context)
    end
  else
    context = run_call(expr)
  end
  if @error
    fail @error
  end
  return context
end
run_function(function, args) click to toggle source
# File lib/testml/runtime.rb, line 27
def run_function(function, args)
  signature = apply_signature(function, args)

  parent = @function
  @function = function

  function.statements.each do |statement|
    if statement.kind_of? TestML::Assignment
      run_assignment(statement)
    else
      run_statement(statement)
    end
  end

  @function = parent
  return
end
run_statement(statement) click to toggle source
# File lib/testml/runtime.rb, line 60
def run_statement(statement)
  blocks = select_blocks(statement.points || [])
  blocks.each do |block|
    @function.setvar('Block', block) if block != 1

    result = run_expression(statement.expr)
    if assertion = statement.assert
      run_assertion(result, assertion)
    end
  end
end
select_blocks(wanted) click to toggle source
# File lib/testml/runtime.rb, line 181
def select_blocks(wanted)
  return [1] if wanted.empty?
  selected = []
  @function.data.each do |block|
    points = block.points
    next if points.key?('SKIP')
    next unless wanted.all?{|point| points.key?(point)}
    if points.key?('ONLY')
      selected = [block]
      break
    end
    selected << block
    break if points.key?('LAST')
  end
  return selected
end