Class: Rufus::Lua::State

Inherits:
Object
  • Object
show all
Includes:
StateMixin
Defined in:
lib/rufus/lua/state.rb

Overview

A Lua state, wraps a Lua runtime.

require 'rufus/lua'
s = Rufus::Lua::State.new
s.eval "a = 1 + 2"

p s['a'] # => 3.0

Constant Summary

Constants included from StateMixin

Rufus::Lua::StateMixin::LUA_ENVIRONINDEX, Rufus::Lua::StateMixin::LUA_GCCOLLECT, Rufus::Lua::StateMixin::LUA_GCCOUNT, Rufus::Lua::StateMixin::LUA_GCCOUNTB, Rufus::Lua::StateMixin::LUA_GCRESTART, Rufus::Lua::StateMixin::LUA_GCSETPAUSE, Rufus::Lua::StateMixin::LUA_GCSETSTEPMUL, Rufus::Lua::StateMixin::LUA_GCSTEP, Rufus::Lua::StateMixin::LUA_GCSTOP, Rufus::Lua::StateMixin::LUA_GLOBALSINDEX, Rufus::Lua::StateMixin::LUA_MULTRET, Rufus::Lua::StateMixin::LUA_NOREF, Rufus::Lua::StateMixin::LUA_REFNIL, Rufus::Lua::StateMixin::LUA_REGISTRYINDEX, Rufus::Lua::StateMixin::SIMPLE_TYPES, Rufus::Lua::StateMixin::TBOOLEAN, Rufus::Lua::StateMixin::TFUNCTION, Rufus::Lua::StateMixin::TLIGHTUSERDATA, Rufus::Lua::StateMixin::TNIL, Rufus::Lua::StateMixin::TNONE, Rufus::Lua::StateMixin::TNUMBER, Rufus::Lua::StateMixin::TSTRING, Rufus::Lua::StateMixin::TTABLE, Rufus::Lua::StateMixin::TTHREAD, Rufus::Lua::StateMixin::TUSERDATA

Instance Method Summary collapse

Constructor Details

#initialize(include_libs = true) ⇒ State

Instantiates a Lua state (runtime).

