├── LuaInterface ├── CallLua │ └── CallLua.csproj ├── LuaInterface.csproj ├── LuaInterface.sln ├── LuaNetRunner │ └── LuaNetRunner.csproj └── Tests │ ├── Tests.csproj │ └── bin │ ├── Debug │ └── test.lua │ └── Linux │ └── test.lua ├── README.md ├── bin └── lua │ ├── CLRForm.lua │ └── CLRPackage.lua ├── doc ├── LuaInterface.txt ├── LuaRunner.txt ├── guide.pdf └── luainterface.pdf ├── samples ├── NPlot.dll ├── TextBox.cs ├── TextBox.dll ├── auto1.wlua ├── auto2.wlua ├── autoform.wlua ├── com.lua ├── ctype.lua ├── error.lua ├── form.lua ├── form1.wlua ├── form2.wlua ├── form3.wlua ├── gtk-list.lua ├── gui.glade ├── hello-glade.lua ├── hello-gtk.lua ├── hello1.lua ├── hello2.lua ├── hello3.lua ├── hello4.lua ├── ilua.lua ├── layout0.wlua ├── layout1.wlua ├── lconsole.lua ├── lua-gtk.lua ├── lua.lua ├── nplot1.lua ├── socket.lua ├── table1.wlua ├── test-com.lua ├── testbox.wlua └── testluaform.lua ├── src ├── AssemblyInfo.cs ├── CheckType.cs ├── GenerateEventAssembly.cs ├── Lua.cs ├── LuaBase.cs ├── LuaDLL.cs ├── LuaException.cs ├── LuaFunction.cs ├── LuaGlobalAttribute.cs ├── LuaHideAttribute.cs ├── LuaNetRunner.cs ├── LuaRegistrationHelper.cs ├── LuaScriptException.cs ├── LuaTable.cs ├── LuaUserData.cs ├── Metatables.cs ├── MethodWrapper.cs ├── ObjectTranslator.cs ├── ProxyType.cs ├── README.txt ├── config.win ├── configure ├── install ├── luastdcall-unix.h ├── luastdcall-windows.h ├── luastdcall.c ├── makefile ├── makefile.win └── rmakefile.win └── tests ├── CallLua.cs ├── Entity.cs ├── TestLua.cs ├── makefile ├── makefile.win └── test.lua /LuaInterface/CallLua/CallLua.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 9.0.21022 7 | 2.0 8 | {DF85F36C-5970-462F-874D-564D0C4214F2} 9 | Exe 10 | CallLua 11 | CallLua 12 | v3.5 13 | 14 | 15 | true 16 | full 17 | false 18 | bin\Debug 19 | DEBUG 20 | prompt 21 | 4 22 | x86 23 | 24 | 25 | none 26 | false 27 | bin\Release 28 | prompt 29 | 4 30 | x86 31 | 32 | 33 | none 34 | false 35 | bin\Unix 36 | 4 37 | 38 | 39 | full 40 | false 41 | bin\Linux 42 | 4 43 | true 44 | 45 | 46 | none 47 | false 48 | bin\Windows 49 | 4 50 | 51 | 52 | 53 | 54 | 55 | 56 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1} 57 | LuaInterface 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /LuaInterface/LuaInterface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1} 9 | Library 10 | LuaInterface 11 | LuaInterface 12 | v3.5 13 | 14 | 15 | true 16 | full 17 | true 18 | bin\Debug 19 | DEBUG __Windows__ 20 | prompt 21 | 4 22 | x86 23 | 24 | 25 | none 26 | false 27 | bin\Release 28 | prompt 29 | 4 30 | x86 31 | 32 | 33 | none 34 | false 35 | bin\Unix 36 | 4 37 | true 38 | 39 | 40 | none 41 | false 42 | bin\Unix 43 | 4 44 | 45 | 46 | none 47 | false 48 | bin\Debug 49 | 4 50 | 51 | 52 | none 53 | false 54 | bin\Release 55 | 4 56 | 57 | 58 | true 59 | full 60 | false 61 | bin\Linux 62 | DEBUG __lib__ __dot__ 63 | 4 64 | 65 | 66 | none 67 | false 68 | bin\Windows 69 | 4 70 | __Windows__ 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /LuaInterface/LuaInterface.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaInterface", "LuaInterface.csproj", "{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CallLua", "CallLua\CallLua.csproj", "{DF85F36C-5970-462F-874D-564D0C4214F2}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaNetRunner", "LuaNetRunner\LuaNetRunner.csproj", "{378C7B25-6C91-4F74-B5A7-A18F6D685EB1}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{EED940BE-E828-463E-BF90-8BC824758149}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x86 = Debug|x86 15 | Release|x86 = Release|x86 16 | Linux|Any CPU = Linux|Any CPU 17 | Windows|Any CPU = Windows|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Debug|x86.ActiveCfg = Debug|x86 21 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Debug|x86.Build.0 = Debug|x86 22 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Linux|Any CPU.ActiveCfg = Linux|Any CPU 23 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Linux|Any CPU.Build.0 = Linux|Any CPU 24 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Release|x86.ActiveCfg = Release|x86 25 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Release|x86.Build.0 = Release|x86 26 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Windows|Any CPU.ActiveCfg = Windows|Any CPU 27 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1}.Windows|Any CPU.Build.0 = Windows|Any CPU 28 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Debug|x86.ActiveCfg = Debug|x86 29 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Debug|x86.Build.0 = Debug|x86 30 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Linux|Any CPU.ActiveCfg = Linux|Any CPU 31 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Linux|Any CPU.Build.0 = Linux|Any CPU 32 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Release|x86.ActiveCfg = Release|x86 33 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Release|x86.Build.0 = Release|x86 34 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Windows|Any CPU.ActiveCfg = Windows|Any CPU 35 | {DF85F36C-5970-462F-874D-564D0C4214F2}.Windows|Any CPU.Build.0 = Windows|Any CPU 36 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|x86.ActiveCfg = Debug|x86 37 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|x86.Build.0 = Debug|x86 38 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Linux|Any CPU.ActiveCfg = Linux|Any CPU 39 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Linux|Any CPU.Build.0 = Linux|Any CPU 40 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|x86.ActiveCfg = Release|x86 41 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|x86.Build.0 = Release|x86 42 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Windows|Any CPU.ActiveCfg = Windows|Any CPU 43 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Windows|Any CPU.Build.0 = Windows|Any CPU 44 | {EED940BE-E828-463E-BF90-8BC824758149}.Debug|x86.ActiveCfg = Debug|x86 45 | {EED940BE-E828-463E-BF90-8BC824758149}.Debug|x86.Build.0 = Debug|x86 46 | {EED940BE-E828-463E-BF90-8BC824758149}.Linux|Any CPU.ActiveCfg = Linux|Any CPU 47 | {EED940BE-E828-463E-BF90-8BC824758149}.Linux|Any CPU.Build.0 = Linux|Any CPU 48 | {EED940BE-E828-463E-BF90-8BC824758149}.Release|x86.ActiveCfg = Release|x86 49 | {EED940BE-E828-463E-BF90-8BC824758149}.Release|x86.Build.0 = Release|x86 50 | {EED940BE-E828-463E-BF90-8BC824758149}.Windows|Any CPU.ActiveCfg = Windows|Any CPU 51 | {EED940BE-E828-463E-BF90-8BC824758149}.Windows|Any CPU.Build.0 = Windows|Any CPU 52 | EndGlobalSection 53 | GlobalSection(MonoDevelopProperties) = preSolution 54 | StartupItem = CallLua\CallLua.csproj 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /LuaInterface/LuaNetRunner/LuaNetRunner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 9.0.21022 7 | 2.0 8 | {378C7B25-6C91-4F74-B5A7-A18F6D685EB1} 9 | Exe 10 | LuaNetRunner 11 | LuaNetRunner 12 | v3.5 13 | 14 | 15 | true 16 | full 17 | false 18 | bin\Debug 19 | DEBUG; 20 | prompt 21 | 4 22 | x86 23 | false 24 | 25 | 26 | none 27 | true 28 | bin\Release 29 | prompt 30 | 4 31 | x86 32 | false 33 | 34 | 35 | none 36 | false 37 | bin\Unix 38 | 4 39 | 40 | 41 | none 42 | false 43 | bin\Linux 44 | 4 45 | 46 | 47 | none 48 | false 49 | bin\Windows 50 | 4 51 | 52 | 53 | 54 | 55 | LuaNetRunner.cs 56 | 57 | 58 | 59 | 60 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1} 61 | LuaInterface 62 | 63 | 64 | -------------------------------------------------------------------------------- /LuaInterface/Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 9.0.21022 7 | 2.0 8 | {EED940BE-E828-463E-BF90-8BC824758149} 9 | Exe 10 | Tests 11 | v3.5 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug 18 | DEBUG; 19 | prompt 20 | 4 21 | x86 22 | false 23 | TestLua 24 | 25 | 26 | none 27 | true 28 | bin\Release 29 | prompt 30 | 4 31 | x86 32 | false 33 | TestLua 34 | 35 | 36 | none 37 | false 38 | bin\Linux 39 | 4 40 | TestLua 41 | 42 | 43 | none 44 | false 45 | bin\Windows 46 | 4 47 | Tests 48 | 49 | 50 | 51 | 52 | Entity.cs 53 | 54 | 55 | TestLua.cs 56 | 57 | 58 | 59 | 60 | {E915A0A4-2641-4F7E-8A88-8F123FA88BF1} 61 | LuaInterface 62 | 63 | 64 | -------------------------------------------------------------------------------- /LuaInterface/Tests/bin/Debug/test.lua: -------------------------------------------------------------------------------- 1 | width=100 2 | height=200 3 | message="Hello World!" 4 | color={r=100,g=20,b=50} 5 | tree={branch1={leaf1=10,leaf2="leaf2"},leaf3="leaf3"} 6 | 7 | function func(x,y) 8 | return x,x+y 9 | end 10 | -------------------------------------------------------------------------------- /LuaInterface/Tests/bin/Linux/test.lua: -------------------------------------------------------------------------------- 1 | width=100 2 | height=200 3 | message="Hello World!" 4 | color={r=100,g=20,b=50} 5 | tree={branch1={leaf1=10,leaf2="leaf2"},leaf3="leaf3"} 6 | 7 | function func(x,y) 8 | return x,x+y 9 | end 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LuaInterface is a library for integration between the Lua language and Microsoft 2 | .NET platform's Common Language Runtime (CLR). Lua scripts can use it to 3 | instantiate CLR objects, access properties, call methods, and even handle 4 | events with Lua functions. 5 | 6 | Originally written by Fabio Mascarenhas, and currently maintained by Craig 7 | Presti at 8 | [here](http://code.google.com/p/luainterface) 9 | 10 | This corresponds to the latest version 2.0.3, backported to use P/Invoke against 11 | standard Lua 5.1 shared libraries. It provides a working version of LuaInterface 12 | buildable on Mono. 13 | 14 | On Debian/Ubuntu, you will need the `liblua5.1-dev` and `mono-devel` packages. 15 | 16 | To build, go into the src directory, and: 17 | 18 | $ ./configure 19 | 20 | This requires a Lua installation to run, but no other dependencies. It will look 21 | for the Lua headers in the usual places, `/usr/include` and `/usr/include/lua51`, 22 | if your Lua directory is somewhere else altogether set LUA_INCLUDE: 23 | 24 | $ ./configure LUA_INCLUDE=/home/you/lua-5.1.5/src DEFINES=lua 25 | 26 | `DEFINES` here is overriding the default on Linux, which is to assume 27 | the Lua shared library looks like `liblua5.1.so` rather than `lua51.so`. 28 | 29 | Configuration of LuaInterface is controlled by two C# preprocessor defines, 30 | `__Windows__` and `__liblua__`. The first makes the shared library extension '.dll', 31 | and the second makes the Lua shared library name 'liblua5.1' rather than 'lua51'. 32 | The latter is the default for Linux, at least for Debian/Ubuntu. Look at 33 | `src/LuaDLL.cs` to see how these are used, and how to modify for your 34 | configuration. 35 | 36 | (Currently, this project builds against Lua 5.1 or LuaJIT.) 37 | 38 | $ make 39 | $ ./ 40 | 41 | Last step assumes you have a `~/bin` directory, but you can install globally with 42 | 43 | $ sudo ./install /usr/local/bin 44 | 45 | (You can also install globally with e.g `sudo ./install /usr/local/bin`) 46 | 47 | It will generate a wrapper script called `luai` looking like this: 48 | 49 | #!/bin/sh 50 | LUAI=/home/azisa/lua/MonoLuaInterface/bin 51 | export LD_LIBRARY_PATH=$LUAI 52 | export LUA_PATH=";;$LUAI/lua/?.lua" 53 | /usr/bin/mono $LUAI/luai.exe $* 54 | 55 | We have to locally mess with `LD_LIBRARY_PATH` (or `DYLD_LIBRARY_PATH` on 56 | OS X) because LuaInterface will need to find both the Lua shared library and the 57 | stub library `luanet.so`. 58 | 59 | The samples directory contains the original samples, plus some extended ones 60 | from the Lua for Windows project. 61 | 62 | Here is the proverbial 'Hello World': 63 | 64 | ```lua 65 | -- hello.lua 66 | luanet.load_assembly "System" 67 | local Console = luanet.import_type "System.Console" 68 | local Math = luanet.import_type "System.Math" 69 | 70 | Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2)) 71 | ``` 72 | 73 | Using the `CLRPackage` utilities, it is even simpler, since individual classes will be 74 | loaded as needed: 75 | 76 | ```lua 77 | -- hello2.lua 78 | require 'CLRPackage' 79 | import "System" 80 | Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2)) 81 | ``` 82 | 83 | If you want an interactive prompt, then there is a Lua interpreter in Lua, called 84 | `lua.lua`, in the samples directory: 85 | 86 | ``` 87 | samples$ luai lua.lua 88 | Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio 89 | lua.lua (c) David Manura, 2008-08 90 | > require 'CLRPackage' 91 | > Console.WriteLine("hello from Mono") -- 'System' is already loaded... 92 | hello from Mono 93 | > 94 | ``` 95 | 96 | It is straightforward to write GTK# applications in Lua - note that here the 97 | `import` call is passed the package name and the namespace, in cases where 98 | they are not the same: 99 | 100 | ```Lua 101 | -- hello-gtk.lua 102 | require 'CLRPackage' 103 | import ('gtk-sharp','Gtk') 104 | 105 | Application.Init() 106 | 107 | local win = Window("Hello from GTK#") 108 | 109 | win.DeleteEvent:Add(function() 110 | Application.Quit() 111 | end) 112 | 113 | win:Resize(300,300) 114 | 115 | local label = Label() 116 | label.Text = "Hello World!" 117 | win:Add(label) 118 | 119 | win:ShowAll() 120 | 121 | Application.Run() 122 | 123 | ``` 124 | 125 | This automatically creates globals, which is the only way that Lua can mimick the 126 | usual C# scope resolution rules. For larger programs, this is probably not a 127 | good idea, and so there's a way of explicitly creating namespaces: 128 | 129 | 130 | ```Lua 131 | -- hello-gtk2.lua 132 | require 'CLRPackage' 133 | luanet.load_assembly 'gtk-sharp' 134 | local gtk = luanet.namespace 'Gtk' 135 | 136 | gtk.Application.Init() 137 | 138 | local win = gtk.Window("Hello from GTK#") 139 | 140 | win.DeleteEvent:Add(function() 141 | gtk.Application.Quit() 142 | end) 143 | 144 | win:Resize(300,300) 145 | 146 | local label = gtk.Label() 147 | label.Text = "Hello World!" 148 | win:Add(label) 149 | 150 | win:ShowAll() 151 | 152 | gtk.Application.Run() 153 | 154 | ``` 155 | 156 | ## LuaInterface from C# 157 | 158 | LuaInterface is usually used by CLR applications which need a scripting language. 159 | An example of the high-level interface with Lua is `tests/CallLua.cs`; this directory 160 | also has the original C# tests. (These all pass, except for passing a managed function 161 | to `string.gsub`, which is a Lua limitation.) 162 | 163 | A basic C# program is here; it evaluates Lua expressions: 164 | 165 | ```C# 166 | using System; 167 | using LuaInterface; 168 | 169 | public class TestLua { 170 | 171 | public static void Main(string[] args) { 172 | if (args.Length == 0) { 173 | Console.WriteLine("provide a Lua expression!"); 174 | } else { 175 | Lua L = new Lua(); // will open all the standard Lua libraries 176 | try { 177 | object[] results = L.DoString("return "+args[0]); 178 | Console.WriteLine("answer is {0}",results[0]); 179 | } catch(Exception e) { 180 | Console.WriteLine("error: {0}",e.Message); 181 | } 182 | L.Close(); 183 | } 184 | } 185 | 186 | } 187 | ``` 188 | 189 | It must be compiled with a reference to LuaInterface.dll, and both luanet.so and liblua5.1.so must 190 | be accessible on the library path. 191 | 192 | ## Lua API 193 | 194 | These are contained in the global table `luanet`: 195 | 196 | ''luanet.load_assembly'' 197 | 198 | Loads a CLR assembly; will throw an error if not found. 199 | 200 | ''luanet.import_type'' 201 | 202 | Bring in a class using the fully qualified name, e.g. 203 | `C = luanet.import_type 'System.Console'`. The assembly must have been previously 204 | loaded. 205 | 206 | ''luanet.get_object_member'' 207 | 208 | This is given an object, and an index (string or integer). It can be used to look 209 | up a property value, and will return nil + error message if the property does 210 | not exist. (Looking up fields and indices directly will fail with an error). 211 | 212 | ''luanet.make_object'' 213 | 214 | This takes a table and a CLR class name and allows you to override any virtual methods 215 | of that class. 216 | 217 | For instance, given this C# class: 218 | 219 | ```C# 220 | public class CSharp { 221 | public virtual string MyMethod(string s) { 222 | return s.ToUpper(); 223 | } 224 | 225 | public static string UseMe (CSharp obj, string val) { 226 | return obj.MyMethod(val); 227 | } 228 | } 229 | ``` 230 | 231 | then the following Lua code creates a proxy object where `MyMethod` is overriden: 232 | 233 | ```Lua 234 | luanet.load_assembly 'CallLua' -- load the assembly containing CSharp 235 | local CSharp = luanet.import_type 'CSharp' 236 | local T = {} 237 | function T:MyMethod(s) 238 | return s:lower() 239 | end 240 | luanet.make_object(T,'CSharp') 241 | print(CSharp.UseMe(T,'CoOl')) 242 | ``` 243 | 244 | There is a corresponding ``luanet.free_object`` for explicit disposal. 245 | 246 | (See tests/CallLua.cs) 247 | 248 | In addition, this version of LuaInterface defines two extra functions 249 | 250 | ''luanet.ctype'' 251 | 252 | This is the equivalent of `typeof` in C#; given a class proxy object, return the 253 | actual CLR type. 254 | 255 | ```Lua 256 | samples $ luai lua.lua 257 | Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio 258 | lua.lua (c) David Manura, 2008-08 259 | > = String 260 | ProxyType(System.String): 54267293 261 | > ctype = luanet.ctype 262 | > = ctype(String) 263 | System.String: 2033388324 264 | 265 | ``` 266 | 267 | ''luanet.enum'' 268 | 269 | This has two forms. The first casts an integer into an enum type: 270 | 271 | ```Lua 272 | > enum = luanet.enum 273 | > import 'System.Reflection' 274 | > = BindingFlags.Static 275 | Static: 8 276 | > = enum(BindingFlags,8) 277 | Static: 8 278 | ``` 279 | 280 | The second form parses a string representation for an enumeration type. 281 | This is useful for enum flags: 282 | 283 | ```Lua 284 | > = enum(BindingFlags,'Static,Public') 285 | Static, Public: 24 286 | ``` 287 | 288 | It's now possible to use CLR reflection in a non-clumsy way; see `tests/ctype.lua` 289 | for an example of using the Lua API directly from Lua itself, by importing all 290 | static methods of the `LuaDLL` class. 291 | 292 | ## CLRPackage 293 | 294 | This Lua module provides some very useful shortcuts. We have already seen `import`, 295 | which brings classes into the global Lua table. This is not appropriate for larger 296 | applications, so there is `luanet.namespace`. Note that its argument may be a table: 297 | 298 | ```Lua 299 | local gtk,gdk = luanet.namespace {'Gtk','Gdk'} 300 | ``` 301 | 302 | (You do have to explicitly load the assemblies before using this) 303 | 304 | ''luanet.make_array'' 305 | 306 | This is a convenience function for creating CLR arrays; it is passed a class (the proxy, 307 | not the type) and a table of values. 308 | 309 | Note that the Lua expression `Class[10]` already makes us a `Class[]` array! 310 | 311 | Bear in mind that CLR arrays index from zero, and throw a range error if the index 312 | is out of bounds. 313 | 314 | ''luanet.each'' 315 | 316 | This constructs a Lua iterator from an IEnumerable interface: 317 | 318 | ```Lua 319 | > dd = make_array(Double,{1,2,10}) 320 | > for x in each(dd) do print(x) end 321 | 1 322 | 2 323 | 10 324 | > import 'System.Collections' 325 | > al = ArrayList() 326 | > al:Add(10) 327 | > al:Add('hello') 328 | > for o in each(al) do print(o) end 329 | 10 330 | hello 331 | > ht = Hashtable() 332 | > ht.one = 1 333 | > ht.two = 2 334 | > for p in each(ht) do print(p.Key,p.value) end 335 | one 1 336 | two 2 337 | 338 | ``` 339 | 340 | 341 | 342 | 343 | 344 | 345 | -------------------------------------------------------------------------------- /bin/lua/CLRForm.lua: -------------------------------------------------------------------------------- 1 | require "CLRPackage" 2 | import "System" 3 | import "System.Windows.Forms" 4 | import "System.Drawing" 5 | local Directory = luanet.import_type("System.IO.Directory") 6 | local Path = luanet.import_type("System.IO.Path") 7 | local File = luanet.import_type("System.IO.File") 8 | local append = table.insert 9 | local ferr = io.stderr -- for debugging 10 | 11 | ----------- some generally useful functions ----------------- 12 | --- Can be used to set multiple properties of an object, by supplying a table. 13 | -- e.g. set(button,{Text="Click Me",Dock = DockStyle.Fill}) 14 | function set (obj,props) 15 | for k,v in pairs(props) do 16 | if type(k) == 'string' then 17 | obj[k] = v 18 | end 19 | end 20 | end 21 | 22 | --- works like AddRange, except it takes a table of controls 23 | -- e.g add_controls(form,{button1,button2}) 24 | function add_controls (ctrl,ctrls) 25 | for k,v in pairs(ctrls) do 26 | ctrl.Controls:Add(v) 27 | end 28 | end 29 | 30 | function ShowMessageBox (caption,icon) 31 | icon = icon or MessageBoxIcon.Information 32 | MessageBox.Show(caption,arg[0],MessageBoxButtons.OK,icon) 33 | end 34 | 35 | function ShowError (caption) 36 | ShowMessageBox(caption,MessageBoxIcon.Error) 37 | end 38 | 39 | ---------- Utility function for creating classes ------------- 40 | --- Does single-inheritance and _delegation_ 41 | function class(base) 42 | local c = {} -- a new class instance, which is the metatable for all objects of this type 43 | local mt = {} -- a metatable for the class instance 44 | local userdata_base 45 | if base == nil then 46 | --nada 47 | elseif type(base) == 'table' then 48 | -- our new class is a shallow copy of the base class! 49 | for i,v in pairs(base) do 50 | c[i] = v 51 | end 52 | c._base = base 53 | -- inherit the 'not found' handler, if present 54 | if c._handler then mt.__index = c._handler end 55 | end 56 | -- the class will be the metatable for all its objects, 57 | -- and they will look up their methods in it. 58 | c.__index = c 59 | 60 | -- expose a ctor which can be called by () 61 | mt.__call = function(class_tbl,...) 62 | local obj= {} 63 | setmetatable(obj,c) 64 | -- nice alias for the base class ctor (which you have to call explicitly if you have a ctor) 65 | if base then c.super = base._init end 66 | if c._init then 67 | c._init(obj,...) 68 | else 69 | -- make sure that any stuff from the base class is initialized! 70 | if base and base._init then 71 | base._init(obj,...) 72 | end 73 | end 74 | return obj 75 | end 76 | 77 | -- Call Class.catch to set a handler for methods/properties not found in the class! 78 | c.catch = function(handler) 79 | c._handler = handler 80 | mt.__index = handler 81 | end 82 | c._init = ctor 83 | c.is_a = function(self,klass) 84 | local m = getmetatable(self) 85 | if not m then return false end --*can't be an object! 86 | while m do 87 | if m == klass then return true end 88 | m = rawget(m,'_base') 89 | end 90 | return false 91 | end 92 | c.class_of = function(obj) 93 | return c.is_a(obj,c) 94 | end 95 | -- any object can have a specified delegate which is called with unrecognized methods 96 | -- if _handler exists and obj[key] is nil, then pass onto handler! 97 | c.delegate = function(self,obj) 98 | local me = self 99 | mt.__index = function(tbl,key) 100 | -- handling fields! 101 | local getter = rawget(c,"Get_"..key) 102 | if getter then return getter(me) end 103 | getter = rawget(me,"_"..key) 104 | if getter then return getter end 105 | local method = obj[key] 106 | if method then 107 | -- it exists in the delegate! First check if it's callable 108 | if type(method) == 'function' or getmetatable(method).__call then 109 | return function(self,...) 110 | return method(obj,...) 111 | end 112 | else -- otherwise, just return 113 | return method 114 | end 115 | elseif self._handler then 116 | return self._handler(tbl,key) 117 | end 118 | end 119 | c.__newindex = function(self,key,val) 120 | local setter = rawget(c,"Set_"..key) 121 | if setter then 122 | setter(self,val) 123 | else 124 | obj[key] = val 125 | end 126 | end 127 | end 128 | setmetatable(c,mt) 129 | return c 130 | end 131 | 132 | ----------- Creating Menus ----------------------------- 133 | 134 | local ShortcutType = Shortcut.F1:GetType() 135 | 136 | local function parse_shortcut (s) 137 | local res 138 | if pcall(function() -- we have to catch the exception! 139 | res = Enum.Parse(ShortcutType,s,false) 140 | end) then return res end 141 | end 142 | 143 | local function add_menu_items (item,tbl) 144 | for i = 1,#tbl,2 do 145 | item.MenuItems:Add(create_menu_item(tbl[i],tbl[i+1])) 146 | end 147 | end 148 | 149 | function create_menu_item (label,action) 150 | local item = MenuItem() 151 | local shortcut = label:match('%((%w+)%)') 152 | if shortcut then 153 | local shortcut = parse_shortcut(shortcut) 154 | if shortcut then item.Shortcut = shortcut end 155 | label = label:match('(.+)%(') 156 | end 157 | item.Text = label 158 | if type(action) == 'function' then 159 | item.Click:Add(action) 160 | else 161 | add_menu_items(item,action) 162 | end 163 | return item 164 | end 165 | 166 | function main_menu (tbl) 167 | local mm = MainMenu() 168 | add_menu_items(mm,tbl) 169 | return mm 170 | end 171 | 172 | function popup_menu (tbl) 173 | local mm = ContextMenu() 174 | add_menu_items(mm,tbl) 175 | return mm 176 | end 177 | 178 | -- a useful function for creating menu callbacks to methods of a given object. 179 | function method (obj,fun) 180 | return function() 181 | fun(obj) 182 | end 183 | end 184 | 185 | local function populate_control (form,tbl) 186 | set(form,tbl) 187 | if #tbl > 0 then -- has an array part, containing controls 188 | if #tbl == 1 then 189 | table.insert(tbl,1,"Fill") 190 | end 191 | local i = 1 192 | while i <= #tbl do 193 | local c = tbl[i] 194 | local dock 195 | if type(c) == 'string' then 196 | dock = c 197 | c = tbl[i+1] 198 | i = i + 1 199 | c.Dock = DockStyle[dock] 200 | end 201 | form.Controls:Add(c) 202 | i = i + 1 203 | end 204 | end 205 | return form 206 | end 207 | 208 | function LuaForm (tbl) 209 | return populate_control(Form(),tbl) 210 | end 211 | 212 | function LuaPanel (tbl) 213 | return populate_control(Panel(),tbl) 214 | end 215 | 216 | 217 | ---------------- Stream Layout -------------- 218 | StreamLayout = class() 219 | 220 | function StreamLayout:_init(panel) 221 | self.xsep = 10 222 | self.X = self.xsep 223 | self.Y = self.xsep 224 | self.panel = panel 225 | self.newline = true 226 | self.maxX = 0 227 | self.maxHeight = 0 228 | self.labels = {} 229 | self.panel:SuspendLayout() 230 | end 231 | 232 | function StreamLayout:Add(c,like) 233 | if like then self.X = like.Left end 234 | c.Location = Point(self.X,self.Y) 235 | self.panel.Controls:Add(c) 236 | self.X = self.X + c.Width + self.xsep 237 | self.maxX = math.max(self.maxX,self.X) 238 | self.maxHeight = math.max(self.maxHeight,c.Height) 239 | if self.newline then 240 | self.firstC = c 241 | self.newline = false 242 | self.maxHeight = 0 243 | end 244 | end 245 | 246 | function StreamLayout:AddRow(lbl,...) 247 | local row = {...} 248 | if lbl then 249 | local label = Label() 250 | label.AutoSize = true 251 | label.Text = lbl 252 | row.label = label 253 | append(self.labels,row) 254 | self:Add(label) 255 | end 256 | for i,c in ipairs(row) do 257 | self:Add(c) 258 | end 259 | self:NextRow() 260 | end 261 | 262 | function StreamLayout:Height() 263 | return self.Y + self.maxHeight + self.xsep 264 | end 265 | 266 | function StreamLayout:Width() 267 | return self.maxX 268 | end 269 | 270 | function StreamLayout:NextRow() 271 | self.Y = self:Height() 272 | self.X = self.xsep 273 | self.newline = true 274 | end 275 | 276 | function StreamLayout:Finish () 277 | local width = 0 278 | for i,row in ipairs(self.labels) do 279 | width = math.max(width,row.label.Width) 280 | end 281 | if width > 0 then -- i.e there is an explicit row of labels 282 | for i,row in ipairs(self.labels) do 283 | local lbl = row.label 284 | for j,c in ipairs(row) do 285 | c.Left = c.Left + (width - lbl.Width) 286 | self.maxX = math.max(self.maxX,c.Left+c.Width+self.xsep) 287 | end 288 | end 289 | end 290 | self.panel:ResumeLayout(false) 291 | end 292 | 293 | LayoutForm = class() 294 | 295 | function LayoutForm:_init () 296 | self.form = Form() 297 | self.layout = StreamLayout(self.form) 298 | self.hasButtons = false 299 | self.ok = false 300 | self.cancel = false 301 | self.finishedLayout = false 302 | -- this method can only be called once we've set up our own fields! 303 | self:delegate(self.form) 304 | self.FormBorderStyle = FormBorderStyle.FixedDialog 305 | self.MaximizeBox = false 306 | self.MinimizeBox = false 307 | end 308 | 309 | function LayoutForm:AddControl(c) 310 | self.layout:Add(c) 311 | end 312 | 313 | function LayoutForm:AddControlRow(lbl,...) 314 | self.layout:AddRow(lbl,...) 315 | end 316 | 317 | function LayoutForm:AddTextBoxRow(lbl) 318 | local textBox = TextBox() 319 | self:AddControlRow(lbl,textBox) 320 | return textBox 321 | end 322 | 323 | function LayoutForm:Btn (title,res) 324 | local b = Button() 325 | b.Text = title 326 | if res == DialogResult.OK then 327 | self.AcceptButton = b 328 | elseif res == DialogResult.Cancel then 329 | self.CancelButton = b 330 | end 331 | self.layout:Add(b) 332 | self.hasButtons = true 333 | return b 334 | end 335 | 336 | function LayoutForm:OkBtn (title) 337 | return self:Btn(title,DialogResult.OK) 338 | end 339 | 340 | function LayoutForm:CancelBtn (title) 341 | return self:Btn(title,DialogResult.Cancel) 342 | end 343 | 344 | function LayoutForm:NextRow() 345 | self.layout:NextRow() 346 | end 347 | 348 | function LayoutForm:OkCancel () 349 | if not self.layout.newline then self:NextRow() end 350 | self.ok = self:OkBtn "OK" 351 | self.cancel = self:CancelBtn "Cancel" 352 | end 353 | 354 | function LayoutForm:OnOK() 355 | return true 356 | end 357 | 358 | function LayoutForm:CenterControls (...) 359 | local w = 0 360 | local ctrls = {...} 361 | for _,c in ipairs(ctrls) do 362 | w = w + c.Width 363 | end 364 | local diff = (self.layout:Width() - w)/(#ctrls + 1) 365 | local xx = diff 366 | for _,c in ipairs(ctrls) do 367 | c.Left = xx 368 | xx = xx + c.Width + diff 369 | end 370 | end 371 | 372 | function LayoutForm:FinishLayout() 373 | if not self.hasButtons then 374 | self:OkCancel() 375 | self:CenterControls(self.ok,self.cancel) 376 | self.ok.Click:Add(function() 377 | if self:OnOK() then 378 | self.DialogResult = DialogResult.OK 379 | else 380 | self.DialogResult = DialogResult.None 381 | end 382 | end) 383 | end 384 | local layout = self.layout 385 | layout:Finish() 386 | self.ClientSize = Size(layout:Width(), layout:Height()) 387 | self.finishedLayout = true 388 | end 389 | 390 | function LayoutForm:ShowDialogOK () 391 | if not self.finishedLayout then 392 | self:FinishLayout() 393 | end 394 | return self:ShowDialog() == DialogResult.OK 395 | end 396 | 397 | ------------------- Converters ------------------------------ 398 | -- These classes convert values between controls and Lua values, and provide basic verification, 399 | -- like ensuring that a string is a valid number, for instance. 400 | -- They provide an appropriate control for editing the particular value. 401 | Converter = class() 402 | 403 | function Converter:Control () 404 | self.box = TextBox() 405 | return self.box 406 | end 407 | 408 | function Converter:Read (c) 409 | return c.Text 410 | end 411 | 412 | function Converter:Write (c,text) 413 | c.Text = text 414 | end 415 | 416 | NumberConverter = class(Converter) 417 | 418 | function NumberConverter:Read (c) 419 | local txt = c.Text 420 | local value = tonumber(txt) 421 | if not value then return nil, "Cannot convert '"..txt.."' to a number" end 422 | return value 423 | end 424 | 425 | BoolConverter = class(Converter) 426 | 427 | function BoolConverter:Control () 428 | return CheckBox() 429 | end 430 | 431 | function BoolConverter:Read (c) 432 | return c.Checked 433 | end 434 | 435 | function BoolConverter:Write (c,val) 436 | c.Checked = val 437 | end 438 | 439 | ListConverter = class(Converter) 440 | 441 | function ListConverter:_init (list) 442 | self.list = list 443 | end 444 | 445 | function ListConverter:Control () 446 | local c = ComboBox() 447 | if not self.list.Editable then 448 | c.DropDownStyle = ComboBoxStyle.DropDownList 449 | end 450 | for i,item in ipairs(self.list) do 451 | c.Items:Add(item) 452 | end 453 | return c 454 | end 455 | 456 | function ListConverter:Read (c) 457 | local val = c.SelectedItem 458 | if not val then val = c.Text end 459 | return val 460 | end 461 | 462 | function ListConverter:Write (c,val) 463 | c.SelectedItem = val 464 | end 465 | 466 | FileConverter = class(Converter) 467 | 468 | function FileConverter:_init (reading,mask) 469 | -- the filter is in a simplified form like 'Lua Files (*.lua)|C# Files (*.cs)" 470 | -- this will expand it into the required form. 471 | self.filter = mask:gsub("%((.-)%)",function(pat) 472 | return "("..pat..")|"..pat 473 | end) 474 | self.reading = reading 475 | end 476 | 477 | -- ExtraControl is an optional method which gives a converter the opportunity of adding another 478 | -- control to the row after the primary control. In this case, we create a file browser button. 479 | function FileConverter:ExtraControl () 480 | local btn = Button() 481 | local box = self.box 482 | btn.Width = 30 483 | btn.Text = ".." 484 | btn.Click:Add(function() 485 | -- if possible, open the file browser in the same directory as the filename 486 | local path = self:Read(box) 487 | if not File.Exists(path) then 488 | path = Directory.GetCurrentDirectory() 489 | else 490 | path = Path.GetDirectoryName(path) 491 | end 492 | -- depending on whether we want a file to read or write ("Save as"), pick the file dialog. 493 | local filebox 494 | if self.reading then filebox = OpenFileDialog 495 | else filebox = SaveFileDialog end 496 | local dlg = filebox() 497 | dlg.Filter = self.filter 498 | dlg.InitialDirectory = path 499 | if dlg:ShowDialog() == DialogResult.OK then 500 | self:Write(box,dlg.FileName) 501 | end 502 | end) 503 | return btn 504 | end 505 | 506 | -- Note an important convention: this converter puts the full file path in the text box's Tag field, 507 | 508 | function FileConverter:Write (c,val) 509 | c.Text = Path.GetFileName(val) 510 | c.Tag = val 511 | end 512 | 513 | function FileConverter:Read (c) 514 | return c.Tag 515 | end 516 | 517 | -- there are then two subclasses, depending if you want to open a file for reading or writing. 518 | 519 | FileIn = class(FileConverter) 520 | 521 | function FileIn:_init (mask) 522 | self:super(true,mask) 523 | end 524 | 525 | FileOut = class(FileConverter) 526 | 527 | function FileOut:_init (mask) 528 | self:super(false,mask) 529 | end 530 | 531 | local converters = { 532 | number = NumberConverter(), 533 | string = Converter(), 534 | boolean = BoolConverter(), 535 | } 536 | 537 | function Converter.AddConverter (typename,conv) 538 | converters[typename] = conv 539 | end 540 | 541 | ---------------- AutoVarDialog ------------------------------ 542 | 543 | local function callable (method) 544 | local mt = getmetatable(method) 545 | return type(method) == 'function' or (mt and mt.__call) 546 | end 547 | 548 | local function simple_list (nxt) 549 | return type(nxt) == 'table' and #nxt > 0 550 | end 551 | 552 | AutoVarDialog = class(LayoutForm) 553 | 554 | function AutoVarDialog:_init (tbl) 555 | self.rows = {} 556 | self.T = tbl.Object or _G 557 | self.verify = tbl.Verify 558 | self.verify_exists = tbl.Verify ~= nil 559 | --end of local fields; NOW we can initialize the form! 560 | self.super(self) 561 | self.Text = tbl.Text or "untitled" 562 | local i,n = 1,#tbl 563 | while i <= n do 564 | local converter,constraint,extra 565 | local lbl = tbl[i] 566 | local var = tbl[i+1] 567 | local value = self.T[var] 568 | local vtype = type(value) 569 | -- is there a particular default or constraint set? 570 | if i+1 < n then 571 | local nxt = tbl[i+2] 572 | if type(nxt) ~= 'string' then 573 | if callable(nxt) then 574 | constraint = nxt 575 | elseif simple_list(nxt) then 576 | -- have been given a list of possible values 577 | converter = ListConverter(nxt) 578 | elseif Converter.class_of(nxt) then 579 | converter = nxt 580 | else 581 | ShowError("Unknown converter or verify function: "..nxt) 582 | return 583 | end 584 | i = i + 1 585 | end 586 | end 587 | if not converter then 588 | -- use a default converter appropriate to this type 589 | converter = converters[vtype] 590 | if not converter then 591 | ShowError("Cannot find a converter for type: "..vtype) 592 | return 593 | end 594 | end 595 | local c = converter:Control() 596 | self:AddControlRow(lbl,c,converter.ExtraControl and converter:ExtraControl()) 597 | converter:Write(c,value) 598 | append(self.rows,{cntrl=c,converter=converter,var=var, constraint=constraint}) 599 | i = i + 2 600 | end 601 | end 602 | 603 | function AutoVarDialog:OnOK () 604 | local T = {} 605 | for _,t in ipairs(self.rows) do 606 | local value,err = t.converter:Read(t.cntrl) 607 | if not err and t.constraint then 608 | err = t.constraint(value) 609 | end 610 | if err then 611 | ShowError(err) 612 | t.cntrl:Focus() 613 | return false 614 | end 615 | T[t.var] = value 616 | end 617 | -- a function to verify the fields has been supplied 618 | if self.verify_exists then 619 | local err = self.verify(T) 620 | if err then 621 | ShowError(err) 622 | return false 623 | end 624 | end 625 | -- NOW we can finally copy the changed values into the target table! 626 | for k,v in pairs(T) do 627 | self.T[k] = v 628 | end 629 | return true 630 | end 631 | 632 | function Match (pat,err) 633 | return function (s) 634 | --ferr:write(',',s,',',pat,'\n') 635 | if not s:find(pat) then return err end 636 | end 637 | end 638 | 639 | function Range (x1,x2) 640 | if not x2 then -- unbound upper range 641 | return function(x) 642 | if x < x1 then return "Must be greater than "..x1 end 643 | end 644 | elseif not x1 then -- unbound lower range 645 | return function(x) 646 | if x > x2 then return "Must be less than "..x2 end 647 | end 648 | else 649 | return function(x) 650 | if x < x1 or x > x2 then return "Must be in range "..x1.." to "..x2 end 651 | end 652 | end 653 | end 654 | 655 | NonBlank = Match ('%S+','Must be a non-blank string') 656 | Word = Match('^%w+$','Must be a word') 657 | 658 | --- A useful function for prompting a user for a single value. 659 | -- returns a non-nil value if the user clicks on OK or presses . 660 | function PromptForString (caption,prompt,default) 661 | local tbl = {val = default or ""} 662 | local form = AutoVarDialog {Text = caption, Object = tbl; 663 | prompt,"val" 664 | } 665 | if form:ShowDialogOK() then 666 | return tbl.val 667 | end 668 | end 669 | -------------------------------------------------------------------------------- /bin/lua/CLRPackage.lua: -------------------------------------------------------------------------------- 1 | --- 2 | --- This lua module provides auto importing of .net classes into a named package. 3 | --- Makes for super easy use of LuaInterface glue 4 | --- 5 | --- example: 6 | --- Threading = CLRPackage("System", "System.Threading") 7 | --- Threading.Thread.Sleep(100) 8 | --- 9 | --- Extensions: 10 | --- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup, 11 | --- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local 12 | --- assembly, which must end with an explicit .dll extension. 13 | 14 | --- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace: 15 | --- local sys,sysi = luanet.namespace {'System','System.IO'} 16 | -- sys.Console.WriteLine("we are at {0}",sysi.Directory.GetCurrentDirectory()) 17 | 18 | 19 | -- LuaInterface hosted with stock Lua interpreter will need to explicitly require this... 20 | if not luanet then require 'luanet' end 21 | 22 | local import_type, load_assembly = luanet.import_type, luanet.load_assembly 23 | 24 | local mt = { 25 | --- Lookup a previously unfound class and add it to our table 26 | __index = function(package, classname) 27 | local class = rawget(package, classname) 28 | if class == nil then 29 | class = import_type(package.packageName .. "." .. classname) 30 | package[classname] = class -- keep what we found around, so it will be shared 31 | end 32 | return class 33 | end 34 | } 35 | 36 | function luanet.namespace(ns) 37 | if type(ns) == 'table' then 38 | local res = {} 39 | for i = 1,#ns do 40 | res[i] = luanet.namespace(ns[i]) 41 | end 42 | return unpack(res) 43 | end 44 | -- FIXME - table.packageName could instead be a private index (see Lua 13.4.4) 45 | local t = { packageName = ns } 46 | setmetatable(t,mt) 47 | return t 48 | end 49 | 50 | local globalMT, packages 51 | 52 | local function set_global_mt() 53 | packages = {} 54 | globalMT = { 55 | __index = function(T,classname) 56 | for i,package in ipairs(packages) do 57 | local class = package[classname] 58 | if class then 59 | _G[classname] = class 60 | return class 61 | end 62 | end 63 | end 64 | } 65 | setmetatable(_G, globalMT) 66 | end 67 | 68 | --- Create a new Package class 69 | function CLRPackage(assemblyName, packageName) 70 | -- a sensible default... 71 | packageName = packageName or assemblyName 72 | local ok = pcall(load_assembly,assemblyName) -- Make sure our assembly is loaded 73 | return luanet.namespace(packageName) 74 | end 75 | 76 | function import (assemblyName, packageName) 77 | if not globalMT then 78 | set_global_mt() 79 | end 80 | if not packageName then 81 | local i = assemblyName:find('%.dll$') 82 | if i then packageName = assemblyName:sub(1,i-1) 83 | else packageName = assemblyName end 84 | end 85 | local t = CLRPackage(assemblyName,packageName) 86 | table.insert(packages,t) 87 | return t 88 | end 89 | 90 | 91 | function luanet.make_array (tp,tbl) 92 | local arr = tp[#tbl] 93 | for i,v in ipairs(tbl) do 94 | arr:SetValue(v,i-1) 95 | end 96 | return arr 97 | end 98 | 99 | function luanet.each(o) 100 | local e = o:GetEnumerator() 101 | return function() 102 | if e:MoveNext() then 103 | return e.Current 104 | end 105 | end 106 | end 107 | 108 | -------------------------------------------------------------------------------- /doc/LuaRunner.txt: -------------------------------------------------------------------------------- 1 | LuaRunner -- runs Lua scripts with CLR access 2 | Usage: luarunner [{}] 3 | -------------------------------------------------------------------------------- /doc/guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevedonovan/MonoLuaInterface/b923c54ccc36c7d8788ff049e9778bb8e9278668/doc/guide.pdf -------------------------------------------------------------------------------- /doc/luainterface.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevedonovan/MonoLuaInterface/b923c54ccc36c7d8788ff049e9778bb8e9278668/doc/luainterface.pdf -------------------------------------------------------------------------------- /samples/NPlot.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevedonovan/MonoLuaInterface/b923c54ccc36c7d8788ff049e9778bb8e9278668/samples/NPlot.dll -------------------------------------------------------------------------------- /samples/TextBox.cs: -------------------------------------------------------------------------------- 1 | // build@ csc /debug /nologo /target:library TextBox.cs 2 | using System; 3 | using System.Windows.Forms; 4 | using System.Drawing; 5 | 6 | namespace TextBox { 7 | 8 | public delegate bool SpecialKeyHandler(Keys key); 9 | 10 | class ConsoleTextBox : RichTextBox { 11 | public SpecialKeyHandler handler; 12 | 13 | protected const int WM_KEYDOWN = 0x0100; 14 | 15 | public void SetHandler(SpecialKeyHandler handler_) { 16 | handler = handler_; 17 | } 18 | 19 | public override bool PreProcessMessage(ref Message msg) { 20 | if (msg.Msg == WM_KEYDOWN) { 21 | Keys keyData = ((Keys) (int) msg.WParam) | ModifierKeys; 22 | if (keyData == Keys.Enter || keyData == Keys.Escape || keyData == Keys.Tab || 23 | keyData == Keys.Up || keyData == Keys.Down ) 24 | { 25 | if (handler(keyData)) { 26 | return true; 27 | } 28 | } 29 | } 30 | return base.PreProcessMessage(ref msg); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /samples/TextBox.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevedonovan/MonoLuaInterface/b923c54ccc36c7d8788ff049e9778bb8e9278668/samples/TextBox.dll -------------------------------------------------------------------------------- /samples/auto1.wlua: -------------------------------------------------------------------------------- 1 | -- auto1.wlua 2 | require "CLRForm" 3 | 4 | data = { 5 | firstname = "", 6 | lastname = "", 7 | age = 0, 8 | title = "", 9 | phone = "", 10 | email = "" 11 | } 12 | 13 | form = AutoVarDialog { Text = "Please Supply Details", Object = data; 14 | "First Name:","firstname", 15 | "Last Name:","lastname", 16 | "Age:","age", 17 | "Title:","title", 18 | "Phone number:","phone", 19 | "E-mail Address:","email" 20 | } 21 | 22 | if form:ShowDialogOK() then 23 | print 'ok' 24 | os.exit(0) 25 | end 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/auto2.wlua: -------------------------------------------------------------------------------- 1 | -- auto1.wlua 2 | require "CLRForm" 3 | 4 | data = { 5 | firstname = "steve", 6 | lastname = "donovan", 7 | age = 16, 8 | title = "Mr", 9 | phone = "+27116481212", 10 | email = "steve.j.donovan@gmail.com" 11 | } 12 | 13 | form = AutoVarDialog { Text = "Please Supply Details", Object = data; 14 | "First Name:","firstname",NonBlank, 15 | "Last Name:","lastname",NonBlank, 16 | "Age:","age",Range(16,120), 17 | "Title:","title",{"Mr","Ms","Dr","Prof"}, 18 | "Phone number:","phone",Match ('^%+%d+$',"Must be a valid phone number"), 19 | "E-mail Address:","email",Match ("%S+@%S+","Must be valid email address") 20 | } 21 | 22 | if form:ShowDialogOK() then 23 | print 'ok' 24 | end 25 | 26 | os.exit(0) 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/autoform.wlua: -------------------------------------------------------------------------------- 1 | -- autoform.wlua 2 | require "CLRForm" 3 | 4 | tbl = { 5 | x = 2.3, 6 | y = 10.2, 7 | z = "two", 8 | t = -1.0, 9 | file = "c:\\lang\\lua\\ilua.lua", 10 | outfile = "", 11 | res = true, 12 | } 13 | 14 | form = AutoVarDialog { Text = "Test AutoVar", Object = tbl; 15 | "First variable:","x", Range(0,4), 16 | "Second Variable:","y", 17 | "Domain name:","z", {"one","two","three"; Editable=true}, 18 | "Blonheim's Little Adjustment:","t", 19 | "Input File:","file",FileIn "Lua (*.lua)|C# (*.cs)", 20 | "Output File:","outfile",FileOut "Text (*.txt)", 21 | "Make a Note?","res", 22 | } 23 | 24 | if form:ShowDialogOK() then 25 | print(tbl.x,tbl.z,tbl.res,tbl.file) 26 | end 27 | 28 | os.exit(0) 29 | -------------------------------------------------------------------------------- /samples/com.lua: -------------------------------------------------------------------------------- 1 | --require 'luanet' 2 | require 'CLRPackage' 3 | import 'System' 4 | import 'System.Reflection' 5 | local get_flags = luanet.enum(BindingFlags,'GetProperty,IgnoreCase,Public') 6 | local put_flags = luanet.enum(BindingFlags,'SetProperty,IgnoreCase,Public') 7 | local call_flags = luanet.enum(BindingFlags,'InvokeMethod,IgnoreCase,Public') 8 | 9 | local function A(a) 10 | return luanet.make_array(Object,a) 11 | end 12 | 13 | local empty = A{} 14 | local com_wrapper 15 | local T = luanet.ctype(__ComObject) 16 | 17 | 18 | local function maybe_wrap(res) 19 | if type(res) == 'userdata' then 20 | if res:GetType() == T then return com_wrapper(res) end 21 | end 22 | return res 23 | end 24 | 25 | 26 | local function caller(obj,key) 27 | local T = obj:GetType() 28 | return setmetatable({},{ 29 | __call = function(t,o,...) 30 | return maybe_wrap(T:InvokeMember(key,call_flags,nil,obj,A{...})) 31 | end 32 | }) 33 | end 34 | 35 | function com_wrapper(obj) 36 | local T = obj:GetType() 37 | return setmetatable({},{ 38 | __index = function(self,key) 39 | local ok,res = pcall(T.InvokeMember,T,key,get_flags,nil,obj,empty) 40 | if not ok then 41 | res = tostring(res) 42 | if res:match 'Member not found' then 43 | return caller(obj,key) --local c = 44 | --~ rawset(self,key,c) 45 | --~ return c 46 | else 47 | error("cannot find "..key,2) 48 | end 49 | else 50 | return maybe_wrap(res) 51 | end 52 | end; 53 | __newindex = function(self,key,value) 54 | T:InvokeMember(key,put_flags,nil,A{value}) 55 | end 56 | }) 57 | end 58 | 59 | com = {} 60 | 61 | function com.CreateObject(progid) 62 | local ft = Type.GetTypeFromProgID(progid) 63 | local f = Activator.CreateInstance(ft) 64 | return com_wrapper(f) 65 | end 66 | 67 | com.wrap = maybe_wrap 68 | 69 | return com 70 | -------------------------------------------------------------------------------- /samples/ctype.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import 'System.Reflection' 3 | import 'LuaInterface' 4 | 5 | local ctype, enum = luanet.ctype, luanet.enum 6 | 7 | -- get all the static methods of LuaDLL and import them into global 8 | local mm = ctype(LuaDLL):GetMethods(enum(BindingFlags,'Static,Public')) 9 | for i = 0, mm.Length-1 do 10 | local name = mm[i].Name 11 | _G[name] = LuaDLL[name] 12 | end 13 | 14 | -- we can now do standard Lua API things in Lua... 15 | local L = luaL_newstate() 16 | luaL_openlibs(L) 17 | lua_pushstring(L,"hello dolly") 18 | print(lua_gettop(L)) 19 | print(lua_tostring(L,-1)) 20 | 21 | -------------------------------------------------------------------------------- /samples/error.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import 'System' 3 | local arr = luanet.make_array(Double,{1,2}) 4 | print(arr.Length) 5 | print(arr.Foo) 6 | -------------------------------------------------------------------------------- /samples/form.lua: -------------------------------------------------------------------------------- 1 | -- kevinh - the following lines are part of our standard init 2 | -- require("compat-5.1") 3 | 4 | luanet.load_assembly("System.Windows.Forms") 5 | luanet.load_assembly("System.Drawing") 6 | 7 | Form=luanet.import_type("System.Windows.Forms.Form") 8 | Button=luanet.import_type("System.Windows.Forms.Button") 9 | Point=luanet.import_type("System.Drawing.Point") 10 | 11 | form1=Form() 12 | button1=Button() 13 | button2=Button() 14 | 15 | function handleClick(sender,data) 16 | if sender.Text=="OK" then 17 | sender.Text="Clicked" 18 | else 19 | sender.Text="OK" 20 | end 21 | button1.MouseUp:Remove(handler) 22 | print(sender:ToString()) 23 | end 24 | 25 | button1.Text = "OK" 26 | button1.Location=Point(10,10) 27 | button2.Text = "Cancel" 28 | button2.Location=Point(button1.Left, button1.Height + button1.Top + 10) 29 | handler=button1.MouseUp:Add(handleClick) 30 | form1.Text = "My Dialog Box" 31 | form1.HelpButton = true 32 | form1.MaximizeBox=false 33 | form1.MinimizeBox=false 34 | form1.AcceptButton = button1 35 | form1.CancelButton = button2 36 | form1.Controls:Add(button1) 37 | form1.Controls:Add(button2) 38 | form1:ShowDialog() 39 | -------------------------------------------------------------------------------- /samples/form1.wlua: -------------------------------------------------------------------------------- 1 | require 'luanet' 2 | luanet.load_assembly "System.Windows.Forms" 3 | Form = luanet.import_type "System.Windows.Forms.Form" 4 | form = Form() 5 | form.Text = "Hello, World!" 6 | form:ShowDialog() 7 | -------------------------------------------------------------------------------- /samples/form2.wlua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import "System.Windows.Forms" 3 | import "System.Drawing" 4 | 5 | form = Form() 6 | form.Text = "Hello, World!" 7 | button = Button() 8 | button.Text = "Click Me!" 9 | button.Location = Point(20,20) 10 | button.Click:Add(function() 11 | MessageBox.Show("We wuz clicked!",arg[0],MessageBoxButtons.OK) 12 | end) 13 | 14 | form.Controls:Add(button) 15 | form:ShowDialog() 16 | -------------------------------------------------------------------------------- /samples/form3.wlua: -------------------------------------------------------------------------------- 1 | -- form3.wlua 2 | require 'CLRPackage' 3 | import "System.Windows.Forms" 4 | import "System.Drawing" 5 | 6 | button = Button() 7 | button.Text = "Click!" 8 | button.Dock = DockStyle.Top 9 | edit = RichTextBox() 10 | edit.Dock = DockStyle.Fill 11 | 12 | form = Form() 13 | form.Text = "Hello, World!" 14 | form.Controls:Add(edit) 15 | form.Controls:Add(button) 16 | form:ShowDialog() 17 | -------------------------------------------------------------------------------- /samples/gtk-list.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import 'System' 3 | import ('gtk-sharp','Gtk') 4 | import('glib-sharp','GLib') 5 | local ctype = luanet.ctype 6 | 7 | Application.Init() 8 | 9 | local win = Window("Hello from GTK#") 10 | win.DeleteEvent:Add(function() 11 | Application.Quit() 12 | end) 13 | 14 | win:Resize(300,300) 15 | 16 | local store = ListStore({GType.String,GType.String}) 17 | --local store = ListStore({ctype(String),ctype(String)}) 18 | store:AppendValues {"Dachsie","Fritz"} 19 | store:AppendValues {"Collie","Butch"} 20 | 21 | local view = TreeView() 22 | view.Model = store 23 | view.HeadersVisible = true 24 | 25 | -- the long way to make a column 26 | function new_col(title,kind,idx) 27 | local col = TreeViewColumn() 28 | col.Title = title 29 | local r = CellRendererText() 30 | col:PackStart(r,true) 31 | col:AddAttribute(r,kind,idx) 32 | view:AppendColumn(col) 33 | end 34 | 35 | new_col("Dogs","text",0) 36 | --new_col("Name","text",1) 37 | 38 | -- and the short way 39 | local col = TreeViewColumn("Name",CellRendererText(),{"text",1}) 40 | view:AppendColumn(col) 41 | 42 | view.Selection.Changed:Add(function(o,args) 43 | local selected, model, iter = o:GetSelected(); 44 | if selected then 45 | local val = model:GetValue(iter,0) 46 | print("selected",val) 47 | end 48 | end) 49 | 50 | win:Add(view) 51 | 52 | win:ShowAll() 53 | 54 | Application.Run() 55 | 56 | 57 | -------------------------------------------------------------------------------- /samples/gui.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | True 8 | Glade Window 9 | GTK_WINDOW_TOPLEVEL 10 | GTK_WIN_POS_CENTER 11 | False 12 | 256 13 | 256 14 | True 15 | False 16 | True 17 | False 18 | False 19 | GDK_WINDOW_TYPE_HINT_NORMAL 20 | GDK_GRAVITY_NORTH_WEST 21 | True 22 | 23 | 24 | 25 | True 26 | True 27 | GTK_POLICY_ALWAYS 28 | GTK_POLICY_ALWAYS 29 | GTK_SHADOW_IN 30 | GTK_CORNER_TOP_LEFT 31 | 32 | 33 | 34 | True 35 | 400 36 | 400 37 | 0 0 400 10 212.4 236 38 | 0 0 400 10 212.4 236 39 | 40 | 41 | 42 | 38 43 | 17 44 | True 45 | label1 46 | False 47 | False 48 | GTK_JUSTIFY_LEFT 49 | False 50 | False 51 | 0.5 52 | 0.5 53 | 0 54 | 0 55 | PANGO_ELLIPSIZE_NONE 56 | -1 57 | False 58 | 0 59 | 60 | 61 | 96 62 | 88 63 | 64 | 65 | 66 | 67 | 68 | 60 69 | 27 70 | True 71 | True 72 | button1 73 | True 74 | GTK_RELIEF_NORMAL 75 | True 76 | 77 | 78 | 88 79 | 168 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /samples/hello-glade.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import ('System') 3 | import ('gtk-sharp','Gtk') 4 | import ('glade-sharp','Glade') 5 | 6 | Application.Init() 7 | 8 | local gxml = XML("gui.glade","window1",nil) --(nil,"gui.glade","window1",nil) 9 | --gxml:AutoConnect (nil) 10 | local win = gxml:GetWidget "window1" 11 | win.DeleteEvent:Add(function() 12 | Application.Quit() 13 | end) 14 | 15 | local btn = gxml:GetWidget "button1" 16 | btn.Clicked:Add(function(e,a) 17 | -- Console.WriteLine("I was clicked") 18 | -- note how we have to pass an empty Object[] as the last argument! 19 | local args = luanet.make_array(Object,{}) 20 | local md = MessageDialog(win, 21 | DialogFlags.DestroyWithParent, 22 | MessageType.Question, 23 | ButtonsType.YesNo, "Are you sure you wanted to click that button?", 24 | args 25 | ) 26 | local res = md:Run() 27 | res = luanet.enum(ResponseType,res) 28 | if res == ResponseType.Yes then 29 | Console.WriteLine("ok!") 30 | end 31 | md:Destroy() 32 | end) 33 | 34 | 35 | Application.Run() 36 | 37 | 38 | -------------------------------------------------------------------------------- /samples/hello-gtk.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import ('gtk-sharp','Gtk') 3 | 4 | Application.Init() 5 | 6 | local win = Window("Hello from GTK#") 7 | win.DeleteEvent:Add(function() 8 | Application.Quit() 9 | end) 10 | 11 | win:Resize(300,300) 12 | 13 | local label = Label() 14 | label.Text = "Hello World!" 15 | win:Add(label) 16 | 17 | win:ShowAll() 18 | 19 | Application.Run() 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/hello1.lua: -------------------------------------------------------------------------------- 1 | luanet.load_assembly "System" 2 | Console = luanet.import_type "System.Console" 3 | Math = luanet.import_type "System.Math" 4 | Directory = luanet.import_type "System.IO.Directory" 5 | 6 | Console.WriteLine("we are at {0}",Directory.GetCurrentDirectory()) 7 | Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2)) 8 | -------------------------------------------------------------------------------- /samples/hello2.lua: -------------------------------------------------------------------------------- 1 | require 'CLRPackage' 2 | import "System" 3 | Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2)) 4 | -------------------------------------------------------------------------------- /samples/hello3.lua: -------------------------------------------------------------------------------- 1 | #!/home/azisa/bin/luai 2 | require 'CLRPackage' 3 | import "System" 4 | import "System.IO" 5 | Console.WriteLine("we are at {0}",Directory.GetCurrentDirectory()) 6 | -------------------------------------------------------------------------------- /samples/hello4.lua: -------------------------------------------------------------------------------- 1 | #!/home/azisa/bin/luai 2 | --- another variant of hello3: look, Ma, no globals! 3 | require 'CLRPackage' 4 | local sys,sysi = luanet.namespace {'System','System.IO'} 5 | sys.Console.WriteLine("we are at {0}",sysi.Directory.GetCurrentDirectory()) 6 | -------------------------------------------------------------------------------- /samples/ilua.lua: -------------------------------------------------------------------------------- 1 | -- ilua.lua 2 | -- A more friendly Lua interactive prompt 3 | -- doesn't need '=', and will try to print out tables recursively. 4 | -- On Unix, will use readline.so if available. 5 | -- Steve Donovan, 2007 6 | -- 7 | local usage = [[ilua -lLtTvsq (lua files) 8 | -l load a library 9 | -L load a library and bring into global namespace 10 | -t write transcript to file; ilua.log if not specified 11 | -T write transcript to file of format ilua_yyyy_mm_dd_HH_MM.log 12 | -s switch off strict mode (don't report undeclared globals) 13 | -v be verbose 14 | -q require standalone expressions to end with '?' (e.g, 23*1.5?) 15 | If a file called ilua-defs is on your library path, it will be loaded first. 16 | ]] 17 | 18 | local pretty_print_limit = 20 19 | local max_depth = 7 20 | local table_clever = true 21 | local prompt = '> ' 22 | local verbose = false 23 | local strict = false 24 | local que = false 25 | -- suppress strict warnings 26 | _ = true 27 | 28 | -- imported global functions 29 | local sub = string.sub 30 | local match = string.match 31 | local find = string.find 32 | local push = table.insert 33 | local pop = table.remove 34 | local append = table.insert 35 | local concat = table.concat 36 | local floor = math.floor 37 | local write = io.write 38 | local read = io.read 39 | 40 | 41 | 42 | local savef 43 | local collisions = {} 44 | local G_LIB = {} 45 | local declared = {} 46 | local line_handler_fn, global_handler_fn 47 | local print_handlers = {} 48 | 49 | ilua = {} 50 | 51 | function ilua.set_writer (writer) 52 | write = writer 53 | end 54 | 55 | local num_prec 56 | local num_all 57 | 58 | local jstack = {} 59 | 60 | local function oprint(...) 61 | if savef then 62 | savef:write(concat({...},' '),'\n') 63 | end 64 | write(...) 65 | --write '\r\n' 66 | write '\n' 67 | end 68 | 69 | local function is_map_like(tbl) 70 | for k,v in pairs(tbl) do 71 | if type(k) ~= 'number' then 72 | return true 73 | end 74 | end 75 | return false 76 | end 77 | 78 | local function join(tbl,delim,limit,depth) 79 | if not limit then limit = pretty_print_limit end 80 | if not depth then depth = max_depth end 81 | local n = #tbl 82 | local res = '' 83 | local k = 0 84 | -- very important to avoid disgracing ourselves with circular references or 85 | -- excessively nested tables... 86 | if #jstack > depth then 87 | return "..." 88 | end 89 | for i,t in ipairs(jstack) do 90 | if tbl == t then 91 | return "" 92 | end 93 | end 94 | push(jstack,tbl) 95 | -- a table may have a 'list-like' part if it has a non-zero size 96 | -- and may have have a 'map-like' part if it has non-numerical keys 97 | -- you can switch off this cleverness with ilua.table_options {clever = false} 98 | local is_list,is_map 99 | if table_clever then 100 | is_list = #tbl > 0 101 | is_map = is_map_like(tbl) 102 | else 103 | is_map = true -- that is, treat all keys equally 104 | end 105 | if is_list then 106 | for i,v in ipairs(tbl) do 107 | res = res..delim..val2str(v) 108 | k = k + 1 109 | if k > limit then 110 | res = res.." ... " 111 | break 112 | end 113 | end 114 | end 115 | if is_map then 116 | for key,v in pairs(tbl) do 117 | local num = type(key) == 'number' 118 | key = tostring(key) 119 | if not num or (num and not is_list) then 120 | if num then 121 | key = '['..key..']' 122 | end 123 | res = res..delim..key..'='..val2str(v) 124 | k = k + 1 125 | if k > limit then 126 | res = res.." ... " 127 | break 128 | end 129 | end 130 | end 131 | end 132 | pop(jstack) 133 | return sub(res,2) 134 | end 135 | 136 | 137 | function val2str(val) 138 | local tp = type(val) 139 | if print_handlers[tp] then 140 | local s = print_handlers[tp](val) 141 | return s or '?' 142 | end 143 | if tp == 'function' then 144 | return tostring(val) 145 | elseif tp == 'table' then 146 | if val.__tostring then 147 | return tostring(val) 148 | else 149 | return '{'..join(val,',')..'}' 150 | end 151 | elseif tp == 'string' then 152 | return "'"..val.."'" 153 | elseif tp == 'number' then 154 | -- we try only to apply floating-point precision for numbers deemed to be floating-point, 155 | -- unless the 3rd arg to precision() is true. 156 | if num_prec and (num_all or floor(val) ~= val) then 157 | return num_prec:format(val) 158 | else 159 | return tostring(val) 160 | end 161 | else 162 | return tostring(val) 163 | end 164 | end 165 | 166 | function _pretty_print(...) 167 | local args = {n=select('#',...),...} 168 | for i = 1,args.n do 169 | oprint(val2str(args[i])) 170 | end 171 | _G['_'] = args[1] 172 | end 173 | 174 | local function compile(line) 175 | if verbose then oprint(line) end 176 | local f,err = loadstring(line,'local') 177 | return err,f 178 | end 179 | 180 | local function evaluate(chunk) 181 | local ok,res = pcall(chunk) 182 | if not ok then 183 | return res 184 | end 185 | return nil -- meaning, fine! 186 | end 187 | 188 | function eval_lua(line) 189 | -- write to transcript, if open 190 | if savef then savef:write(prompt,line,'\n') end 191 | -- is the line handler interested? 192 | if line_handler_fn then 193 | -- returning nil here means that the handler doesn't want Lua to see the string 194 | line = line_handler_fn(line) 195 | if not line then return end 196 | end 197 | local err,chunk 198 | --~ if not que then -- try compiling first as expression, then as statement 199 | --~ -- is it an expression? 200 | --~ err,chunk = compile('_pretty_print('..line..')') 201 | --~ if err then -- otherwise, a statement? 202 | --~ err,chunk = compile(line) 203 | --~ end 204 | --~ else -- expressions must be explicitly terminated with ? 205 | if line:match '^%s*=' then --or line:match '%?$' then 206 | line = line:gsub ('^%s*=','') 207 | err,chunk = compile('_pretty_print('..line..')') 208 | else 209 | err,chunk = compile(line) 210 | end 211 | --~ end 212 | if not err then 213 | -- we can now execute the chunk 214 | err = evaluate(chunk) 215 | end 216 | if err then -- if there was any compile or runtime error, print it out 217 | oprint(err) 218 | end 219 | end 220 | 221 | local function quit(code,msg) 222 | io.stderr:write(msg,'\n') 223 | os.exit(code) 224 | end 225 | 226 | -- functions available in scripts 227 | function ilua.precision(len,prec,all) 228 | if not len then num_prec = nil 229 | else 230 | num_prec = '%'..len..'.'..prec..'f' 231 | end 232 | num_all = all 233 | end 234 | 235 | function ilua.table_options(t) 236 | if t.limit then pretty_print_limit = t.limit end 237 | if t.depth then max_depth = t.depth end 238 | if t.clever ~= nil then table_clever = t.clever end 239 | end 240 | 241 | -- inject @tbl into the global namespace 242 | function ilua.import(tbl,dont_complain,lib) 243 | lib = lib or '' 244 | if type(tbl) == 'table' then 245 | for k,v in pairs(tbl) do 246 | local key = rawget(_G,k) 247 | -- NB to keep track of collisions! 248 | if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then 249 | append(collisions,{k,lib,G_LIB[k]}) 250 | end 251 | _G[k] = v 252 | G_LIB[k] = lib 253 | end 254 | end 255 | if not dont_complain and #collisions > 0 then 256 | for i, coll in ipairs(collisions) do 257 | local name,lib,oldlib = coll[1],coll[2],coll[3] 258 | write('warning: ',lib,'.',name,' overwrites ') 259 | if oldlib then 260 | write(oldlib,'.',name,'\n') 261 | else 262 | write('global ',name,'\n') 263 | end 264 | end 265 | end 266 | end 267 | 268 | function ilua.print_handler(name,handler) 269 | print_handlers[name] = handler 270 | end 271 | 272 | function ilua.line_handler(handler) 273 | line_handler_fn = handler 274 | end 275 | 276 | function ilua.global_handler(handler) 277 | global_handler_fn = handler 278 | end 279 | 280 | function ilua.print_variables() 281 | for name,v in pairs(declared) do 282 | print(name,type(_G[name])) 283 | end 284 | end 285 | -- 286 | -- strict.lua 287 | -- checks uses of undeclared global variables 288 | -- All global variables must be 'declared' through a regular assignment 289 | -- (even assigning nil will do) in a main chunk before being used 290 | -- anywhere. 291 | -- 292 | local function set_strict() 293 | 294 | local mt = getmetatable(_G) 295 | if mt == nil then 296 | mt = {} 297 | setmetatable(_G, mt) 298 | end 299 | 300 | local function what () 301 | local d = debug.getinfo(3, "S") 302 | return d and d.what or "C" 303 | end 304 | 305 | declared.__tostring = true 306 | 307 | mt.__newindex = function (t, n, v) 308 | declared[n] = true 309 | rawset(t, n, v) 310 | end 311 | 312 | mt.__index = function (t, n) 313 | if not declared[n] and what() ~= "C" then 314 | local lookup = global_handler_fn and global_handler_fn(n) 315 | if not lookup then 316 | error("variable '"..n.."' is not declared", 2) 317 | else 318 | return lookup 319 | end 320 | end 321 | return rawget(t, n) 322 | end 323 | 324 | end 325 | 326 | --- Initial operations which may not succeed! 327 | -- try to bring in any ilua configuration file; don't complain if this is unsuccessful 328 | pcall(function() 329 | require 'ilua-defs' 330 | end) 331 | 332 | -- Unix readline support, if readline.so is available... 333 | local rl,readline,saveline 334 | err = pcall(function() 335 | rl = require 'readline' 336 | readline = rl.readline 337 | saveline = rl.add_history 338 | end) 339 | if not rl then 340 | readline = function(prompt) 341 | write(prompt) 342 | return read() 343 | end 344 | saveline = function(s) end 345 | end 346 | 347 | -- process command-line parameters 348 | if arg then 349 | local i = 1 350 | 351 | local function parm_value(opt,parm,def) 352 | local val = parm:sub(3) 353 | if #val == 0 then 354 | i = i + 1 355 | if i > #arg then 356 | if not def then 357 | quit(-1,"expecting parameter for option '-"..opt.."'") 358 | else 359 | return def 360 | end 361 | end 362 | val = arg[i] 363 | end 364 | return val 365 | end 366 | 367 | while i <= #arg do 368 | local v = arg[i] 369 | local opt = v:sub(1,1) 370 | if opt == '-' then 371 | opt = v:sub(2,2) 372 | if opt == 'h' then 373 | quit(0,usage) 374 | elseif opt == 'l' then 375 | require (parm_value(opt,v)) 376 | elseif opt == 'L' then 377 | local lib = parm_value(opt,v) 378 | local tbl = require (lib) 379 | -- we cannot always trust require to return the table! 380 | if type(tbl) ~= 'table' then 381 | tbl = _G[lib] 382 | end 383 | ilua.import(tbl,true,lib) 384 | elseif opt == 't' or opt == 'T' then 385 | local file 386 | if opt == 'T' then 387 | file = 'ilua_'..os.date ('%y_%m_%d_%H_%M')..'.log' 388 | else 389 | file = parm_value(opt,v,"ilua.log") 390 | end 391 | print('saving transcript "'..file..'"') 392 | savef = io.open(file,'w') 393 | savef:write('! ilua ',concat(arg,' '),'\n') 394 | elseif opt == 's' then 395 | strict = true 396 | elseif opt == 'v' then 397 | verbose = true 398 | elseif opt == 'q' then 399 | que = true 400 | end 401 | else -- a plain file to be executed immediately 402 | dofile(v) 403 | end 404 | i = i + 1 405 | end 406 | 407 | 408 | end 409 | 410 | if not arg or arg[0]:match('\\ilua%.lua$') then 411 | print 'ILUA: Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio\n"quit" to end' 412 | 413 | -- any import complaints? 414 | ilua.import() 415 | 416 | -- enable 'not declared' error 417 | if strict then 418 | set_strict() 419 | end 420 | 421 | local line = readline(prompt) 422 | while line do 423 | if line == 'quit' then break end 424 | eval_lua(line) 425 | saveline(line) 426 | line = readline(prompt) 427 | end 428 | 429 | if savef then 430 | savef:close() 431 | end 432 | end 433 | 434 | 435 | -------------------------------------------------------------------------------- /samples/layout0.wlua: -------------------------------------------------------------------------------- 1 | -- layout0.wlua 2 | require 'CLRPackage' 3 | import "System.Windows.Forms" 4 | 5 | panel = FlowLayoutPanel() 6 | 7 | function button(text,callback) 8 | local b = Button() 9 | callback = callback or function() print(text) end 10 | b.Text = text 11 | b.Click:Add(callback) 12 | return b 13 | end 14 | 15 | function add2panel(c) 16 | panel.Controls:Add(c) 17 | end 18 | 19 | --panel.WrapContents = false 20 | b = button ("Toggle Wrap",function() 21 | panel.WrapContents = not panel.WrapContents 22 | end) 23 | add2panel(b) 24 | b = button "Two" 25 | add2panel(b) 26 | b = button "Three" 27 | add2panel(b) 28 | 29 | form = Form() 30 | form.Text = "Hello, World!" 31 | panel.Dock = DockStyle.Fill 32 | 33 | form.Controls:Add(panel) 34 | form:ShowDialog() 35 | -------------------------------------------------------------------------------- /samples/layout1.wlua: -------------------------------------------------------------------------------- 1 | -- layout1.wlua 2 | require 'CLRForm' 3 | 4 | panel = Panel() 5 | layout = StreamLayout(panel) 6 | b = Button() 7 | b.Text = "One" 8 | layout:Add(b) 9 | b = Button() 10 | b.Text = "Two" 11 | layout:Add(b) 12 | t = TextBox() 13 | layout:Add(t) 14 | layout:Finish() 15 | 16 | form = Form() 17 | form.Text = "Hello, World!" 18 | panel.Dock = DockStyle.Top 19 | 20 | form.Controls:Add(panel) 21 | form:ShowDialog() 22 | -------------------------------------------------------------------------------- /samples/lconsole.lua: -------------------------------------------------------------------------------- 1 | require "CLRPackage" 2 | require "ilua" 3 | require "CLRForm" 4 | import "System.Windows.Forms" 5 | import "System.Drawing" 6 | import "System.IO" 7 | 8 | import "TextBox.dll" 9 | 10 | local ferr = io.stderr --debug 11 | local append = table.insert 12 | 13 | -- it appears necessary to force a delayed evaluation, for which we use a timer.... 14 | local timer = Timer() 15 | timer.Interval = 10 16 | local callback 17 | 18 | timer.Tick:Add(function() 19 | timer:Stop() 20 | if not pcall(callback) then 21 | ferr:write 'callback hosed\n' 22 | end 23 | end) 24 | 25 | local function call_later (fun) 26 | callback = fun 27 | timer:Start() 28 | end 29 | 30 | local function readfile (file) 31 | local f = io.open(file,'r') 32 | if not f then return end 33 | local res = f:read("*a") 34 | f:close() 35 | return res 36 | end 37 | 38 | local function writefile (file,s) 39 | local f = io.open(file,'w') 40 | if not f then return end 41 | f:write(s) 42 | f:close() 43 | return true 44 | end 45 | 46 | function current_line (pane) 47 | return pane:GetLineFromCharIndex(pane.SelectionStart) 48 | end 49 | 50 | -- a useful function for selecting lines in Rich text boxes; if lno is not specified, 51 | -- then use the current line 52 | function select_line (pane,lno) 53 | if not lno then -- current line 54 | lno = current_line(pane) 55 | end 56 | local pos = pane.SelectionStart 57 | local start = pane:GetFirstCharIndexFromLine(lno) 58 | pane:Select(start,pos - start + 1) 59 | end 60 | 61 | local lines = {} 62 | local list = ListBox() 63 | local no_name = true 64 | local this_dir = Environment.CurrentDirectory 65 | local user_dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) 66 | local session_dir = user_dir..'/'..'li-session' 67 | 68 | if Directory.Exists(session_dir) then 69 | local files = Directory.GetFiles(session_dir,"*.lua") 70 | for i = 1,files.Length do 71 | list.Items:Add(Path.GetFileNameWithoutExtension(files[i-1])) 72 | end 73 | else 74 | Directory.CreateDirectory(session_dir) 75 | end 76 | 77 | local function list_contains (name) 78 | for i = 1,list.Items.Count do 79 | if list.Items[i-1] == name then return true end 80 | end 81 | end 82 | 83 | local function add_to_list (name) 84 | list.Items:Add(name) 85 | list.SelectedItem = name 86 | current_name = name 87 | end 88 | 89 | local function session_file (name) 90 | if not name then return end 91 | return session_dir..'/'..name..'.lua' 92 | end 93 | 94 | local code = ConsoleTextBox() --RichTextBox() 95 | code.Font = Font("Tahoma",10,FontStyle.Bold) 96 | code.WordWrap = false 97 | 98 | code:SetHandler(function(key) 99 | if key == Keys.Tab then 100 | code.SelectedText = " " 101 | return true 102 | end 103 | return false 104 | end) 105 | 106 | local text = ConsoleTextBox() --RichTextBox() 107 | text.Font = code.Font 108 | text.WordWrap = false 109 | 110 | -- please note that you must explicitly return false, since LuaInterface is 111 | -- expecting a boolean return value! 112 | text:SetHandler(function(key) 113 | if key == Keys.Up then 114 | get_history(true) 115 | return true 116 | elseif key == Keys.Down then 117 | get_history(false) 118 | return true 119 | end 120 | return false 121 | end) 122 | 123 | local function write (s) 124 | text:AppendText(s) 125 | end 126 | 127 | list.SelectedIndexChanged:Add(function() 128 | local file = session_file(list.SelectedItem) 129 | if not file or not File.Exists(file) then return end 130 | local txt = readfile(file) 131 | if not txt then 132 | ShowError ("Cannot open '"..file.."'") 133 | return 134 | end 135 | code.Text = txt 136 | end) 137 | 138 | local function load_lua_file (file) 139 | local oldFun = fun 140 | fun = function(file) end 141 | local res,err = pcall(dofile,file) 142 | if not res then -- we have an error! 143 | ShowError(err) 144 | print(err) 145 | end 146 | fun = oldFun 147 | end 148 | 149 | local function load_lua () 150 | local dlg = OpenFileDialog() 151 | dlg.Filter = "Lua (*.lua)|*.lua" 152 | dlg.InitialDirectory = this_dir 153 | if dlg:ShowDialog() == DialogResult.OK then 154 | load_lua_file(dlg.FileName) 155 | end 156 | end 157 | 158 | local function save_session () 159 | local dlg = SaveFileDialog() 160 | dlg.Filter = "Lua (*.lua)|*.lua" 161 | dlg.InitialDirectory = this_dir 162 | if dlg:ShowDialog() ~= DialogResult.OK then return end 163 | local f = io.open(dlg.FileName,"w") 164 | f:write(table.concat(lines,'\n')) 165 | f:close() 166 | end 167 | 168 | function clear_code () 169 | code:Clear() 170 | no_name = true 171 | end 172 | 173 | function delete_list_item () 174 | local path = session_file(list.SelectedItem) 175 | os.remove(path) 176 | list.Items:Remove(list.SelectedItem) 177 | clear_code() 178 | end 179 | 180 | function save_code () 181 | local file 182 | if code.Lines.Length == 0 then return end 183 | if not no_name then 184 | file = list.SelectedItem 185 | else -- no name has been assigned, after clearing the code pane 186 | -- try make up an appropriate one! 187 | local firstline = code.Lines[0] 188 | local comment = firstline:match('%s*%-%-%s*(.*)') 189 | if comment then file = comment 190 | else file = "[current]" end 191 | no_name = false 192 | end 193 | local path = session_file(file) 194 | writefile(path,code.Text) 195 | if not list_contains(file) then 196 | add_to_list(file) 197 | else 198 | list.SelectedItem = file 199 | end 200 | return path 201 | end 202 | 203 | local function save_text () 204 | local dlg = SaveFileDialog() 205 | dlg.Filter = "Text (*.txt)|*.txt" 206 | dlg.InitialDirectory = this_dir 207 | if dlg:ShowDialog() ~= DialogResult.OK then return end 208 | writefile(dlg.FileName,text.Text) 209 | end 210 | 211 | local function save_and_go () 212 | local file = save_code() 213 | if not file then return end 214 | local res,err = pcall(dofile,file) 215 | --ferr:write(file,'\n') 216 | if not res then 217 | local i1,i2,line = err:find(':(%d+):') 218 | if i1 then 219 | print(err:sub(i2+1)) 220 | write '\n> ' 221 | code:Focus() 222 | select_line(code,tonumber(line)-1) 223 | return 224 | end 225 | end 226 | write '\n> ' 227 | append(lines,'dofile[['..file..']]') 228 | text:Focus() 229 | end 230 | 231 | function fun (fn) 232 | if not fn then -- prompt for a function name 233 | fn = PromptForString("Lua Interface Console","Function name","") 234 | if not fn then return end 235 | end 236 | if list_contains(fn) then 237 | ShowError("'"..fn.."' already exists. Pick another name") 238 | return 239 | end 240 | no_name = false 241 | local txt = "function "..fn.."( )\n\nend\n" 242 | code.Text = txt 243 | add_to_list(fn) 244 | code:Focus() 245 | end 246 | 247 | ---------------------- Main Menu -------------------------------------- 248 | local menu = main_menu { 249 | "File",{ 250 | "Load Lua(CtrlO)",load_lua, 251 | "Save Session(CtrlS)",save_session, 252 | "Save As Text",save_text, 253 | "E&xit(CtrlX)",function() os.exit(0) end, 254 | }, 255 | "Run",{ 256 | "Save and Go(F5)",save_and_go, 257 | "Create Function",function() fun() end, 258 | "Delete Item",delete_list_item, 259 | "Clear Code Pane",clear_code, 260 | }, 261 | "History", { 262 | "Last(AltUpArrow)", function() get_history(true) end, 263 | "Previous(AltDownArrow)", function() get_history(false) end 264 | } 265 | } 266 | 267 | local function method (obj,fun) 268 | return function() 269 | fun(obj) 270 | end 271 | end 272 | 273 | local popup = popup_menu { 274 | "Copy",method(text,text.Copy), 275 | "Paste",method(text,text.Paste), 276 | "Cut",method(text,text.Cut), 277 | } 278 | 279 | ------------ Managing Command History ----------- 280 | local help_idx = 1 281 | 282 | function get_history (up) 283 | call_later(function() 284 | local delta 285 | -- awful hack, cancelling out the last up/down arrow movement! 286 | if up then 287 | delta = -1 288 | else 289 | delta = 1 290 | end 291 | key_sent = true 292 | call_later(function() 293 | help_idx = help_idx + delta 294 | local txt = lines[help_idx] 295 | if not txt then 296 | help_idx = help_idx - delta 297 | return 298 | end 299 | select_line(text) 300 | text.SelectedText = '> '..txt 301 | end) 302 | end) 303 | end 304 | 305 | ------------ Special Key Handling ------------------ 306 | local lastLine = -1 307 | 308 | text.KeyDown:Add(function(sender,args) 309 | if args.KeyCode == Keys.Enter then 310 | local lineNo = text:GetLineFromCharIndex(text.SelectionStart) 311 | if lineNo ~= lastLine then -- for some reason, happens twice! 312 | if lineNo >= text.Lines.Length then 313 | lineNo = text.Lines.Length - 1 314 | --ferr:write(lineNo,' ',text.Lines.Length,' goofed\n') 315 | end 316 | do 317 | local line = text.Lines[lineNo] 318 | line = line:gsub('^> ','') 319 | lastLine = lineNo 320 | call_later(function() 321 | eval_lua(line) 322 | append(lines,line) 323 | help_idx = #lines + 1 324 | write '> ' 325 | end) 326 | end 327 | end 328 | end 329 | end) 330 | 331 | ----------------------- Ouput Redirection --------------------------------- 332 | function write_out (expand,...) 333 | local t = {...} 334 | local n = #t - 1 335 | for i = 1,n do 336 | write(tostring(t[i])) 337 | if expand then write '\t' end 338 | end 339 | write(tostring(t[n+1])) 340 | end 341 | 342 | function writer (...) 343 | write_out(false,...) 344 | end 345 | 346 | ilua.set_writer(writer) 347 | function print (...) 348 | write_out(true,...) 349 | write '\r\n' 350 | end 351 | 352 | -------- Layout Controls --------------------------------------------- 353 | local form = Form() 354 | form.Menu = menu 355 | form.Text = "LuaInterface GUI Prompt" 356 | form.Size = Size(500,500) 357 | form.Closing:Add(function() 358 | os.exit(0) 359 | end) 360 | 361 | local panel = Panel() 362 | panel.Dock = DockStyle.Top 363 | 364 | local hsplitter = Splitter() 365 | hsplitter.Dock = DockStyle.Left 366 | hsplitter.MinSize = 70 367 | 368 | code.Dock = DockStyle.Fill 369 | code.Height = 70 370 | 371 | list.Dock = DockStyle.Left 372 | list.Width = 70 373 | 374 | panel.Controls:Add(code) 375 | panel.Controls:Add(hsplitter) 376 | panel.Controls:Add(list) 377 | 378 | -- note the particular order! 379 | local splitter = Splitter() 380 | splitter.Dock = DockStyle.Top 381 | splitter.MinSize = 70 382 | splitter.MinExtra = 100 383 | text.Dock = DockStyle.Fill 384 | text.ContextMenu = popup 385 | form.Controls:Add(text) 386 | form.Controls:Add(splitter) 387 | form.Controls:Add(panel) 388 | 389 | -- stuff exported to the interactive console 390 | gettype = luanet.import_type 391 | app = {code=code,text=text, list=list, form=form} 392 | function cd (path) 393 | if not path or #path == 0 then 394 | print(Directory.GetCurrentDirectory()) 395 | else 396 | Directory.SetCurrentDirectory(path) 397 | end 398 | end 399 | 400 | write 'Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio\r\n' 401 | write '> ' 402 | 403 | if arg[1] and File.Exists(arg[1]) then 404 | load_lua_file(arg[1]) 405 | end 406 | 407 | text.WordWrap = true 408 | console = text 409 | 410 | form:ShowDialog() 411 | 412 | 413 | -------------------------------------------------------------------------------- /samples/lua-gtk.lua: -------------------------------------------------------------------------------- 1 | ---- A simple interactive Console for LuaInterface using Gtk# 2 | require 'CLRPackage' 3 | import ('gtk-sharp','Gtk') 4 | luanet.load_assembly 'gdk-sharp' 5 | luanet.load_assembly 'glib-sharp' 6 | local Gdk = luanet.namespace 'Gdk' 7 | local Glib = luanet.namespace 'GLib' 8 | 9 | local ferr = io.stderr -- for debugging 10 | 11 | --~ Glib.ExceptionManager.UnhandledException:Add(function(ex,what) 12 | --~ ferr:write(tostring(ex),' ',tostring(what),'\n') 13 | --~ ex.ExitApplication = false 14 | --~ end) 15 | 16 | local Up, Down, Return = Gdk.Key.Up, Gdk.Key.Down, Gdk.Key.Return 17 | 18 | Application.Init() 19 | 20 | local win = Window("Gtk# Lua") 21 | 22 | win.DeleteEvent:Add(function() 23 | Application.Quit() 24 | end) 25 | 26 | win:Resize(500,500) 27 | 28 | local buffer 29 | local history = {idx=1} 30 | 31 | function add_history(line) 32 | if line ~= history[#history] then 33 | table.insert(history,line) 34 | history.idx = #history + 1 35 | end 36 | end 37 | 38 | local function clamp(i,s,n) 39 | if i < s then return 1 40 | elseif i > n then return n 41 | else return i 42 | end 43 | end 44 | 45 | function line_range(lno) 46 | lno = lno or buffer.LineCount - 1 47 | local start = buffer:GetIterAtLine(lno-1) 48 | local endi = buffer:GetIterAtLine(lno) 49 | return start,endi 50 | end 51 | 52 | local function set_last_line(text) 53 | if not text then return end 54 | local start = buffer:GetIterAtLine(buffer.LineCount-1) 55 | local endi = buffer.EndIter 56 | start = buffer:Delete(start,endi) 57 | buffer:Insert(start,'> '..text) 58 | end 59 | 60 | -- we need to subclass Gtk.TextView, since we want to trap 61 | -- the up and down keys for accessing command history 62 | 63 | local edit = {} 64 | 65 | function edit:OnKeyPressEvent(event) 66 | local key = event.Key 67 | if key == Up or key == Down then 68 | local delta 69 | if key == Down then 70 | delta = 1 71 | else 72 | delta = -1 73 | end 74 | history.idx = clamp(history.idx + delta,1,#history) 75 | set_last_line(history[history.idx]) 76 | return true 77 | else 78 | return self.base:OnKeyPressEvent(event) 79 | end 80 | end 81 | 82 | luanet.make_object(edit,'Gtk.TextView') 83 | buffer = edit.Buffer 84 | 85 | 86 | local function create_tag(colour) 87 | local tag = TextTag(colour) 88 | tag.Foreground = colour 89 | buffer.TagTable:Add(tag) 90 | return {tag} 91 | end 92 | 93 | local plain,err_style = create_tag "#00F" , create_tag "#F00" 94 | 95 | function write(txt,tag) 96 | tag = tag or plain 97 | buffer:InsertWithTags(buffer.EndIter,txt,tag) 98 | end 99 | 100 | local prompt = '> ' 101 | write 'Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio\n' 102 | write(prompt) 103 | 104 | function print(...) 105 | local args,n = {...},select('#',...) 106 | for i = 1,n do 107 | write(tostring(args[i])..'\t') 108 | end 109 | write '\n' 110 | end 111 | 112 | local function collect(ok,...) 113 | local args = {...} 114 | args.n = select('#',...) 115 | return ok, args 116 | end 117 | 118 | function eval(s) 119 | if s == 'quit' then Application.Quit() end 120 | local expr = s:match('^%s*=%s+(.+)') 121 | if expr then s = 'return '..expr end 122 | local chunk,err = loadstring(s,'tmp') 123 | local ok,res 124 | if chunk then 125 | ok,res = collect(pcall(chunk)) 126 | if not ok then err = res[1] end 127 | end 128 | if err then 129 | write(tostring(err)..'\n',err_style) 130 | elseif res.n > 0 then 131 | print(unpack(res,1,res.n)) 132 | _G._ = res[1] -- last expression put into underscore global 133 | end 134 | end 135 | 136 | edit.KeyReleaseEvent:Add(function(obj,e) 137 | local event = e.Event 138 | if event.Key == Return then 139 | local start,endi = line_range() 140 | local stmt = start:GetText(endi) 141 | local i1,i2 = stmt:find(prompt,1,true) 142 | if i1 == 1 then 143 | stmt = stmt:sub(i2+1) 144 | end 145 | stmt = stmt:gsub('\n$','') 146 | eval(stmt) 147 | add_history(stmt) 148 | buffer:InsertAtCursor(prompt) 149 | end 150 | end) 151 | 152 | local sbox = ScrolledWindow() 153 | sbox.VscrollbarPolicy = PolicyType.Always 154 | sbox:Add(edit) 155 | win:Add(sbox) 156 | win:ShowAll() 157 | Application.Run() 158 | 159 | -------------------------------------------------------------------------------- /samples/lua.lua: -------------------------------------------------------------------------------- 1 | -- lua.lua - Lua 5.1 interpreter (lua.c) reimplemented in Lua. 2 | -- 3 | -- WARNING: This is not completed but was quickly done just an experiment. 4 | -- Fix omissions/bugs and test if you want to use this in production. 5 | -- Particularly pay attention to error handling. 6 | -- 7 | -- (c) David Manura, 2008-08 8 | -- Licensed under the same terms as Lua itself. 9 | -- Based on lua.c from Lua 5.1.3. 10 | -- Improvements by Shmuel Zeigerman. 11 | 12 | -- Variables analogous to those in luaconf.h 13 | local LUA_INIT = "LUA_INIT" 14 | local LUA_PROGNAME = "lua" 15 | local LUA_PROMPT = "> " 16 | local LUA_PROMPT2 = ">> " 17 | local function LUA_QL(x) return "'" .. x .. "'" end 18 | 19 | local lua51 = _VERSION:match '5%.1$' 20 | -- Variables analogous to those in lua.h 21 | local LUA_RELEASE, LUA_COPYRIGHT, eof_ender 22 | if lua51 then 23 | LUA_RELEASE = "Lua 5.1.4" 24 | LUA_COPYRIGHT = "Copyright (C) 1994-2008 Lua.org, PUC-Rio" 25 | eof_ender = LUA_QL("") 26 | else 27 | LUA_RELEASE = "Lua 5.2.0" 28 | LUA_COPYRIGHT = "Copyright (C) 1994-2011 Lua.org, PUC-Rio" 29 | eof_ender = '' 30 | end 31 | local EXTRA_COPYRIGHT = "lua.lua (c) David Manura, 2008-08" 32 | 33 | -- Note: don't allow user scripts to change implementation. 34 | -- Check for globals with "cat lua.lua | luac -p -l - | grep ETGLOBAL" 35 | 36 | local _G = _G 37 | local assert = assert 38 | local collectgarbage = collectgarbage 39 | local loadfile = loadfile 40 | local loadstring = loadstring or load 41 | local pcall = pcall 42 | local rawget = rawget 43 | local select = select 44 | local tostring = tostring 45 | local type = type 46 | local unpack = unpack or table.unpack 47 | local xpcall = xpcall 48 | local io_stderr = io.stderr 49 | local io_stdout = io.stdout 50 | local io_stdin = io.stdin 51 | local string_format = string.format 52 | local string_sub = string.sub 53 | local os_getenv = os.getenv 54 | local os_exit = os.exit 55 | 56 | 57 | local progname = LUA_PROGNAME 58 | 59 | -- Use external functions, if available 60 | local lua_stdin_is_tty = function() return true end 61 | local setsignal = function() end 62 | 63 | local function print_usage() 64 | io_stderr:write(string_format( 65 | "usage: %s [options] [script [args]].\n" .. 66 | "Available options are:\n" .. 67 | " -e stat execute string " .. LUA_QL("stat") .. "\n" .. 68 | " -l name require library " .. LUA_QL("name") .. "\n" .. 69 | " -i enter interactive mode after executing " .. 70 | LUA_QL("script") .. "\n" .. 71 | " -v show version information\n" .. 72 | " -- stop handling options\n" .. 73 | " - execute stdin and stop handling options\n" 74 | , 75 | progname)) 76 | io_stderr:flush() 77 | end 78 | 79 | local our_tostring = tostring 80 | 81 | local tuple = table.pack or function(...) 82 | return {n=select('#', ...), ...} 83 | end 84 | 85 | local using_lsh,lsh 86 | 87 | local function our_print (...) 88 | local args = tuple(...) 89 | for i = 1,args.n do 90 | io.write(our_tostring(args[i]),'\t') 91 | end 92 | _G._ = args[1] 93 | io.write '\n' 94 | end 95 | 96 | local function saveline(s) 97 | if using_lsh then 98 | lsh.saveline(s) 99 | end 100 | end 101 | 102 | local function getline(prmt) 103 | if using_lsh then 104 | return lsh.readline(prmt) 105 | else 106 | io_stdout:write(prmt) 107 | io_stdout:flush() 108 | return io_stdin:read'*l' 109 | end 110 | end 111 | 112 | local function l_message (pname, msg) 113 | if pname then io_stderr:write(string_format("%s: ", pname)) end 114 | io_stderr:write(string_format("%s\n", msg)) 115 | io_stderr:flush() 116 | end 117 | 118 | local function report(status, msg) 119 | if not status and msg ~= nil then 120 | msg = tostring(msg) 121 | --~ msg = (type(msg) == 'string' or type(msg) == 'number') and tostring(msg) 122 | --~ or "(error object is not a string)" 123 | l_message(progname, msg); 124 | end 125 | return status 126 | end 127 | 128 | local function traceback (message) 129 | local tp = type(message) 130 | if tp ~= "string" and tp ~= "number" then return message end 131 | local debug = _G.debug 132 | if type(debug) ~= "table" then return message end 133 | local tb = debug.traceback 134 | if type(tb) ~= "function" then return message end 135 | return tb(message, 2) 136 | end 137 | 138 | local function docall(f, ...) 139 | local tp = {...} -- no need in tuple (string arguments only) 140 | local F = function() return f(unpack(tp)) end 141 | setsignal(true) 142 | local result = tuple(xpcall(F, traceback)) 143 | setsignal(false) 144 | -- force a complete garbage collection in case of errors 145 | if not result[1] then collectgarbage("collect") end 146 | return unpack(result, 1, result.n) 147 | end 148 | 149 | function dofile(name) 150 | local f, msg = loadfile(name) 151 | if f then f, msg = docall(f) end 152 | return report(f, msg) 153 | end 154 | 155 | local function dostring(s, name) 156 | local f, msg = loadstring(s, name) 157 | if f then f, msg = docall(f) end 158 | return report(f, msg) 159 | end 160 | 161 | local function dolibrary (name) 162 | return report(docall(_G.require, name)) 163 | end 164 | 165 | local function print_version() 166 | l_message(nil, LUA_RELEASE .. " " .. LUA_COPYRIGHT.."\n"..EXTRA_COPYRIGHT) 167 | end 168 | 169 | local function getargs (argv, n) 170 | local arg = {} 171 | for i=1,#argv do arg[i - n] = argv[i] end 172 | if _G.arg then 173 | local i = 0 174 | while _G.arg[i] do 175 | arg[i - n] = _G.arg[i] 176 | i = i - 1 177 | end 178 | end 179 | return arg 180 | end 181 | 182 | local function get_prompt (firstline) 183 | -- use rawget to play fine with require 'strict' 184 | local pmt = rawget(_G, firstline and "_PROMPT" or "_PROMPT2") 185 | local tp = type(pmt) 186 | if tp == "string" or tp == "number" then 187 | return tostring(pmt) 188 | end 189 | return firstline and LUA_PROMPT or LUA_PROMPT2 190 | end 191 | 192 | local function fetchline(firstline) 193 | return getline(get_prompt(firstline)) 194 | end 195 | 196 | local function incomplete (msg) 197 | if msg then 198 | if string_sub(msg, -#eof_ender) == eof_ender then 199 | return true 200 | end 201 | end 202 | return false 203 | end 204 | 205 | 206 | local function pushline (firstline) 207 | local fine,b = true 208 | repeat 209 | b = fetchline(firstline) 210 | if not b then return end -- no input 211 | if using_lsh then 212 | fine = lsh.checkline(b) 213 | end 214 | until fine 215 | if firstline and string_sub(b, 1, 1) == '=' then 216 | return "return " .. string_sub(b, 2) -- change '=' to `return' 217 | else 218 | return b 219 | end 220 | end 221 | 222 | 223 | local function loadline () 224 | local b = pushline(true) 225 | if not b then return -1 end -- no input 226 | local f, msg 227 | while true do -- repeat until gets a complete line 228 | f, msg = loadstring(b, "=stdin") 229 | if not incomplete(msg) then break end -- cannot try to add lines? 230 | local b2 = pushline(false) 231 | if not b2 then -- no more input? 232 | return -1 233 | end 234 | b = b .. "\n" .. b2 -- join them 235 | end 236 | 237 | saveline(b) 238 | 239 | return f, msg 240 | end 241 | 242 | 243 | local function dotty () 244 | local oldprogname = progname 245 | progname = nil 246 | using_lsh,lsh = false -- pcall(require, 'luaish') blows with LI ?? 247 | if using_lsh then 248 | our_tostring = lsh.tostring 249 | else 250 | --print('problem loading luaish:',lsh) 251 | our_tostring = tostring 252 | end 253 | while true do 254 | local result 255 | local status, msg = loadline() 256 | if status == -1 then break end 257 | if status then 258 | result = tuple(docall(status)) 259 | status, msg = result[1], result[2] 260 | end 261 | report(status, msg) 262 | if status and result.n > 1 then -- any result to print? 263 | status, msg = pcall(our_print, unpack(result, 2, result.n)) 264 | if not status then 265 | l_message(progname, string_format( 266 | "error calling %s (%s)", 267 | LUA_QL("print"), msg)) 268 | end 269 | end 270 | end 271 | io_stdout:write"\n" 272 | io_stdout:flush() 273 | progname = oldprogname 274 | end 275 | 276 | 277 | local function handle_script(argv, n) 278 | _G.arg = getargs(argv, n) -- collect arguments 279 | local fname = argv[n] 280 | if fname == "-" and argv[n-1] ~= "--" then 281 | fname = nil -- stdin 282 | end 283 | local status, msg = loadfile(fname) 284 | if status then 285 | status, msg = docall(status, unpack(_G.arg)) 286 | end 287 | return report(status, msg) 288 | end 289 | 290 | 291 | local function collectargs (argv, p) 292 | local i = 1 293 | while i <= #argv do 294 | if string_sub(argv[i], 1, 1) ~= '-' then -- not an option? 295 | return i 296 | end 297 | local prefix = string_sub(argv[i], 1, 2) 298 | if prefix == '--' then 299 | if #argv[i] > 2 then return -1 end 300 | return argv[i+1] and i+1 or 0 301 | elseif prefix == '-' then 302 | return i 303 | elseif prefix == '-i' then 304 | if #argv[i] > 2 then return -1 end 305 | p.i = true 306 | p.v = true 307 | elseif prefix == '-v' then 308 | if #argv[i] > 2 then return -1 end 309 | p.v = true 310 | elseif prefix == '-e' then 311 | p.e = true 312 | if #argv[i] == 2 then 313 | i = i + 1 314 | if argv[i] == nil then return -1 end 315 | end 316 | elseif prefix == '-l' then 317 | if #argv[i] == 2 then 318 | i = i + 1 319 | if argv[i] == nil then return -1 end 320 | end 321 | else 322 | return -1 -- invalid option 323 | end 324 | i = i + 1 325 | end 326 | return 0 327 | end 328 | 329 | 330 | local function runargs(argv, n) 331 | local i = 1 332 | while i <= n do if argv[i] then 333 | assert(string_sub(argv[i], 1, 1) == '-') 334 | local c = string_sub(argv[i], 2, 2) -- option 335 | if c == 'e' then 336 | local chunk = string_sub(argv[i], 3) 337 | if chunk == '' then i = i + 1; chunk = argv[i] end 338 | assert(chunk) 339 | if not dostring(chunk, "=(command line)") then return false end 340 | elseif c == 'l' then 341 | local filename = string_sub(argv[i], 3) 342 | if filename == '' then i = i + 1; filename = argv[i] end 343 | assert(filename) 344 | if not dolibrary(filename) then return false end 345 | end 346 | i = i + 1 347 | end end 348 | return true 349 | end 350 | 351 | 352 | local function handle_luainit() 353 | local init = os_getenv(LUA_INIT) 354 | if init == nil then 355 | return -- status OK 356 | elseif string_sub(init, 1, 1) == '@' then 357 | dofile(string_sub(init, 2)) 358 | else 359 | dostring(init, "=" .. LUA_INIT) 360 | end 361 | end 362 | 363 | 364 | local import_ = _G.import 365 | if import_ then 366 | lua_stdin_is_tty = import_.lua_stdin_is_tty or lua_stdin_is_tty 367 | setsignal = import_.setsignal or setsignal 368 | LUA_RELEASE = import_.LUA_RELEASE or LUA_RELEASE 369 | LUA_COPYRIGHT = import_.LUA_COPYRIGHT or LUA_COPYRIGHT 370 | _G.import = nil 371 | end 372 | 373 | if _G.arg and _G.arg[0] and #_G.arg[0] > 0 then progname = _G.arg[0] end 374 | local argv = {...} 375 | handle_luainit() 376 | local has = {i=false, v=false, e=false} 377 | local script = collectargs(argv, has) 378 | if script < 0 then -- invalid args? 379 | print_usage() 380 | os_exit(1) 381 | end 382 | if has.v then print_version() end 383 | local status = runargs(argv, (script > 0) and script-1 or #argv) 384 | if not status then os_exit(1) end 385 | if script ~= 0 then 386 | status = handle_script(argv, script) 387 | if not status then os_exit(1) end 388 | else 389 | _G.arg = nil 390 | end 391 | if has.i then 392 | dotty() 393 | elseif script == 0 and not has.e and not has.v then 394 | if lua_stdin_is_tty() then 395 | print_version() 396 | require 'CLRPackage' 397 | import 'System' 398 | dotty() 399 | else dofile(nil) -- executes stdin as a file 400 | end 401 | end 402 | -------------------------------------------------------------------------------- /samples/nplot1.lua: -------------------------------------------------------------------------------- 1 | require "CLRPackage" 2 | require "CLRForm" 3 | import "System" 4 | import "System.Windows.Forms" 5 | import "System.Drawing" 6 | import 'NPlot.dll' 7 | NPW = CLRPackage('NPlot.dll','NPlot.Windows') 8 | 9 | function doubles(t) 10 | return luanet.make_array(Double,t) 11 | end 12 | 13 | f = Form() 14 | 15 | s = NPW.PlotSurface2D() 16 | s.Dock = DockStyle.Fill 17 | f.Controls:Add(s) 18 | 19 | lp = LinePlot() 20 | lp.AbscissaData = doubles{1,2,3,4} 21 | lp.OrdinateData = doubles{10,25,30,27} 22 | 23 | s:Add(lp) 24 | 25 | f:ShowDialog() 26 | 27 | -------------------------------------------------------------------------------- /samples/socket.lua: -------------------------------------------------------------------------------- 1 | --require("compat-5.1") 2 | 3 | luanet.load_assembly("System") 4 | Math=luanet.import_type("System.Math") 5 | print(Math.Pow(2,3)) 6 | 7 | WebClient=luanet.import_type("System.Net.WebClient") 8 | StreamReader=luanet.import_type("System.IO.StreamReader") 9 | 10 | 11 | 12 | 13 | myWebClient = WebClient() 14 | myStream = myWebClient:OpenRead(arg[1]) 15 | sr = StreamReader(myStream) 16 | line=sr:ReadLine() 17 | repeat 18 | print(line) 19 | line=sr:ReadLine() 20 | until not line 21 | myStream:Close() 22 | -------------------------------------------------------------------------------- /samples/table1.wlua: -------------------------------------------------------------------------------- 1 | -- table1.wlua 2 | require 'CLRPackage' 3 | import "System.Windows.Forms" 4 | 5 | panel = TableLayoutPanel() 6 | panel.RowCount = 2 7 | panel.ColumnCount = 2 8 | 9 | function button(text,callback) 10 | local b = Button() 11 | b.Text = text 12 | panel.Controls:Add(b) 13 | return b 14 | end 15 | 16 | b1 = button "One" 17 | b2 = button "Two" 18 | b3 = button "Three" 19 | b4 = button "Four" 20 | 21 | panel:SetRow(b1,0); panel:SetColumn(b1,0) 22 | panel:SetRow(b2,0); panel:SetColumn(b2,1) 23 | panel:SetRow(b3,1); panel:SetColumn(b3,0) 24 | panel:SetRow(b4,1); panel:SetColumn(b4,1) 25 | 26 | form = Form() 27 | form.Text = "Hello, World!" 28 | panel.Dock = DockStyle.Fill 29 | 30 | form.Controls:Add(panel) 31 | form:ShowDialog() 32 | -------------------------------------------------------------------------------- /samples/test-com.lua: -------------------------------------------------------------------------------- 1 | require 'com' 2 | 3 | -- http://ss64.com/vb/filesystemobject.html 4 | 5 | fo = com.CreateObject("Scripting.FileSystemObject") 6 | each = luanet.each 7 | print(fo:FileExists 'com.lua') 8 | print 'and' 9 | f = fo:GetFile 'com.lua' 10 | print (f.Name) 11 | 12 | drives = fo.Drives 13 | print(drives.Count) 14 | print(drives) 15 | 16 | -- this is weird: can access as property! 17 | ee = drives.GetEnumerator 18 | while ee:MoveNext() do 19 | -- have to wrap this COM object explicitly! 20 | local drive = com.wrap(ee.Current) 21 | print(drive.DriveLetter) 22 | end 23 | 24 | function com.each(obj) 25 | local e = obj.GetEnumerator 26 | return function() 27 | if e:MoveNext() then 28 | return com.wrap(e.Current) 29 | end 30 | end 31 | end 32 | 33 | for d in com.each(drives) do print(d.DriveType) end 34 | 35 | --print(fo.Drives['C']) 36 | 37 | print(fo:FolderExists 'lua') 38 | print(fo:GetAbsolutePathName 'lua') 39 | 40 | this = fo:GetFolder '..' 41 | for f in com.each(this.SubFolders) do print(f.Name) end 42 | 43 | --~ drive = fo:Drives 'C' 44 | --~ print(drive.AvailableSpace) 45 | -------------------------------------------------------------------------------- /samples/testbox.wlua: -------------------------------------------------------------------------------- 1 | require "CLRForm" 2 | 3 | text = RichTextBox() 4 | text.Multiline = true 5 | text.Text = [[ 6 | here is 7 | a set of lines 8 | for you 9 | ]] 10 | 11 | LuaForm ({text}):ShowDialog() 12 | -------------------------------------------------------------------------------- /samples/testluaform.lua: -------------------------------------------------------------------------------- 1 | require("CLRPackage") 2 | 3 | Forms = CLRPackage("System.Windows.Forms", "System.Windows.Forms") 4 | Drawing = CLRPackage("System.Drawing", "System.Drawing") 5 | LuaInterface = CLRPackage("LuaInterface", "LuaInterface") 6 | IO = CLRPackage("System.IO", "System.IO") 7 | System = CLRPackage("System", "System") 8 | 9 | Form=Forms.Form 10 | TextBox=Forms.TextBox 11 | Label=Forms.Label 12 | ListBox=Forms.ListBox 13 | Button=Forms.Button 14 | Point=Drawing.Point 15 | Size=Drawing.Size 16 | Lua=LuaInterface.Lua 17 | OpenFileDialog=Forms.OpenFileDialog 18 | File=IO.File 19 | StreamReader=IO.StreamReader 20 | FileMode=IO.FileMode 21 | ScrollBars=Forms.ScrollBars 22 | FormBorderStyle=Forms.FormBorderStyle 23 | FormStartPosition=Forms.FormStartPosition 24 | 25 | function clear_click(sender,args) 26 | code:Clear() 27 | end 28 | 29 | function execute_click(sender,args) 30 | results.Items:Clear() 31 | result=lua:DoString(code.Text) 32 | if result then 33 | for i=0,result.Length-1 do 34 | results.Items:Add(result[i]) 35 | end 36 | end 37 | end 38 | 39 | function load_click(sender,args) 40 | open_file:ShowDialog() 41 | file=StreamReader(open_file.FileName) 42 | code.Text=file:ReadToEnd() 43 | file:Close() 44 | end 45 | 46 | form = Form() 47 | code = TextBox() 48 | label1 = Label() 49 | execute = Button() 50 | clear = Button() 51 | results = ListBox() 52 | label2 = Label() 53 | load = Button() 54 | lua = Lua() 55 | --lua:OpenBaseLib() -- steffenj: Open*Lib() functions no longer exist 56 | open_file = OpenFileDialog() 57 | 58 | form:SuspendLayout() 59 | 60 | code.Location = Point(16, 24) 61 | code.Multiline = true 62 | code.Name = "Code" 63 | code.Size = Size(440, 128) 64 | code.ScrollBars = ScrollBars.Vertical 65 | code.TabIndex = 0 66 | code.Text = "" 67 | 68 | label1.Location = Point(16, 8) 69 | label1.Name = "label1" 70 | label1.Size = Size(100, 16) 71 | label1.TabIndex = 1 72 | label1.Text = "Lua Code:" 73 | 74 | execute.Location = Point(96, 160) 75 | execute.Name = "Execute" 76 | execute.TabIndex = 2 77 | execute.Text = "Execute" 78 | execute.Click:Add(execute_click) 79 | 80 | clear.Location = Point(176, 160) 81 | clear.Name = "Clear" 82 | clear.TabIndex = 3 83 | clear.Text = "Clear" 84 | clear.Click:Add(clear_click) 85 | 86 | results.Location = Point(16, 208) 87 | results.Name = "Results" 88 | results.Size = Size(440, 95) 89 | results.TabIndex = 4 90 | 91 | label2.Location = Point(16, 192) 92 | label2.Name = "label2" 93 | label2.Size = Size(100, 16) 94 | label2.TabIndex = 5 95 | label2.Text = "Results:" 96 | 97 | load.Location = Point(16, 160) 98 | load.Name = "Load" 99 | load.TabIndex = 6 100 | load.Text = "Load..." 101 | load.Click:Add(load_click) 102 | 103 | open_file.DefaultExt = "lua" 104 | open_file.Filter = "Lua Scripts|*.lua|All Files|*.*" 105 | open_file.Title = "Pick a File" 106 | 107 | form.AutoScaleBaseSize = Size(5, 13) 108 | form.ClientSize = Size(472, 315) 109 | form.Controls:Add(load) 110 | form.Controls:Add(label2) 111 | form.Controls:Add(results) 112 | form.Controls:Add(clear) 113 | form.Controls:Add(execute) 114 | form.Controls:Add(label1) 115 | form.Controls:Add(code) 116 | form.Name = "MainForm" 117 | form.Text = "LuaNet" 118 | form.FormBorderStyle = FormBorderStyle.Fixed3D 119 | form.StartPosition = FormStartPosition.CenterScreen 120 | form:ResumeLayout(false) 121 | 122 | form:ShowDialog() 123 | -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Security.Permissions; 5 | 6 | // 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | // 11 | [assembly: AssemblyTitle("LuaInterface")] 12 | [assembly: AssemblyDescription("Bridge between the Lua runtime and the CLR")] 13 | [assembly: AssemblyCopyright("Copyright 2003-2008 Fabio Mascarenhas, Kevin Hester")] 14 | [assembly: CLSCompliant(false)] 15 | 16 | // 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Revision and Build Numbers 25 | // by using the '*' as shown below: 26 | 27 | [assembly: AssemblyVersion("2.0.4.*")] 28 | 29 | // 30 | // In order to sign your assembly you must specify a key to use. Refer to the 31 | // Microsoft .NET Framework documentation for more information on assembly signing. 32 | // 33 | // Use the attributes below to control which key is used for signing. 34 | // 35 | // Notes: 36 | // (*) If no key is specified, the assembly is not signed. 37 | // (*) KeyName refers to a key that has been installed in the Crypto Service 38 | // Provider (CSP) on your machine. KeyFile refers to a file which contains 39 | // a key. 40 | // (*) If the KeyFile and the KeyName values are both specified, the 41 | // following processing occurs: 42 | // (1) If the KeyName can be found in the CSP, that key is used. 43 | // (2) If the KeyName does not exist and the KeyFile does exist, the key 44 | // in the KeyFile is installed into the CSP and used. 45 | // (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. 46 | // When specifying the KeyFile, the location of the KeyFile should be 47 | // relative to the project output directory which is 48 | // %Project Directory%\obj\. For example, if your KeyFile is 49 | // located in the project directory, you would specify the AssemblyKeyFile 50 | // attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] 51 | // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework 52 | // documentation for more information on this. 53 | // 54 | 55 | // We call native DLLs (lua50) and we don't want to be fucked with or validated 56 | // [assembly: SecurityPermission(RequestMinimum, UnmanagedCode = true)] 57 | -------------------------------------------------------------------------------- /src/CheckType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace LuaInterface 6 | { 7 | /* 8 | * Type checking and conversion functions. 9 | * 10 | * Author: Fabio Mascarenhas 11 | * Version: 1.0 12 | */ 13 | class CheckType 14 | { 15 | private ObjectTranslator translator; 16 | 17 | ExtractValue extractNetObject; 18 | Dictionary extractValues = new Dictionary(); 19 | 20 | public CheckType(ObjectTranslator translator) 21 | { 22 | this.translator = translator; 23 | 24 | extractValues.Add(typeof(object).TypeHandle.Value.ToInt64(), new ExtractValue(getAsObject)); 25 | extractValues.Add(typeof(sbyte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsSbyte)); 26 | extractValues.Add(typeof(byte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsByte)); 27 | extractValues.Add(typeof(short).TypeHandle.Value.ToInt64(), new ExtractValue(getAsShort)); 28 | extractValues.Add(typeof(ushort).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUshort)); 29 | extractValues.Add(typeof(int).TypeHandle.Value.ToInt64(), new ExtractValue(getAsInt)); 30 | extractValues.Add(typeof(uint).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUint)); 31 | extractValues.Add(typeof(long).TypeHandle.Value.ToInt64(), new ExtractValue(getAsLong)); 32 | extractValues.Add(typeof(ulong).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUlong)); 33 | extractValues.Add(typeof(double).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDouble)); 34 | extractValues.Add(typeof(char).TypeHandle.Value.ToInt64(), new ExtractValue(getAsChar)); 35 | extractValues.Add(typeof(float).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFloat)); 36 | extractValues.Add(typeof(decimal).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDecimal)); 37 | extractValues.Add(typeof(bool).TypeHandle.Value.ToInt64(), new ExtractValue(getAsBoolean)); 38 | extractValues.Add(typeof(string).TypeHandle.Value.ToInt64(), new ExtractValue(getAsString)); 39 | extractValues.Add(typeof(LuaFunction).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFunction)); 40 | extractValues.Add(typeof(LuaTable).TypeHandle.Value.ToInt64(), new ExtractValue(getAsTable)); 41 | extractValues.Add(typeof(LuaUserData).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUserdata)); 42 | 43 | extractNetObject = new ExtractValue(getAsNetObject); 44 | } 45 | 46 | /* 47 | * Checks if the value at Lua stack index stackPos matches paramType, 48 | * returning a conversion function if it does and null otherwise. 49 | */ 50 | internal ExtractValue getExtractor(IReflect paramType) 51 | { 52 | return getExtractor(paramType.UnderlyingSystemType); 53 | } 54 | internal ExtractValue getExtractor(Type paramType) 55 | { 56 | if(paramType.IsByRef) paramType=paramType.GetElementType(); 57 | 58 | long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64(); 59 | 60 | if(extractValues.ContainsKey(runtimeHandleValue)) 61 | return extractValues[runtimeHandleValue]; 62 | else 63 | return extractNetObject; 64 | } 65 | 66 | internal ExtractValue checkType(IntPtr luaState,int stackPos,Type paramType) 67 | { 68 | LuaTypes luatype = LuaDLL.lua_type(luaState, stackPos); 69 | 70 | if(paramType.IsByRef) paramType=paramType.GetElementType(); 71 | 72 | Type underlyingType = Nullable.GetUnderlyingType(paramType); 73 | if (underlyingType != null) 74 | { 75 | paramType = underlyingType; // Silently convert nullable types to their non null requics 76 | } 77 | 78 | long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64(); 79 | 80 | if (paramType.Equals(typeof(object))) 81 | return extractValues[runtimeHandleValue]; 82 | 83 | //CP: Added support for generic parameters 84 | if (paramType.IsGenericParameter) 85 | { 86 | if (luatype == LuaTypes.LUA_TBOOLEAN) 87 | return extractValues[typeof(bool).TypeHandle.Value.ToInt64()]; 88 | else if (luatype == LuaTypes.LUA_TSTRING) 89 | return extractValues[typeof(string).TypeHandle.Value.ToInt64()]; 90 | else if (luatype == LuaTypes.LUA_TTABLE) 91 | return extractValues[typeof(LuaTable).TypeHandle.Value.ToInt64()]; 92 | else if (luatype == LuaTypes.LUA_TUSERDATA) 93 | return extractValues[typeof(object).TypeHandle.Value.ToInt64()]; 94 | else if (luatype == LuaTypes.LUA_TFUNCTION) 95 | return extractValues[typeof(LuaFunction).TypeHandle.Value.ToInt64()]; 96 | else if (luatype == LuaTypes.LUA_TNUMBER) 97 | return extractValues[typeof(double).TypeHandle.Value.ToInt64()]; 98 | //else // suppress CS0642 99 | ;//an unsupported type was encountered 100 | } 101 | 102 | if (LuaDLL.lua_isnumber(luaState, stackPos)) 103 | return extractValues[runtimeHandleValue]; 104 | 105 | if (paramType == typeof(bool)) 106 | { 107 | if (LuaDLL.lua_isboolean(luaState, stackPos)) 108 | return extractValues[runtimeHandleValue]; 109 | } 110 | else if (paramType == typeof(string)) 111 | { 112 | if (LuaDLL.lua_isstring(luaState, stackPos)) 113 | return extractValues[runtimeHandleValue]; 114 | else if (luatype == LuaTypes.LUA_TNIL) 115 | return extractNetObject; // kevinh - silently convert nil to a null string pointer 116 | } 117 | else if (paramType == typeof(LuaTable)) 118 | { 119 | if (luatype == LuaTypes.LUA_TTABLE) 120 | return extractValues[runtimeHandleValue]; 121 | } 122 | else if (paramType == typeof(LuaUserData)) 123 | { 124 | if (luatype == LuaTypes.LUA_TUSERDATA) 125 | return extractValues[runtimeHandleValue]; 126 | } 127 | else if (paramType == typeof(LuaFunction)) 128 | { 129 | if (luatype == LuaTypes.LUA_TFUNCTION) 130 | return extractValues[runtimeHandleValue]; 131 | } 132 | else if (typeof(Delegate).IsAssignableFrom(paramType) && luatype == LuaTypes.LUA_TFUNCTION) 133 | { 134 | #if __NOGEN__ 135 | translator.throwError(luaState,"Delegates not implemnented"); 136 | #else 137 | return new ExtractValue(new DelegateGenerator(translator, paramType).extractGenerated); 138 | #endif 139 | } 140 | else if (paramType.IsInterface && luatype == LuaTypes.LUA_TTABLE) 141 | { 142 | #if __NOGEN__ 143 | translator.throwError(luaState,"Interfaces not implemnented"); 144 | #else 145 | return new ExtractValue(new ClassGenerator(translator, paramType).extractGenerated); 146 | #endif 147 | } 148 | else if ((paramType.IsInterface || paramType.IsClass) && luatype == LuaTypes.LUA_TNIL) 149 | { 150 | // kevinh - allow nil to be silently converted to null - extractNetObject will return null when the item ain't found 151 | return extractNetObject; 152 | } 153 | else if (LuaDLL.lua_type(luaState, stackPos) == LuaTypes.LUA_TTABLE) 154 | { 155 | if (LuaDLL.luaL_getmetafield(luaState, stackPos, "__index")) 156 | { 157 | object obj = translator.getNetObject(luaState, -1); 158 | LuaDLL.lua_settop(luaState, -2); 159 | if (obj != null && paramType.IsAssignableFrom(obj.GetType())) 160 | return extractNetObject; 161 | } 162 | else 163 | return null; 164 | } 165 | else 166 | { 167 | object obj = translator.getNetObject(luaState, stackPos); 168 | if (obj != null && paramType.IsAssignableFrom(obj.GetType())) 169 | return extractNetObject; 170 | } 171 | 172 | return null; 173 | } 174 | 175 | /* 176 | * The following functions return the value in the Lua stack 177 | * index stackPos as the desired type if it can, or null 178 | * otherwise. 179 | */ 180 | private object getAsSbyte(IntPtr luaState,int stackPos) 181 | { 182 | sbyte retVal=(sbyte)LuaDLL.lua_tonumber(luaState,stackPos); 183 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 184 | return retVal; 185 | } 186 | private object getAsByte(IntPtr luaState,int stackPos) 187 | { 188 | byte retVal=(byte)LuaDLL.lua_tonumber(luaState,stackPos); 189 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 190 | return retVal; 191 | } 192 | private object getAsShort(IntPtr luaState,int stackPos) 193 | { 194 | short retVal=(short)LuaDLL.lua_tonumber(luaState,stackPos); 195 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 196 | return retVal; 197 | } 198 | private object getAsUshort(IntPtr luaState,int stackPos) 199 | { 200 | ushort retVal=(ushort)LuaDLL.lua_tonumber(luaState,stackPos); 201 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 202 | return retVal; 203 | } 204 | private object getAsInt(IntPtr luaState,int stackPos) 205 | { 206 | int retVal=(int)LuaDLL.lua_tonumber(luaState,stackPos); 207 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 208 | return retVal; 209 | } 210 | private object getAsUint(IntPtr luaState,int stackPos) 211 | { 212 | uint retVal=(uint)LuaDLL.lua_tonumber(luaState,stackPos); 213 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 214 | return retVal; 215 | } 216 | private object getAsLong(IntPtr luaState,int stackPos) 217 | { 218 | long retVal=(long)LuaDLL.lua_tonumber(luaState,stackPos); 219 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 220 | return retVal; 221 | } 222 | private object getAsUlong(IntPtr luaState,int stackPos) 223 | { 224 | ulong retVal=(ulong)LuaDLL.lua_tonumber(luaState,stackPos); 225 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 226 | return retVal; 227 | } 228 | private object getAsDouble(IntPtr luaState,int stackPos) 229 | { 230 | double retVal=LuaDLL.lua_tonumber(luaState,stackPos); 231 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 232 | return retVal; 233 | } 234 | private object getAsChar(IntPtr luaState,int stackPos) 235 | { 236 | char retVal=(char)LuaDLL.lua_tonumber(luaState,stackPos); 237 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 238 | return retVal; 239 | } 240 | private object getAsFloat(IntPtr luaState,int stackPos) 241 | { 242 | float retVal=(float)LuaDLL.lua_tonumber(luaState,stackPos); 243 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 244 | return retVal; 245 | } 246 | private object getAsDecimal(IntPtr luaState,int stackPos) 247 | { 248 | decimal retVal=(decimal)LuaDLL.lua_tonumber(luaState,stackPos); 249 | if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null; 250 | return retVal; 251 | } 252 | private object getAsBoolean(IntPtr luaState,int stackPos) 253 | { 254 | return LuaDLL.lua_toboolean(luaState,stackPos); 255 | } 256 | private object getAsString(IntPtr luaState,int stackPos) 257 | { 258 | string retVal=LuaDLL.lua_tostring(luaState,stackPos); 259 | if(retVal=="" && !LuaDLL.lua_isstring(luaState,stackPos)) return null; 260 | return retVal; 261 | } 262 | private object getAsTable(IntPtr luaState,int stackPos) 263 | { 264 | return translator.getTable(luaState,stackPos); 265 | } 266 | private object getAsFunction(IntPtr luaState,int stackPos) 267 | { 268 | return translator.getFunction(luaState,stackPos); 269 | } 270 | private object getAsUserdata(IntPtr luaState,int stackPos) 271 | { 272 | return translator.getUserData(luaState,stackPos); 273 | } 274 | public object getAsObject(IntPtr luaState,int stackPos) 275 | { 276 | if(LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE) 277 | { 278 | if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index")) 279 | { 280 | if(LuaDLL.luaL_checkmetatable(luaState,-1)) 281 | { 282 | LuaDLL.lua_insert(luaState,stackPos); 283 | LuaDLL.lua_remove(luaState,stackPos+1); 284 | } 285 | else 286 | { 287 | LuaDLL.lua_settop(luaState,-2); 288 | } 289 | } 290 | } 291 | object obj=translator.getObject(luaState,stackPos); 292 | return obj; 293 | } 294 | public object getAsNetObject(IntPtr luaState,int stackPos) 295 | { 296 | object obj=translator.getNetObject(luaState,stackPos); 297 | if(obj==null && LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE) 298 | { 299 | if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index")) 300 | { 301 | if(LuaDLL.luaL_checkmetatable(luaState,-1)) 302 | { 303 | LuaDLL.lua_insert(luaState,stackPos); 304 | LuaDLL.lua_remove(luaState,stackPos+1); 305 | obj=translator.getNetObject(luaState,stackPos); 306 | } 307 | else 308 | { 309 | LuaDLL.lua_settop(luaState,-2); 310 | } 311 | } 312 | } 313 | return obj; 314 | } 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/LuaBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuaInterface 6 | { 7 | /// 8 | /// Base class to provide consistent disposal flow across lua objects. Uses code provided by Yves Duhoux and suggestions by Hans Schmeidenbacher and Qingrui Li 9 | /// 10 | public abstract class LuaBase : IDisposable 11 | { 12 | private bool _Disposed; 13 | protected int _Reference; 14 | protected Lua _Interpreter; 15 | 16 | ~LuaBase() 17 | { 18 | Dispose(false); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | Dispose(true); 24 | GC.SuppressFinalize(this); 25 | } 26 | 27 | public virtual void Dispose(bool disposeManagedResources) 28 | { 29 | if (!_Disposed) 30 | { 31 | if (disposeManagedResources) 32 | { 33 | if (_Reference != 0) 34 | _Interpreter.dispose(_Reference); 35 | } 36 | _Interpreter = null; 37 | _Disposed = true; 38 | } 39 | } 40 | 41 | public override bool Equals(object o) 42 | { 43 | if (o is LuaBase) 44 | { 45 | LuaBase l = (LuaBase)o; 46 | return _Interpreter.compareRef(l._Reference, _Reference); 47 | } 48 | else return false; 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return _Reference; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/LuaException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace LuaInterface 5 | { 6 | /// 7 | /// Exceptions thrown by the Lua runtime 8 | /// 9 | [Serializable] 10 | public class LuaException : Exception 11 | { 12 | public LuaException() 13 | {} 14 | 15 | public LuaException(string message) : base(message) 16 | {} 17 | 18 | public LuaException(string message, Exception innerException) : base(message, innerException) 19 | {} 20 | 21 | protected LuaException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | {} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/LuaFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuaInterface 6 | { 7 | public class LuaFunction : LuaBase 8 | { 9 | //private Lua interpreter; 10 | internal LuaCSFunction function; 11 | //internal int reference; 12 | 13 | public LuaFunction(int reference, Lua interpreter) 14 | { 15 | _Reference = reference; 16 | this.function = null; 17 | _Interpreter = interpreter; 18 | } 19 | 20 | public LuaFunction(LuaCSFunction function, Lua interpreter) 21 | { 22 | _Reference = 0; 23 | this.function = function; 24 | _Interpreter = interpreter; 25 | } 26 | 27 | //~LuaFunction() 28 | //{ 29 | // if (reference != 0) 30 | // interpreter.dispose(reference); 31 | //} 32 | 33 | //bool disposed = false; 34 | //~LuaFunction() 35 | //{ 36 | // Dispose(false); 37 | //} 38 | 39 | //public void Dispose() 40 | //{ 41 | // Dispose(true); 42 | // GC.SuppressFinalize(this); 43 | //} 44 | 45 | //public virtual void Dispose(bool disposeManagedResources) 46 | //{ 47 | // if (!this.disposed) 48 | // { 49 | // if (disposeManagedResources) 50 | // { 51 | // if (_Reference != 0) 52 | // _Interpreter.dispose(_Reference); 53 | // } 54 | 55 | // disposed = true; 56 | // } 57 | //} 58 | 59 | 60 | /* 61 | * Calls the function casting return values to the types 62 | * in returnTypes 63 | */ 64 | internal object[] call(object[] args, Type[] returnTypes) 65 | { 66 | return _Interpreter.callFunction(this, args, returnTypes); 67 | } 68 | /* 69 | * Calls the function and returns its return values inside 70 | * an array 71 | */ 72 | public object[] Call(params object[] args) 73 | { 74 | return _Interpreter.callFunction(this, args); 75 | } 76 | /* 77 | * Pushes the function into the Lua stack 78 | */ 79 | internal void push(IntPtr luaState) 80 | { 81 | if (_Reference != 0) 82 | LuaDLL.lua_getref(luaState, _Reference); 83 | else 84 | _Interpreter.pushCSFunction(function); 85 | } 86 | public override string ToString() 87 | { 88 | return "function"; 89 | } 90 | public override bool Equals(object o) 91 | { 92 | if (o is LuaFunction) 93 | { 94 | LuaFunction l = (LuaFunction)o; 95 | if (this._Reference != 0 && l._Reference != 0) 96 | return _Interpreter.compareRef(l._Reference, this._Reference); 97 | else 98 | return this.function == l.function; 99 | } 100 | else return false; 101 | } 102 | public override int GetHashCode() 103 | { 104 | if (_Reference != 0) 105 | return _Reference; 106 | else 107 | return function.GetHashCode(); 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/LuaGlobalAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuaInterface 4 | { 5 | /// 6 | /// Marks a method for global usage in Lua scripts 7 | /// 8 | /// 9 | /// 10 | [AttributeUsage(AttributeTargets.Method)] 11 | // sealed 12 | public class LuaGlobalAttribute : Attribute 13 | { 14 | private string name,descript; 15 | /// 16 | /// An alternative name to use for calling the function in Lua - leave empty for CLR name 17 | /// 18 | public string Name { get { return name; } set { name = value; }} 19 | 20 | /// 21 | /// A description of the function 22 | /// 23 | public string Description { get { return descript; } set { descript = value; }} 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/LuaHideAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuaInterface 4 | { 5 | /// 6 | /// Marks a method, field or property to be hidden from Lua auto-completion 7 | /// 8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 9 | public sealed class LuaHideAttribute : Attribute 10 | {} 11 | } 12 | -------------------------------------------------------------------------------- /src/LuaNetRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using LuaInterface; 3 | using System.Threading; 4 | 5 | /* 6 | * Application to run Lua scripts that can use LuaInterface 7 | * from the console 8 | * 9 | * Author: Fabio Mascarenhas 10 | * Version: 1.0 11 | */ 12 | namespace LuaRunner 13 | { 14 | public class LuaNetRunner 15 | { 16 | /* 17 | * Runs the Lua script passed as the first command-line argument. 18 | * It passed all the command-line arguments to the script. 19 | */ 20 | [STAThread] // steffenj: testluaform.lua "Load" button complained with an exception that STAThread was missing 21 | public static void Main(string[] args) 22 | { 23 | if(args.Length > 0) 24 | { 25 | // For attaching from the debugger 26 | // Thread.Sleep(20000); 27 | 28 | using (Lua lua = new Lua()) 29 | { 30 | //lua.OpenLibs(); // steffenj: Lua 5.1.1 API change (all libs already opened in Lua constructor!) 31 | lua.NewTable("arg"); 32 | LuaTable argc = (LuaTable)lua["arg"]; 33 | argc[-1] = "LuaRunner"; 34 | argc[0] = args[0]; 35 | for (int i = 1; i < args.Length; i++) 36 | { 37 | argc[i] = args[i]; 38 | } 39 | argc["n"] = args.Length - 1; 40 | 41 | try 42 | { 43 | //Console.WriteLine("DoFile(" + args[0] + ");"); 44 | lua.DoFile(args[0]); 45 | } 46 | catch (Exception e) 47 | { 48 | // steffenj: BEGIN error message improved, output is now in decending order of importance (message, where, stacktrace) 49 | // limit size of strack traceback message to roughly 1 console screen height 50 | string trace = e.StackTrace; 51 | if (e.StackTrace.Length > 1300) 52 | trace = e.StackTrace.Substring(0, 1300) + " [...] (traceback cut short)"; 53 | 54 | // sjd: make the error message more like standard Lua messages 55 | Console.WriteLine(e.Source + " " + e.Message); 56 | Console.WriteLine("raised a " + e.GetType().ToString()); 57 | Console.WriteLine(trace); 58 | 59 | // wait for keypress if there is an error 60 | Console.ReadKey(); 61 | // steffenj: END error message improved 62 | } 63 | } 64 | } 65 | else 66 | { 67 | Console.WriteLine("LuaRunner -- runs Lua scripts with CLR access"); 68 | Console.WriteLine("Usage: luarunner [{}]"); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/LuaRegistrationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Reflection; 4 | 5 | namespace LuaInterface 6 | { 7 | public static class LuaRegistrationHelper 8 | { 9 | #region Tagged instance methods 10 | /// 11 | /// Registers all public instance methods in an object tagged with as Lua global functions 12 | /// 13 | /// The Lua VM to add the methods to 14 | /// The object to get the methods from 15 | public static void TaggedInstanceMethods(Lua lua, object o) 16 | { 17 | #region Sanity checks 18 | if (lua == null) throw new ArgumentNullException("lua"); 19 | if (o == null) throw new ArgumentNullException("o"); 20 | #endregion 21 | 22 | foreach (MethodInfo method in o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public)) 23 | { 24 | foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), true)) 25 | { 26 | if (string.IsNullOrEmpty(attribute.Name)) 27 | lua.RegisterFunction(method.Name, o, method); // CLR name 28 | else 29 | lua.RegisterFunction(attribute.Name, o, method); // Custom name 30 | } 31 | } 32 | } 33 | #endregion 34 | 35 | #region Tagged static methods 36 | /// 37 | /// Registers all public static methods in a class tagged with as Lua global functions 38 | /// 39 | /// The Lua VM to add the methods to 40 | /// The class type to get the methods from 41 | public static void TaggedStaticMethods(Lua lua, Type type) 42 | { 43 | #region Sanity checks 44 | if (lua == null) throw new ArgumentNullException("lua"); 45 | if (type == null) throw new ArgumentNullException("type"); 46 | if (!type.IsClass) throw new ArgumentException("The type must be a class!", "type"); 47 | #endregion 48 | 49 | foreach (MethodInfo method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) 50 | { 51 | foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), false)) 52 | { 53 | if (string.IsNullOrEmpty(attribute.Name)) 54 | lua.RegisterFunction(method.Name, null, method); // CLR name 55 | else 56 | lua.RegisterFunction(attribute.Name, null, method); // Custom name 57 | } 58 | } 59 | } 60 | #endregion 61 | 62 | #region Enumeration 63 | /// 64 | /// Registers an enumeration's values for usage as a Lua variable table 65 | /// 66 | /// The enum type to register 67 | /// The Lua VM to add the enum to 68 | [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The type parameter is used to select an enum type")] 69 | public static void Enumeration(Lua lua) 70 | { 71 | #region Sanity checks 72 | if (lua == null) throw new ArgumentNullException("lua"); 73 | #endregion 74 | 75 | Type type = typeof(T); 76 | if (!type.IsEnum) throw new ArgumentException("The type must be an enumeration!"); 77 | 78 | string[] names = Enum.GetNames(type); 79 | T[] values = (T[])Enum.GetValues(type); 80 | 81 | lua.NewTable(type.Name); 82 | for (int i = 0; i < names.Length; i++) 83 | { 84 | string path = type.Name + "." + names[i]; 85 | lua[path] = values[i]; 86 | } 87 | } 88 | #endregion 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/LuaScriptException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LuaInterface 4 | { 5 | /// 6 | /// Exceptions thrown by the Lua runtime because of errors in the script 7 | /// 8 | public class LuaScriptException : LuaException 9 | { 10 | private bool isNet; 11 | /// 12 | /// Returns true if the exception has occured as the result of a .NET exception in user code 13 | /// 14 | public bool IsNetException { 15 | get { return isNet; } 16 | set { isNet = value; } 17 | } 18 | 19 | private readonly string source; 20 | 21 | /// 22 | /// The position in the script where the exception was triggered. 23 | /// 24 | public override string Source { get { return source; } } 25 | 26 | /// 27 | /// Creates a new Lua-only exception. 28 | /// 29 | /// The message that describes the error. 30 | /// The position in the script where the exception was triggered. 31 | public LuaScriptException(string message, string source) : base(message) 32 | { 33 | this.source = source; 34 | } 35 | 36 | /// 37 | /// Creates a new .NET wrapping exception. 38 | /// 39 | /// The .NET exception triggered by user-code. 40 | /// The position in the script where the exception was triggered. 41 | public LuaScriptException(Exception innerException, string source) 42 | : base(innerException.Message, innerException) 43 | { 44 | this.source = source; 45 | this.IsNetException = true; 46 | } 47 | 48 | public override string ToString() 49 | { 50 | // Prepend the error source 51 | return GetType().FullName + ": " + source + Message; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LuaTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Collections; 5 | 6 | namespace LuaInterface 7 | { 8 | /* 9 | * Wrapper class for Lua tables 10 | * 11 | * Author: Fabio Mascarenhas 12 | * Version: 1.0 13 | */ 14 | public class LuaTable : LuaBase 15 | { 16 | //internal int _Reference; 17 | //private Lua _Interpreter; 18 | public LuaTable(int reference, Lua interpreter) 19 | { 20 | _Reference = reference; 21 | _Interpreter = interpreter; 22 | } 23 | 24 | //bool disposed = false; 25 | //~LuaTable() 26 | //{ 27 | // Dispose(false); 28 | //} 29 | 30 | //public void Dispose() 31 | //{ 32 | // Dispose(true); 33 | // GC.SuppressFinalize(this); 34 | //} 35 | 36 | //public virtual void Dispose(bool disposeManagedResources) 37 | //{ 38 | // if (!this.disposed) 39 | // { 40 | // if (disposeManagedResources) 41 | // { 42 | // if (_Reference != 0) 43 | // _Interpreter.dispose(_Reference); 44 | // } 45 | 46 | // disposed = true; 47 | // } 48 | //} 49 | //~LuaTable() 50 | //{ 51 | // _Interpreter.dispose(_Reference); 52 | //} 53 | /* 54 | * Indexer for string fields of the table 55 | */ 56 | public object this[string field] 57 | { 58 | get 59 | { 60 | return _Interpreter.getObject(_Reference, field); 61 | } 62 | set 63 | { 64 | _Interpreter.setObject(_Reference, field, value); 65 | } 66 | } 67 | /* 68 | * Indexer for numeric fields of the table 69 | */ 70 | public object this[object field] 71 | { 72 | get 73 | { 74 | return _Interpreter.getObject(_Reference, field); 75 | } 76 | set 77 | { 78 | _Interpreter.setObject(_Reference, field, value); 79 | } 80 | } 81 | 82 | 83 | public System.Collections.IDictionaryEnumerator GetEnumerator() 84 | { 85 | return _Interpreter.GetTableDict(this).GetEnumerator(); 86 | } 87 | 88 | public ICollection Keys 89 | { 90 | get { return _Interpreter.GetTableDict(this).Keys; } 91 | } 92 | 93 | public ICollection Values 94 | { 95 | get { return _Interpreter.GetTableDict(this).Values; } 96 | } 97 | 98 | /* 99 | * Gets an string fields of a table ignoring its metatable, 100 | * if it exists 101 | */ 102 | internal object rawget(string field) 103 | { 104 | return _Interpreter.rawGetObject(_Reference, field); 105 | } 106 | 107 | internal object rawgetFunction(string field) 108 | { 109 | object obj = _Interpreter.rawGetObject(_Reference, field); 110 | 111 | if (obj is LuaCSFunction) 112 | return new LuaFunction((LuaCSFunction)obj, _Interpreter); 113 | else 114 | return obj; 115 | } 116 | 117 | /* 118 | * Pushes this table into the Lua stack 119 | */ 120 | internal void push(IntPtr luaState) 121 | { 122 | LuaDLL.lua_getref(luaState, _Reference); 123 | } 124 | public override string ToString() 125 | { 126 | return "table"; 127 | } 128 | //public override bool Equals(object o) 129 | //{ 130 | // if (o is LuaTable) 131 | // { 132 | // LuaTable l = (LuaTable)o; 133 | // return _Interpreter.compareRef(l._Reference, _Reference); 134 | // } 135 | // else return false; 136 | //} 137 | //public override int GetHashCode() 138 | //{ 139 | // return _Reference; 140 | //} 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/LuaUserData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuaInterface 6 | { 7 | public class LuaUserData : LuaBase 8 | { 9 | //internal int _Reference; 10 | //private Lua _Interpreter; 11 | public LuaUserData(int reference, Lua interpreter) 12 | { 13 | _Reference = reference; 14 | _Interpreter = interpreter; 15 | } 16 | //~LuaUserData() 17 | //{ 18 | // if (_Reference != 0) 19 | // _Interpreter.dispose(_Reference); 20 | //} 21 | /* 22 | * Indexer for string fields of the userdata 23 | */ 24 | public object this[string field] 25 | { 26 | get 27 | { 28 | return _Interpreter.getObject(_Reference, field); 29 | } 30 | set 31 | { 32 | _Interpreter.setObject(_Reference, field, value); 33 | } 34 | } 35 | /* 36 | * Indexer for numeric fields of the userdata 37 | */ 38 | public object this[object field] 39 | { 40 | get 41 | { 42 | return _Interpreter.getObject(_Reference, field); 43 | } 44 | set 45 | { 46 | _Interpreter.setObject(_Reference, field, value); 47 | } 48 | } 49 | /* 50 | * Calls the userdata and returns its return values inside 51 | * an array 52 | */ 53 | public object[] Call(params object[] args) 54 | { 55 | return _Interpreter.callFunction(this, args); 56 | } 57 | /* 58 | * Pushes the userdata into the Lua stack 59 | */ 60 | internal void push(IntPtr luaState) 61 | { 62 | LuaDLL.lua_getref(luaState, _Reference); 63 | } 64 | public override string ToString() 65 | { 66 | return "userdata"; 67 | } 68 | //public override bool Equals(object o) 69 | //{ 70 | // if (o is LuaUserData) 71 | // { 72 | // LuaUserData l = (LuaUserData)o; 73 | // return _Interpreter.compareRef(l._Reference, _Reference); 74 | // } 75 | // else return false; 76 | //} 77 | //public override int GetHashCode() 78 | //{ 79 | // return _Reference; 80 | //} 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/MethodWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace LuaInterface 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Collections; 6 | using System.Reflection; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | 10 | /* 11 | * Cached method 12 | */ 13 | struct MethodCache 14 | { 15 | private MethodBase _cachedMethod; 16 | 17 | public MethodBase cachedMethod 18 | { 19 | get 20 | { 21 | return _cachedMethod; 22 | } 23 | set 24 | { 25 | _cachedMethod = value; 26 | MethodInfo mi = value as MethodInfo; 27 | if (mi != null) 28 | { 29 | //SJD this is guaranteed to be correct irrespective of actual name used for type.. 30 | IsReturnVoid = mi.ReturnType == typeof(void); 31 | } 32 | } 33 | } 34 | 35 | public bool IsReturnVoid; 36 | 37 | // List or arguments 38 | public object[] args; 39 | // Positions of out parameters 40 | public int[] outList; 41 | // Types of parameters 42 | public MethodArgs[] argTypes; 43 | } 44 | 45 | /* 46 | * Parameter information 47 | */ 48 | struct MethodArgs 49 | { 50 | // Position of parameter 51 | public int index; 52 | // Type-conversion function 53 | public ExtractValue extractValue; 54 | 55 | public bool isParamsArray; 56 | 57 | public Type paramsArrayType; 58 | } 59 | 60 | /* 61 | * Argument extraction with type-conversion function 62 | */ 63 | delegate object ExtractValue(IntPtr luaState, int stackPos); 64 | 65 | /* 66 | * Wrapper class for methods/constructors accessed from Lua. 67 | * 68 | * Author: Fabio Mascarenhas 69 | * Version: 1.0 70 | */ 71 | class LuaMethodWrapper 72 | { 73 | private ObjectTranslator _Translator; 74 | private MethodBase _Method; 75 | private MethodCache _LastCalledMethod = new MethodCache(); 76 | private string _MethodName; 77 | private MemberInfo[] _Members; 78 | private IReflect _TargetType; 79 | private ExtractValue _ExtractTarget; 80 | private object _Target; 81 | private BindingFlags _BindingType; 82 | 83 | /* 84 | * Constructs the wrapper for a known MethodBase instance 85 | */ 86 | public LuaMethodWrapper(ObjectTranslator translator, object target, IReflect targetType, MethodBase method) 87 | { 88 | _Translator = translator; 89 | _Target = target; 90 | _TargetType = targetType; 91 | if (targetType != null) 92 | _ExtractTarget = translator.typeChecker.getExtractor(targetType); 93 | _Method = method; 94 | _MethodName = method.Name; 95 | 96 | if (method.IsStatic) 97 | { _BindingType = BindingFlags.Static; } 98 | else 99 | { _BindingType = BindingFlags.Instance; } 100 | } 101 | /* 102 | * Constructs the wrapper for a known method name 103 | */ 104 | public LuaMethodWrapper(ObjectTranslator translator, IReflect targetType, string methodName, BindingFlags bindingType) 105 | { 106 | _Translator = translator; 107 | _MethodName = methodName; 108 | _TargetType = targetType; 109 | 110 | if (targetType != null) 111 | _ExtractTarget = translator.typeChecker.getExtractor(targetType); 112 | 113 | _BindingType = bindingType; 114 | 115 | //CP: Removed NonPublic binding search and added IgnoreCase 116 | _Members = targetType.UnderlyingSystemType.GetMember(methodName, MemberTypes.Method, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*|BindingFlags.NonPublic*/); 117 | } 118 | 119 | 120 | /// 121 | /// Convert C# exceptions into Lua errors 122 | /// 123 | /// num of things on stack 124 | /// null for no pending exception 125 | int SetPendingException(Exception e) 126 | { 127 | return _Translator.interpreter.SetPendingException(e); 128 | } 129 | 130 | private static bool IsInteger(double x) { 131 | return Math.Ceiling(x) == x; 132 | } 133 | 134 | 135 | /* 136 | * Calls the method. Receives the arguments from the Lua stack 137 | * and returns values in it. 138 | */ 139 | public int call(IntPtr luaState) 140 | { 141 | MethodBase methodToCall = _Method; 142 | object targetObject = _Target; 143 | bool failedCall = true; 144 | int nReturnValues = 0; 145 | 146 | if (!LuaDLL.lua_checkstack(luaState, 5)) 147 | throw new LuaException("Lua stack overflow"); 148 | 149 | bool isStatic = (_BindingType & BindingFlags.Static) == BindingFlags.Static; 150 | 151 | SetPendingException(null); 152 | 153 | if (methodToCall == null) // Method from name 154 | { 155 | if (isStatic) 156 | targetObject = null; 157 | else 158 | targetObject = _ExtractTarget(luaState, 1); 159 | 160 | //LuaDLL.lua_remove(luaState,1); // Pops the receiver 161 | if (_LastCalledMethod.cachedMethod != null) // Cached? 162 | { 163 | int numStackToSkip = isStatic ? 0 : 1; // If this is an instance invoe we will have an extra arg on the stack for the targetObject 164 | int numArgsPassed = LuaDLL.lua_gettop(luaState) - numStackToSkip; 165 | MethodBase method = _LastCalledMethod.cachedMethod; 166 | 167 | if (numArgsPassed == _LastCalledMethod.argTypes.Length) // No. of args match? 168 | { 169 | if (!LuaDLL.lua_checkstack(luaState, _LastCalledMethod.outList.Length + 6)) 170 | throw new LuaException("Lua stack overflow"); 171 | 172 | object[] args = _LastCalledMethod.args; 173 | 174 | try 175 | { 176 | for (int i = 0; i < _LastCalledMethod.argTypes.Length; i++) 177 | { 178 | MethodArgs type = _LastCalledMethod.argTypes[i]; 179 | object luaParamValue = type.extractValue(luaState, i + 1 + numStackToSkip); 180 | if (_LastCalledMethod.argTypes[i].isParamsArray) 181 | { 182 | args[type.index] = _Translator.tableToArray(luaParamValue,type.paramsArrayType); 183 | } 184 | else 185 | { 186 | args[type.index] = luaParamValue; 187 | } 188 | 189 | if (args[type.index] == null && 190 | !LuaDLL.lua_isnil(luaState, i + 1 + numStackToSkip)) 191 | { 192 | throw new LuaException("argument number " + (i + 1) + " is invalid"); 193 | } 194 | } 195 | if ((_BindingType & BindingFlags.Static) == BindingFlags.Static) 196 | { 197 | _Translator.push(luaState, method.Invoke(null, args)); 198 | } 199 | else 200 | { 201 | if (_LastCalledMethod.cachedMethod.IsConstructor) 202 | _Translator.push(luaState, ((ConstructorInfo)method).Invoke(args)); 203 | else 204 | _Translator.push(luaState, method.Invoke(targetObject,args)); 205 | } 206 | failedCall = false; 207 | } 208 | catch (TargetInvocationException e) 209 | { 210 | // Failure of method invocation 211 | return SetPendingException(e.GetBaseException()); 212 | } 213 | catch (Exception e) 214 | { 215 | if (_Members.Length == 1) // Is the method overloaded? 216 | // No, throw error 217 | return SetPendingException(e); 218 | } 219 | } 220 | } 221 | 222 | // Cache miss 223 | if (failedCall) 224 | { 225 | // System.Diagnostics.Debug.WriteLine("cache miss on " + methodName); 226 | 227 | // If we are running an instance variable, we can now pop the targetObject from the stack 228 | if (!isStatic) 229 | { 230 | if (targetObject == null) 231 | { 232 | _Translator.throwError(luaState, String.Format("instance method '{0}' requires a non null target object", _MethodName)); 233 | LuaDLL.lua_pushnil(luaState); 234 | return 1; 235 | } 236 | 237 | LuaDLL.lua_remove(luaState, 1); // Pops the receiver 238 | } 239 | 240 | bool hasMatch = false; 241 | string candidateName = null; 242 | 243 | foreach (MemberInfo member in _Members) 244 | { 245 | candidateName = member.ReflectedType.Name + "." + member.Name; 246 | 247 | MethodBase m = (MethodInfo)member; 248 | 249 | bool isMethod = _Translator.matchParameters(luaState, m, ref _LastCalledMethod); 250 | if (isMethod) 251 | { 252 | hasMatch = true; 253 | break; 254 | } 255 | } 256 | if (!hasMatch) 257 | { 258 | string msg = (candidateName == null) 259 | ? "invalid arguments to method call" 260 | : ("invalid arguments to method: " + candidateName); 261 | 262 | _Translator.throwError(luaState, msg); 263 | LuaDLL.lua_pushnil(luaState); 264 | return 1; 265 | } 266 | } 267 | } 268 | else // Method from MethodBase instance 269 | { 270 | if (methodToCall.ContainsGenericParameters) 271 | { 272 | // bool isMethod = //* not used 273 | _Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod); 274 | 275 | if (methodToCall.IsGenericMethodDefinition) 276 | { 277 | //need to make a concrete type of the generic method definition 278 | List typeArgs = new List(); 279 | 280 | foreach (object arg in _LastCalledMethod.args) 281 | typeArgs.Add(arg.GetType()); 282 | 283 | MethodInfo concreteMethod = (methodToCall as MethodInfo).MakeGenericMethod(typeArgs.ToArray()); 284 | 285 | _Translator.push(luaState, concreteMethod.Invoke(targetObject, _LastCalledMethod.args)); 286 | failedCall = false; 287 | } 288 | else if (methodToCall.ContainsGenericParameters) 289 | { 290 | _Translator.throwError(luaState, "unable to invoke method on generic class as the current method is an open generic method"); 291 | LuaDLL.lua_pushnil(luaState); 292 | return 1; 293 | } 294 | } 295 | else 296 | { 297 | if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null) 298 | { 299 | targetObject = _ExtractTarget(luaState, 1); 300 | LuaDLL.lua_remove(luaState, 1); // Pops the receiver 301 | } 302 | 303 | if (!_Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod)) 304 | { 305 | _Translator.throwError(luaState, "invalid arguments to method call"); 306 | LuaDLL.lua_pushnil(luaState); 307 | return 1; 308 | } 309 | } 310 | } 311 | 312 | if (failedCall) 313 | { 314 | if (!LuaDLL.lua_checkstack(luaState, _LastCalledMethod.outList.Length + 6)) 315 | throw new LuaException("Lua stack overflow"); 316 | try 317 | { 318 | if (isStatic) 319 | { 320 | _Translator.push(luaState, _LastCalledMethod.cachedMethod.Invoke(null, _LastCalledMethod.args)); 321 | } 322 | else 323 | { 324 | if (_LastCalledMethod.cachedMethod.IsConstructor) 325 | _Translator.push(luaState, ((ConstructorInfo)_LastCalledMethod.cachedMethod).Invoke(_LastCalledMethod.args)); 326 | else 327 | _Translator.push(luaState, _LastCalledMethod.cachedMethod.Invoke(targetObject, _LastCalledMethod.args)); 328 | } 329 | } 330 | catch (TargetInvocationException e) 331 | { 332 | return SetPendingException(e.GetBaseException()); 333 | } 334 | catch (Exception e) 335 | { 336 | return SetPendingException(e); 337 | } 338 | } 339 | 340 | // Pushes out and ref return values 341 | for (int index = 0; index < _LastCalledMethod.outList.Length; index++) 342 | { 343 | nReturnValues++; 344 | _Translator.push(luaState, _LastCalledMethod.args[_LastCalledMethod.outList[index]]); 345 | } 346 | 347 | //by isSingle 2010-09-10 11:26:31 348 | //Desc: 349 | // if not return void,we need add 1, 350 | // or we will lost the function's return value 351 | // when call dotnet function like "int foo(arg1,out arg2,out arg3)" in lua code 352 | if (!_LastCalledMethod.IsReturnVoid && nReturnValues > 0) 353 | { 354 | nReturnValues++; 355 | } 356 | 357 | return nReturnValues < 1 ? 1 : nReturnValues; 358 | } 359 | } 360 | 361 | 362 | 363 | 364 | /// 365 | /// We keep track of what delegates we have auto attached to an event - to allow us to cleanly exit a LuaInterface session 366 | /// 367 | class EventHandlerContainer : IDisposable 368 | { 369 | Dictionary dict = new Dictionary(); 370 | 371 | public void Add(Delegate handler, RegisterEventHandler eventInfo) 372 | { 373 | dict.Add(handler, eventInfo); 374 | } 375 | 376 | public void Remove(Delegate handler) 377 | { 378 | bool found = dict.Remove(handler); 379 | Debug.Assert(found); 380 | } 381 | 382 | /// 383 | /// Remove any still registered handlers 384 | /// 385 | public void Dispose() 386 | { 387 | foreach (KeyValuePair pair in dict) 388 | { 389 | pair.Value.RemovePending(pair.Key); 390 | } 391 | 392 | dict.Clear(); 393 | } 394 | } 395 | 396 | 397 | /* 398 | * Wrapper class for events that does registration/deregistration 399 | * of event handlers. 400 | * 401 | * Author: Fabio Mascarenhas 402 | * Version: 1.0 403 | */ 404 | class RegisterEventHandler 405 | { 406 | object target; 407 | EventInfo eventInfo; 408 | EventHandlerContainer pendingEvents; 409 | 410 | public RegisterEventHandler(EventHandlerContainer pendingEvents, object target, EventInfo eventInfo) 411 | { 412 | this.target = target; 413 | this.eventInfo = eventInfo; 414 | this.pendingEvents = pendingEvents; 415 | } 416 | 417 | 418 | /* 419 | * Adds a new event handler 420 | */ 421 | public Delegate Add(LuaFunction function) 422 | { 423 | #if __NOGEN__ 424 | //translator.throwError(luaState,"Delegates not implemnented"); 425 | return null; 426 | #else 427 | //CP: Fix by Ben Bryant for event handling with one parameter 428 | //link: http://luaforge.net/forum/message.php?msg_id=9266 429 | Delegate handlerDelegate = CodeGeneration.Instance.GetDelegate(eventInfo.EventHandlerType, function); 430 | eventInfo.AddEventHandler(target, handlerDelegate); 431 | pendingEvents.Add(handlerDelegate, this); 432 | 433 | return handlerDelegate; 434 | #endif 435 | 436 | 437 | //MethodInfo mi = eventInfo.EventHandlerType.GetMethod("Invoke"); 438 | //ParameterInfo[] pi = mi.GetParameters(); 439 | //LuaEventHandler handler=CodeGeneration.Instance.GetEvent(pi[1].ParameterType,function); 440 | 441 | //Delegate handlerDelegate=Delegate.CreateDelegate(eventInfo.EventHandlerType,handler,"HandleEvent"); 442 | //eventInfo.AddEventHandler(target,handlerDelegate); 443 | //pendingEvents.Add(handlerDelegate, this); 444 | 445 | //return handlerDelegate; 446 | } 447 | 448 | /* 449 | * Removes an existing event handler 450 | */ 451 | public void Remove(Delegate handlerDelegate) 452 | { 453 | RemovePending(handlerDelegate); 454 | pendingEvents.Remove(handlerDelegate); 455 | } 456 | 457 | /* 458 | * Removes an existing event handler (without updating the pending handlers list) 459 | */ 460 | internal void RemovePending(Delegate handlerDelegate) 461 | { 462 | eventInfo.RemoveEventHandler(target, handlerDelegate); 463 | } 464 | } 465 | 466 | /* 467 | * Base wrapper class for Lua function event handlers. 468 | * Subclasses that do actual event handling are created 469 | * at runtime. 470 | * 471 | * Author: Fabio Mascarenhas 472 | * Version: 1.0 473 | */ 474 | public class LuaEventHandler 475 | { 476 | public LuaFunction handler = null; 477 | 478 | // CP: Fix provided by Ben Bryant for delegates with one param 479 | // link: http://luaforge.net/forum/message.php?msg_id=9318 480 | public void handleEvent(object[] args) 481 | { 482 | handler.Call(args); 483 | } 484 | //public void handleEvent(object sender,object data) 485 | //{ 486 | // handler.call(new object[] { sender,data },new Type[0]); 487 | //} 488 | } 489 | 490 | /* 491 | * Wrapper class for Lua functions as delegates 492 | * Subclasses with correct signatures are created 493 | * at runtime. 494 | * 495 | * Author: Fabio Mascarenhas 496 | * Version: 1.0 497 | */ 498 | public class LuaDelegate 499 | { 500 | public Type[] returnTypes; 501 | public LuaFunction function; 502 | public LuaDelegate() 503 | { 504 | function = null; 505 | returnTypes = null; 506 | } 507 | public object callFunction(object[] args, object[] inArgs, int[] outArgs) 508 | { 509 | // args is the return array of arguments, inArgs is the actual array 510 | // of arguments passed to the function (with in parameters only), outArgs 511 | // has the positions of out parameters 512 | object returnValue; 513 | int iRefArgs; 514 | object[] returnValues = function.call(inArgs, returnTypes); 515 | if (returnTypes[0] == typeof(void)) 516 | { 517 | returnValue = null; 518 | iRefArgs = 0; 519 | } 520 | else 521 | { 522 | returnValue = returnValues[0]; 523 | iRefArgs = 1; 524 | } 525 | // Sets the value of out and ref parameters (from 526 | // the values returned by the Lua function). 527 | for (int i = 0; i < outArgs.Length; i++) 528 | { 529 | args[outArgs[i]] = returnValues[iRefArgs]; 530 | iRefArgs++; 531 | } 532 | return returnValue; 533 | } 534 | } 535 | 536 | /* 537 | * Static helper methods for Lua tables acting as CLR objects. 538 | * 539 | * Author: Fabio Mascarenhas 540 | * Version: 1.0 541 | */ 542 | public class LuaClassHelper 543 | { 544 | /* 545 | * Gets the function called name from the provided table, 546 | * returning null if it does not exist 547 | */ 548 | public static LuaFunction getTableFunction(LuaTable luaTable, string name) 549 | { 550 | object funcObj = luaTable.rawget(name); 551 | if (funcObj is LuaFunction) 552 | return (LuaFunction)funcObj; 553 | else 554 | return null; 555 | } 556 | /* 557 | * Calls the provided function with the provided parameters 558 | */ 559 | public static object callFunction(LuaFunction function, object[] args, Type[] returnTypes, object[] inArgs, int[] outArgs) 560 | { 561 | // args is the return array of arguments, inArgs is the actual array 562 | // of arguments passed to the function (with in parameters only), outArgs 563 | // has the positions of out parameters 564 | object returnValue; 565 | int iRefArgs; 566 | object[] returnValues = function.call(inArgs, returnTypes); 567 | if (returnTypes[0] == typeof(void)) 568 | { 569 | returnValue = null; 570 | iRefArgs = 0; 571 | } 572 | else 573 | { 574 | returnValue = returnValues[0]; 575 | iRefArgs = 1; 576 | } 577 | for (int i = 0; i < outArgs.Length; i++) 578 | { 579 | args[outArgs[i]] = returnValues[iRefArgs]; 580 | iRefArgs++; 581 | } 582 | return returnValue; 583 | } 584 | } 585 | } 586 | -------------------------------------------------------------------------------- /src/ProxyType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Reflection; 4 | 5 | namespace LuaInterface 6 | { 7 | /// 8 | /// Summary description for ProxyType. 9 | /// 10 | public class ProxyType : IReflect 11 | { 12 | 13 | Type proxy; 14 | 15 | public ProxyType(Type proxy) 16 | { 17 | this.proxy = proxy; 18 | } 19 | 20 | /// 21 | /// Provide human readable short hand for this proxy object 22 | /// 23 | /// 24 | public override string ToString() 25 | { 26 | return "ProxyType(" + UnderlyingSystemType + ")"; 27 | } 28 | 29 | 30 | public Type UnderlyingSystemType 31 | { 32 | get 33 | { 34 | return proxy; 35 | } 36 | } 37 | 38 | public FieldInfo GetField(string name, BindingFlags bindingAttr) 39 | { 40 | return proxy.GetField(name, bindingAttr); 41 | } 42 | 43 | public FieldInfo[] GetFields(BindingFlags bindingAttr) 44 | { 45 | return proxy.GetFields(bindingAttr); 46 | } 47 | 48 | public MemberInfo[] GetMember(string name, BindingFlags bindingAttr) 49 | { 50 | return proxy.GetMember(name, bindingAttr); 51 | } 52 | 53 | public MemberInfo[] GetMembers(BindingFlags bindingAttr) 54 | { 55 | return proxy.GetMembers(bindingAttr); 56 | } 57 | 58 | public MethodInfo GetMethod(string name, BindingFlags bindingAttr) 59 | { 60 | return proxy.GetMethod(name, bindingAttr); 61 | } 62 | 63 | public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) 64 | { 65 | return proxy.GetMethod(name, bindingAttr, binder, types, modifiers); 66 | } 67 | 68 | public MethodInfo[] GetMethods(BindingFlags bindingAttr) 69 | { 70 | return proxy.GetMethods(bindingAttr); 71 | } 72 | 73 | public PropertyInfo GetProperty(string name, BindingFlags bindingAttr) 74 | { 75 | return proxy.GetProperty(name, bindingAttr); 76 | } 77 | 78 | public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) 79 | { 80 | return proxy.GetProperty(name, bindingAttr, binder, returnType, types, modifiers); 81 | } 82 | 83 | public PropertyInfo[] GetProperties(BindingFlags bindingAttr) 84 | { 85 | return proxy.GetProperties(bindingAttr); 86 | } 87 | 88 | public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) 89 | { 90 | return proxy.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); 91 | } 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevedonovan/MonoLuaInterface/b923c54ccc36c7d8788ff049e9778bb8e9278668/src/README.txt -------------------------------------------------------------------------------- /src/config.win: -------------------------------------------------------------------------------- 1 | # .NET build 2 | #LUA_INCLUDE=c:\users\steve\LuaJIT-2.0\src 3 | #LUA_LIB=c:\users\steve\LuaJIT-2.0\src\lua51.dll 4 | #CSC = csc /nologo 5 | #CC=gcc 6 | # Mono build (against 32-bit lua51.dll, note -m32 7 | LUA_INCLUDE=c:\users\steve\lua\lua-5.1.4\src 8 | LUA_LIB=c:\users\steve\lua\lua-5.1.4\src\lua51.dll 9 | CSC=mcs 10 | SHARED=-shared 11 | CC=gcc -m32 12 | 13 | -------------------------------------------------------------------------------- /src/configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | function shell(cmd) 3 | local p = io.popen (cmd..' 2> /dev/null') 4 | local res = p:read() 5 | p:close() 6 | return res 7 | end 8 | 9 | function build_defines(defs) 10 | local res = {} 11 | for d in defs:gmatch '%a+' do 12 | table.insert(res,'-d:__'..d..'__') 13 | end 14 | return table.concat(res,' ') 15 | end 16 | 17 | if arg[1] == '-h' or arg[1] == '--help' then 18 | return print [[ 19 | ./configure PARM1=VALUE1, ... 20 | Parameters: 21 | LUA_INCLUDE Lua include directory 22 | On Linux, we look first in /usr/include and then /usr/include/lua5.1 23 | otherwise assume ~/lua-5.1.4/src 24 | DEFINES one or more of lib,dot, embed, Windows, novs, default 25 | lib -> liblua51 (Arch) 26 | lib novs -> liblua (Fedora) 27 | lib dot -> liblua5.1 (Debian/Ubuntu) 28 | dot -> lua5.1 (LuaBinaries) 29 | default is lua51 (PUC build) 30 | Extension is .so (.dll if Windows) 31 | Use embed if luastdcall.c is compiled into your Lua shared library 32 | FLAGS debug or optimize (default optimize) 33 | ]] 34 | end 35 | 36 | for _,a in ipairs(arg) do 37 | local var,val = a:match('([^=]+)=(.+)') 38 | if var then 39 | _G[var] = val 40 | end 41 | end 42 | 43 | local csc, shared 44 | local lua_include=LUA_INCLUDE 45 | local defines=DEFINES 46 | local flags=FLAGS 47 | 48 | if not flags then 49 | flags = 'optimize' 50 | end 51 | 52 | if shell 'which gmcs' then 53 | csc = 'gmcs' 54 | elseif shell 'which mcs' then 55 | csc = 'mcs' 56 | elseif shell 'which cli-csc' then 57 | csc = 'cli-csc' 58 | else 59 | return print 'cannot find C# compiler on your path..' 60 | end 61 | 62 | if not defines then 63 | local possibilities = { 64 | ['lua51'] = 'default', 65 | ['lua5.1'] = 'dot', 66 | ['liblua51'] = 'lib', 67 | ['liblua5.1'] = 'lib dot', 68 | ['liblua'] = 'lib novs', 69 | } 70 | 71 | for lib, defs in pairs(possibilities) do 72 | local fn,err = package.loadlib(lib..'.so','luaL_newstate') 73 | if fn then 74 | defines = defs 75 | break 76 | end 77 | end 78 | 79 | if not defines then 80 | return print 'you did not have a recognized Lua shared library!' 81 | end 82 | end 83 | 84 | local plat = shell 'uname' 85 | if plat == 'Darwin' then 86 | shared = '-dynamiclib -undefined dynamic_lookup' 87 | else 88 | shared = '-shared -fpic' 89 | end 90 | 91 | lua_include = lua_include or shell 'ls /usr/include/lua.h' 92 | if not lua_include then 93 | lua_include = shell 'ls /usr/include/lua5.1/lua.h' 94 | end 95 | if not lua_include then 96 | print 'please check Lua include directory in config.inc' 97 | lua_include = os.getenv('HOME')..'/lua-5.1.4/src' 98 | else 99 | lua_include = lua_include:gsub ('/lua%.h$','') 100 | end 101 | 102 | if defines == 'default' then 103 | defines = nil 104 | end 105 | 106 | print 'writing configuration to config.inc' 107 | local f = io.open('config.inc','w') 108 | f:write('LUA_INCLUDE=',lua_include,'\n') 109 | f:write('CSC=',csc,'\n') 110 | f:write('SHARED=',shared,'\n') 111 | f:write('FLAGS=-',flags,'\n') 112 | if defines then 113 | f:write('DEFINES=',build_defines(defines),'\n') 114 | end 115 | f:close() 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | if arg[1] == '-h' then 3 | print "./install (install-dir or '~/bin') (wrapper or 'luai')" 4 | os.exit(1) 5 | end 6 | 7 | local name = arg[2] or 'luai' 8 | local install_dir = arg[1] or (os.getenv('HOME')..'/bin') 9 | 10 | function shell(cmd) 11 | local p = io.popen (cmd..' 2> /dev/null') 12 | local res = p:read() 13 | p:close() 14 | return res 15 | end 16 | 17 | if not shell('ls '..install_dir) then 18 | print "the target directory does not exist" 19 | os.exit(1) 20 | end 21 | 22 | local cwd = shell 'pwd' 23 | local uname = shell 'uname' 24 | local ld = uname == 'Darwin' and 'DYLD' or 'LD' 25 | 26 | local templ = [[ 27 | #!/bin/sh 28 | LUAI=%s/bin 29 | export %s_LIBRARY_PATH=$LUAI 30 | export LUA_PATH=";;$LUAI/lua/?.lua" 31 | /usr/bin/mono $LUAI/luai.exe $* 32 | ]] 33 | 34 | local target = install_dir..'/'..name 35 | local f,err= io.open(target,'w') 36 | if not f then 37 | print ("cannot write "..target.."; you may need to be sudo") 38 | os.exit(1) 39 | end 40 | cwd = cwd:gsub ('/src$','') 41 | local wrapper = templ:format(cwd,ld) 42 | f:write(wrapper) 43 | f:close() 44 | os.execute('chmod +x '..target) 45 | 46 | print("wrapper") 47 | print(wrapper) 48 | print("written to",target) 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/luastdcall-unix.h: -------------------------------------------------------------------------------- 1 | #include "lua.h" 2 | 3 | /* 4 | ** stdcall C function 5 | */ 6 | 7 | typedef int (*lua_stdcallCFunction) (lua_State *L); 8 | 9 | /* 10 | ** push stdcall C function 11 | */ 12 | LUA_API void lua_pushstdcallcfunction(lua_State *L, lua_stdcallCFunction fn); 13 | 14 | /* 15 | ** safe tostring 16 | */ 17 | LUA_API void lua_safetostring(lua_State *L,int index,char* buffer); 18 | 19 | /* 20 | ** check metatable 21 | */ 22 | 23 | LUA_API int luaL_checkmetatable(lua_State *L,int index); 24 | 25 | LUA_API int luanet_tonetobject(lua_State *L,int index); 26 | 27 | LUA_API void luanet_newudata(lua_State *L,int val); 28 | 29 | LUA_API void *luanet_gettag(void); 30 | 31 | LUA_API int luanet_rawnetobj(lua_State *L,int index); 32 | 33 | LUA_API int luanet_checkudata(lua_State *L,int index,const char *meta); 34 | -------------------------------------------------------------------------------- /src/luastdcall-windows.h: -------------------------------------------------------------------------------- 1 | #include "lua.h" 2 | 3 | #define LUA_DLLEXPORT __declspec(dllexport) 4 | 5 | /* 6 | ** stdcall C function 7 | */ 8 | 9 | typedef int (__stdcall *lua_stdcallCFunction) (lua_State *L); 10 | 11 | /* 12 | ** push stdcall C function 13 | */ 14 | LUA_DLLEXPORT void lua_pushstdcallcfunction(lua_State *L, lua_stdcallCFunction fn); 15 | 16 | /* 17 | ** safe tostring 18 | */ 19 | LUA_DLLEXPORT void lua_safetostring(lua_State *L,int index,char* buffer); 20 | 21 | /* 22 | ** check metatable 23 | */ 24 | 25 | LUA_DLLEXPORT int luaL_checkmetatable(lua_State *L,int index); 26 | 27 | LUA_DLLEXPORT int luanet_tonetobject(lua_State *L,int index); 28 | 29 | LUA_DLLEXPORT void luanet_newudata(lua_State *L,int val); 30 | 31 | LUA_DLLEXPORT void *luanet_gettag(void); 32 | 33 | LUA_DLLEXPORT int luanet_rawnetobj(lua_State *L,int index); 34 | 35 | LUA_DLLEXPORT int luanet_checkudata(lua_State *L,int index,const char *meta); 36 | -------------------------------------------------------------------------------- /src/luastdcall.c: -------------------------------------------------------------------------------- 1 | // steffenj: replaced all occurances of LUA_DLLEXPORT with LUA_DLLEXPORT due to "macro redefinition" error 2 | // there's probably a "correct" way to solve this but right now I prefer the one that works :) 3 | 4 | #include "lua.h" 5 | #include "lualib.h" 6 | #include "lauxlib.h" 7 | #ifdef _WIN32 8 | #include "luastdcall-windows.h" 9 | #include 10 | BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved) { return TRUE; } 11 | #else 12 | #define LUA_DLLEXPORT 13 | #include "luastdcall-unix.h" 14 | #endif 15 | #include 16 | #include 17 | 18 | /* 19 | ** stdcall C function support 20 | */ 21 | 22 | static int tag = 0; 23 | 24 | 25 | static int stdcall_closure(lua_State *L) { 26 | lua_stdcallCFunction fn = (lua_stdcallCFunction)lua_touserdata(L, lua_upvalueindex(1)); 27 | return fn(L); 28 | } 29 | 30 | 31 | LUA_DLLEXPORT void lua_pushstdcallcfunction(lua_State *L,lua_stdcallCFunction fn) { 32 | lua_pushlightuserdata(L, fn); 33 | lua_pushcclosure(L, stdcall_closure, 1); 34 | } 35 | 36 | 37 | 38 | LUA_DLLEXPORT int luaL_checkmetatable(lua_State *L,int index) { 39 | int retVal=0; 40 | if(lua_getmetatable(L,index)!=0) { 41 | lua_pushlightuserdata(L,&tag); 42 | lua_rawget(L,-2); 43 | retVal=!lua_isnil(L,-1); 44 | lua_settop(L,-3); 45 | } 46 | return retVal; 47 | } 48 | 49 | LUA_DLLEXPORT void *luanet_gettag() { 50 | return &tag; 51 | } 52 | 53 | 54 | // Starting with 5.1 the auxlib version of checkudata throws an exception if the type isn't right 55 | // Instead, we want to run our own version that checks the type and just returns null for failure 56 | void *checkudata(lua_State *L, int ud, const char *tname) 57 | { 58 | void *p = lua_touserdata(L, ud); 59 | 60 | if (p != NULL) { /* value is a userdata? */ 61 | if (lua_getmetatable(L, ud)) 62 | { 63 | int isEqual; 64 | 65 | /* does it have a metatable? */ 66 | lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ 67 | 68 | isEqual = lua_rawequal(L, -1, -2); 69 | 70 | lua_pop(L, 2); /* remove both metatables */ 71 | 72 | if (isEqual) /* does it have the correct mt? */ 73 | return p; 74 | } 75 | } 76 | 77 | return NULL; 78 | } 79 | 80 | 81 | LUA_DLLEXPORT int luanet_tonetobject(lua_State *L,int index) { 82 | int *udata; 83 | if(lua_type(L,index)==LUA_TUSERDATA) { 84 | if(luaL_checkmetatable(L,index)) { 85 | udata=(int*)lua_touserdata(L,index); 86 | if(udata!=NULL) return *udata; 87 | } 88 | udata=(int*)checkudata(L,index,"luaNet_class"); 89 | if(udata!=NULL) return *udata; 90 | udata=(int*)checkudata(L,index,"luaNet_searchbase"); 91 | if(udata!=NULL) return *udata; 92 | udata=(int*)checkudata(L,index,"luaNet_function"); 93 | if(udata!=NULL) return *udata; 94 | } 95 | return -1; 96 | } 97 | 98 | LUA_DLLEXPORT void luanet_newudata(lua_State *L,int val) { 99 | int* pointer=(int*)lua_newuserdata(L,sizeof(int)); 100 | *pointer=val; 101 | } 102 | 103 | LUA_DLLEXPORT int luanet_checkudata(lua_State *L,int index,const char *meta) { 104 | int *udata=(int*)checkudata(L,index,meta); 105 | if(udata!=NULL) return *udata; 106 | return -1; 107 | } 108 | 109 | LUA_DLLEXPORT int luanet_rawnetobj(lua_State *L,int index) { 110 | int *udata=lua_touserdata(L,index); 111 | if(udata!=NULL) return *udata; 112 | return -1; 113 | } 114 | -------------------------------------------------------------------------------- /src/makefile: -------------------------------------------------------------------------------- 1 | include config.inc 2 | 3 | BINDIR=../bin 4 | ASSEMBLY=$(BINDIR)/LuaInterface.dll 5 | RUNNER=$(BINDIR)/luai.exe 6 | STUB=$(BINDIR)/luanet.so 7 | 8 | all: $(ASSEMBLY) $(STUB) $(RUNNER) 9 | 10 | ASSEMBLY_SRC = \ 11 | AssemblyInfo.cs LuaException.cs LuaTable.cs \ 12 | CheckType.cs LuaFunction.cs LuaUserData.cs \ 13 | GenerateEventAssembly.cs LuaGlobalAttribute.cs Metatables.cs \ 14 | LuaBase.cs LuaHideAttribute.cs MethodWrapper.cs \ 15 | Lua.cs LuaRegistrationHelper.cs ObjectTranslator.cs \ 16 | LuaDLL.cs LuaScriptException.cs ProxyType.cs 17 | 18 | $(ASSEMBLY): $(ASSEMBLY_SRC) 19 | $(CSC) $(DEFINES) $(FLAGS) -out:$(ASSEMBLY) $(DEBUG) -t:library $(ASSEMBLY_SRC) 20 | 21 | $(RUNNER): LuaNetRunner.cs 22 | $(CSC) -out:$(RUNNER) $(DEBUG) -r:$(ASSEMBLY) LuaNetRunner.cs 23 | 24 | $(STUB): luastdcall.c 25 | $(CC) $(SHARED) -o $(STUB) $(DEBUG) -I $(LUA_INCLUDE) luastdcall.c $(LUA_LIB) 26 | 27 | clean: 28 | rm $(STUB) $(ASSEMBLY) $(RUNNER) 29 | -------------------------------------------------------------------------------- /src/makefile.win: -------------------------------------------------------------------------------- 1 | include config.win 2 | 3 | BINDIR=..\bin 4 | ASSEMBLY=$(BINDIR)\LuaInterface.dll 5 | RUNNER=$(BINDIR)\luai.exe 6 | STUB=$(BINDIR)\luanet.dll 7 | 8 | all: $(ASSEMBLY) $(STUB) $(RUNNER) 9 | 10 | ASSEMBLY_SRC = \ 11 | AssemblyInfo.cs LuaException.cs LuaTable.cs \ 12 | CheckType.cs LuaFunction.cs LuaUserData.cs \ 13 | GenerateEventAssembly.cs LuaGlobalAttribute.cs Metatables.cs \ 14 | LuaBase.cs LuaHideAttribute.cs MethodWrapper.cs \ 15 | Lua.cs LuaRegistrationHelper.cs ObjectTranslator.cs \ 16 | LuaDLL.cs LuaScriptException.cs ProxyType.cs 17 | 18 | $(ASSEMBLY): $(ASSEMBLY_SRC) 19 | $(CSC) /define:__Windows__ -out:$(ASSEMBLY) -t:library $(ASSEMBLY_SRC) 20 | 21 | $(RUNNER): LuaNetRunner.cs 22 | $(CSC) -out:$(RUNNER) -r:$(ASSEMBLY) LuaNetRunner.cs 23 | 24 | $(STUB): luastdcall.c 25 | $(CC) $(SHARED) -o $(STUB) -I $(LUA_INCLUDE) luastdcall.c $(LUA_LIB) 26 | 27 | clean: 28 | del $(STUB) $(ASSEMBLY) $(RUNNER) 29 | -------------------------------------------------------------------------------- /src/rmakefile.win: -------------------------------------------------------------------------------- 1 | include config.win 2 | 3 | BINDIR=..\rbin 4 | ASSEMBLY=$(BINDIR)\LuaInterface.dll 5 | RUNNER=$(BINDIR)\luai.exe 6 | STUB=$(BINDIR)\luanet.dll 7 | 8 | all: $(ASSEMBLY) $(STUB) $(RUNNER) 9 | 10 | ASSEMBLY_SRC = \ 11 | AssemblyInfo.cs LuaException.cs LuaTable.cs \ 12 | CheckType.cs LuaFunction.cs LuaUserData.cs \ 13 | LuaGlobalAttribute.cs Metatables.cs \ 14 | LuaBase.cs LuaHideAttribute.cs MethodWrapper.cs \ 15 | Lua.cs LuaRegistrationHelper.cs ObjectTranslator.cs \ 16 | LuaDLL.cs LuaScriptException.cs ProxyType.cs 17 | 18 | # GenerateEventAssembly.cs 19 | 20 | $(ASSEMBLY): $(ASSEMBLY_SRC) 21 | $(CSC) /define:__Windows__ /define:__NOGEN__ -out:$(ASSEMBLY) -t:library $(ASSEMBLY_SRC) 22 | 23 | $(RUNNER): LuaNetRunner.cs 24 | $(CSC) -out:$(RUNNER) -r:$(ASSEMBLY) LuaNetRunner.cs 25 | 26 | $(STUB): luastdcall.c 27 | $(CC) $(SHARED) -o $(STUB) -I $(LUA_INCLUDE) luastdcall.c $(LUA_LIB) 28 | 29 | clean: 30 | del $(STUB) $(ASSEMBLY) $(RUNNER) 31 | -------------------------------------------------------------------------------- /tests/CallLua.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Demonstrates using the higher-level LuaInterface API 3 | */ 4 | 5 | using System; 6 | using LuaInterface; 7 | 8 | // adding new Lua functions is particularly easy... 9 | public class MyClass { 10 | 11 | [LuaGlobal] 12 | public static double Sqr(double x) { 13 | return x*x; 14 | } 15 | 16 | [LuaGlobal] 17 | public static double Sum(double x, double y) { 18 | return x + y; 19 | } 20 | 21 | // can put into a particular table with a new name 22 | [LuaGlobal(Name="utils.sum",Description="sum a table of numbers")] 23 | public static double SumX(params double[] values) { 24 | double sum = 0.0; 25 | for (int i = 0; i < values.Length; i++) 26 | sum += values[i]; 27 | return sum; 28 | } 29 | 30 | public static void Register(Lua L) { 31 | L.NewTable("utils"); 32 | LuaRegistrationHelper.TaggedStaticMethods(L,typeof(MyClass)); 33 | } 34 | 35 | } 36 | 37 | public class CSharp { 38 | public virtual string MyMethod(string s) { 39 | return s.ToUpper(); 40 | } 41 | 42 | protected virtual string Protected(string s) { 43 | return s + "?"; 44 | } 45 | 46 | protected virtual bool ProtectedBool() { 47 | return false; 48 | } 49 | 50 | public static string UseMe (CSharp obj, string val) { 51 | Console.WriteLine("calling protected {0} {1}",obj.Protected(val),obj.ProtectedBool()); 52 | return obj.MyMethod(val); 53 | } 54 | } 55 | 56 | public class RefParms { 57 | public void Args(out int a, out int b) { 58 | a = 2; 59 | b = 3; 60 | } 61 | 62 | public int ArgsI(out int a, out int b) { 63 | a = 2; 64 | b = 3; 65 | return 1; 66 | } 67 | 68 | public void ArgsVar(params object[] obj) { 69 | int i = (int)obj[0]; 70 | Console.WriteLine("cool {0}",i); 71 | } 72 | 73 | } 74 | 75 | public class CallLua { 76 | 77 | public static bool IsInteger(double x) { 78 | return Math.Ceiling(x) == x; 79 | } 80 | 81 | 82 | public static void Main(string[] args) { 83 | Lua L = new Lua(); 84 | 85 | // testing out parameters and type coercion for object[] args. 86 | L["obj"] = new RefParms(); 87 | dump("void,out,out",L.DoString("return obj:Args()")); 88 | dump("int,out,out",L.DoString("return obj:ArgsI()")); 89 | L.DoString("obj:ArgsVar{1}"); 90 | Console.WriteLine("equals {0} {1} {2}",IsInteger(2.3),IsInteger(0),IsInteger(44)); 91 | //Environment.Exit(0); 92 | 93 | object[] res = L.DoString("return 20,'hello'","tmp"); 94 | Console.WriteLine("returned {0} {1}",res[0],res[1]); 95 | 96 | 97 | 98 | L.DoString("answer = 42"); 99 | Console.WriteLine("answer was {0}",L["answer"]); 100 | 101 | MyClass.Register(L); 102 | 103 | L.DoString(@" 104 | print(Sqr(4)) 105 | print(Sum(1.2,10)) 106 | -- please note that this isn't true varargs syntax! 107 | print(utils.sum {1,5,4.2}) 108 | "); 109 | 110 | L.DoString("X = {1,2,3}; Y = {fred='dog',alice='cat'}"); 111 | 112 | LuaTable X = (LuaTable)L["X"]; 113 | Console.WriteLine("1st {0} 2nd {1}",X[1],X[2]); 114 | // (but no Length defined: an oversight?) 115 | Console.WriteLine("X[4] was nil {0}",X[4] == null); 116 | 117 | // only do this if the table only has string keys 118 | LuaTable Y = (LuaTable)L["Y"]; 119 | foreach (string s in Y.Keys) 120 | Console.WriteLine("{0}={1}",s,Y[s]); 121 | 122 | // getting and calling functions by name 123 | LuaFunction f = L.GetFunction("string.gsub"); 124 | object[] ans = f.Call("here is the dog's dog","dog","cat"); 125 | Console.WriteLine("results '{0}' {1}",ans[0],ans[1]); 126 | 127 | // and it's entirely possible to do these things from Lua... 128 | L["L"] = L; 129 | L.DoString(@" 130 | L:DoString 'print(1,2,3)' 131 | "); 132 | 133 | // it is also possible to override a CLR class in Lua using luanet.make_object. 134 | // This defines a proxy object which will successfully fool any C# code 135 | // receiving it. 136 | object[] R = L.DoString(@" 137 | luanet.load_assembly 'CallLua' -- load this program 138 | local CSharp = luanet.import_type 'CSharp' 139 | local T = {} 140 | function T:MyMethod(s) 141 | return s:lower() 142 | end 143 | function T:Protected(s) 144 | return s:upper() 145 | end 146 | function T:ProtectedBool() 147 | return true 148 | end 149 | luanet.make_object(T,'CSharp') 150 | print(CSharp.UseMe(T,'CoOl')) 151 | io.flush() 152 | return T 153 | "); 154 | // but it's still a table, and there's no way to cast it to CSharp from here... 155 | Console.WriteLine("type of returned value {0}",R[0].GetType()); 156 | 157 | 158 | } 159 | 160 | static void dump(string msg, object[] values) { 161 | Console.WriteLine("{0}:",msg); 162 | foreach(object o in values) { 163 | if (o == null) { 164 | Console.WriteLine("\tnull"); 165 | } else { 166 | Console.WriteLine("\t({0}) {1}",o.GetType(),o); 167 | } 168 | } 169 | } 170 | 171 | 172 | } 173 | 174 | -------------------------------------------------------------------------------- /tests/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LuaInterface.Tests 6 | { 7 | public class Entity 8 | { 9 | public event EventHandler Clicked; 10 | 11 | protected virtual void OnEntityClicked(EventArgs e) 12 | { 13 | EventHandler handler = Clicked; 14 | 15 | if (handler != null) 16 | { 17 | // Use the () operator to raise the event. 18 | handler(this, e); 19 | } 20 | } 21 | 22 | public Entity() 23 | { 24 | 25 | } 26 | 27 | public void Click() 28 | { 29 | OnEntityClicked(new EventArgs()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/makefile: -------------------------------------------------------------------------------- 1 | include ../src/config.inc 2 | 3 | BINDIR=../bin 4 | LDPATH=LD_LIBRARY_PATH=$(BINDIR) 5 | ASSEMBLY=LuaInterface.dll 6 | TEST=TestLua.exe 7 | EX=CallLua.exe 8 | 9 | all: $(ASSEMBLY) $(TEST) $(EX) run 10 | 11 | $(TEST): TestLua.cs Entity.cs 12 | $(CSC) -r:$(ASSEMBLY) $^ 13 | 14 | $(EX): CallLua.cs 15 | $(CSC) -r:$(ASSEMBLY) $^ 16 | 17 | $(ASSEMBLY): $(BINDIR)/$(ASSEMBLY) 18 | cp $< $@ 19 | 20 | # to run a CLR program with LuaInterface, then 21 | # 1. LuaInterface.dll must be in same dir, or be in the GAC 22 | # 2. luanet.so and (liblua5.1.so or lua51.so) must be on the shared library path. 23 | run: $(TEST) 24 | $(LDPATH) mono $(EX) 25 | $(LDPATH) mono $(TEST) 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/makefile.win: -------------------------------------------------------------------------------- 1 | include ../src/config.win 2 | 3 | BINDIR=..\bin 4 | ASSEMBLY=LuaInterface.dll 5 | TEST=TestLua.exe 6 | EX=CallLua.exe 7 | 8 | all: $(ASSEMBLY) $(TEST) $(EX) run 9 | 10 | $(TEST): TestLua.cs Entity.cs 11 | $(CSC) -r:$(ASSEMBLY) $^ 12 | 13 | $(EX): CallLua.cs 14 | $(CSC) -r:$(ASSEMBLY) $^ 15 | 16 | $(ASSEMBLY): $(BINDIR)\$(ASSEMBLY) 17 | copy $< 18 | copy $(BINDIR)\luanet.dll 19 | 20 | run: $(TEST) 21 | $(EX) 22 | $(TEST) 23 | 24 | clean: 25 | del $(ASSEMBLY) $(TEST) $(EX) luanet.dll 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/test.lua: -------------------------------------------------------------------------------- 1 | width=100 2 | height=200 3 | message="Hello World!" 4 | color={r=100,g=20,b=50} 5 | tree={branch1={leaf1=10,leaf2="leaf2"},leaf3="leaf3"} 6 | 7 | function func(x,y) 8 | return x,x+y 9 | end 10 | --------------------------------------------------------------------------------