Class: RubyProlog::Core

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-prolog/ruby-prolog.rb

Instance Method Summary collapse

Constructor Details

#initializeCore

Returns a new instance of Core.



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/ruby-prolog/ruby-prolog.rb', line 470

def initialize
  @db = Database.new
  # These predicates are made available in all environments
  write[:X].calls{|env| print env[:X]; true}
  writenl[:X].calls{|env| puts env[:X]; true}
  nl[:X].calls{|e| puts; true}
  eq[:X,:Y].calls{|env| env.unify(env[:X], env[:Y])}
  noteq[:X,:Y].calls{|env| env[:X] != env[:Y]}
  atomic[:X].calls do |env|
    case env[:X]
    when Symbol, Predicate, Goal; false
    else true
    end
  end
  notatomic[:X].calls do |env|
    case env[:X]
    when Symbol, Predicate, Goal; true
    else false
    end
  end
  numeric[:X].calls{|env| Numeric === env[:X] }

  not_[:X].calls do |env|
    found_solution = false
    resolve(env[:X], :CUT) { found_solution = true }
    found_solution == false
  end

  # Enable here so the predicates above don't make it in to_prolog output
  @db.enable_listing
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object



456
457
458
459
460
461
462
463
# File 'lib/ruby-prolog/ruby-prolog.rb', line 456

def method_missing(meth, *args)
  pred = @db.register(meth)

  # We only want to define the method on this specific object instance to avoid polluting global namespaces.
  define_singleton_method(meth){ @db.by_name[meth] }

  pred
end

Instance Method Details

#_resolve_body(body, env, cut) ⇒ Object



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/ruby-prolog/ruby-prolog.rb', line 377

def _resolve_body(body, env, cut)
  if body.nil?
    yield
  else
    goal, rest = body
    if goal == :CUT
      _resolve_body(rest, env, cut) {
        yield
      }
      cut[0] = true
    else
      d_env = Environment.new
      d_cut = [false]
      for d_head, d_body in @db.by_id[goal.pred_id].clauses
        break if d_cut[0] or cut[0]
        trail = []
        if _unify_(goal, env, d_head, d_env, trail, d_env)
          if Proc === d_body
            if d_body[CallbackEnvironment.new(d_env, trail, self)]
              _resolve_body(rest, env, cut) {
                yield
              }
            end
          else
            _resolve_body(d_body, d_env, d_cut) {
              _resolve_body(rest, env, cut) {
                yield
              }
              d_cut[0] ||= cut[0]
            }
          end
        end
        for x, x_env in trail
          x_env.delete(x)
        end
        d_env.clear
      end
    end
  end
end

#_unify(x, x_env, y, y_env, trail, tmp_env) ⇒ Object



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/ruby-prolog/ruby-prolog.rb', line 319

def _unify(x, x_env, y, y_env, trail, tmp_env)

  loop {
    if x == :_
      return true
    elsif Symbol === x
      xp = x_env.get(x)
      if xp.nil?
        y, y_env = y_env.dereference(y)
        unless x == y and x_env == y_env
          x_env.put(x, [y, y_env])
          trail << [x, x_env] unless x_env == tmp_env
        end
        return true
      else
        x, x_env = xp
        x, x_env = x_env.dereference(x)
      end
    elsif Symbol === y
      x, x_env, y, y_env = y, y_env, x, x_env
    else
      break
    end
  }

  if Goal === x and Goal === y
    return false unless x.pred_id == y.pred_id
    x, y = x.args, y.args
  end

  if Array === x and Array === y
    return false unless x.length == y.length
    for i in 0 ... x.length     # x.each_index do |i| も可
      return false unless _unify(x[i], x_env, y[i], y_env, trail, tmp_env)
    end
    return true
  else
    return x == y
  end

end

#_unify_(x, x_env, y, y_env, trail, tmp_env) ⇒ Object



425
426
427
428
429
430
# File 'lib/ruby-prolog/ruby-prolog.rb', line 425

def _unify_(x, x_env, y, y_env, trail, tmp_env)
  lhs, rhs = x_env[x].inspect, y.inspect if $_trace
  unified = _unify(x, x_env, y, y_env, trail, tmp_env)
  printf("\t%s %s %s\n", lhs, (unified ? "~" : "!~"), rhs) if $_trace
  return unified
end

#initialize_copy(orig) ⇒ Object



502
503
504
505
# File 'lib/ruby-prolog/ruby-prolog.rb', line 502

def initialize_copy(orig)
  super
  @db = @db.clone
end

#is(*syms, &block) ⇒ Object



445
446
447
448
449
450
451
452
453
454
# File 'lib/ruby-prolog/ruby-prolog.rb', line 445

def is(*syms,&block)
  $is_cnt ||= 0
  is = @db.register("IS_#{$is_cnt += 1}", skip_listing: true)
  raise "At least one symbol needed" unless syms.size > 0
  is[*syms].calls do |env|
    value = block.call(*syms[1..-1].map{|x| env[x]})
    env.unify(syms.first, value)
  end
  is[*syms]
end

#list(*x) ⇒ Object



362
363
364
365
366
# File 'lib/ruby-prolog/ruby-prolog.rb', line 362

def list(*x)
  y = nil
  x.reverse_each {|e| y = Cons.new(e, y)}
  return y
end

#query(&block) ⇒ Object



433
434
435
436
437
438
439
440
441
442
# File 'lib/ruby-prolog/ruby-prolog.rb', line 433

def query(&block)
  goals = instance_eval(&block)
  goals = [goals] unless goals.is_a?(Array)
  results = []

  resolve(*goals.map(&:to_goal)) {|env|
    results << env.solution
  }
  return results
end

#resolve(*goals) ⇒ Object



369
370
371
372
373
374
# File 'lib/ruby-prolog/ruby-prolog.rb', line 369

def resolve(*goals)
  env = Environment.new
  _resolve_body(list(*goals), env, [false]) {
    yield env
  }
end

#to_prologObject



465
466
467
# File 'lib/ruby-prolog/ruby-prolog.rb', line 465

def to_prolog
  @db.listing.map(&:to_prolog).join("\n\n")
end

#trace(flag) ⇒ Object



420
421
422
# File 'lib/ruby-prolog/ruby-prolog.rb', line 420

def trace(flag)
  $_trace = flag
end