Accepts an ‘include_libs’ optional arg. When set to true (the default, all the base Lua libs are loaded in the runtime.

This optional arg can be set to false, when no libs should be present, or to an array of libs to load in order to prepare the state.

The list may include ‘base’, ‘package’, ‘table’, ‘string’, ‘math’, ‘io’, ‘os’ and ‘debug’.



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/rufus/lua/state.rb', line 400

def initialize(include_libs=true)

  @pointer = Lib.luaL_newstate

  open_libraries(include_libs)

  #
  # preparing library methods cache

  class << @pointer
    attr_reader :__lib_method_cache
  end
  @pointer.instance_variable_set(:@__lib_method_cache, {})

  #
  # an array for preserving callback (Ruby functions) from Ruby
  # garbage collection (Scott).

  @callbacks = []

  @error_handler = 0
end

Instance Method Details

#[](k) ⇒ Object

Returns a value set at the ‘global’ level in the state.

state.eval('a = 1 + 2')
puts state['a'] # => "3.0"


464
465
466
467
# File 'lib/rufus/lua/state.rb', line 464

def [](k)

  k.index('.') ?  self.eval("return #{k}") : get_global(k)
end

#[]=(k, v) ⇒ Object

Allows for setting a Lua varible immediately.

state['var'] = [ 1, 2, 3 ]
puts state['var'].to_a[0] # => 1


474
475
476
477
478
# File 'lib/rufus/lua/state.rb', line 474

def []=(k, v)

  #puts; puts("#{k} = #{Rufus::Lua.to_lua_s(v)}")
  self.eval("#{k} = #{Rufus::Lua.to_lua_s(v)}")
end

#closeObject

Closes the state.

It’s probably a good idea (mem leaks) to close a Lua state once you’re done with it.



597
598
599
600
601
602
# File 'lib/rufus/lua/state.rb', line 597

def close

  fail 'State already closed' unless @pointer
  Lib.lua_close(@pointer)
  @pointer = nil
end

#eval(s, bndng = nil, filename = nil, lineno = nil) ⇒ Object

Evaluates a piece (string) of Lua code within the state.



454
455
456
457
# File 'lib/rufus/lua/state.rb', line 454

def eval(s, bndng=nil, filename=nil, lineno=nil)

  loadstring_and_call(s, bndng, filename, lineno)
end

#function(name, opts = {}, &block) ⇒ Object

Binds a Ruby function (callback) in the top environment of Lua

require 'rufus/lua'

s = Rufus::Lua::State.new

s.function 'key_up' do |table|
  table.inject({}) do |h, (k, v)|
    h[k.to_s.upcase] = v
  end
end

p s.eval(%{
  local table = {}
  table['CoW'] = 2
  table['pigs'] = 3
  table['DUCKS'] = 'none'
  return key_up(table)
}).to_h
  # => { 'COW' => 2.0, 'DUCKS => 'none', 'PIGS' => 3.0 }

s.close

:to_ruby => true

Without this option set to true, Lua tables passed to the wrapped Ruby code are instances of Rufus::Lua::Table. With this option set, rufus-lua will call #to_ruby on any parameter that responds to it (And Rufus::Lua::Table does).

s = Rufus::Lua::State.new

s.function 'is_array', :to_ruby => true do |table|
  table.is_a?(Array)
end

s.eval(return is_array({ 1, 2 }))
  # => true
s.eval(return is_array({ 'a' = 'b' }))
  # => false


521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/rufus/lua/state.rb', line 521

def function(name, opts={}, &block)

  fail 'please pass a block for the body of the function' unless block

  to_ruby = opts[:to_ruby]

  callback = Proc.new do |state|

    s = CallbackState.new(state)
    args = []

    loop do

      break if s.stack_top == 0 # never touch stack[0] !!

      arg = s.stack_fetch

      args.unshift(arg)

      s.stack_unstack unless args.first.is_a?(Rufus::Lua::Ref)
    end

    while args.size < block.arity
      args << nil
    end

    args = args.collect { |a| a.respond_to?(:to_ruby) ? a.to_ruby : a } \
      if to_ruby

    result = block.call(*args)

    s.stack_push(result)

    1
  end

  @callbacks << callback
    # preserving the callback from garbage collection

  name = name.to_s

  name, index = if ri = name.rindex('.')
    #
    # bind in the given table

    table_name = name[0..ri-1]

    t = self.eval("return #{table_name}") rescue nil

    raise ArgumentError.new(
      "won't create automatically nested tables"
    ) if (not t) and table_name.index('.')

    t = self.eval("#{table_name} = {}; return #{table_name}") \
      unless t

    t.send(:load_onto_stack)

    [ name[ri+1..-1], -2 ]

  else
    #
    # bind function at the global level

    [ name, LUA_GLOBALSINDEX ]
  end

  Lib.lua_pushcclosure(@pointer, callback, 0)
  Lib.lua_setfield(@pointer, index, name)
end

#gc_collect!Object

Runs garbage collection



614
615
616
617
618
# File 'lib/rufus/lua/state.rb', line 614

def gc_collect!

  fail 'State got closed, cannot proceed' unless @pointer
  Lib.lua_gc(@pointer, LUA_GCCOLLECT, 0)
end

#gc_countObject

Returns current amount of memory in KB in use by Lua



606
607
608
609
610
# File 'lib/rufus/lua/state.rb', line 606

def gc_count

  fail 'State got closed, cannot proceed' unless @pointer
  Lib.lua_gc(@pointer, LUA_GCCOUNT, 0)
end

#gc_resumeObject

Restart garbage collection for this state



630
631
632
633
634
# File 'lib/rufus/lua/state.rb', line 630

def gc_resume

  fail 'State got closed, cannot proceed' unless @pointer
  Lib.lua_gc(@pointer, LUA_GCRESTART, 0)
end

#gc_stopObject

Stop garbage collection for this state



622
623
624
625
626
# File 'lib/rufus/lua/state.rb', line 622

def gc_stop

  fail 'State got closed, cannot proceed' unless @pointer
  Lib.lua_gc(@pointer, LUA_GCSTOP, 0)
end

#open_libraries(libs) ⇒ Object



660
661
662
663
664
665
666
667
# File 'lib/rufus/lua/state.rb', line 660

def open_libraries(libs)

  if libs == true
    Lib.luaL_openlibs(@pointer)
  elsif libs.is_a?(Array)
    libs.each { |l| open_library(l) }
  end
end

#open_library(libname) ⇒ Object

#open_library(libname) - load a lua library via lua_call().

This is needed because is the Lua 5.1 Reference Manual Section 5 (www.lua.org/manual/5.1/manual.html#5) it says:

“The luaopen_* functions (to open libraries) cannot be called directly, like a regular C function. They must be called through Lua, like a Lua function.”

“..you must call them like any other Lua C function, e.g., by using lua_call.”

(by Matthew Nielsen - github.com/xunker)



650
651
652
653
654
655
656
657
658
# File 'lib/rufus/lua/state.rb', line 650

def open_library(libname)

  Lib.lua_pushcclosure(
    @pointer, lambda { |ptr| Lib.send("luaopen_#{libname}", @pointer) }, 0)
  Lib.lua_pushstring(
    @pointer, (libname.to_s == "base" ? "" : libname.to_s))
  Lib.lua_call(
    @pointer, 1, 0)
end

#set_error_handler(lua_code = nil, &block) ⇒ Object



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/rufus/lua/state.rb', line 423

def set_error_handler(lua_code=nil, &block)

  if lua_code == nil && block

    function('_ruby_error_handler', &block)
    stack_load_path('_ruby_error_handler')
    @error_handler = stack_top

  elsif lua_code == nil

    Lib.lua_remove(@pointer, @error_handler) if @error_handler > 0
    @error_handler = 0

  elsif lua_code == :traceback

    stack_load_path('debug.traceback')
    @error_handler = stack_top

  else

    lua_code = lua_code.strip
    lua_code = 'return ' + lua_code unless lua_code.match(/^return\s+/)

    r = self.eval(lua_code)
    r.send(:load_onto_stack)
    @error_handler = stack_top
  end
end