From 6e5023efef2758e0a82f7895c804e492bbc3aef8 Mon Sep 17 00:00:00 2001 From: Brett Langdon Date: Sun, 25 Nov 2012 23:07:50 -0500 Subject: [PATCH] added pop, push, replace, gettop, settop LuaObject functions, setup LuaObject to use LuaFunction objects, version bump and updated readme --- README.md | 78 ++++++++++++++++++++++------ example/config/config.lua | 5 ++ example/config/index.js | 17 +++++++ example/constants/index.js | 10 ++++ example/example.js | 26 ---------- example/functions/index.js | 22 ++++++++ example/simple/index.js | 11 ++++ example/test.lua | 6 --- package.json | 2 +- src/luafunction.cc | 2 +- src/luafunction.h | 2 +- src/luaobject.cc | 102 ++++++++++++++++++++++++++++++++++--- src/luaobject.h | 5 ++ 13 files changed, 230 insertions(+), 58 deletions(-) create mode 100644 example/config/config.lua create mode 100644 example/config/index.js create mode 100644 example/constants/index.js delete mode 100755 example/example.js create mode 100644 example/functions/index.js create mode 100644 example/simple/index.js delete mode 100644 example/test.lua diff --git a/README.md b/README.md index 5fabd6c..2a6276a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ var nodelua = require('nodelua'); ## API ### NodeLua -The `NodeLua` module itself only contains a single object `LuaObject` used to initialize a single `lua_State` as well as some constants. +The `NodeLua` module itself contains the objects `LuaObject`, `LuaFunction`, as well as some constants. ```javascript var lua = new nodelua.LuaObject() ``` @@ -55,6 +55,25 @@ var lua = new nodelua.LuaObject() * `COPYRIGHT` * `AUTHORS` +### LuaFunction +The `LuaFunction` is used to initialize a javascript function for use by lua. + +One caveat to using `LuaFunction`s and multiple `LuaObject`s is that `LuaFunction`s regardless of which `LuaObject` +they are registered with are visable to ALL `LuaObject`s. + +#### LuaFunction(func_name, func) +The constructor for `LuaFunction` requires the `func_name` to use from within Lua (`nodelua('name')`) as well as the function itself `func`. +```javascript +var func = new nodelua.LuaFunction('test', function(){ + console.log('This Is A Test'); + return 42; +}); +``` + +#### name +The `name` property of the `LuaFunction` is exposed, but it cannot be changed. + + ### LuaObject The `LuaObject` is an object wrapper around a `lua_State` instance. @@ -64,30 +83,32 @@ The `doFile` method is used to load and execute lua code stored in `file_name`. lua.doFile('test.lua'); ``` -### doString(lua_code) +#### doString(lua_code) The `doString` method is the same as `doFile` except the code is loaded from `lua_code` rather than from a file. ```javascript lua.doString("print('Hello, Lua')"); ``` -### setGlobal(name, value) +#### setGlobal(name, value) The `setGlobal` method is used to provide lua with the global variable `name` containing the value `value`. ```javascript lua.setGlobal('test', 'value'); ``` -### getGlobal(name) +#### getGlobal(name) The `getGlobal` method is used to retrieve either a value set by `setGlobal` or a global variable in any lua code that has been run. ```javascript console.log(lua.getGlobal('test')); ``` -### registerFunction(name, func) -`registerFunction` is used to expose a javascript function `func` to lua with the name `name`. +#### registerFunction(func) +`registerFunction` is used to expose a `LuaFunction` `func` to lua. ```javascript -lua.registerFunction('add_them', function(a, b){ +var func = new nodelua.LuaFunction('add_them', function(a, b){ console.log(a+b); }); +lua.registerFunction(func); +lua.doString('nodelua("add_them", 2, 4)'); ``` There are a few caveats with `registerFunction`. @@ -95,10 +116,9 @@ For starters in order to invoke the javascript function from within lua you must ```lua nodelua('add_them', 3, 5) ``` +All `LuaFunction`s registered with `registerFunction` is registered globally for all `LuaObject`s regardless of which object is used to register it. -As well, there are problems with using `registerFunction` with multiple `LuaObjects`, you will probably end up with a `Segmentation fault: 11` error when running the code. I am working on this issue. - -### status() +#### status() `status` will return the current status code for lua. The result can be `0` for normal or one of the error codes in `nodelua.STATUS`. ```javascript if(lua.status() == nodelua.STATUS.ERRSYNTAX){ @@ -106,13 +126,43 @@ if(lua.status() == nodelua.STATUS.ERRSYNTAX){ } ``` -### collectGarbage(GC_CODE) +#### collectGarbage(GC_CODE) `collectGarbage` is used to control the lua garbage collector. `GC_CODE` should be one of the codes taken from `nodelua.GC`. ```javascript lua.collectGarbage(nodelua.GC.COLLECT); ``` -### close() +#### push(value) +Push `value` onto the Lua stack. +```javascript +lua.push(5); +``` + +#### pop(num) +Pop `num` items from the stack. Default is 1. +```javascript +lua.pop(5); +``` + +#### getTop() +Return the number of elements on the Lua stack. +```javascript +var num = lua.getTop(); +``` + +#### setTop(index) +Set the top of the Lua stack to `index`. +```javascript +lua.setTop(3); +``` + +#### replace(index) +Replaces the top stack element into the specified `index` +```javascript +lua.repalce(3); +``` + +#### close() `close` should be used whenever you have finished using a `LuaObject`. This will simply call `lua_close` on the `lua_State` for that object. ## Example @@ -132,10 +182,6 @@ console.dir(lua.getGlobal('some_var')); lua.close() ``` -## TODO - * Currently there are issues with having multiple `LuaObjects`, this is my next task. - * I want to expose the stack functions from lua so it is easy to pop/push objects to lua. - ## License The MIT License (MIT) Copyright (c) 2012 Brett Langdon diff --git a/example/config/config.lua b/example/config/config.lua new file mode 100644 index 0000000..cc1f323 --- /dev/null +++ b/example/config/config.lua @@ -0,0 +1,5 @@ +val_one = 1 +val_two = 'two' +val_three = nil + +js_value = string.gsub(js_value, 'js_value', 'lua_value') diff --git a/example/config/index.js b/example/config/index.js new file mode 100644 index 0000000..281b0a8 --- /dev/null +++ b/example/config/index.js @@ -0,0 +1,17 @@ +var path = require('path'); +var nodelua = require('../../'); + +var lua = new nodelua.LuaObject(); + +// set a default value +lua.setGlobal('js_value', 'this is js_value'); +console.log('js_value: ' + lua.getGlobal('js_value')); + +console.log('Processing Config.lua'); +var config = path.resolve(__dirname, 'config.lua'); +lua.doFile(config); + +console.log('js_value: ' + lua.getGlobal('js_value')); +console.log('val_one: ' + lua.getGlobal('val_one')); +console.log('val_two: ' + lua.getGlobal('val_two')); +console.log('val_three: ' + lua.getGlobal('val_three')); \ No newline at end of file diff --git a/example/constants/index.js b/example/constants/index.js new file mode 100644 index 0000000..3620f2e --- /dev/null +++ b/example/constants/index.js @@ -0,0 +1,10 @@ +var nodelua = require('../../'); + +console.log('nodelua.INFO'); +console.dir(nodelua.INFO); + +console.log('nodelua.STATUS'); +console.dir(nodelua.STATUS); + +console.log('nodelua.GC'); +console.dir(nodelua.GC); \ No newline at end of file diff --git a/example/example.js b/example/example.js deleted file mode 100755 index 06dac29..0000000 --- a/example/example.js +++ /dev/null @@ -1,26 +0,0 @@ -var path = require('path'); -var nodelua = require('../'); - -console.log('Lua Info:'); -console.dir(nodelua.INFO); - -var lua = new nodelua.LuaObject(); - -var func = new nodelua.LuaFunction('test_func', function(a,b){ - console.dir(a+b); - }); -lua.registerFunction(func); - - -lua.setGlobal("test", 5); - -var test_file = path.resolve(__dirname, 'test.lua'); -lua.doFile(test_file); - -console.log("Global Var 'global_var' From Lua:"); -console.dir(lua.getGlobal('global_var')); - -var code = "print('Hello, Lua')"; -lua.doString(code); - -console.log("Status: " + lua.status()); diff --git a/example/functions/index.js b/example/functions/index.js new file mode 100644 index 0000000..dc278f2 --- /dev/null +++ b/example/functions/index.js @@ -0,0 +1,22 @@ +var nodelua = require('../../'); + +var lua = new nodelua.LuaObject(); + +var add_them = new nodelua.LuaFunction('add_them', function(a, b){ + console.log('Adding ' + a + ' and ' + b + ' in js'); + return a + b; + }); +lua.registerFunction(add_them); + +// Functionas are registered globally +// for all LuaObjects +var lua_two = new nodelua.LuaObject(); +lua_two.doString("print('Result in Lua: ' .. nodelua('add_them', 10, 5))"); + +var subtract_them = new nodelua.LuaFunction('subtract_them', function(a, b){ + console.log('Subtracting ' + a + ' and ' + b + ' in js'); + return a - b; + }); + +lua_two.registerFunction(subtract_them); +lua.doString("print('Result in Lua: ' .. nodelua('subtract_them', 10, 5))"); \ No newline at end of file diff --git a/example/simple/index.js b/example/simple/index.js new file mode 100644 index 0000000..99a8e6c --- /dev/null +++ b/example/simple/index.js @@ -0,0 +1,11 @@ +var nodelua = require('../../'); + +var lua = new nodelua.LuaObject(); + +lua.setGlobal('js_value', 500); + +lua.doString('print(js_value)'); + +lua.doString('js_value = "something new"'); + +console.dir(lua.getGlobal('js_value')); \ No newline at end of file diff --git a/example/test.lua b/example/test.lua deleted file mode 100644 index 54b10b7..0000000 --- a/example/test.lua +++ /dev/null @@ -1,6 +0,0 @@ -print("Calling JS Function 'test_func' From Lua"); -nodelua("test_func", 3, 5) - -global_var = 'this is a global variable from lua' - -print("Global Var 'test' From JS: " .. test) \ No newline at end of file diff --git a/package.json b/package.json index ecc046d..eba55f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodelua", - "version": "0.1.3", + "version": "0.1.4", "description": "Lua Bindings For Node.JS", "keywords": [ "lua" diff --git a/src/luafunction.cc b/src/luafunction.cc index 4b828da..4e078c4 100644 --- a/src/luafunction.cc +++ b/src/luafunction.cc @@ -43,7 +43,7 @@ Handle LuaFunction::New(const Arguments& args) { LuaFunction* obj = new LuaFunction(); obj->func_name = get_str(args[0]); - obj->func_def_ = Handle::Cast(args[1]); + obj->func_def_ = Persistent::New(Handle::Cast(args[1])); obj->Wrap(args.This()); return args.This(); diff --git a/src/luafunction.h b/src/luafunction.h index 281cdc3..28d05eb 100644 --- a/src/luafunction.h +++ b/src/luafunction.h @@ -15,7 +15,7 @@ class LuaFunction : public node::ObjectWrap { static LuaFunction* unwrap(v8::Handle handle); char *func_name; - v8::Handle func_def_; + v8::Persistent func_def_; private: LuaFunction(); ~LuaFunction(); diff --git a/src/luaobject.cc b/src/luaobject.cc index 6a5f90a..da169e4 100644 --- a/src/luaobject.cc +++ b/src/luaobject.cc @@ -1,5 +1,6 @@ #define BUILDING_NODELUA #include +#include #include "luaobject.h" using namespace v8; @@ -7,7 +8,7 @@ using namespace v8; LuaObject::LuaObject() {}; LuaObject::~LuaObject() {}; -Persistent functions = Persistent(Object::New()); +std::map > functions; void LuaObject::Init(Handle target) { // Prepare constructor template @@ -31,6 +32,16 @@ void LuaObject::Init(Handle target) { FunctionTemplate::New(CollectGarbage)->GetFunction()); tpl->PrototypeTemplate()->Set(String::NewSymbol("close"), FunctionTemplate::New(Close)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("push"), + FunctionTemplate::New(Push)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("pop"), + FunctionTemplate::New(Pop)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("getTop"), + FunctionTemplate::New(GetTop)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("setTop"), + FunctionTemplate::New(SetTop)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("replace"), + FunctionTemplate::New(Replace)->GetFunction()); Persistent constructor = Persistent::New(tpl->GetFunction()); target->Set(String::NewSymbol("LuaObject"), constructor); @@ -164,6 +175,79 @@ Handle LuaObject::SetGlobal(const Arguments& args) { return scope.Close(Undefined()); } +Handle LuaObject::Push(const Arguments& args) { + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("Wrong number of arguments"))); + return scope.Close(Undefined()); + } + + LuaObject* obj = ObjectWrap::Unwrap(args.This()); + + push_value_to_lua(obj->lua_, args[0]); + + return scope.Close(Undefined()); +} + +Handle LuaObject::Pop(const Arguments& args) { + HandleScope scope; + + int pop_n = 1; + if(args.Length() > 0 && args[0]->IsNumber()){ + pop_n = (int)args[0]->ToNumber()->Value(); + } + + LuaObject* obj = ObjectWrap::Unwrap(args.This()); + lua_pop(obj->lua_, pop_n); + + return scope.Close(Undefined()); +} + +Handle LuaObject::GetTop(const Arguments& args) { + HandleScope scope; + + LuaObject* obj = ObjectWrap::Unwrap(args.This()); + int n = lua_gettop(obj->lua_); + + return scope.Close(Number::New(n)); +} + +Handle LuaObject::SetTop(const Arguments& args) { + HandleScope scope; + + int set_n = 0; + if(args.Length() > 0 && args[0]->IsNumber()){ + set_n = (int)args[0]->ToNumber()->Value(); + } + + LuaObject* obj = ObjectWrap::Unwrap(args.This()); + lua_settop(obj->lua_, set_n); + + return scope.Close(Undefined()); +} + +Handle LuaObject::Replace(const Arguments& args) { + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("Must Have 1 Argument"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsNumber()){ + ThrowException(Exception::TypeError(String::New("Argument 1 Must Be A Number"))); + return scope.Close(Undefined()); + } + + int index = (int)args[0]->ToNumber()->Value(); + + LuaObject* obj = ObjectWrap::Unwrap(args.This()); + lua_replace(obj->lua_, index); + + return scope.Close(Undefined()); +} + Handle LuaObject::RegisterFunction(const Arguments& args){ HandleScope scope; @@ -181,8 +265,7 @@ Handle LuaObject::RegisterFunction(const Arguments& args){ Handle handle = Handle::Cast(args[0]); class LuaFunction* func = LuaFunction::unwrap(handle); - functions->Set(String::New(func->func_name), - func->func_def_); + functions[func->func_name] = func->func_def_; return scope.Close(Undefined()); } @@ -198,7 +281,7 @@ int LuaObject::CallFunction(lua_State *L){ lua_error(L); } - Local func_name = String::New((char *)lua_tostring(L, 1)); + char * func_name = (char *)lua_tostring(L, 1); const unsigned argc = n - 1; Local* argv = new Local[argc]; @@ -208,9 +291,14 @@ int LuaObject::CallFunction(lua_State *L){ } Handle ret_val = Undefined(); - if(functions->Has(func_name)){ - Local func = Local::Cast(functions->Get(func_name)); - ret_val = func->Call(Context::GetCurrent()->Global(), argc, argv); + + std::map >::iterator iter; + for(iter = functions.begin(); iter != functions.end(); iter++){ + if(strcmp(iter->first, func_name) == 0){ + Persistent func = iter->second; + ret_val = func->Call(Context::GetCurrent()->Global(), argc, argv); + break; + } } push_value_to_lua(L, ret_val); diff --git a/src/luaobject.h b/src/luaobject.h index a2957e6..8bb137d 100644 --- a/src/luaobject.h +++ b/src/luaobject.h @@ -30,6 +30,11 @@ class LuaObject : public node::ObjectWrap { static v8::Handle Status(const v8::Arguments& args); static v8::Handle CollectGarbage(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); + static v8::Handle Push(const v8::Arguments& args); + static v8::Handle Pop(const v8::Arguments& args); + static v8::Handle GetTop(const v8::Arguments& args); + static v8::Handle SetTop(const v8::Arguments& args); + static v8::Handle Replace(const v8::Arguments& args); lua_State *lua_; };