aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/LuaSL
diff options
context:
space:
mode:
authorDavid Walter Seikel2014-04-22 15:13:38 +1000
committerDavid Walter Seikel2014-04-22 15:13:38 +1000
commitdd009ccdfd62f9153dbc72f5f5de5d5f72979690 (patch)
tree50d62438c6d47dccf82f440986919b1ed3edbbd9 /src/LuaSL
parentMove most of the README's and other docs into the new docs directory. (diff)
downloadSledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.zip
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.gz
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.bz2
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.xz
Move all source into the new src directory, and shuffle a few other things around.
Diffstat (limited to 'src/LuaSL')
-rw-r--r--src/LuaSL/LuaSL.edc205
-rw-r--r--src/LuaSL/LuaSL.h82
-rw-r--r--src/LuaSL/LuaSL_LSL_tree.h443
-rw-r--r--src/LuaSL/LuaSL_compile.c2345
-rw-r--r--src/LuaSL/LuaSL_lemon_yaccer.y275
-rw-r--r--src/LuaSL/LuaSL_lexer.l164
-rw-r--r--src/LuaSL/LuaSL_main.c708
-rw-r--r--src/LuaSL/LuaSL_test.c467
-rw-r--r--src/LuaSL/LuaSL_threads.c496
-rw-r--r--src/LuaSL/LuaSL_threads.h54
-rw-r--r--src/LuaSL/LuaSL_utilities.c60
-rwxr-xr-xsrc/LuaSL/build.lua23
-rwxr-xr-xsrc/LuaSL/test.sh27
13 files changed, 5349 insertions, 0 deletions
diff --git a/src/LuaSL/LuaSL.edc b/src/LuaSL/LuaSL.edc
new file mode 100644
index 0000000..844fc8e
--- /dev/null
+++ b/src/LuaSL/LuaSL.edc
@@ -0,0 +1,205 @@
1color_classes {
2 color_class { name: "test_colour"; color: 255 255 255 255; }
3}
4
5//fonts {
6// font: "Vera.ttf" "default";
7//}
8
9images {
10 image: "bubble.png" COMP;
11 image: "test.png" COMP;
12}
13
14collections {
15 group {
16 name: "main";
17 lua_script_only: 1;
18 lua_script {
19 --// stick object private/local vars here
20 local D;
21 local text_geom;
22
23 --// init object here
24 D = {}; --// data is empty table to start
25
26 --// send some random edje message
27 edje.messagesend(7, "none" );
28 edje.messagesend(7, "sig", "signal", "source");
29 edje.messagesend(7, "str", "hello world");
30 edje.messagesend(7, "int", 987);
31 edje.messagesend(7, "float", 987.321);
32 edje.messagesend(7, "strset", {"hello", "there", "world"});
33 edje.messagesend(7, "intset", {1, 2, 3});
34 edje.messagesend(7, "floatset", {1.1, 2.2, 3.3});
35 edje.messagesend(7, "strint", "hello world", 7);
36 edje.messagesend(7, "strfloat", "hello world", 7.654);
37 edje.messagesend(7, "strintset","hello world", {1, 2, 3});
38
39 D.edje = edje.edje();
40 D.edje:file("plain/edje/group");
41 D.edje:show();
42
43 D.text = edje.text();
44 D.text:geom (50, 5, 150, 50);
45 D.text:color (255, 0, 0, 255);
46 D.text:font("Sans:style=Bold", 32);
47 D.text:text("Lua rocks!");
48 text_geom = D.text:geom();
49--// print(D.text:text());
50 D.text:show();
51
52
53 --// shutdown func - generally empty or not there. everything garbage collected for you
54 function shutdown ()
55--// print("lua::shutdown");
56 end
57
58 function show ()
59--// print("lua::show");
60 end
61
62 function hide ()
63--// print("lua::hide");
64 end
65
66 function move (x, y)
67--// print("lua::move x=" .. x .. " x=" .. y);
68 D.edje:move(0, 0);
69 end
70
71 function resize (w, h)
72--// print("lua::resize w=" .. w .. " h=" .. h);
73 D.text:move((w - text_geom.w) / 2, (h - text_geom.h) / 8);
74 D.edje:resize(w, h);
75 end
76
77 function message (id, type, ...)
78 print("lua::message id=" .. id .. " type=" .. type);
79 --// handle your message type here. check id + type then use the
80 --// vararg appropriately. they are the same as the params passed
81 --// to edje:messagesend() (if any are passed at all). Any array
82 --// arguments are passed as a single table.
83
84 if ("none" == type) then
85 print("lua::message no args");
86 elseif ("strset" == type) then
87 strs = ... ;
88 print_table_start(strs, "", "lua::message strings");
89 elseif ("intset" == type) then
90 ints = ... ;
91 print_table_start(ints, "", "lua::message ints");
92 elseif ("floatset" == type) then
93 floats = ... ;
94 print_table_start(floats, "", "lua::message floats");
95 elseif ("strintset" == type) then
96 str, ints = ... ;
97 print("lua::message " .. str);
98 print_table_start(ints, "", "lua::message ints");
99 elseif ("strfloatset" == type) then
100 str, floats = ... ;
101 print("lua::message " .. str);
102 print_table_start(floats, "", "lua::message floats");
103 else
104 print("lua::message " .. ... );
105 end
106 end
107
108 function signal (sig, src)
109 print("lua::signal sig= " .. sig .. " src= " .. src);
110 end
111 }
112 }
113
114 // The group name NEEDS a / in it,
115 // or the part below that tries to swallow it won't work.
116 // Leaving just the lua part visible.
117 group {
118 name: "bubbles/lua";
119 lua_script_only: 1;
120 lua_script {
121 local bubbles = { };
122 local bubbleCols = 8;
123 local bubbleRows = 6;
124
125 for i = 1, bubbleRows do
126 row = { };
127 for j = 1, bubbleCols do
128 image = edje.image();
129 image:image("bubble.png");
130 image:show();
131 table.insert(row, image);
132 end
133 table.insert(bubbles, row);
134 end
135
136 function resize (w, h)
137 for i = 1, bubbleRows do
138 for j = 1, bubbleCols do
139 w1 = w / bubbleCols;
140 h1 = h / bubbleRows;
141 bubbles[i][j]:geom((j - 1) * w1, (i - 1) * h1, w1, h1);
142 if ((1 == i) or (1 == j) or (bubbleRows == i) or (bubbleCols == j)) then
143 bubbles[i][j]:color(0, 255, 0, 200);
144 else
145 bubbles[i][j]:color(math.random(200) + 55, 0, math.random(255) + 55, 200);
146 end
147 end
148 end
149 end
150 }
151 }
152
153 group {
154 name: "plain/edje/group";
155 parts {
156 part {
157 name: "background";
158 type: RECT;
159 mouse_events: 0;
160 description {
161 state: "default" 0.0;
162 color: 0 0 0 255;
163 }
164 }
165
166 // A lua group embedded in an edje group.
167 part {
168 name: "bubbles_lua";
169 type: GROUP;
170 source: "bubbles/lua";
171 mouse_events: 0;
172 description { state: "default" 0.0; }
173 }
174
175 part {
176 name: "background_image";
177 type: IMAGE;
178 mouse_events: 0;
179 description {
180 state: "default" 0.0;
181 aspect_preference: HORIZONTAL;
182 color_class: "test_colour";
183 image { normal: "test.png"; }
184 }
185 }
186
187 part {
188 name: "some_text";
189 type: TEXT;
190 mouse_events: 0;
191 description {
192 state: "default" 0;
193 text
194 {
195 text: "This is test text.";
196 text_class: "test_text_class";
197 }
198 }
199 }
200
201 }
202 }
203
204}
205
diff --git a/src/LuaSL/LuaSL.h b/src/LuaSL/LuaSL.h
new file mode 100644
index 0000000..d1f448e
--- /dev/null
+++ b/src/LuaSL/LuaSL.h
@@ -0,0 +1,82 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#else
4//#define PACKAGE_EXAMPLES_DIR "."
5#define __UNUSED__
6#endif
7
8#include <Eet.h>
9#include <Ecore.h>
10#include <Ecore_Con.h>
11#include <Ecore_Evas.h>
12#include <Ecore_File.h>
13#include <Edje.h>
14#include <stdio.h>
15#include <ctype.h>
16
17#include <lua.h>
18#include <luajit.h>
19#include <lualib.h>
20#include <lauxlib.h>
21
22typedef struct _script script; // Define this here, so LuaSL_threads.h can use it.
23typedef struct _gameGlobals gameGlobals; // Define this here, so LuaSL_threads.h can use it.
24
25#include "LuaSL_threads.h"
26#include "LumbrJack.h"
27
28
29#define WIDTH (512)
30#define HEIGHT (384)
31
32
33#define TABLE_WIDTH 7
34#define TABLE_HEIGHT 42
35
36
37struct _gameGlobals
38{
39 Ecore_Evas *ee; // Our window.
40 Evas *canvas; // The canvas for drawing directly onto.
41 Evas_Object *bg; // Our background edje, also the game specific stuff.
42 Evas_Object *edje; // The edje of the background.
43 Ecore_Con_Server *server;
44 Eina_Hash *scripts, *names;
45 int logDom;
46 const char *address;
47 int port;
48 boolean ui; // Wether we actually start up the UI.
49};
50
51struct _script
52{
53 Eina_Clist node;
54 gameGlobals *game;
55 char SID[PATH_MAX];
56 char fileName[PATH_MAX];
57 lua_State *L;
58 struct timeval startTime;
59 float compileTime, timerTime;
60 int bugs, warnings;
61 boolean running;
62 int status;
63 int args;
64 Eina_Clist messages;
65 Ecore_Con_Client *client;
66 Ecore_Timer *timer;
67};
68
69typedef struct
70{
71 Eina_Clist node;
72 script *script;
73 const char message[PATH_MAX];
74} scriptMessage;
75
76
77void scriptSendBack(void * data);
78void sendBack(gameGlobals *ourGlobals, Ecore_Con_Client *client, const char *SID, const char *message, ...);
79void sendForth(gameGlobals *ourGlobals, const char *SID, const char *message, ...);
80float timeDiff(struct timeval *now, struct timeval *then);
81
82#include "LuaSL_LSL_tree.h"
diff --git a/src/LuaSL/LuaSL_LSL_tree.h b/src/LuaSL/LuaSL_LSL_tree.h
new file mode 100644
index 0000000..5415228
--- /dev/null
+++ b/src/LuaSL/LuaSL_LSL_tree.h
@@ -0,0 +1,443 @@
1
2#ifndef __LUASL_TREE_H__
3#define __LUASL_TREE_H__
4
5#define LUASL_DEBUG 0
6#define LUASL_DIFF_CHECK 0
7
8
9#include <stddef.h> // So we can have NULL defined.
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13
14#include "assert.h"
15#include <unistd.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <limits.h> // For PATH_MAX.
19
20#include "LuaSL_lemon_yaccer.h"
21
22#define YYERRCODE 256
23#define YYDEBUG 1
24
25
26// http://w-hat.com/stackdepth is a useful discussion about some aspects of the LL parser.
27
28
29typedef struct _allowedTypes allowedTypes;
30typedef struct _LSL_Token LSL_Token;
31typedef struct _LSL_Text LSL_Text;
32typedef struct _LSL_Leaf LSL_Leaf;
33typedef struct _LSL_Numby LSL_Numby;
34typedef struct _LSL_Parenthesis LSL_Parenthesis;
35typedef struct _LSL_Identifier LSL_Identifier;
36typedef struct _LSL_Statement LSL_Statement;
37typedef struct _LSL_Block LSL_Block;
38typedef struct _LSL_Function LSL_Function;
39typedef struct _LSL_FunctionCall LSL_FunctionCall;
40typedef struct _LSL_State LSL_State;
41typedef struct _LSL_Script LSL_Script;
42
43extern LSL_Token **tokens;
44extern int lowestToken;
45
46typedef int LSL_Type;
47
48typedef enum
49{
50 OM_LSL,
51 OM_LUA
52} outputMode;
53
54typedef void (*outputToken) (FILE *file, outputMode mode, LSL_Leaf *content);
55
56//#ifndef FALSE
57//typedef enum
58//{
59// FALSE = 0,
60// TRUE = 1
61//} boolean;
62//#endif
63
64typedef enum
65{
66 LSL_NONE = 0,
67 LSL_LEFT2RIGHT = 1,
68 LSL_RIGHT2LEFT = 2,
69 LSL_INNER2OUTER = 4,
70 LSL_UNARY = 8,
71 LSL_ASSIGNMENT = 16,
72 LSL_CREATION = 32,
73 LSL_NOIGNORE = 64,
74 LSL_TYPE = 128
75} LSL_Flags;
76
77
78// VERY IMPORTANT to keep this in sync with allowedTypes allowed[] from LuaSL_compile.c!
79typedef enum
80{
81 OT_nothing,
82
83 OT_bool,
84 OT_integer,
85 OT_float,
86 OT_key,
87 OT_list,
88 OT_rotation,
89 OT_string,
90 OT_vector,
91 OT_other,
92
93 OT_boolBool,
94 OT_intBool,
95 OT_intInt,
96 OT_intFloat,
97 OT_floatInt,
98 OT_floatFloat,
99 OT_keyKey,
100 OT_keyString,
101 OT_stringKey,
102 OT_stringString,
103 OT_listList,
104 OT_listBool,
105 OT_listInt,
106 OT_listFloat,
107 OT_listString,
108 OT_intList,
109 OT_floatList,
110 OT_listOther,
111 OT_vectorVector,
112 OT_vectorFloat,
113 OT_vectorRotation,
114 OT_rotationRotation,
115 OT_otherOther,
116 OT_undeclared,
117 OT_invalid
118} opType;
119
120typedef enum
121{
122 ST_NONE = 0,
123 ST_ASSIGNMENT = 1, // -= *= /=
124 ST_BIT_NOT = 2, // ~
125 ST_BOOL_NOT = 4, // !
126 ST_BITWISE = 8, // & | ^ << >>
127 ST_BOOLEAN = 16, // && !!
128 ST_COMPARISON = 32, // < > <= >=
129 ST_CONCATENATION = 64, // = +=
130 ST_EQUALITY = 128, // == !=
131 ST_ADD = 512, // +
132 ST_SUBTRACT = 1024, // -
133 ST_NEGATE = 2048, // -
134 ST_MULTIPLY = 4096, // * /
135 ST_MODULO = 8192 // % %=
136} opSubType;
137
138typedef enum
139{
140 MF_NONE = 0,
141 MF_LOCAL = 1,
142 MF_NOASSIGN = 2, // These two are for completely different purposes. This one is for variable definitions with no assignment.
143 MF_ASSIGNEXP = 4, // These two are for completely different purposes. This one is for assignments being used as expressions.
144 MF_WRAPFUNC = 8,
145 MF_PREDEC = 16,
146 MF_PREINC = 32,
147 MF_POSTDEC = 64,
148 MF_POSTINC = 128,
149 MF_LSLCONST = 256,
150 MF_TYPECAST = 512
151} miscFlags;
152
153struct _allowedTypes
154{
155 opType result;
156 const char *name;
157 int subTypes;
158};
159
160struct _LSL_Token
161{
162 LSL_Type type;
163 opSubType subType;
164 const char *toKen;
165 LSL_Flags flags;
166 outputToken output;
167};
168
169struct _LSL_Text
170{
171 const char *text;
172#if LUASL_DIFF_CHECK
173 Eina_Strbuf *ignorable;
174#endif
175};
176
177struct _LSL_Leaf
178{
179 LSL_Leaf *left;
180 LSL_Leaf *right;
181 LSL_Token *toKen;
182#if LUASL_DIFF_CHECK
183 Eina_Strbuf *ignorable;
184#endif
185 int line, column, len;
186 opType basicType;
187 miscFlags flags;
188 union
189 {
190 float floatValue;
191 float vectorValue[3];
192 float rotationValue[4];
193 int integerValue;
194 LSL_Numby *numbyValue;
195 LSL_Leaf *listValue;
196 const char *stringValue;
197 opType operationValue;
198 LSL_Parenthesis *parenthesis;
199 LSL_Identifier *identifierValue;
200 LSL_Statement *statementValue;
201 LSL_Block *blockValue;
202 LSL_Function *functionValue;
203 LSL_FunctionCall *functionCallValue;
204 LSL_State *stateValue;
205 LSL_Script *scriptValue;
206 } value;
207};
208
209struct _LSL_Numby
210{
211 LSL_Text text;
212 LSL_Type type;
213 union
214 {
215 float floatValue;
216 int integerValue;
217 } value;
218};
219
220struct _LSL_Parenthesis
221{
222 LSL_Leaf *contents;
223#if LUASL_DIFF_CHECK
224 Eina_Strbuf *rightIgnorable;
225#endif
226 LSL_Type type;
227 miscFlags flags;
228};
229
230struct _LSL_Identifier // For variables and function parameters.
231{
232 LSL_Text name;
233 LSL_Statement *definition;
234 Eina_Strbuf *ignorable;
235 const char *sub;
236 LSL_Leaf value;
237 miscFlags flags;
238};
239
240struct _LSL_Statement
241{
242 Eina_Clist statement; // For block statement lists, this is the entry.
243 LSL_Text identifier;
244 LSL_Parenthesis *parenthesis;
245 LSL_Leaf *expressions; // A for statement will have three expressions, and two semicolons, everything else has zero or one.
246 LSL_Block *block;
247 LSL_Statement *single; // For single statement "blocks".
248 LSL_Statement *elseBlock;
249 LSL_Type type; // Expression type.
250#if LUASL_DIFF_CHECK
251 Eina_Strbuf **ignorable; // Can be up to five of these I think.
252#endif
253 miscFlags flags;
254};
255
256struct _LSL_Block
257{
258 LSL_Block *outerBlock;
259 Eina_Clist statements; // For statement lists, this is the HEAD.
260 Eina_Hash *variables; // Those variables in this scope.
261 LSL_Function *function; // A pointer to the function if this block is a function.
262#if LUASL_DIFF_CHECK
263 Eina_Strbuf *openIgnorable;
264 Eina_Strbuf *closeIgnorable;
265#endif
266};
267
268struct _LSL_Function
269{
270 LSL_Text name;
271 LSL_Text type;
272 const char *state;
273#if LUASL_DIFF_CHECK
274// LSL_Leaf *params; // So we store the parenthesis, and their ignorables.
275 // This points to the params leaf, which is a function, pointing to this structure. The actual params are in vars.
276#endif
277 Eina_Inarray vars; // Eina Inarray has not been released yet (Eina 1.2).
278 LSL_Block *block;
279 miscFlags flags;
280};
281
282struct _LSL_FunctionCall
283{
284 LSL_Function *function;
285 Eina_Inarray params; // Eina Inarray has not been released yet (Eina 1.2).
286 Eina_Clist dangler; // Entry for function calls used before the function is defined.
287 LSL_Leaf *call; // This is to stash the details for dangling ones, to search later.
288 // The line and column details are needed for bitching, so we need the leaf.
289 // Also need the stringValue for the search.
290 // On top of all that, the leaf is still used in expressions, so need to keep it around and update it when resolving danglers.
291};
292
293struct _LSL_State
294{
295 LSL_Text name;
296 LSL_Text state;
297 LSL_Block *block;
298 Eina_Hash *handlers;
299};
300
301struct _LSL_Script
302{
303 const char *name;
304 Eina_Hash *functions;
305 Eina_Hash *states;
306 Eina_Hash *variables;
307 int bugCount, warningCount;
308};
309
310/* Tracking variables.
311
312There are global variables, block local variables, and function parameters.
313
314For outputting Lua, which is the ultimate goal -
315 track order, name, and type.
316
317For looking them up during the compile -
318 quick access from name.
319
320For validating them during compile -
321 track type.
322
323For outputting LSL to double check -
324 track order, name, type, and white space.
325
326For executing directly from the AST -
327 track order, name, type, and value.
328 In this case, order is only important for functions.
329
330We can assume that names are stringshared. This means we only have to
331compare pointers. It also means the same name stored at diffferent
332scopes, must be stored in separate structures, coz the pointers are the
333same.
334
335Order is taken care of by the AST anyway, but for somethings we want to
336condense the AST down to something more efficient.
337
338On the other hand, no need to micro optimise it just yet, we should be
339able to try out other data structures at a later date, then benchmark
340them with typical scripts.
341
342Right now I see nothing wrong with the current use of hash for script
343and block variables. The same for script states and functions, as well
344as state functions. Though in the near future, they will have similar
345problems to functions I think - the need to track order and white
346space.
347
348Function params got unwieldy. Cleaned that up now.
349
350*/
351
352/* General design.
353
354NOTE We can remove the white space tracking at compile time, as it's
355only a debugging aid. Will be a performance and memory gain for
356productidon use. Tracking values on the other hand will still be useful
357for constants.
358
359The compile process starts with turning tokens into AST nodes connected
360in a tree. During that process the parser wants to condense nodes down
361to more efficient data structures. This is a good idea, as we will
362spend a fair amount of time looking up names, no matter which part of
363the process is important at the time.
364
365Once the parser has condensed things down, it only deals with the
366condensed nodes. So we can get rid of some of the AST parts at this
367time, so long as we keep the relevant information. This is what the
368other data structures above are for. Lemon tries to free the no longer
369needed AST nodes itself, even if we are still using them internally.
370Need to do something about that.
371
372*/
373
374// Define the type for flex and lemon.
375#define YYSTYPE LSL_Leaf
376
377typedef struct
378{
379 gameGlobals *game;
380 Ecore_Con_Client *client;
381 void *scanner; // This should be of type yyscan_t, which is typedef to void * anyway, but that does not get defined until LuaSL_lexer.h, which depends on this struct being defined first.
382 int argc;
383 char **argv;
384 char SID[37];
385 char fileName[PATH_MAX];
386 FILE *file;
387 LSL_Leaf *ast;
388 LSL_Script script;
389 LSL_State state;
390#if LUASL_DIFF_CHECK
391 Eina_Strbuf *ignorable;
392#endif
393 LSL_Leaf *lval;
394 LSL_Block *currentBlock;
395 LSL_Function *currentFunction;
396 Eina_Clist danglingCalls; // HEAD for function calls used before the function is defined.
397 int column, line;
398 int undeclared;
399 boolean inState;
400} LuaSL_compiler;
401
402
403#ifndef excludeLexer
404 #include "LuaSL_lexer.h"
405#endif
406
407
408
409boolean compilerSetup(gameGlobals *ourGlobals);
410boolean compileLSL(gameGlobals *ourGlobals, Ecore_Con_Client *client, char *SID, char *script, boolean doConstants);
411void burnLeaf(void *data);
412
413LSL_Leaf *addBlock(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right);
414LSL_Leaf *addCrement(LuaSL_compiler *compiler, LSL_Leaf *variable, LSL_Leaf *crement, LSL_Type type);
415LSL_Leaf *addFor(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr0, LSL_Leaf *stat0, LSL_Leaf *expr1, LSL_Leaf *stat1, LSL_Leaf *expr2, LSL_Leaf *right, LSL_Leaf *block);
416LSL_Leaf *addFunction(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close);
417LSL_Leaf *addFunctionBody(LuaSL_compiler *compiler, LSL_Leaf *function, LSL_Leaf *block);
418LSL_Leaf *addFunctionCall(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close);
419LSL_Leaf *addIfElse(LuaSL_compiler *compiler, LSL_Leaf *ifBlock, LSL_Leaf *elseBlock);
420LSL_Leaf *addList(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right);
421LSL_Leaf *addNumby(LSL_Leaf *numby);
422LSL_Leaf *addOperation(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right);
423LSL_Leaf *addParameter(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *newParam);
424LSL_Leaf *addParenthesis(LSL_Leaf *lval, LSL_Leaf *expr, LSL_Type type, LSL_Leaf *rval);
425LSL_Leaf *addRotVec(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right);
426LSL_Leaf *addState(LuaSL_compiler *compiler, LSL_Leaf *state, LSL_Leaf *identifier, LSL_Leaf *block);
427LSL_Leaf *addStatement(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr, LSL_Leaf *right, LSL_Leaf *block, LSL_Leaf *identifier);
428LSL_Leaf *addTypecast(LSL_Leaf *lval, LSL_Leaf *type, LSL_Leaf *rval, LSL_Leaf *expr);
429LSL_Leaf *addVariable(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *assignment, LSL_Leaf *expr);
430
431LSL_Leaf *beginBlock(LuaSL_compiler *compiler, LSL_Leaf *block);
432LSL_Leaf *checkVariable(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *dot, LSL_Leaf *sub);
433LSL_Leaf *collectArguments(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *arg);
434LSL_Leaf *collectParameters(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *newParam);
435LSL_Leaf *collectStatements(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *newStatement);
436
437void *ParseAlloc(void *(*mallocProc)(size_t));
438void ParseTrace(FILE *TraceFILE, char *zTracePrompt);
439void Parse(void *yyp, int yymajor, LSL_Leaf *yyminor, LuaSL_compiler *compiler);
440void ParseFree(void *p, void (*freeProc)(void*));
441
442
443#endif // __LUASL_LSL_TREE_H__
diff --git a/src/LuaSL/LuaSL_compile.c b/src/LuaSL/LuaSL_compile.c
new file mode 100644
index 0000000..771888e
--- /dev/null
+++ b/src/LuaSL/LuaSL_compile.c
@@ -0,0 +1,2345 @@
1
2#include "LuaSL.h"
3
4/* TODO - problem de jour
5*/
6
7
8static void outputBitOp(FILE *file, outputMode mode, LSL_Leaf *leaf);
9static void outputBlockToken(FILE *file, outputMode mode, LSL_Leaf *content);
10static void outputCrementsToken(FILE *file, outputMode mode, LSL_Leaf *content);
11static void outputFloatToken(FILE *file, outputMode mode, LSL_Leaf *content);
12static void outputFunctionToken(FILE *file, outputMode mode, LSL_Leaf *content);
13static void outputFunctionCallToken(FILE *file, outputMode mode, LSL_Leaf *content);
14static void outputIntegerToken(FILE *file, outputMode mode, LSL_Leaf *content);
15static void outputIdentifierToken(FILE *file, outputMode mode, LSL_Leaf *content);
16static void outputListToken(FILE *file, outputMode mode, LSL_Leaf *content);
17static void outputParameterListToken(FILE *file, outputMode mode, LSL_Leaf *content);
18static void outputParenthesisToken(FILE *file, outputMode mode, LSL_Leaf *content);
19static void outputStateToken(FILE *file, outputMode mode, LSL_Leaf *content);
20static void outputStatementToken(FILE *file, outputMode mode, LSL_Leaf *content);
21static void outputStringToken(FILE *file, outputMode mode, LSL_Leaf *content);
22
23LSL_Token LSL_Tokens[] =
24{
25 // Various forms of "space".
26 {LSL_COMMENT, ST_NONE, "/*", LSL_NONE, NULL},
27 {LSL_COMMENT_LINE, ST_NONE, "//", LSL_NONE, NULL},
28 {LSL_SPACE, ST_NONE, " ", LSL_NONE, NULL},
29
30 // Operators, in order of precedence, low to high
31 // Left to right, unless otherwise stated.
32 // According to http://wiki.secondlife.com/wiki/Category:LSL_Operators, which was obsoleted by http://wiki.secondlife.com/wiki/LSL_Operators but that has less info.
33
34 {LSL_ASSIGNMENT_CONCATENATE,ST_CONCATENATION, "+=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
35 {LSL_ASSIGNMENT_ADD, ST_CONCATENATION, "+=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
36 {LSL_ASSIGNMENT_SUBTRACT, ST_ASSIGNMENT, "-=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
37 {LSL_ASSIGNMENT_MULTIPLY, ST_ASSIGNMENT, "*=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
38 {LSL_ASSIGNMENT_MODULO, ST_MODULO, "%=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
39 {LSL_ASSIGNMENT_DIVIDE, ST_ASSIGNMENT, "/=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
40 {LSL_ASSIGNMENT_PLAIN, ST_CONCATENATION, "=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
41
42 {LSL_BOOL_AND, ST_BOOLEAN, "&&", LSL_RIGHT2LEFT, NULL},
43// QUIRK - Seems to be some disagreement about BOOL_AND/BOOL_OR precedence. Either they are equal, or OR is higher.
44// QUIRK - No boolean short circuiting.
45// LUA - Short circiuts boolean operations, and goes left to right.
46// LUA - "and" returns its first argument if it is false, otherwise, it returns its second argument. "or" returns its first argument if it is not false, otherwise it returns its second argument.
47// Note that the above means that "and/or" can return any type.
48 {LSL_BOOL_OR, ST_BOOLEAN, "||", LSL_RIGHT2LEFT, NULL},
49 {LSL_BIT_OR, ST_BITWISE, "|", LSL_LEFT2RIGHT, outputBitOp},
50 {LSL_BIT_XOR, ST_BITWISE, "^", LSL_LEFT2RIGHT, outputBitOp},
51 {LSL_BIT_AND, ST_BITWISE, "&", LSL_LEFT2RIGHT, outputBitOp},
52// QUIRK - Booleans and conditionals are executed right to left. Or maybe not, depending on who you believe.
53 {LSL_NOT_EQUAL, ST_EQUALITY, "!=", LSL_RIGHT2LEFT, NULL},
54 {LSL_EQUAL, ST_EQUALITY, "==", LSL_RIGHT2LEFT, NULL},
55 {LSL_GREATER_EQUAL, ST_COMPARISON, ">=", LSL_RIGHT2LEFT, NULL},
56 {LSL_LESS_EQUAL, ST_COMPARISON, "<=", LSL_RIGHT2LEFT, NULL},
57 {LSL_GREATER_THAN, ST_COMPARISON, ">", LSL_RIGHT2LEFT, NULL},
58 {LSL_LESS_THAN, ST_COMPARISON, "<", LSL_RIGHT2LEFT, NULL},
59// LUA - comparisons are always false if they are different types. Tables, userdata, and functions are compared by reference. Strings compare in alphabetical order, depending on current locale.
60// LUA - really only has three conditionals, as it translates a ~= b to not (a == b), a > b to b < a, and a >= b to b <= a.
61 {LSL_RIGHT_SHIFT, ST_BITWISE, ">>", LSL_LEFT2RIGHT, outputBitOp},
62 {LSL_LEFT_SHIFT, ST_BITWISE, "<<", LSL_LEFT2RIGHT, outputBitOp},
63 {LSL_CONCATENATE, ST_ADD, "+", LSL_LEFT2RIGHT, NULL},
64 {LSL_ADD, ST_ADD, "+", LSL_LEFT2RIGHT, NULL},
65 {LSL_SUBTRACT, ST_SUBTRACT, "-", LSL_LEFT2RIGHT, NULL},
66 {LSL_CROSS_PRODUCT, ST_NONE, "%", LSL_LEFT2RIGHT, NULL},
67 {LSL_DOT_PRODUCT, ST_NONE, "*", LSL_LEFT2RIGHT, NULL},
68 {LSL_MULTIPLY, ST_MULTIPLY, "*", LSL_LEFT2RIGHT, NULL},
69 {LSL_MODULO, ST_MODULO, "%", LSL_LEFT2RIGHT, NULL},
70 {LSL_DIVIDE, ST_MULTIPLY, "/", LSL_LEFT2RIGHT, NULL},
71 {LSL_NEGATION, ST_NEGATE, "-", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
72 {LSL_BOOL_NOT, ST_BOOL_NOT, "!", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
73 {LSL_BIT_NOT, ST_BIT_NOT, "~", LSL_RIGHT2LEFT | LSL_UNARY, outputBitOp},
74
75// LUA precedence - (it has no bit operators, at least not until 5.2, but LuaJIT has them as table functions.)
76// or
77// and
78// < > <= >= ~= ==
79// ..
80// + -
81// * /
82// not negate
83// exponentiation (^)
84
85 {LSL_TYPECAST_CLOSE, ST_NONE, ")", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
86 {LSL_TYPECAST_OPEN, ST_NONE, "(", LSL_RIGHT2LEFT | LSL_UNARY, outputParenthesisToken},
87 {LSL_ANGLE_CLOSE, ST_NONE, ">", LSL_LEFT2RIGHT | LSL_CREATION, NULL},
88 {LSL_ANGLE_OPEN, ST_NONE, "<", LSL_LEFT2RIGHT | LSL_CREATION, NULL},
89 {LSL_BRACKET_CLOSE, ST_NONE, "]", LSL_INNER2OUTER | LSL_CREATION, NULL},
90 {LSL_BRACKET_OPEN, ST_NONE, "[", LSL_INNER2OUTER | LSL_CREATION, NULL},
91 {LSL_PARENTHESIS_CLOSE, ST_NONE, ")", LSL_INNER2OUTER, NULL},
92 {LSL_PARENTHESIS_OPEN, ST_NONE, "(", LSL_INNER2OUTER, outputParenthesisToken},
93 {LSL_DOT, ST_NONE, ".", LSL_RIGHT2LEFT, NULL},
94 {LSL_DECREMENT_POST, ST_NONE, "--", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
95 {LSL_DECREMENT_PRE, ST_NONE, "--", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
96 {LSL_INCREMENT_POST, ST_NONE, "++", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
97 {LSL_INCREMENT_PRE, ST_NONE, "++", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
98 {LSL_COMMA, ST_NONE, ",", LSL_LEFT2RIGHT, NULL},
99
100 {LSL_EXPRESSION, ST_NONE, "expression", LSL_NONE , NULL},
101
102 // Types.
103 {LSL_FLOAT, ST_NONE, "float", LSL_NONE, outputFloatToken},
104 {LSL_INTEGER, ST_NONE, "integer", LSL_NONE, outputIntegerToken},
105 {LSL_KEY, ST_NONE, "key", LSL_NONE, outputStringToken},
106 {LSL_LIST, ST_NONE, "list", LSL_NONE, outputListToken},
107 {LSL_ROTATION, ST_NONE, "rotation", LSL_NONE, outputListToken},
108 {LSL_STRING, ST_NONE, "string", LSL_NONE, outputStringToken},
109 {LSL_VECTOR, ST_NONE, "vector", LSL_NONE, outputListToken},
110
111 // Types names.
112 {LSL_TYPE_FLOAT, ST_NONE, "float", LSL_TYPE, NULL},
113 {LSL_TYPE_INTEGER, ST_NONE, "integer", LSL_TYPE, NULL},
114 {LSL_TYPE_KEY, ST_NONE, "key", LSL_TYPE, NULL},
115 {LSL_TYPE_LIST, ST_NONE, "list", LSL_TYPE, NULL},
116 {LSL_TYPE_ROTATION, ST_NONE, "rotation", LSL_TYPE, NULL},
117 {LSL_TYPE_STRING, ST_NONE, "string", LSL_TYPE, NULL},
118 {LSL_TYPE_VECTOR, ST_NONE, "vector", LSL_TYPE, NULL},
119
120 // Then the rest of the syntax tokens.
121 {LSL_FUNCTION_CALL, ST_NONE, "funccall", LSL_NONE, outputFunctionCallToken},
122 {LSL_IDENTIFIER, ST_NONE, "identifier", LSL_NONE, outputIdentifierToken},
123 {LSL_VARIABLE, ST_NONE, "variable", LSL_NONE, outputIdentifierToken},
124
125 {LSL_LABEL, ST_NONE, "@", LSL_NONE, NULL},
126
127 {LSL_DO, ST_NONE, "do", LSL_NONE, NULL},
128 {LSL_FOR, ST_NONE, "for", LSL_NONE, NULL},
129 {LSL_ELSE, ST_NONE, "else", LSL_NONE, NULL},
130 {LSL_ELSEIF, ST_NONE, "elseif", LSL_NONE, NULL},
131 {LSL_IF, ST_NONE, "if", LSL_NONE, NULL},
132 {LSL_JUMP, ST_NONE, "jump", LSL_NONE, NULL},
133 {LSL_RETURN, ST_NONE, "return", LSL_NONE, NULL},
134 {LSL_STATE_CHANGE, ST_NONE, "state", LSL_NONE, NULL},
135 {LSL_WHILE, ST_NONE, "while", LSL_NONE, NULL},
136 {LSL_STATEMENT, ST_NONE, ";", LSL_NOIGNORE, outputStatementToken},
137
138 {LSL_BLOCK_CLOSE, ST_NONE, "}", LSL_NONE, NULL},
139 {LSL_BLOCK_OPEN, ST_NONE, "{", LSL_NONE, outputBlockToken},
140 {LSL_PARAMETER, ST_NONE, "parameter", LSL_NONE, outputIdentifierToken},
141 {LSL_PARAMETER_LIST, ST_NONE, "plist", LSL_NONE, outputParameterListToken},
142 {LSL_FUNCTION, ST_NONE, "function", LSL_NONE, outputFunctionToken},
143 {LSL_DEFAULT, ST_NONE, "default", LSL_NONE, outputStateToken},
144 {LSL_STATE, ST_NONE, "state", LSL_NONE, outputStateToken},
145 {LSL_SCRIPT, ST_NONE, "", LSL_NONE, NULL},
146
147 {LSL_UNKNOWN, ST_NONE, "unknown", LSL_NONE, NULL},
148
149 // A sentinal.
150 {999999, ST_NONE, NULL, LSL_NONE, NULL}
151};
152
153// VERY IMPORTANT to keep this in sync with enum opType from LuaSL_LSL_tree.h!
154allowedTypes allowed[] =
155{
156 {OT_nothing, "nothing", (ST_NONE)}, //
157
158 {OT_bool, "boolean", (ST_BOOL_NOT)}, // bool !
159 {OT_integer, "integer", (ST_BOOL_NOT | ST_BIT_NOT | ST_NEGATE)}, // int ! - ~
160 {OT_float, "float", (ST_BOOL_NOT | ST_NEGATE)}, // float ! -
161 {OT_key, "key", (ST_BOOL_NOT)}, // key !
162 {OT_list, "list", (ST_NONE)}, //
163 {OT_rotation, "rotation", (ST_NONE)}, //
164 {OT_string, "string", (ST_BOOL_NOT)}, // string !
165 {OT_vector, "vector", (ST_NONE)}, //
166 {OT_other, "other", (ST_NONE)}, //
167
168 {OT_bool, "boolean", (ST_BOOLEAN | ST_EQUALITY)}, // bool bool == != = && ||
169
170 {OT_integer, "integer", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO | ST_BITWISE)}, // int boolean * / + - % == != < > <= >= = += -= *= /= %= & | ^ << >>
171 {OT_integer, "integer", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO | ST_BITWISE)}, // int int * / + - % == != < > <= >= = += -= *= /= %= & | ^ << >>
172 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // int float cast to float float
173 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // float int cast to float float
174 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // float float * / + - == != < > <= >= = += -= *= /=
175
176 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // key key cast to string string
177 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // key string cast to string string
178 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // string key cast to string string
179 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // string string + == != = +=
180
181 {OT_list, "list", (ST_ADD | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT )}, // list list + == != = +=
182 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list boolean + < > <= >= = +=
183 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list integer + < > <= >= = +=
184 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list float + < > <= >= = +=
185 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list string + < > <= >= = +=
186 {OT_integer, "integer", (ST_ADD | ST_COMPARISON)}, // integer list + < > <= >=
187 {OT_float, "float", (ST_ADD | ST_COMPARISON)}, // float list + < > <= >=
188 {OT_list, "list", (ST_ADD | ST_CONCATENATION)}, // list other + = +=
189
190 {OT_vector, "vector", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO)}, // vector vector * / + - % == != = += -= *= /= %=
191 {OT_vector, "vector", (ST_MULTIPLY | ST_ASSIGNMENT)}, // vector float * / *= /=
192 {OT_vector, "vector", (ST_MULTIPLY)}, // vector rotation * /
193
194 {OT_rotation, "rotation", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT)}, // rotation rotation * / + - == != = += -= *= /=
195
196 {OT_other, "other", (ST_NONE)}, //
197 {OT_undeclared, "undeclared", (ST_NONE)}, //
198 {OT_invalid, "invalid", (ST_NONE)} //
199};
200
201opType opExpr[][10] =
202{
203 {OT_nothing, OT_bool, OT_integer, OT_float, OT_key, OT_list, OT_rotation, OT_string, OT_vector, OT_other},
204 {OT_bool, OT_boolBool, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
205 {OT_integer, OT_intBool, OT_intInt, OT_intFloat, OT_invalid, OT_intList, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
206 {OT_float, OT_invalid, OT_floatInt, OT_floatFloat, OT_invalid, OT_floatList, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
207 {OT_key, OT_invalid, OT_invalid, OT_invalid, OT_keyKey, OT_invalid, OT_invalid, OT_keyString, OT_invalid, OT_invalid},
208 {OT_list, OT_listBool, OT_listInt, OT_listFloat, OT_invalid, OT_listList, OT_invalid, OT_listString, OT_invalid, OT_listOther},
209 {OT_rotation, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_rotationRotation, OT_invalid, OT_invalid, OT_invalid},
210 {OT_string, OT_invalid, OT_invalid, OT_invalid, OT_stringKey, OT_invalid, OT_invalid, OT_stringString, OT_invalid, OT_invalid},
211 {OT_vector, OT_invalid, OT_invalid, OT_vectorFloat, OT_invalid, OT_invalid, OT_vectorRotation, OT_invalid, OT_vectorVector, OT_invalid},
212 {OT_other, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_otherOther}
213};
214
215
216LSL_Token **tokens = NULL;
217LSL_Script constants;
218int lowestToken = 999999;
219
220
221static LSL_Leaf *newLeaf(LSL_Type type, LSL_Leaf *left, LSL_Leaf *right)
222{
223 LSL_Leaf *leaf = calloc(1, sizeof(LSL_Leaf));
224
225 if (leaf)
226 {
227 leaf->left = left;
228 leaf->right = right;
229 leaf->toKen = tokens[type - lowestToken];
230 }
231
232 return leaf;
233}
234
235void burnLeaf(void *data)
236{
237 LSL_Leaf *leaf = data;
238
239 if (leaf)
240 {
241// TODO - the problem here is that lemon wants to free these after a reduce, but we might want to keep them around. Should ref count them or something.
242// burnLeaf(leaf->left);
243// burnLeaf(leaf->right);
244 // TODO - Should free up the value to.
245//#if LUASL_DIFF_CHECK
246// eina_strbuf_free(leaf->ignorable);
247//#endif
248// free(leaf);
249 }
250}
251
252static LSL_Leaf *findFunction(LuaSL_compiler *compiler, const char *name)
253{
254 LSL_Leaf *func = NULL;
255
256 if (name)
257 {
258 if (NULL == func)
259 func = eina_hash_find(constants.functions, name);
260 if (NULL != func)
261 {
262 func->flags |= MF_LSLCONST;
263 func->value.functionValue->flags |= MF_LSLCONST;
264 }
265 else
266 func = eina_hash_find(compiler->script.functions, name);
267
268 }
269
270 return func;
271}
272
273static LSL_Leaf *findVariable(LuaSL_compiler *compiler, const char *name)
274{
275 LSL_Leaf *var = NULL;
276
277 if (name)
278 {
279 LSL_Block *block = compiler->currentBlock;
280
281 while ((block) && (NULL == var))
282 {
283 if (block->function)
284 {
285 LSL_Leaf *param = NULL;
286 EINA_INARRAY_FOREACH((&(block->function->vars)), param)
287 {
288 if ((param) && (LSL_PARAMETER == param->toKen->type))
289 {
290// if (name == param->value.identifierValue->name.text) // Assuming they are stringshares.
291 if (0 == strcmp(name, param->value.identifierValue->name.text)) // Not assuming they are stringeshares. They should be.
292 var = param;
293 }
294 }
295 }
296 if ((NULL == var) && block->variables)
297 var = eina_hash_find(block->variables, name);
298 block = block->outerBlock;
299 }
300
301 if (NULL == var)
302 {
303 var = eina_hash_find(constants.variables, name);
304 if (var)
305 {
306 var->flags |= MF_LSLCONST;
307 var->value.identifierValue->flags |= MF_LSLCONST;
308 }
309 }
310 if (NULL == var)
311 var = eina_hash_find(compiler->script.variables, name);
312 }
313
314 return var;
315}
316
317LSL_Leaf *checkVariable(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *dot, LSL_Leaf *sub)
318{
319 gameGlobals *ourGlobals = compiler->game;
320 const char *search;
321
322 if (dot)
323 search = identifier->value.identifierValue->name.text;
324 else
325 search = identifier->value.stringValue;
326
327 if (identifier)
328 {
329 LSL_Leaf *var = findVariable(compiler, search);
330
331 if (var)
332 {
333 if (LUASL_DEBUG)
334 PI("Found %s!", identifier->value.stringValue);
335 identifier->value.identifierValue = var->value.identifierValue;
336 identifier->basicType = var->basicType;
337 if ((dot) && (sub))
338 {
339 LSL_Identifier *id = calloc(1, sizeof(LSL_Identifier));
340
341 if (id)
342 {
343 memcpy(id, var->value.identifierValue, sizeof(LSL_Identifier));
344 identifier->value.identifierValue = id;
345 if (LSL_ROTATION == var->toKen->type)
346 {
347 // TODO - check if it's one of x, y, z, or s.
348 }
349 if (LSL_VECTOR == var->toKen->type)
350 {
351 // TODO - check if it's one of x, y, or z.
352 }
353 identifier->value.identifierValue->sub = sub->value.stringValue;
354 identifier->basicType = OT_float;
355 }
356 }
357 }
358 else
359 {
360 compiler->script.bugCount++;
361 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerError(%d,%d,NOT found %s)", identifier->line, identifier->column, identifier->value.stringValue);
362 }
363 }
364
365 return identifier;
366}
367
368LSL_Leaf *addOperation(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right)
369{
370 gameGlobals *ourGlobals = compiler->game;
371
372 if (lval)
373 {
374 opType lType, rType;
375
376 lval->left = left;
377 lval->right = right;
378
379 // Convert subtract to negate if needed.
380 if ((NULL == left) && (LSL_SUBTRACT == lval->toKen->type))
381 lval->toKen = tokens[LSL_NEGATION - lowestToken];
382
383 // Try to figure out what type of operation this is.
384 if (NULL == left)
385 lType = OT_nothing;
386 else
387 {
388 if ((left->toKen) && (LSL_IDENTIFIER == left->toKen->type) && (left->value.identifierValue))
389 {
390 LSL_Leaf *var = findVariable(compiler, left->value.identifierValue->name.text);
391
392 if (var)
393 lType = var->basicType;
394 if (left->value.identifierValue->sub)
395 {
396 // TODO - keep an eye on this, but I think all the sub types are floats.
397 lType = OT_float;
398 }
399 }
400 else
401 lType = left->basicType;
402 if (OT_undeclared == lType)
403 {
404 compiler->script.warningCount++;
405 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerWarning(%d,%d,Undeclared identifier issue, deferring this until the second pass)", lval->line, lval->column);
406 lval->basicType = OT_undeclared;
407 return lval;
408 }
409 if (OT_vector < lType)
410 lType = allowed[lType].result;
411 }
412 if (NULL == right)
413 rType = OT_nothing;
414 else
415 {
416 if ((right->toKen) && (LSL_IDENTIFIER == right->toKen->type) && (right->value.identifierValue))
417 {
418 LSL_Leaf *var = findVariable(compiler, right->value.identifierValue->name.text);
419
420 if (var)
421 rType = var->basicType;
422 if (right->value.identifierValue->sub)
423 {
424 // TODO - keep an eye on this, but I think all the sub types are floats.
425 rType = OT_float;
426 }
427 }
428 else
429 rType = right->basicType;
430 if (OT_undeclared == rType)
431 {
432 compiler->script.warningCount++;
433 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerWarning(%d,%d,Undeclared identifier issue, deferring this until the second pass)", lval->line, lval->column);
434 lval->basicType = OT_undeclared;
435 return lval;
436 }
437 if (OT_vector < rType)
438 rType = allowed[rType].result;
439 }
440
441 // Convert add to concatenate if needed.
442 if ((LSL_ADD == lval->toKen->type) && (OT_string == lType) && (OT_string == rType))
443 lval->toKen = tokens[LSL_CONCATENATE - lowestToken];
444
445 switch (lval->toKen->subType)
446 {
447 case ST_BOOLEAN :
448 case ST_COMPARISON :
449 case ST_EQUALITY :
450 lval->basicType = OT_bool;
451 break;
452 default :
453 // The basic lookup.
454 lval->basicType = opExpr[lType][rType];
455 if (OT_invalid != lval->basicType)
456 {
457 // Check if it's an allowed operation.
458 if (0 == (lval->toKen->subType & allowed[lval->basicType].subTypes))
459 lval->basicType = OT_invalid;
460 else
461 {
462 // Double check the corner cases.
463 switch (lval->toKen->subType)
464 {
465 case ST_MULTIPLY :
466 if (OT_vectorVector == lval->basicType)
467 {
468 if (LSL_MULTIPLY == lval->toKen->type)
469 {
470 lval->basicType = OT_float;
471 lval->toKen = tokens[LSL_DOT_PRODUCT - lowestToken];
472 }
473 else
474 lval->basicType = OT_vector;
475 }
476 break;
477 default :
478 break;
479 }
480 }
481 }
482 break;
483 }
484
485 /* Flag assignments for the "assignments are statements, which can't happen inside expressions" test.
486 *
487 * Assignments in the middle of expressions are legal in LSL, but not in Lua.
488 * The big complication is that they often happen in the conditionals of flow control statements. That's a big bitch.
489 *
490 * So things like -
491 *
492 * while ((x = doSomething()) == foo)
493 * {
494 * buggerAround();
495 * }
496 *
497 * Turns into -
498 *
499 * x = doSomething();
500 * while (x == foo)
501 * {
502 * buggerAround();
503 * x = doSomething();
504 * }
505 *
506 * http://lua-users.org/wiki/StatementsInExpressions was helpful. Which suggests something like this -
507 *
508 * while ( (function() x = doSomething(); return x; end)() == foo)
509 * {
510 * buggerAround();
511 * }
512 *
513 * The remaining problem is when to recognise the need to do that.
514 * That's what this code and the matching code in addParenthesis() does.
515 */
516 // TODO - Only got one of these in my test scripts, so leave all this debugging shit in until it's been tested more.
517 if (left)
518 {
519 if (left->flags & MF_ASSIGNEXP)
520 {
521//if ((left) && (right))
522// printf("%s %s %s\n", left->toKen->toKen, lval->toKen->toKen, right->toKen->toKen);
523//else if (left)
524// printf("%s %s NORIGHT\n", left->toKen->toKen, lval->toKen->toKen);
525//else if (right)
526// printf("NOLEFT %s %s\n", lval->toKen->toKen, right->toKen->toKen);
527//else
528// printf("NOLEFT %s NORIGHT\n", lval->toKen->toKen);
529// printf("############################################################################## left\n");
530 left->flags |= MF_WRAPFUNC;
531 if (LSL_PARENTHESIS_OPEN == left->toKen->type)
532 left->value.parenthesis->flags |= MF_WRAPFUNC;
533 }
534 }
535 if (lval)
536 {
537// if (lval->flags & MF_ASSIGNEXP)
538// printf("############################################################################## lval %s %s\n", left->toKen->toKen, right->toKen->toKen);
539 if (LSL_ASSIGNMENT & lval->toKen->flags)
540 {
541 lval->flags |= MF_ASSIGNEXP;
542//// printf("******************* lval %s %s\n", left->toKen->toKen, right->toKen->toKen);
543 if (LSL_IDENTIFIER == left->toKen->type) // It always should be.
544 {
545 left->flags |= MF_ASSIGNEXP;
546//// printf("$$$$$$$$$$$$$$$$$ lval\n");
547 }
548 }
549 }
550 // TODO - Don't think I have to do this on the right.
551 if (right)
552 {
553 if (right->flags & MF_ASSIGNEXP)
554 {
555if ((left) && (right))
556 printf("%s %s %s\n", left->toKen->toKen, lval->toKen->toKen, right->toKen->toKen);
557else if (left)
558 printf("%s %s NORIGHT\n", left->toKen->toKen, lval->toKen->toKen);
559else if (right)
560 printf("NOLEFT %s %s\n", lval->toKen->toKen, right->toKen->toKen);
561else
562 printf("NOLEFT %s NORIGHT\n", lval->toKen->toKen);
563 printf("############################################################################## right\n");
564 right->flags |= MF_WRAPFUNC;
565 }
566 }
567
568 if (OT_invalid == lval->basicType)
569 {
570 const char *leftType = "", *rightType = "", *leftToken = "", *rightToken = "";
571
572 if (left)
573 {
574 if (left->toKen)
575 leftToken = left->toKen->toKen;
576 else
577 PE("BROKEN LEFT TOKEN!!!!!!!!!!!!!!!!!!");
578 leftType = allowed[left->basicType].name;
579 }
580 if (right)
581 {
582 if (right->toKen)
583 rightToken = right->toKen->toKen;
584 else
585 PE("BROKEN RIGHT TOKEN!!!!!!!!!!!!!!!!!!");
586 rightType = allowed[right->basicType].name;
587 }
588
589 compiler->script.bugCount++;
590 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerError(%d,%d,Invalid operation [%s(%s) %s %s(%s)])", lval->line, lval->column, leftType, leftToken, lval->toKen->toKen, rightType, rightToken);
591 }
592 }
593
594 return lval;
595}
596
597LSL_Leaf *addBlock(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right)
598{
599 // Damn, look ahead. The } symbol is getting read (and thus endBlock called) before the last statement in the block is reduced (which actually calls the add*() functions).
600 compiler->currentBlock = compiler->currentBlock->outerBlock;
601#if LUASL_DIFF_CHECK
602 if ((left) && (right))
603 {
604 left->value.blockValue->closeIgnorable = right->ignorable;
605 right->ignorable = NULL;
606 }
607#endif
608 return lval;
609}
610
611LSL_Leaf *addCrement(LuaSL_compiler *compiler, LSL_Leaf *variable, LSL_Leaf *crement, LSL_Type type)
612{
613 if ((variable) && (crement))
614 {
615 crement->value.identifierValue = variable->value.identifierValue;
616#if LUASL_DIFF_CHECK
617 crement->value.identifierValue->ignorable = variable->ignorable;
618 variable->ignorable = NULL;
619#endif
620 crement->basicType = variable->basicType;
621 crement->toKen = tokens[type - lowestToken];
622 switch (crement->toKen->type)
623 {
624 case LSL_DECREMENT_PRE : variable->value.identifierValue->flags |= MF_PREDEC; break;
625 case LSL_INCREMENT_PRE : variable->value.identifierValue->flags |= MF_PREINC; break;
626 case LSL_DECREMENT_POST : variable->value.identifierValue->flags |= MF_POSTDEC; break;
627 case LSL_INCREMENT_POST : variable->value.identifierValue->flags |= MF_POSTINC; break;
628 }
629 variable->value.identifierValue->definition->flags = variable->value.identifierValue->flags;
630 }
631
632 return crement;
633}
634
635LSL_Leaf *addParameter(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier)
636{
637 LSL_Identifier *result = calloc(1, sizeof(LSL_Identifier));
638
639 if ( (identifier) && (result))
640 {
641 result->name.text = identifier->value.stringValue;
642#if LUASL_DIFF_CHECK
643 result->name.ignorable = identifier->ignorable;
644 identifier->ignorable = NULL;
645#endif
646 result->value.toKen = tokens[LSL_UNKNOWN - lowestToken];
647 identifier->value.identifierValue = result;
648 identifier->toKen = tokens[LSL_PARAMETER - lowestToken];
649 identifier->left = type;
650 if (type)
651 {
652 identifier->basicType = type->basicType;
653 result->value.basicType = type->basicType;
654 result->value.toKen = type->toKen; // This is the LSL_TYPE_* toKen instead of the LSL_* toKen. Not sure if that's a problem.
655 }
656 }
657 return identifier;
658}
659
660LSL_Leaf *collectParameters(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *newParam)
661{
662 LSL_Function *func = NULL;
663
664 if (NULL == list)
665 list = newLeaf(LSL_FUNCTION, NULL, NULL);
666
667 if (list)
668 {
669 func = list->value.functionValue;
670 if (NULL == func)
671 {
672 func = calloc(1, sizeof(LSL_Function));
673 if (func)
674 {
675 list->value.functionValue = func;
676 eina_inarray_step_set(&(func->vars), sizeof(Eina_Inarray), sizeof(LSL_Leaf), 3);
677 }
678 }
679
680 if (func)
681 {
682 if (newParam)
683 {
684 if (LUASL_DIFF_CHECK)
685 {
686 // Stash the comma for diff later.
687 if (comma)
688 eina_inarray_push(&(func->vars), comma);
689 }
690 eina_inarray_push(&(func->vars), newParam);
691 // At this point, pointers to newParams are not pointing to the one in func->vars, AND newParam is no longer needed.
692 }
693 }
694 }
695 return list;
696}
697
698LSL_Leaf *addFunction(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close)
699{
700 LSL_Function *func = NULL;
701
702 if (params)
703 {
704 func = params->value.functionValue;
705 // At this point, params is no longer needed, except if we are doing diff.
706 // open and close are not needed either if we are not doing diff.
707 if (func)
708 {
709 if (identifier)
710 {
711 func->name.text = identifier->value.stringValue;
712#if LUASL_DIFF_CHECK
713 func->name.ignorable = identifier->ignorable;
714 identifier->ignorable = NULL;
715#endif
716 identifier->toKen = tokens[LSL_FUNCTION - lowestToken];
717 identifier->value.functionValue = func;
718 if (type)
719 {
720 func->type.text = type->toKen->toKen;
721#if LUASL_DIFF_CHECK
722 func->type.ignorable = type->ignorable;
723 type->ignorable = NULL;
724#endif
725 identifier->basicType = type->basicType;
726 }
727 else
728 identifier->basicType = OT_nothing;
729 if (compiler->inState)
730 eina_hash_add(compiler->state.handlers, func->name.text, func);
731 else
732 eina_hash_add(compiler->script.functions, func->name.text, identifier);
733#if LUASL_DIFF_CHECK
734// func->params = addParenthesis(open, params, LSL_PARAMETER_LIST, close);
735#endif
736 }
737 compiler->currentFunction = func;
738 }
739 }
740
741 return identifier;
742}
743
744LSL_Leaf *addFunctionBody(LuaSL_compiler *compiler, LSL_Leaf *function, LSL_Leaf *block)
745{
746 LSL_Leaf *statement = NULL;
747
748 if (function)
749 {
750 function->value.functionValue->block = block->value.blockValue;
751 statement = addStatement(compiler, NULL, function, NULL, function, NULL, NULL, NULL);
752 }
753
754 return statement;
755}
756
757LSL_Leaf *collectArguments(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *arg)
758{
759 LSL_FunctionCall *call = NULL;
760
761 if (NULL == list)
762 list = newLeaf(LSL_FUNCTION_CALL, NULL, NULL);
763
764 if (list)
765 {
766 call = list->value.functionCallValue;
767 if (NULL == call)
768 {
769 call = calloc(1, sizeof(LSL_FunctionCall));
770 if (call)
771 {
772 list->value.functionCallValue = call;
773 eina_inarray_step_set(&(call->params), sizeof(Eina_Inarray), sizeof(LSL_Leaf), 3);
774 }
775 }
776
777 if (call)
778 {
779 if (arg)
780 {
781 if (LUASL_DIFF_CHECK)
782 {
783 // Stash the comma for diff later.
784 if (comma)
785 eina_inarray_push(&(call->params), comma);
786 }
787 eina_inarray_push(&(call->params), arg);
788 // At this point, pointers to arg are not pointing to the one in call->params, AND arg is no longer needed.
789 }
790 }
791 }
792 return list;
793}
794
795LSL_Leaf *addFunctionCall(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close)
796{
797 LSL_Leaf *func = findFunction(compiler, identifier->value.stringValue);
798 LSL_FunctionCall *call = NULL;
799
800 if (params)
801 {
802 call = params->value.functionCallValue;
803 }
804 else
805 call = calloc(1, sizeof(LSL_FunctionCall));
806
807 if (func)
808 {
809 if (call)
810 {
811 call->function = func->value.functionValue;
812 eina_clist_element_init(&(call->dangler));
813 }
814 identifier->value.functionCallValue = call;
815 identifier->toKen = tokens[LSL_FUNCTION_CALL - lowestToken];
816 identifier->basicType = func->basicType;
817 }
818 else
819 {
820 // It may be declared later, so store it and check later.
821 if (call)
822 {
823 eina_clist_add_tail(&(compiler->danglingCalls), &(call->dangler));
824 call->call = identifier;
825 }
826 // Here the identifier stringValue needs to be kept for later searching.
827 identifier->toKen = tokens[LSL_UNKNOWN - lowestToken];
828 identifier->basicType = OT_undeclared;
829 compiler->undeclared = TRUE;
830 }
831
832 return identifier;
833}
834
835LSL_Leaf *addList(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right)
836{
837 left = addParenthesis(left, list, LSL_LIST, right);
838 left->toKen = tokens[LSL_LIST - lowestToken];
839 left->basicType = OT_list;
840 return left;
841}
842
843LSL_Leaf *addRotVec(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right)
844{
845 LSL_Type type = LSL_ROTATION;
846 opType otype = OT_rotation;
847
848 // TODO - count the members of list to see if it's a vector.
849 left = addParenthesis(left, list, type, right);
850 left->toKen = tokens[type - lowestToken];
851 left->basicType = otype;
852 return left;
853}
854
855LSL_Leaf *addNumby(LSL_Leaf *numby)
856{
857 LSL_Numby *num = calloc(1, sizeof(LSL_Numby));
858
859 if ((numby) && (num))
860 {
861 num->text.text = numby->value.stringValue;
862#if LUASL_DIFF_CHECK
863 num->text.ignorable = numby->ignorable;
864 numby->ignorable = NULL;
865#endif
866 switch (numby->toKen->type)
867 {
868 case LSL_FLOAT :
869 {
870 num->value.floatValue = atof(num->text.text);
871 numby->basicType = OT_float;
872 break;
873 }
874 case LSL_INTEGER :
875 {
876 num->value.integerValue = atoi(num->text.text);
877 numby->basicType = OT_integer;
878 break;
879 }
880 default:
881 break;
882 }
883 numby->value.numbyValue = num;
884 num->type = numby->basicType;
885 }
886 return numby;
887}
888
889LSL_Leaf *addParenthesis(LSL_Leaf *lval, LSL_Leaf *expr, LSL_Type type, LSL_Leaf *rval)
890{
891 LSL_Parenthesis *parens = calloc(1, sizeof(LSL_Parenthesis));
892
893 if (parens)
894 {
895 parens->contents = expr;
896 parens->type = type;
897#if LUASL_DIFF_CHECK
898 parens->rightIgnorable = rval->ignorable;
899 // Actualy, at this point, rval is no longer needed.
900 rval->ignorable = NULL;
901#endif
902 if (lval)
903 {
904 lval->value.parenthesis = parens;
905 if (expr)
906 {
907 lval->basicType = expr->basicType;
908 // Propagate these flags inwards and outwards.
909 if (MF_ASSIGNEXP & expr->flags)
910 lval->flags |= MF_ASSIGNEXP;
911 if (MF_WRAPFUNC & expr->flags)
912 parens->flags |= MF_WRAPFUNC;
913 }
914 }
915 }
916 return lval;
917}
918
919LSL_Leaf *addState(LuaSL_compiler *compiler, LSL_Leaf *state, LSL_Leaf *identifier, LSL_Leaf *block)
920{
921 LSL_State *result = calloc(1, sizeof(LSL_State));
922
923 if ((identifier) && (result))
924 {
925 Eina_Iterator *handlers;
926 LSL_Function *func;
927
928 memcpy(result, &(compiler->state), sizeof(LSL_State));
929 compiler->state.block = NULL;
930 compiler->state.handlers = NULL;
931 result->name.text = identifier->value.stringValue;
932#if LUASL_DIFF_CHECK
933 result->name.ignorable = identifier->ignorable;
934 identifier->ignorable = NULL;
935#endif
936 handlers = eina_hash_iterator_data_new(result->handlers);
937 while(eina_iterator_next(handlers, (void **) &func))
938 {
939 func->state = result->name.text;
940 }
941 result->block = block->value.blockValue;
942 if (state)
943 {
944 result->state.text = state->toKen->toKen;
945#if LUASL_DIFF_CHECK
946 result->state.ignorable = state->ignorable;
947 state->ignorable = NULL;
948#endif
949 }
950 identifier->value.stateValue = result;
951 identifier->toKen = tokens[LSL_STATE - lowestToken];
952 eina_hash_add(compiler->script.states, result->name.text, identifier);
953 compiler->inState = FALSE;
954 }
955
956 return identifier;
957}
958
959LSL_Leaf *addIfElse(LuaSL_compiler *compiler, LSL_Leaf *ifBlock, LSL_Leaf *elseBlock)
960{
961 if (ifBlock->value.statementValue->elseBlock)
962 {
963 LSL_Statement *oldElseIf = ifBlock->value.statementValue->elseBlock;
964
965 while (oldElseIf->elseBlock)
966 oldElseIf = oldElseIf->elseBlock;
967
968 oldElseIf->elseBlock = elseBlock->value.statementValue;
969 }
970 else
971 ifBlock->value.statementValue->elseBlock = elseBlock->value.statementValue;
972 return ifBlock;
973}
974
975LSL_Leaf *addFor(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr0, LSL_Leaf *stat0, LSL_Leaf *expr1, LSL_Leaf *stat1, LSL_Leaf *expr2, LSL_Leaf *right, LSL_Leaf *block)
976{
977 LSL_Leaf **exprs = calloc(5, sizeof(LSL_Leaf *));
978
979 if (exprs)
980 {
981 lval = addStatement(compiler, lval, flow, left, expr0, right, block, NULL);
982 exprs[0] = expr0;
983 exprs[1] = stat0;
984 exprs[2] = expr1;
985 exprs[3] = stat1;
986 exprs[4] = expr2;
987 lval->value.statementValue->expressions = (LSL_Leaf *) exprs;
988 }
989 return lval;
990}
991
992LSL_Leaf *addStatement(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr, LSL_Leaf *right, LSL_Leaf *block, LSL_Leaf *identifier)
993{
994 gameGlobals *ourGlobals = compiler->game;
995 LSL_Statement *stat = calloc(1, sizeof(LSL_Statement));
996 boolean justOne = FALSE;
997
998 if (NULL == lval)
999 lval = newLeaf(LSL_STATEMENT, NULL, NULL);
1000
1001 if (stat)
1002 {
1003 stat->type = flow->toKen->type;
1004 stat->expressions = expr;
1005 if (block)
1006 {
1007 if (LSL_BLOCK_OPEN == block->toKen->type)
1008 stat->block = block->value.blockValue;
1009 else
1010 stat->single = block->value.statementValue;
1011 }
1012 eina_clist_element_init(&(stat->statement));
1013 if (identifier)
1014 {
1015 stat->identifier.text = identifier->value.stringValue;
1016#if LUASL_DIFF_CHECK
1017 stat->identifier.ignorable = identifier->ignorable;
1018 identifier->ignorable = NULL;
1019#endif
1020 }
1021 if (left)
1022 {
1023 LSL_Leaf *parens = addParenthesis(left, expr, LSL_EXPRESSION, right);
1024
1025 if (parens)
1026 stat->parenthesis = parens->value.parenthesis;
1027 }
1028
1029 switch (stat->type)
1030 {
1031 case LSL_EXPRESSION :
1032 {
1033 break;
1034 }
1035 case LSL_FUNCTION :
1036 {
1037 break;
1038 }
1039 case LSL_DO :
1040 {
1041 break;
1042 }
1043 case LSL_FOR :
1044 {
1045 justOne = TRUE;
1046 break;
1047 }
1048 case LSL_IF :
1049 {
1050 justOne = TRUE;
1051 break;
1052 }
1053 case LSL_ELSE :
1054 {
1055 justOne = TRUE;
1056 break;
1057 }
1058 case LSL_ELSEIF :
1059 {
1060 justOne = TRUE;
1061 break;
1062 }
1063 case LSL_JUMP :
1064 {
1065 justOne = TRUE;
1066 break;
1067 }
1068 case LSL_LABEL :
1069 {
1070 justOne = TRUE;
1071 break;
1072 }
1073 case LSL_RETURN :
1074 {
1075 justOne = TRUE;
1076 break;
1077 }
1078 case LSL_STATE_CHANGE :
1079 {
1080 justOne = TRUE;
1081 break;
1082 }
1083 case LSL_STATEMENT :
1084 {
1085 break;
1086 }
1087 case LSL_WHILE :
1088 {
1089 stat->identifier.text = NULL;
1090 justOne = TRUE;
1091 break;
1092 }
1093 case LSL_IDENTIFIER :
1094 {
1095 break;
1096 }
1097 case LSL_VARIABLE :
1098 {
1099 if (identifier)
1100 {
1101 stat->identifier.text = identifier->value.identifierValue->name.text;
1102 identifier->value.identifierValue->definition = stat;
1103 stat->flags = identifier->value.identifierValue->flags;
1104 }
1105 break;
1106 }
1107 default :
1108 {
1109 compiler->script.bugCount++;
1110 PE("Should not be here %d.", stat->type);
1111 break;
1112 }
1113 }
1114
1115#if LUASL_DIFF_CHECK
1116 if (justOne && (flow))
1117 {
1118 stat->ignorable = calloc(2, sizeof(Eina_Strbuf *));
1119 if (stat->ignorable)
1120 {
1121 stat->ignorable[1] = flow->ignorable;
1122 flow->ignorable = NULL;
1123 }
1124 }
1125#endif
1126
1127 if (lval)
1128 {
1129#if LUASL_DIFF_CHECK
1130 if (NULL == stat->ignorable)
1131 stat->ignorable = calloc(1, sizeof(Eina_Strbuf *));
1132 if (stat->ignorable)
1133 {
1134 stat->ignorable[0] = lval->ignorable;
1135 lval->ignorable = NULL;
1136 }
1137#endif
1138 lval->value.statementValue = stat;
1139 }
1140
1141#if LUASL_DIFF_CHECK
1142 if (left)
1143 {
1144 if (NULL == stat->ignorable)
1145 stat->ignorable = calloc(3, sizeof(Eina_Strbuf *));
1146 else
1147 stat->ignorable = realloc(stat->ignorable, 3 * sizeof(Eina_Strbuf *));
1148 if (stat->ignorable)
1149 {
1150 stat->ignorable[2] = left->ignorable;
1151 left->ignorable = NULL;
1152 }
1153 }
1154#endif
1155 }
1156
1157 return lval;
1158}
1159
1160LSL_Leaf *collectStatements(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *statement)
1161{
1162// Guess this is not needed after all, and seemed to cause the "states with only one function get dropped" bug.
1163// boolean wasNull = FALSE;
1164
1165 if (NULL == list)
1166 {
1167 list = newLeaf(LSL_BLOCK_OPEN, NULL, NULL);
1168// wasNull = TRUE;
1169 }
1170
1171 if (list)
1172 {
1173 if (statement)
1174 {
1175// if (!wasNull)
1176 list->value.blockValue = compiler->currentBlock; // Maybe NULL.
1177
1178 if ((compiler->inState) && (LSL_FUNCTION == statement->value.statementValue->type))
1179 {
1180 eina_clist_add_tail(&(compiler->state.block->statements), &(statement->value.statementValue->statement));
1181 }
1182 else if (list->value.blockValue)
1183 {
1184 eina_clist_add_tail(&(list->value.blockValue->statements), &(statement->value.statementValue->statement));
1185 }
1186 }
1187 }
1188
1189 return list;
1190}
1191
1192/* Typecasting
1193
1194LSL is statically typed, so stored values are not converted, only the values used in expressions are.
1195Lua is dynamically typed, so stored values are changed (sometimes I think).
1196
1197LSL implicitly typecasts - There is a shitload of QUIRKs about this. Apparently some don't work anyway.
1198 integer -> float (Says in lslwiki that precision is never lost, which is bullshit, since they are both 32 bit. Would be true if the float is 64 bit. Lua suggest to use 64 bit floats to emulate 32 bit integers.)
1199 string -> key
1200 Some functions need help with this or the other way around.
1201 string -> vector (Maybe, should test that.)
1202 vector -> string (Maybe, should test that.)
1203 Also happens when getting stuff from lists.
1204
1205Explicit type casting -
1206 string -> integer
1207 Leading spaces are ignored, as are any characters after the run of digits.
1208 All other strings convert to 0.
1209 Which means "" and " " convert to 0.
1210 Strings in hexadecimal format will work, same in Lua (though Lua can't handle "0x", but "0x0" is fine).
1211 keys <-> string
1212 No other typecasting can be done with keys.
1213 float -> string
1214 You get a bunch of trailing 0s.
1215
1216QUIRK - I have seen cases where a double explicit typecast was needed in SL, but was considered to be invalid syntax in OS.
1217
1218Any binary operation involving a float and an integer implicitly casts the integer to float.
1219
1220A boolean operation deals with TRUE (1) and FALSE (0). Any non zero value is a TRUE (generally sigh).
1221On the other hand, in Lua, only false and nil are false, everything else is true. 0 is true. sigh
1222Bitwise operations only apply to integers. Right shifts are arithmetic, not logical.
1223
1224integer = integer0 % integer1; // Apparently only applies to integers, but works fine on floats in OS.
1225string = string0 + string1; // Concatenation.
1226list = list0 + list1; // Concatenation. Also works if either is not a list, it's promoted to a list first.
1227list = (list=[]) + list + ["new_item"]; // Voodoo needed for old LSL, works in Mono but not needed, does not work in OS. Works for strings to.
1228bool = list == != int // Only compares the lengths, probably applies to the other conditionals to.
1229vector = vector0 + vector1; // Add elements together.
1230vector = vector0 - vector1; // Subtract elements of vector1 from elements of vector0.
1231float = vector0 * vector1; // A dot product of the vectors.
1232vector = vector0 % vector1; // A cross product of the vectors.
1233vector = vector * float; // Scale the vector, works the other way around I think. Works for integer to, but it will end up being cast to float.
1234vector = vector / float; // Scale the vector, works the other way around I think. Works for integer to, but it will end up being cast to float.
1235vector = vector * rotation; // Rotate the vector by the rotation. Other way around wont compile.
1236vector = vector / rotation; // Rotate the vector by the rotation, in the opposite direction. Other way around wont compile.
1237rotation = llGetRot() * rotation; // Rotate an object around the global axis.
1238rotation = rotation * llGetLocalRot(); // Rotate an object around the local axis.
1239rotation = rotation0 * rotation1; // Add two rotations, so the result is as if you applied each rotation one after the other.
1240 // Division rotates in the opposite direction.
1241rotation = rotation0 + rotation1; // Similar to vector, but it's a meaningless thing as far as rotations go.
1242rotation = rotation0 - rotation1; // Similar to vector, but it's a meaningless thing as far as rotations go.
1243
1244A boolean operator results in a boolean value. (any types)
1245A comparison operator results in a boolean value. (any types)
1246A bitwise operator results in an integer value. (intInt or int)
1247A dot product operator results in a float value. (vector * vector)
1248A vectorFloat results in a vector value.
1249
1250*/
1251
1252LSL_Leaf *addTypecast(LSL_Leaf *lval, LSL_Leaf *type, LSL_Leaf *rval, LSL_Leaf *expr)
1253{
1254 addParenthesis(lval, expr, LSL_TYPECAST_OPEN, rval);
1255 if (lval)
1256 {
1257 if (type)
1258 {
1259 lval->basicType = type->basicType;
1260 if ((expr) && (OT_integer == type->basicType)) // TODO - Should be from string, but I guess I'm not propagating basic types up from function calls and parenthesis?
1261 lval->value.parenthesis->flags |= MF_TYPECAST;
1262 }
1263 // Actualy, at this point, type is no longer needed.
1264 lval->toKen = tokens[LSL_TYPECAST_OPEN - lowestToken];
1265 }
1266// if (rval)
1267// rval->toKen = tokens[LSL_TYPECAST_CLOSE - lowestToken];
1268
1269 return lval;
1270}
1271
1272LSL_Leaf *addVariable(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *assignment, LSL_Leaf *expr)
1273{
1274 LSL_Identifier *result = calloc(1, sizeof(LSL_Identifier));
1275
1276 if ( (identifier) && (result))
1277 {
1278 result->name.text = identifier->value.stringValue;
1279#if LUASL_DIFF_CHECK
1280 result->name.ignorable = identifier->ignorable;
1281 identifier->ignorable = NULL;
1282#endif
1283 result->value.toKen = tokens[LSL_UNKNOWN - lowestToken];
1284 identifier->value.identifierValue = result;
1285 identifier->toKen = tokens[LSL_VARIABLE - lowestToken];
1286 identifier->left = type;
1287 identifier->right = assignment;
1288 if (assignment)
1289 assignment->right = expr;
1290 else
1291 identifier->flags |= MF_NOASSIGN;
1292 if (type)
1293 {
1294 if (compiler->currentBlock)
1295 {
1296 identifier->flags |= MF_LOCAL;
1297 result->flags |= MF_LOCAL;
1298 type->flags |= MF_LOCAL;
1299 }
1300 identifier->basicType = type->basicType;
1301 result->value.basicType = type->basicType;
1302 result->value.toKen = type->toKen; // This is the LSL_TYPE_* toKen instead of the LSL_* toKen. Not sure if that's a problem.
1303 }
1304 if (compiler->currentBlock)
1305 eina_hash_add(compiler->currentBlock->variables, result->name.text, identifier);
1306 else
1307 eina_hash_add(compiler->script.variables, result->name.text, identifier);
1308 }
1309
1310 return identifier;
1311}
1312
1313LSL_Leaf *beginBlock(LuaSL_compiler *compiler, LSL_Leaf *block)
1314{
1315 LSL_Block *blok = calloc(1, sizeof(LSL_Block));
1316
1317 if (blok)
1318 {
1319 eina_clist_init(&(blok->statements));
1320 blok->variables = eina_hash_stringshared_new(burnLeaf);
1321 block->value.blockValue = blok;
1322 if ((NULL == compiler->currentBlock) && (NULL == compiler->currentFunction))
1323 {
1324 compiler->inState = TRUE;
1325 compiler->state.block=blok;
1326 compiler->state.handlers = eina_hash_stringshared_new(free);
1327 }
1328 blok->outerBlock = compiler->currentBlock;
1329 compiler->currentBlock = blok;
1330 blok->function = compiler->currentFunction;
1331 compiler->currentFunction = NULL;
1332#if LUASL_DIFF_CHECK
1333 blok->openIgnorable = block->ignorable;
1334 block->ignorable = NULL;
1335#endif
1336 }
1337 return block;
1338}
1339
1340static void secondPass(LuaSL_compiler *compiler, LSL_Leaf *leaf)
1341{
1342 if (leaf)
1343 {
1344 secondPass(compiler, leaf->left);
1345 if (OT_undeclared == leaf->basicType)
1346 leaf = addOperation(compiler, leaf->left, leaf, leaf->right);
1347 secondPass(compiler, leaf->right);
1348 }
1349}
1350
1351static void outputLeaf(FILE *file, outputMode mode, LSL_Leaf *leaf)
1352{
1353 if (leaf)
1354 {
1355 if ((OM_LUA == mode) &&(ST_BITWISE != leaf->toKen->subType))
1356 outputLeaf(file, mode, leaf->left);
1357#if LUASL_DIFF_CHECK
1358 if ((!(LSL_NOIGNORE & leaf->toKen->flags)) && (leaf->ignorable))
1359 fwrite(eina_strbuf_string_get(leaf->ignorable), 1, eina_strbuf_length_get(leaf->ignorable), file);
1360#endif
1361 if (leaf->toKen->output)
1362 leaf->toKen->output(file, mode, leaf);
1363 else
1364 {
1365 if (OM_LUA == mode)
1366 {
1367 if (MF_WRAPFUNC & leaf->flags)
1368 {
1369// TODO - Leaving this here in case we trip over one.
1370if ((leaf->left) && (leaf->right))
1371 printf("%s %s %s\n", leaf->left->toKen->toKen, leaf->toKen->toKen, leaf->right->toKen->toKen);
1372else if (leaf->left)
1373 printf("%s %s NORIGHT\n", leaf->left->toKen->toKen, leaf->toKen->toKen);
1374else if (leaf->right)
1375 printf("NOLEFT %s %s\n", leaf->toKen->toKen, leaf->right->toKen->toKen);
1376else
1377 printf("NOLEFT %s NORIGHT\n", leaf->toKen->toKen);
1378 }
1379 if ((LSL_ASSIGNMENT & leaf->toKen->flags) && (LSL_ASSIGNMENT_PLAIN != leaf->toKen->type))
1380 {
1381 if (leaf->left->value.identifierValue->sub)
1382 fprintf(file, " --[[%s]] = %s.%s %.1s ", leaf->toKen->toKen, leaf->left->value.identifierValue->name.text, leaf->left->value.identifierValue->sub, leaf->toKen->toKen);
1383 else
1384 fprintf(file, " --[[%s]] = %s %.1s ", leaf->toKen->toKen, leaf->left->value.identifierValue->name.text, leaf->toKen->toKen);
1385 }
1386 else if (LSL_TYPE & leaf->toKen->flags)
1387 {
1388 if (MF_LOCAL & leaf->flags)
1389 fprintf(file, " local ");
1390 fprintf(file, " --[[%s]] ", leaf->toKen->toKen);
1391 }
1392 else if (LSL_BOOL_AND == leaf->toKen->type)
1393 fprintf(file, " and ");
1394 else if (LSL_BOOL_OR == leaf->toKen->type)
1395 fprintf(file, " or ");
1396 else if (LSL_BOOL_NOT == leaf->toKen->type)
1397 fprintf(file, " not ");
1398 else if (LSL_CONCATENATE == leaf->toKen->type)
1399 fprintf(file, " .. ");
1400 else if (LSL_NOT_EQUAL == leaf->toKen->type)
1401 fprintf(file, " ~= ");
1402 else
1403 fprintf(file, "%s", leaf->toKen->toKen);
1404 }
1405 else
1406 fprintf(file, "%s", leaf->toKen->toKen);
1407 }
1408 if ((OM_LUA == mode) &&(ST_BITWISE != leaf->toKen->subType))
1409 outputLeaf(file, mode, leaf->right);
1410 }
1411}
1412
1413// Circular references, so declare this one first.
1414static void outputRawStatement(FILE *file, outputMode mode, LSL_Statement *statement);
1415
1416static void outputRawBlock(FILE *file, outputMode mode, LSL_Block *block, boolean doEnd)
1417{
1418 if (block)
1419 {
1420 LSL_Statement *stat = NULL;
1421
1422#if LUASL_DIFF_CHECK
1423 if (block->openIgnorable)
1424 fwrite(eina_strbuf_string_get(block->openIgnorable), 1, eina_strbuf_length_get(block->openIgnorable), file);
1425 if (OM_LSL == mode)
1426 fprintf(file, "{");
1427#else
1428 if (OM_LSL == mode)
1429 fprintf(file, "\n{\n");
1430 else if (doEnd && (OM_LUA == mode))
1431 fprintf(file, "\n");
1432#endif
1433 EINA_CLIST_FOR_EACH_ENTRY(stat, &(block->statements), LSL_Statement, statement)
1434 {
1435 outputRawStatement(file, mode, stat);
1436 }
1437#if LUASL_DIFF_CHECK
1438 if (block->closeIgnorable)
1439 fwrite(eina_strbuf_string_get(block->closeIgnorable), 1, eina_strbuf_length_get(block->closeIgnorable), file);
1440#endif
1441 if (OM_LSL == mode)
1442 fprintf(file, "}");
1443 else if (doEnd && (OM_LUA == mode))
1444 fprintf(file, "end ");
1445 }
1446}
1447
1448// TODO - should clean this up by refactoring the bits in the switch outside.
1449static void outputRawParenthesisToken(FILE *file, outputMode mode, LSL_Parenthesis *parenthesis, const char *typeName)
1450{
1451 if ((OM_LUA == mode) && (LSL_TYPECAST_OPEN == parenthesis->type))
1452 {
1453 if (MF_TYPECAST & parenthesis->flags)
1454 fprintf(file, " _LSL.%sTypecast(", typeName);
1455 else
1456 fprintf(file, " --[[%s]] ", typeName);
1457 outputLeaf(file, mode, parenthesis->contents);
1458 if (MF_TYPECAST & parenthesis->flags)
1459 fprintf(file, ") ");
1460 return;
1461 }
1462
1463 if ((OM_LUA == mode) && (MF_WRAPFUNC & parenthesis->flags))
1464 fprintf(file, " (function() ");
1465 else
1466 fprintf(file, "(");
1467 if (LSL_TYPECAST_OPEN == parenthesis->type)
1468 fprintf(file, "%s", typeName); // TODO - We are missing the type ignorable text here.
1469 else
1470 outputLeaf(file, mode, parenthesis->contents);
1471 if ((OM_LUA == mode) && (MF_WRAPFUNC & parenthesis->flags))
1472 fprintf(file, "; return x; end)() ");
1473 else
1474 {
1475#if LUASL_DIFF_CHECK
1476 fprintf(file, "%s)", eina_strbuf_string_get(parenthesis->rightIgnorable));
1477#else
1478 fprintf(file, ")");
1479#endif
1480 }
1481
1482 if (LSL_TYPECAST_OPEN == parenthesis->type)
1483 outputLeaf(file, mode, parenthesis->contents);
1484}
1485
1486static void outputText(FILE *file, LSL_Text *text, boolean ignore)
1487{
1488 if (text->text)
1489 {
1490#if LUASL_DIFF_CHECK
1491 if (ignore && (text->ignorable))
1492 fwrite(eina_strbuf_string_get(text->ignorable), 1, eina_strbuf_length_get(text->ignorable), file);
1493#endif
1494 fprintf(file, "%s", text->text);
1495 }
1496}
1497
1498static void outputRawStatement(FILE *file, outputMode mode, LSL_Statement *statement)
1499{
1500 boolean isBlock = FALSE;
1501
1502 if (statement)
1503 {
1504 switch (statement->type)
1505 {
1506 case LSL_EXPRESSION :
1507 {
1508 break;
1509 }
1510 case LSL_FUNCTION :
1511 {
1512 isBlock = TRUE;
1513 break;
1514 }
1515 case LSL_DO :
1516 {
1517 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1518 break;
1519 }
1520 case LSL_FOR :
1521 {
1522#if LUASL_DIFF_CHECK
1523 if ((statement->ignorable) && (statement->ignorable[1]))
1524 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1525#endif
1526 if (OM_LSL == mode)
1527 {
1528 isBlock = TRUE;
1529 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1530 }
1531 else if (OM_LUA == mode)
1532 {
1533 LSL_Leaf **exprs = (LSL_Leaf **) statement->expressions;
1534
1535 outputLeaf(file, mode, exprs[0]);
1536 fprintf(file, ";\nwhile (");
1537 outputLeaf(file, mode, exprs[2]);
1538#if LUASL_DIFF_CHECK
1539 fprintf(file, "%s)\n", eina_strbuf_string_get(statement->parenthesis->rightIgnorable));
1540#else
1541 fprintf(file, ") do\n");
1542#endif
1543 if (statement->block)
1544 outputRawBlock(file, mode, statement->block, FALSE);
1545 if (statement->single)
1546 outputRawStatement(file, mode, statement->single);
1547 fprintf(file, "\n");
1548 outputLeaf(file, mode, exprs[4]);
1549 fprintf(file, ";\nend\n");
1550 return;
1551 }
1552 break;
1553 }
1554 case LSL_IF :
1555 case LSL_ELSE :
1556 case LSL_ELSEIF :
1557 {
1558 isBlock = TRUE;
1559#if LUASL_DIFF_CHECK
1560 if ((statement->ignorable) && (statement->ignorable[1]))
1561 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1562#endif
1563 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1564 if (OM_LUA == mode)
1565 {
1566 fprintf(file, " ");
1567 if (LSL_ELSE != statement->type)
1568 {
1569 if (statement->parenthesis)
1570 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1571 else
1572 outputLeaf(file, mode, statement->expressions);
1573 fprintf(file, " then\n");
1574 }
1575 if (statement->block)
1576 outputRawBlock(file, mode, statement->block, FALSE);
1577 if (statement->single)
1578 outputRawStatement(file, mode, statement->single);
1579 if (statement->elseBlock)
1580 outputRawStatement(file, mode, statement->elseBlock);
1581 if (LSL_IF == statement->type)
1582 {
1583#if 1
1584 fprintf(file, " end\n");
1585#else
1586 fprintf(file, " end --[[");
1587 if (statement->parenthesis)
1588 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1589 else
1590 outputLeaf(file, mode, statement->expressions);
1591 fprintf(file, "]]\n");
1592#endif
1593 }
1594 return;
1595 }
1596 break;
1597 }
1598 case LSL_JUMP :
1599 {
1600#if LUASL_DIFF_CHECK
1601 if ((statement->ignorable) && (statement->ignorable[1]))
1602 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1603#endif
1604 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1605 break;
1606 }
1607 case LSL_LABEL :
1608 {
1609#if LUASL_DIFF_CHECK
1610 if ((statement->ignorable) && (statement->ignorable[1]))
1611 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1612#endif
1613 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1614 break;
1615 }
1616 case LSL_RETURN :
1617 {
1618#if LUASL_DIFF_CHECK
1619 if ((statement->ignorable) && (statement->ignorable[1]))
1620 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1621#endif
1622 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1623 break;
1624 }
1625 case LSL_STATE_CHANGE :
1626 {
1627#if LUASL_DIFF_CHECK
1628 if ((statement->ignorable) && (statement->ignorable[1]))
1629 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1630#endif
1631 if (OM_LSL == mode)
1632 {
1633 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1634 if (statement->identifier.text)
1635 outputText(file, &(statement->identifier), TRUE);
1636 }
1637 else if (OM_LUA == mode)
1638 {
1639 fprintf(file, "return _LSL.stateChange(_");
1640 if (statement->identifier.text)
1641 outputText(file, &(statement->identifier), TRUE);
1642 fprintf(file, "State)");
1643 }
1644 break;
1645 }
1646 case LSL_STATEMENT :
1647 {
1648 break;
1649 }
1650 case LSL_WHILE :
1651 {
1652 isBlock = TRUE;
1653#if LUASL_DIFF_CHECK
1654 if ((statement->ignorable) && (statement->ignorable[1]))
1655 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1656#else
1657 if (OM_LUA == mode)
1658 fprintf(file, "\n");
1659#endif
1660 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1661 if (OM_LUA == mode)
1662 {
1663 if (statement->parenthesis)
1664 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1665 fprintf(file, " do ");
1666 if (statement->block)
1667 outputRawBlock(file, mode, statement->block, TRUE);
1668 if (statement->single)
1669 outputRawStatement(file, mode, statement->single);
1670 fprintf(file, "\n");
1671 return;
1672 }
1673 break;
1674 }
1675 case LSL_IDENTIFIER :
1676 {
1677 break;
1678 }
1679 case LSL_VARIABLE :
1680 {
1681 break;
1682 }
1683 default :
1684 {
1685 fprintf(file, "@@Should not be here %s.@@", tokens[statement->type - lowestToken]->toKen);
1686 break;
1687 }
1688 }
1689
1690#if LUASL_DIFF_CHECK
1691 if ((statement->ignorable) && (statement->ignorable[2]))
1692 fwrite(eina_strbuf_string_get(statement->ignorable[2]), 1, eina_strbuf_length_get(statement->ignorable[2]), file);
1693#else
1694 if (OM_LUA == mode)
1695 fprintf(file, " ");
1696#endif
1697 if (LSL_FOR == statement->type)
1698 {
1699 LSL_Leaf **exprs = (LSL_Leaf **) statement->expressions;
1700 int i;
1701
1702 fprintf(file, "(");
1703 for (i = 0; i < 5; i++)
1704 {
1705 outputLeaf(file, mode, exprs[i]);
1706 if (i % 2)
1707 fprintf(file, ";");
1708 }
1709#if LUASL_DIFF_CHECK
1710 fprintf(file, "%s)", eina_strbuf_string_get(statement->parenthesis->rightIgnorable));
1711#else
1712 fprintf(file, ")");
1713#endif
1714 }
1715 else if (statement->parenthesis)
1716 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1717 else
1718 outputLeaf(file, mode, statement->expressions);
1719
1720 if (statement->block)
1721 outputRawBlock(file, mode, statement->block, TRUE);
1722 if (statement->single)
1723 outputRawStatement(file, mode, statement->single);
1724
1725#if LUASL_DIFF_CHECK
1726 if ((statement->ignorable) && (statement->ignorable[0]))
1727 fwrite(eina_strbuf_string_get(statement->ignorable[0]), 1, eina_strbuf_length_get(statement->ignorable[0]), file);
1728#endif
1729
1730 if (!isBlock)
1731 {
1732 fprintf(file, ";");
1733 if (!LUASL_DIFF_CHECK)
1734 fprintf(file, "\n");
1735 }
1736
1737 if ((LSL_VARIABLE == statement->type) && (OM_LUA == mode) && (MF_LOCAL & statement->flags))
1738 {
1739 const char *name = statement->identifier.text;
1740
1741// if ((MF_PREDEC | MF_PREINC | MF_POSTDEC | MF_POSTINC) & statement->flags)
1742// fprintf(file, "\n");
1743 if (MF_PREDEC & statement->flags) fprintf(file, "local function _preDecrement_%s() %s = %s - 1; return %s; end\n", name, name, name, name);
1744 if (MF_PREINC & statement->flags) fprintf(file, "local function _preIncrement_%s() %s = %s + 1; return %s; end\n", name, name, name, name);
1745 if (MF_POSTDEC & statement->flags) fprintf(file, "local function _postDecrement_%s() local _temp = %s; %s = %s - 1; return _temp; end\n", name, name, name, name);
1746 if (MF_POSTINC & statement->flags) fprintf(file, "local function _postDecrement_%s() local _temp = %s; %s = %s + 1; return _temp; end\n", name, name, name, name);
1747 }
1748
1749 if (statement->elseBlock)
1750 outputRawStatement(file, mode, statement->elseBlock);
1751 }
1752}
1753
1754static void outputBitOp(FILE *file, outputMode mode, LSL_Leaf *leaf)
1755{
1756 if (OM_LSL == mode)
1757 outputLeaf(file, mode, leaf);
1758 else if (OM_LUA == mode)
1759 {
1760 switch (leaf->toKen->type)
1761 {
1762 case LSL_BIT_AND : fprintf(file, " _bit.band("); break;
1763 case LSL_BIT_OR : fprintf(file, " _bit.bor("); break;
1764 case LSL_BIT_XOR : fprintf(file, " _bit.xor("); break;
1765 case LSL_BIT_NOT : fprintf(file, " _bit.bnot("); break;
1766 case LSL_LEFT_SHIFT : fprintf(file, " _bit.lshift("); break;
1767 case LSL_RIGHT_SHIFT : fprintf(file, " _bit.arshift("); break;
1768 default : break;
1769 }
1770 outputLeaf(file, mode, leaf->left);
1771 if (LSL_BIT_NOT != leaf->toKen->type)
1772 {
1773 fprintf(file, ", ");
1774 outputLeaf(file, mode, leaf->right);
1775 }
1776 fprintf(file, ") ");
1777 }
1778}
1779
1780static void outputBlockToken(FILE *file, outputMode mode, LSL_Leaf *content)
1781{
1782 if (content)
1783 outputRawBlock(file, mode, content->value.blockValue, TRUE);
1784}
1785
1786static void outputCrementsToken(FILE *file, outputMode mode, LSL_Leaf *content)
1787{
1788 if (content)
1789 {
1790 if (OM_LSL == mode)
1791 {
1792 switch (content->toKen->type)
1793 {
1794 case LSL_DECREMENT_PRE :
1795 case LSL_INCREMENT_PRE :
1796 {
1797 fprintf(file, "%s", content->toKen->toKen);
1798#if LUASL_DIFF_CHECK
1799 if (content->value.identifierValue->ignorable)
1800 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1801#endif
1802 outputText(file, &(content->value.identifierValue->name), FALSE);
1803 break;
1804 }
1805 case LSL_DECREMENT_POST :
1806 case LSL_INCREMENT_POST :
1807 {
1808#if LUASL_DIFF_CHECK
1809 if (content->value.identifierValue->ignorable)
1810 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1811#endif
1812 outputText(file, &(content->value.identifierValue->name), FALSE);
1813 fprintf(file, "%s", content->toKen->toKen);
1814 break;
1815 }
1816 default :
1817 break;
1818 }
1819 }
1820 else if (OM_LUA == mode)
1821 {
1822 if (MF_LOCAL & content->value.identifierValue->flags)
1823 fprintf(file, " _");
1824 else
1825 fprintf(file, " _LSL.");
1826 switch (content->toKen->type)
1827 {
1828 case LSL_DECREMENT_PRE : fprintf(file, "preDecrement"); break;
1829 case LSL_INCREMENT_PRE : fprintf(file, "preIncrement"); break;
1830 case LSL_DECREMENT_POST : fprintf(file, "postDecrement"); break;
1831 case LSL_INCREMENT_POST : fprintf(file, "postIncrement"); break;
1832 default :
1833 break;
1834 }
1835 if (MF_LOCAL & content->value.identifierValue->flags)
1836 fprintf(file, "_");
1837 else
1838 fprintf(file, "(\"");
1839#if LUASL_DIFF_CHECK
1840 if (content->value.identifierValue->ignorable)
1841 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1842#endif
1843 outputText(file, &(content->value.identifierValue->name), FALSE);
1844 if (MF_LOCAL & content->value.identifierValue->flags)
1845 fprintf(file, "()");
1846 else
1847 fprintf(file, "\")");
1848 }
1849 }
1850}
1851
1852static void outputFloatToken(FILE *file, outputMode mode, LSL_Leaf *content)
1853{
1854 if (content)
1855 outputText(file, &(content->value.numbyValue->text), !(LSL_NOIGNORE & content->toKen->flags));
1856}
1857
1858static void outputFunctionToken(FILE *file, outputMode mode, LSL_Leaf *content)
1859{
1860 if (content)
1861 {
1862 LSL_Function *func = content->value.functionValue;
1863 LSL_Leaf *param = NULL;
1864 int first = TRUE;
1865
1866 if (OM_LSL == mode)
1867 {
1868 outputText(file, &(func->type), !(LSL_NOIGNORE & content->toKen->flags));
1869 outputText(file, &(func->name), !(LSL_NOIGNORE & content->toKen->flags));
1870 // TODO - should print comma and parenthesis ignorables.
1871 fprintf(file, "(");
1872 EINA_INARRAY_FOREACH((&(func->vars)), param)
1873 {
1874 if (!LUASL_DIFF_CHECK)
1875 {
1876 if (!first)
1877 fprintf(file, ", ");
1878 }
1879 outputLeaf(file, mode, param);
1880 first = FALSE;
1881 }
1882 fprintf(file, ")");
1883 outputRawBlock(file, mode, func->block, TRUE);
1884 if (!LUASL_DIFF_CHECK)
1885 fprintf(file, "\n");
1886 }
1887 else if (OM_LUA == mode)
1888 {
1889 if (func->state)
1890 fprintf(file, "\n\n_%sState.%s = function(", func->state, func->name.text);
1891 else
1892 {
1893 fprintf(file, "\n\nfunction ");
1894 if (func->type.text)
1895 fprintf(file, " --[[%s]] ", func->type.text);
1896 fprintf(file, "%s(", func->name.text);
1897 }
1898 EINA_INARRAY_FOREACH((&(func->vars)), param)
1899 {
1900 // Commenting out the types is done in outputLeaf() which outputs all the types.
1901 if (!LUASL_DIFF_CHECK)
1902 {
1903 if (!first)
1904 fprintf(file, ", ");
1905 }
1906 outputLeaf(file, mode, param);
1907 first = FALSE;
1908 }
1909 fprintf(file, ")");
1910 outputRawBlock(file, mode, func->block, TRUE);
1911 }
1912 }
1913}
1914
1915static void outputFunctionCallToken(FILE *file, outputMode mode, LSL_Leaf *content)
1916{
1917 if (content)
1918 {
1919 LSL_FunctionCall *call = content->value.functionCallValue;
1920 LSL_Leaf *param = NULL;
1921 boolean first = TRUE;
1922
1923 // TODO - should output it's own ignorable here.
1924 if ((OM_LUA == mode) && (MF_LSLCONST & call->function->flags))
1925 fprintf(file, "_LSL.");
1926 outputText(file, &(call->function->name), FALSE); // Don't output the function definitions ignorable.
1927 fprintf(file, "(");
1928 EINA_INARRAY_FOREACH((&(call->params)), param)
1929 {
1930 if ((OM_LUA == mode) && (!first))
1931 fprintf(file, ", ");
1932 outputLeaf(file, mode, param);
1933 first = FALSE;
1934 }
1935 fprintf(file, ")");
1936 }
1937}
1938
1939static void outputIntegerToken(FILE *file, outputMode mode, LSL_Leaf *content)
1940{
1941 if (content)
1942 outputText(file, &(content->value.numbyValue->text), !(LSL_NOIGNORE & content->toKen->flags));
1943}
1944
1945static void outputIdentifierToken(FILE *file, outputMode mode, LSL_Leaf *content)
1946{
1947 if (content)
1948 {
1949 if (LSL_IDENTIFIER == content->toKen->type)
1950 {
1951 if ((OM_LUA == mode) && (MF_LSLCONST & content->value.identifierValue->flags))
1952 fprintf(file, "_LSL.");
1953 outputText(file, &(content->value.identifierValue->name), FALSE);
1954 if (content->value.identifierValue->sub)
1955 fprintf(file, ".%s", content->value.identifierValue->sub);
1956 }
1957 else
1958 if ((LSL_VARIABLE == content->toKen->type) && (MF_NOASSIGN & content->flags))
1959 {
1960 outputText(file, &(content->value.identifierValue->name), !(LSL_NOIGNORE & content->toKen->flags));
1961 if (OM_LUA == mode)
1962 {
1963 switch (content->basicType)
1964 {
1965 case OT_bool : fprintf(file, " = false"); break;
1966 case OT_integer : fprintf(file, " = 0"); break;
1967 case OT_float : fprintf(file, " = 0.0"); break;
1968 case OT_key : fprintf(file, " = _LSL.NULL_KEY"); break;
1969 case OT_list : fprintf(file, " = {}"); break;
1970 case OT_rotation : fprintf(file, " = _LSL.ZERO_ROTATION"); break;
1971 case OT_string : fprintf(file, " = \"\""); break;
1972 case OT_vector : fprintf(file, " = _LSL.ZERO_VECTOR"); break;
1973 default : fprintf(file, " = nil"); break;
1974 }
1975 }
1976 }
1977 else
1978 outputText(file, &(content->value.identifierValue->name), !(LSL_NOIGNORE & content->toKen->flags));
1979 }
1980}
1981
1982static void outputListToken(FILE *file, outputMode mode, LSL_Leaf *content)
1983{
1984 if (content)
1985 {
1986 LSL_Parenthesis *parens = content->value.parenthesis;
1987
1988 if (parens->contents)
1989 {
1990 LSL_FunctionCall *call = parens->contents->value.functionCallValue;
1991 LSL_Leaf *param = NULL;
1992 const char *ig = "";
1993
1994 // TODO - should output it's own ignorable here.
1995 if (OM_LSL == mode)
1996 {
1997 switch (parens->type)
1998 {
1999 case LSL_LIST : fprintf(file, "["); break;
2000 case LSL_ROTATION :
2001 case LSL_VECTOR : fprintf(file, "<");
2002 default : break;
2003 }
2004 }
2005 else if (OM_LUA == mode)
2006 {
2007 switch (parens->type)
2008 {
2009 case LSL_LIST : fprintf(file, "{"); break;
2010 case LSL_ROTATION :
2011 case LSL_VECTOR : fprintf(file, "{");
2012 default : break;
2013 }
2014 }
2015 EINA_INARRAY_FOREACH((&(call->params)), param)
2016 {
2017 outputLeaf(file, mode, param);
2018 if (OM_LUA == mode)
2019 fprintf(file, ", ");
2020 }
2021#if LUASL_DIFF_CHECK
2022 ig = eina_strbuf_string_get(parens->rightIgnorable);
2023#endif
2024 if (OM_LSL == mode)
2025 {
2026 switch (parens->type)
2027 {
2028 case LSL_LIST : fprintf(file, "%s]", ig); break;
2029 case LSL_ROTATION :
2030 case LSL_VECTOR : fprintf(file, "%s>", ig);
2031 default : break;
2032 }
2033 }
2034 else if (OM_LUA == mode)
2035 {
2036 switch (parens->type)
2037 {
2038 case LSL_LIST : fprintf(file, "%s}", ig); break;
2039 case LSL_ROTATION :
2040 case LSL_VECTOR : fprintf(file, "%s}", ig);
2041 default : break;
2042 }
2043 }
2044 }
2045 }
2046}
2047
2048static void outputParameterListToken(FILE *file, outputMode mode, LSL_Leaf *content)
2049{
2050 if (content)
2051 outputLeaf(file, mode, content->value.listValue);
2052 // TODO - Should go through the list, and output any crements functions we need at the top of the block.
2053}
2054
2055static void outputParenthesisToken(FILE *file, outputMode mode, LSL_Leaf *content)
2056{
2057 if (content)
2058 outputRawParenthesisToken(file, mode, content->value.parenthesis, allowed[content->basicType].name);
2059}
2060
2061static void outputStateToken(FILE *file, outputMode mode, LSL_Leaf *content)
2062{
2063 if (content)
2064 {
2065 LSL_State *state = content->value.stateValue;
2066
2067 if (state)
2068 {
2069 if (OM_LSL == mode)
2070 {
2071 outputText(file, &(state->state), !(LSL_NOIGNORE & content->toKen->flags));
2072 outputText(file, &(state->name), !(LSL_NOIGNORE & content->toKen->flags));
2073 outputRawBlock(file, mode, state->block, TRUE);
2074 }
2075 else if (OM_LUA == mode)
2076 {
2077 fprintf(file, "\n\n--[[state]] _");
2078 outputText(file, &(state->name), !(LSL_NOIGNORE & content->toKen->flags));
2079 fprintf(file, "State = {};");
2080 outputRawBlock(file, mode, state->block, FALSE);
2081 }
2082 }
2083 }
2084}
2085
2086static void outputStatementToken(FILE *file, outputMode mode, LSL_Leaf *content)
2087{
2088 if (content)
2089 outputRawStatement(file, mode, content->value.statementValue);
2090}
2091
2092static void outputStringToken(FILE *file, outputMode mode, LSL_Leaf *content)
2093{
2094 if (content)
2095 fprintf(file, "%s", content->value.stringValue); // The quotes are part of the string value already.
2096}
2097
2098boolean compilerSetup(gameGlobals *ourGlobals)
2099{
2100 int i;
2101
2102 // Figure out what numbers lemon gave to our tokens.
2103 for (i = 0; LSL_Tokens[i].toKen != NULL; i++)
2104 {
2105 if (lowestToken > LSL_Tokens[i].type)
2106 lowestToken = LSL_Tokens[i].type;
2107 }
2108 tokens = calloc(i + 1, sizeof(LSL_Token *));
2109 if (tokens)
2110 {
2111 char buf[PATH_MAX];
2112
2113 // Sort the token table.
2114 for (i = 0; LSL_Tokens[i].toKen != NULL; i++)
2115 {
2116 int j = LSL_Tokens[i].type - lowestToken;
2117
2118 tokens[j] = &(LSL_Tokens[i]);
2119 }
2120
2121 // Compile the constants.
2122 snprintf(buf, sizeof(buf), "lua -e 'require(\"LSL\").gimmeLSL()' > %s/libraries/constants.lsl", PACKAGE_DATA_DIR);
2123 system(buf);
2124 snprintf(buf, sizeof(buf), "%s/libraries/constants.lsl", PACKAGE_DATA_DIR);
2125 compileLSL(ourGlobals, NULL, "FAKE_SID", buf, TRUE);
2126
2127 return TRUE;
2128 }
2129 else
2130 PC("No memory for tokens!");
2131
2132 return FALSE;
2133}
2134
2135static int luaWriter(lua_State *L, const void* p, size_t sz, void* ud)
2136{
2137 FILE *out = ud;
2138 int result = 0;
2139
2140 if (sz != fwrite(p, 1, sz, out))
2141 result = -1;
2142 return result;
2143}
2144
2145boolean compileLSL(gameGlobals *ourGlobals, Ecore_Con_Client *client, char *SID, char *script, boolean doConstants)
2146{
2147 boolean result = FALSE;
2148 LuaSL_compiler compiler;
2149 void *pParser = ParseAlloc(malloc);
2150 int yv;
2151
2152// Parse the LSL script, validating it and reporting errors.
2153// Just pass all LSL constants and ll*() )function names through to Lua, assume they are globals there.
2154
2155 memset(&compiler, 0, sizeof(LuaSL_compiler));
2156 compiler.game = ourGlobals;
2157 compiler.client = client;
2158 compiler.script.functions = eina_hash_stringshared_new(burnLeaf);
2159 compiler.script.states = eina_hash_stringshared_new(burnLeaf);
2160 compiler.script.variables = eina_hash_stringshared_new(burnLeaf);
2161 eina_clist_init(&(compiler.danglingCalls));
2162#if LUASL_DIFF_CHECK
2163 compiler.ignorable = eina_strbuf_new();
2164#endif
2165
2166 strncpy(compiler.SID, SID, 36);
2167 compiler.SID[36] = '\0';
2168 strncpy(compiler.fileName, script, PATH_MAX - 1);
2169 compiler.fileName[PATH_MAX - 1] = '\0';
2170 compiler.file = fopen(compiler.fileName, "r");
2171 if (NULL == compiler.file)
2172 {
2173 PE("Error opening file %s.", compiler.fileName);
2174 return FALSE;
2175 }
2176 compiler.ast = NULL;
2177 compiler.lval = newLeaf(LSL_UNKNOWN, NULL, NULL);
2178 // Text editors usually start counting at 1, even programmers editors. mcedit is an exception, but you can deal with that yourself.
2179 compiler.column = 1;
2180 compiler.line = 1;
2181
2182 if (yylex_init_extra(&compiler, &(compiler.scanner)))
2183 return result;
2184 if (LUASL_DEBUG)
2185 {
2186 yyset_debug(1, compiler.scanner);
2187 ParseTrace(stdout, "LSL_lemon ");
2188 }
2189 yyset_in(compiler.file, compiler.scanner);
2190 // on EOF yylex will return 0
2191 while((yv = yylex(compiler.lval, compiler.scanner)) != 0)
2192 {
2193 Parse(pParser, yv, compiler.lval, &compiler);
2194 if (LSL_SCRIPT == yv)
2195 break;
2196 compiler.lval = newLeaf(LSL_UNKNOWN, NULL, NULL);
2197 }
2198
2199 yylex_destroy(compiler.scanner);
2200 Parse (pParser, 0, compiler.lval, &compiler);
2201 ParseFree(pParser, free);
2202
2203 if (compiler.undeclared)
2204 {
2205// PW("A second pass is needed to check if functions where used before they where declared. To avoid this second pass, don't do that.");
2206 if (eina_clist_count(&(compiler.danglingCalls)))
2207 {
2208 LSL_FunctionCall *call = NULL;
2209
2210 EINA_CLIST_FOR_EACH_ENTRY(call, &(compiler.danglingCalls), LSL_FunctionCall, dangler)
2211 {
2212 LSL_Leaf *func = findFunction(&(compiler), call->call->value.stringValue);
2213
2214 if (func)
2215 {
2216 call->function = func->value.functionValue;
2217 // Coz the leaf still had the stringValue from before.
2218 call->call->value.functionCallValue = call;
2219 call->call->toKen = tokens[LSL_FUNCTION_CALL - lowestToken];
2220 call->call->basicType = func->basicType;
2221 }
2222 else
2223 sendBack(ourGlobals, compiler.client, compiler.SID, "compilerError(%d,%d,Undeclared function %s called)", call->call->line, call->call->column, call->call->value.stringValue);
2224 }
2225 }
2226 secondPass(&compiler, compiler.ast);
2227// PD("Second pass completed.");
2228 }
2229
2230 if (doConstants)
2231 {
2232 memcpy(&constants, &(compiler.script), sizeof(LSL_Script));
2233 result = TRUE;
2234 }
2235 else
2236 {
2237 result = FALSE;
2238
2239 if (compiler.ast)
2240 {
2241 FILE *out;
2242 char buffer[PATH_MAX];
2243 char outName[PATH_MAX];
2244 char luaName[PATH_MAX];
2245 int count;
2246
2247 if (LUASL_DIFF_CHECK)
2248 {
2249 strcpy(outName, compiler.fileName);
2250 strcat(outName, "2");
2251 out = fopen(outName, "w");
2252 if (out)
2253 {
2254 char diffName[PATH_MAX];
2255
2256 strcpy(diffName, compiler.fileName);
2257 strcat(diffName, ".diff");
2258 outputLeaf(out, OM_LSL, compiler.ast);
2259 fclose(out);
2260 sprintf(buffer, "diff -u \"%s\" \"%s\" > \"%s\"", compiler.fileName, outName, diffName);
2261 count = system(buffer);
2262 if (0 != count)
2263 PE("LSL output file is different - %s!", outName);
2264 else
2265 result = TRUE;
2266 }
2267 else
2268 PC("Unable to open file %s for writing!", outName);
2269 }
2270 strcpy(luaName, compiler.fileName);
2271 strcat(luaName, ".lua");
2272 out = fopen(luaName, "w");
2273 // Generate the Lua source code.
2274 if (out)
2275 {
2276 lua_State *L;
2277 int err;
2278 char *ext;
2279
2280 fprintf(out, "--// Generated code goes here.\n\n");
2281 fprintf(out, "local _bit = require(\"bit\")\n");
2282 fprintf(out, "local _LSL = require(\"LSL\")\n\n");
2283 fprintf(out, "local _SID = [=[%s]=]\n\n", compiler.SID);
2284 strcpy(buffer, basename(compiler.fileName));
2285 if ((ext = rindex(buffer, '.')))
2286 ext[0] = '\0';
2287 fprintf(out, "local _scriptName = [=[%s]=]\n\n", buffer);
2288 outputLeaf(out, OM_LUA, compiler.ast);
2289 fprintf(out, "\n\n_LSL.mainLoop(_SID, _scriptName, _defaultState)\n"); // This actually starts the script running.
2290 fprintf(out, "\n--// End of generated code.\n\n");
2291 fclose(out);
2292
2293 // Compile the Lua source code.
2294 L = luaL_newstate();
2295 luaL_openlibs(L);
2296 // This ends up pushing a function onto the stack. The function is the compiled code.
2297 err = luaL_loadfile(L, luaName);
2298 if (err)
2299 {
2300 compiler.script.bugCount++;
2301 if (LUA_ERRSYNTAX == err)
2302 PE("Lua syntax error in %s: %s", luaName, lua_tostring(L, -1));
2303 else if (LUA_ERRFILE == err)
2304 PE("Lua compile file error in %s: %s", luaName, lua_tostring(L, -1));
2305 else if (LUA_ERRMEM == err)
2306 PE("Lua compile memory allocation error in %s: %s", luaName, lua_tostring(L, -1));
2307 }
2308 else
2309 {
2310 // Write the compiled code to a file.
2311 strcat(luaName, ".out");
2312 out = fopen(luaName, "w");
2313 if (out)
2314 {
2315 err = lua_dump(L, luaWriter, out);
2316 if (err)
2317 {
2318 compiler.script.bugCount++;
2319 PE("Lua compile file error writing to %s", luaName);
2320 }
2321 fclose(out);
2322 }
2323 else
2324 PC("Unable to open file %s for writing!", luaName);
2325 }
2326 }
2327 else
2328 PC("Unable to open file %s for writing!", luaName);
2329 }
2330
2331 if (0 == compiler.script.bugCount)
2332 result = TRUE;
2333 }
2334
2335 if (NULL != compiler.file)
2336 {
2337 fclose(compiler.file);
2338 compiler.file = NULL;
2339 }
2340
2341 if (!doConstants)
2342 burnLeaf(compiler.ast);
2343
2344 return result;
2345}
diff --git a/src/LuaSL/LuaSL_lemon_yaccer.y b/src/LuaSL/LuaSL_lemon_yaccer.y
new file mode 100644
index 0000000..182789f
--- /dev/null
+++ b/src/LuaSL/LuaSL_lemon_yaccer.y
@@ -0,0 +1,275 @@
1%include
2{
3 #include "LuaSL.h"
4}
5
6%extra_argument {LuaSL_compiler *compiler}
7
8%stack_size 1024
9
10%token_type {LSL_Leaf *}
11%default_type {LSL_Leaf *}
12%token_destructor {burnLeaf($$);}
13%default_destructor {burnLeaf($$);}
14
15// The start symbol, just coz we need one.
16
17// Lemon does not like the start symbol to be on the RHS, so give it a dummy start symbol.
18program ::= script LSL_SCRIPT(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
19
20// Various forms of "space". The lexer takes care of them for us.
21
22%nonassoc LSL_SPACE LSL_COMMENT LSL_COMMENT_LINE LSL_UNKNOWN.
23
24// Basic script structure.
25
26%nonassoc LSL_SCRIPT.
27script ::= script state(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
28script ::= script functionBody(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
29script ::= script statement(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
30script ::= .
31
32// State definitions.
33
34%nonassoc LSL_BLOCK_OPEN LSL_BLOCK_CLOSE LSL_STATE.
35stateBlock(A) ::= beginBlock(L) functionList(B) LSL_BLOCK_CLOSE(R). { A = addBlock(compiler, L, B, R); }
36state(A) ::= LSL_DEFAULT(I) stateBlock(B). { A = addState(compiler, NULL, I, B); }
37state(A) ::= LSL_STATE_CHANGE(S) LSL_IDENTIFIER(I) stateBlock(B). { A = addState(compiler, S, I, B); }
38
39// Function definitions.
40
41%nonassoc LSL_PARAMETER LSL_PARAMETER_LIST LSL_FUNCTION.
42functionList(A) ::= functionList(B) functionBody(C). { A = collectStatements(compiler, B, C); }
43functionList(A) ::= functionBody(C). { A = collectStatements(compiler, NULL, C); }
44// No such thing as a function list with no functions.
45//functionList(A) ::= . { A = collectStatements(compiler, NULL, NULL); }
46
47functionBody(A) ::= function(F) block(B). { A = addFunctionBody(compiler, F, B); } // addFunctionBody returns an implied addStatement(compiler, NULL, F, NULL, F, NULL, NULL);
48
49parameterList(A) ::= parameterList(B) LSL_COMMA(C) parameter(D). { A = collectParameters(compiler, B, C, D); }
50parameterList(A) ::= parameter(D). { A = collectParameters(compiler, NULL, NULL, D); }
51parameterList(A) ::= . { A = collectParameters(compiler, NULL, NULL, NULL); }
52parameter(A) ::= type(B) LSL_IDENTIFIER(C). { A = addParameter(compiler, B, C); }
53function(A) ::= LSL_IDENTIFIER(C) LSL_PARENTHESIS_OPEN(D) parameterList(E) LSL_PARENTHESIS_CLOSE(F). { A = addFunction(compiler, NULL, C, D, E, F); }
54function(A) ::= type(B) LSL_IDENTIFIER(C) LSL_PARENTHESIS_OPEN(D) parameterList(E) LSL_PARENTHESIS_CLOSE(F). { A = addFunction(compiler, B, C, D, E, F); }
55
56// Blocks.
57
58block(A) ::= beginBlock(L) statementList(B) LSL_BLOCK_CLOSE(R). { A = addBlock(compiler, L, B, R); }
59
60// Various forms of statement.
61
62%nonassoc LSL_STATEMENT.
63statementList(A) ::= statementList(B) statement(C). { A = collectStatements(compiler, B, C); }
64// This causes infinite loops (and about 150 conflicts) with - if () single statement;
65//statementList(A) ::= statement(C). { A = collectStatements(compiler, NULL, C); }
66// Yes, you can have an empty block.
67statementList(A) ::= . { A = collectStatements(compiler, NULL, NULL); }
68
69%nonassoc LSL_DO LSL_FOR LSL_IF LSL_JUMP LSL_RETURN LSL_STATE_CHANGE LSL_WHILE.
70%nonassoc LSL_ELSE LSL_ELSEIF.
71statement(A) ::= LSL_DO(F) block(B) LSL_WHILE(W) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, L, E, R, B, W); }
72statement(A) ::= LSL_FOR(F) LSL_PARENTHESIS_OPEN(L) expr(E0) LSL_STATEMENT(S0) expr(E1) LSL_STATEMENT(S1) expr(E2) LSL_PARENTHESIS_CLOSE(R) block(B). { A = addFor(compiler, NULL, F, L, E0, S0, E1, S1, E2, R, B); }
73statement(A) ::= LSL_FOR(F) LSL_PARENTHESIS_OPEN(L) expr(E0) LSL_STATEMENT(S0) expr(E1) LSL_STATEMENT(S1) expr(E2) LSL_PARENTHESIS_CLOSE(R) statement(S). { A = addFor(compiler, NULL, F, L, E0, S0, E1, S1, E2, R, S); }
74
75statement(A) ::= ifBlock(B). { A = B; }
76ifBlock(A) ::= ifBlock(B) elseIfBlock(E). { A = addIfElse(compiler, B, E); }
77ifBlock(A) ::= ifBlock(B) elseBlock(E). { A = addIfElse(compiler, B, E); }
78ifBlock(A) ::= LSL_IF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). [LSL_ELSE] { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
79ifBlock(A) ::= LSL_IF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). [LSL_ELSE] { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
80elseIfBlock(A) ::= LSL_ELSEIF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). [LSL_ELSEIF] { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
81elseIfBlock(A) ::= LSL_ELSEIF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). [LSL_ELSEIF] { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
82elseBlock(A) ::= LSL_ELSE(F) block(B). { A = addStatement(compiler, NULL, F, NULL, NULL, NULL, B, NULL); }
83elseBlock(A) ::= LSL_ELSE(F) statement(S). { A = addStatement(compiler, NULL, F, NULL, NULL, NULL, S, NULL); }
84
85statement(A) ::= LSL_JUMP(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
86statement(A) ::= LSL_RETURN(F) expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, E, NULL, NULL, NULL); }
87statement(A) ::= LSL_RETURN(F) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, NULL); }
88statement(A) ::= LSL_STATE_CHANGE(F) LSL_DEFAULT(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
89statement(A) ::= LSL_STATE_CHANGE(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
90statement(A) ::= LSL_WHILE(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
91statement(A) ::= LSL_WHILE(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
92
93%nonassoc LSL_LABEL.
94statement(A) ::= LSL_LABEL(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
95
96beginBlock(A) ::= LSL_BLOCK_OPEN(B). { A = beginBlock(compiler, B); }
97
98// This might be bogus, or might be valid LSL, but it lets us test the expression parser by evaluating them.
99statement(A) ::= expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, S, NULL, E, NULL, NULL, NULL); }
100
101// Various forms of expression.
102
103// Used for function call params, and list contents.
104exprList(A) ::= exprList(B) LSL_COMMA(C) expr(D). { A = collectArguments(compiler, B, C, D); }
105exprList(A) ::= expr(D). { A = collectArguments(compiler, NULL, NULL, D); }
106exprList(A) ::= . { A = collectArguments(compiler, NULL, NULL, NULL); }
107
108// Assignments and variable declarations.
109
110%right LSL_ASSIGNMENT_CONCATENATE LSL_ASSIGNMENT_ADD LSL_ASSIGNMENT_SUBTRACT LSL_ASSIGNMENT_MULTIPLY LSL_ASSIGNMENT_MODULO LSL_ASSIGNMENT_DIVIDE LSL_ASSIGNMENT_PLAIN.
111// Variables and dealing with them.
112
113expr(A) ::= identifier(B). { A = B; }
114
115// Yes, these can be expressions, and can happen in if statements and such. In Lua they are NOT expressions. sigh
116expr(A) ::= identifier(B) LSL_ASSIGNMENT_CONCATENATE(C) expr(D). { A = addOperation(compiler, B, C, D); }
117expr(A) ::= identifier(B) LSL_ASSIGNMENT_ADD(C) expr(D). { A = addOperation(compiler, B, C, D); }
118expr(A) ::= identifier(B) LSL_ASSIGNMENT_SUBTRACT(C) expr(D). { A = addOperation(compiler, B, C, D); }
119expr(A) ::= identifier(B) LSL_ASSIGNMENT_MULTIPLY(C) expr(D). { A = addOperation(compiler, B, C, D); }
120expr(A) ::= identifier(B) LSL_ASSIGNMENT_MODULO(C) expr(D). { A = addOperation(compiler, B, C, D); }
121expr(A) ::= identifier(B) LSL_ASSIGNMENT_DIVIDE(C) expr(D). { A = addOperation(compiler, B, C, D); }
122expr(A) ::= identifier(B) LSL_ASSIGNMENT_PLAIN(C) expr(D). { A = addOperation(compiler, B, C, D); }
123
124// Hmm think this can have commas seperating the assignment parts, or is that only in C?. If so, best to separate them when converting to Lua, as it uses that syntax for something else.
125// Well, not in OpenSim at least, nor in SL. So we are safe. B-)
126// On the other hand, it might be legal to have comma separated bits in a for loop - for ((i = 1), (j=1); ...
127statement(A) ::= type(T) LSL_IDENTIFIER(I) LSL_ASSIGNMENT_PLAIN(D) expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, I, NULL, addVariable(compiler, T, I, D, E), NULL, NULL, I); }
128statement(A) ::= type(T) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, I, NULL, addVariable(compiler, T, I, NULL, NULL), NULL, NULL, I); }
129
130// Basic operators.
131
132%right LSL_BOOL_AND.
133expr(A) ::= expr(B) LSL_BOOL_AND(C) expr(D). { A = addOperation(compiler, B, C, D); }
134%right LSL_BOOL_OR.
135expr(A) ::= expr(B) LSL_BOOL_OR(C) expr(D). { A = addOperation(compiler, B, C, D); }
136
137%left LSL_BIT_AND LSL_BIT_XOR LSL_BIT_OR.
138expr(A) ::= expr(B) LSL_BIT_OR(C) expr(D). { A = addOperation(compiler, B, C, D); }
139expr(A) ::= expr(B) LSL_BIT_XOR(C) expr(D). { A = addOperation(compiler, B, C, D); }
140expr(A) ::= expr(B) LSL_BIT_AND(C) expr(D). { A = addOperation(compiler, B, C, D); }
141
142%right LSL_EQUAL LSL_NOT_EQUAL.
143expr(A) ::= expr(B) LSL_NOT_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
144expr(A) ::= expr(B) LSL_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
145%right LSL_LESS_THAN LSL_GREATER_THAN LSL_LESS_EQUAL LSL_GREATER_EQUAL.
146expr(A) ::= expr(B) LSL_GREATER_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
147expr(A) ::= expr(B) LSL_LESS_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
148expr(A) ::= expr(B) LSL_GREATER_THAN(C) expr(D). { A = addOperation(compiler, B, C, D); }
149expr(A) ::= expr(B) LSL_LESS_THAN(C) expr(D). { A = addOperation(compiler, B, C, D); }
150
151%left LSL_LEFT_SHIFT LSL_RIGHT_SHIFT.
152expr(A) ::= expr(B) LSL_RIGHT_SHIFT(C) expr(D). { A = addOperation(compiler, B, C, D); }
153expr(A) ::= expr(B) LSL_LEFT_SHIFT(C) expr(D). { A = addOperation(compiler, B, C, D); }
154
155%left LSL_SUBTRACT LSL_ADD LSL_CONCATENATE.
156expr(A) ::= expr(B) LSL_ADD(C) expr(D). { A = addOperation(compiler, B, C, D); }
157expr(A) ::= expr(B) LSL_SUBTRACT(C) expr(D). { A = addOperation(compiler, B, C, D); }
158%left LSL_DIVIDE LSL_MODULO LSL_MULTIPLY LSL_DOT_PRODUCT LSL_CROSS_PRODUCT.
159expr(A) ::= expr(B) LSL_MULTIPLY(C) expr(D). { A = addOperation(compiler, B, C, D); }
160expr(A) ::= expr(B) LSL_MODULO(C) expr(D). { A = addOperation(compiler, B, C, D); }
161expr(A) ::= expr(B) LSL_DIVIDE(C) expr(D). { A = addOperation(compiler, B, C, D); }
162
163%right LSL_BIT_NOT LSL_BOOL_NOT LSL_NEGATION.
164expr(A) ::= LSL_BIT_NOT(B) expr(C). { A = addOperation(compiler, NULL, B, C); }
165expr(A) ::= LSL_BOOL_NOT(B) expr(C). { A = addOperation(compiler, NULL, B, C); }
166expr(A) ::= LSL_SUBTRACT(B) expr(C). [LSL_NEGATION] { A = addOperation(compiler, NULL, B, C); }
167
168// Types, typecasts, and expression reordering.
169
170%right LSL_TYPECAST_OPEN LSL_TYPECAST_CLOSE.
171%nonassoc LSL_TYPE_FLOAT LSL_TYPE_INTEGER LSL_TYPE_KEY LSL_TYPE_LIST LSL_TYPE_ROTATION LSL_TYPE_STRING LSL_TYPE_VECTOR.
172type(A) ::= LSL_TYPE_FLOAT(B). { B->basicType = OT_float; A = B; }
173type(A) ::= LSL_TYPE_INTEGER(B). { B->basicType = OT_integer; A = B; }
174type(A) ::= LSL_TYPE_KEY(B). { B->basicType = OT_key; A = B; }
175type(A) ::= LSL_TYPE_LIST(B). { B->basicType = OT_list; A = B; }
176type(A) ::= LSL_TYPE_ROTATION(B). { B->basicType = OT_rotation; A = B; }
177type(A) ::= LSL_TYPE_STRING(B). { B->basicType = OT_string; A = B; }
178type(A) ::= LSL_TYPE_VECTOR(B). { B->basicType = OT_vector; A = B; }
179
180%left LSL_ANGLE_OPEN LSL_ANGLE_CLOSE.
181%nonassoc LSL_BRACKET_OPEN LSL_BRACKET_CLOSE.
182%nonassoc LSL_PARENTHESIS_OPEN LSL_PARENTHESIS_CLOSE LSL_EXPRESSION.
183
184expr(A) ::= LSL_PARENTHESIS_OPEN(B) expr(C) LSL_PARENTHESIS_CLOSE(D). { A = addParenthesis(B, C, LSL_EXPRESSION, D); }
185expr(A) ::= LSL_PARENTHESIS_OPEN(B) type(C) LSL_PARENTHESIS_CLOSE(D) expr(E). { A = addTypecast(B, C, D, E); }
186
187%right LSL_DOT LSL_IDENTIFIER LSL_FUNCTION_CALL LSL_VARIABLE.
188identifier(A) ::= identifier(I) LSL_DOT(D) LSL_IDENTIFIER(B). { A = checkVariable(compiler, I, D, B); }
189identifier(A) ::= LSL_IDENTIFIER(B). { A = checkVariable(compiler, B, NULL, NULL); }
190
191%right LSL_DECREMENT_PRE LSL_INCREMENT_PRE LSL_DECREMENT_POST LSL_INCREMENT_POST.
192expr(A) ::= identifier(B) LSL_DECREMENT_PRE(C). { A = addCrement(compiler, B, C, LSL_DECREMENT_POST); }
193expr(A) ::= identifier(B) LSL_INCREMENT_PRE(C). { A = addCrement(compiler, B, C, LSL_INCREMENT_POST); }
194expr(A) ::= LSL_DECREMENT_PRE(C) identifier(B). { A = addCrement(compiler, B, C, LSL_DECREMENT_PRE); }
195expr(A) ::= LSL_INCREMENT_PRE(C) identifier(B). { A = addCrement(compiler, B, C, LSL_INCREMENT_PRE); }
196
197// Function call.
198
199// Causes a conflict when exprList is empty with a function definition with no type and no parameters.
200expr(A) ::= LSL_IDENTIFIER(B) LSL_PARENTHESIS_OPEN(C) exprList(D) LSL_PARENTHESIS_CLOSE(E). { A = addFunctionCall(compiler, B, C, D, E); }
201
202%nonassoc LSL_COMMA.
203
204// Values.
205
206%nonassoc LSL_FLOAT.
207expr(A) ::= LSL_FLOAT(B). { A = addNumby(B); }
208%nonassoc LSL_INTEGER.
209expr(A) ::= LSL_INTEGER(B). { A = addNumby(B); }
210%nonassoc LSL_KEY.
211expr(A) ::= LSL_KEY(B). { B->basicType = OT_key; A = B; }
212%nonassoc LSL_LIST.
213expr(A) ::= LSL_BRACKET_OPEN(L) exprList(E) LSL_BRACKET_CLOSE(R). [LSL_BRACKET_OPEN] { A = addList(L, E, R); }
214%nonassoc LSL_ROTATION LSL_VECTOR.
215// Uses the same symbol for less than, greater than, and the rotation / vector delimiters.
216expr(A) ::= LSL_LESS_THAN(L) exprList(E) LSL_GREATER_THAN(R). [LSL_ANGLE_OPEN] { A = addRotVec(L, E, R); }
217%nonassoc LSL_STRING.
218expr(A) ::= LSL_STRING(B). { B->basicType = OT_string; A = B; }
219
220
221// Parser callbacks.
222
223%parse_accept
224{
225// gameGlobals *ourGlobals = compiler->game;
226
227// PI("Parsing complete.");
228}
229
230%parse_failure
231{
232 gameGlobals *ourGlobals = compiler->game;
233
234 compiler->script.bugCount++;
235 PE("Giving up. Parser is hopelessly lost!");
236}
237
238%stack_overflow
239{
240 gameGlobals *ourGlobals = compiler->game;
241
242 compiler->script.bugCount++;
243 PE("Giving up. Parser stack overflow @ line %d, column %d!", yypMinor->yy0->line, yypMinor->yy0->column); // Gotta love consistancy, if it ever happens.
244}
245
246%syntax_error
247{
248 gameGlobals *ourGlobals = compiler->game;
249
250 compiler->script.bugCount++;
251 PE("Syntax error @ line %d, column %d!", yyminor.yy0->line, yyminor.yy0->column);
252}
253
254
255/* Undocumented shit that might be useful later. Pffft
256
257** The next table maps tokens into fallback tokens. If a construct
258** like the following:
259**.
260** %fallback ID X Y Z.
261**
262** appears in the grammar, then ID becomes a fallback token for X, Y,
263** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
264** but it does not parse, the type of the token is changed to ID and
265** the parse is retried before an error is thrown.
266
267%wildcard
268%code
269
270%ifdef
271%endif
272%ifndef
273%endif
274
275*/
diff --git a/src/LuaSL/LuaSL_lexer.l b/src/LuaSL/LuaSL_lexer.l
new file mode 100644
index 0000000..85b2821
--- /dev/null
+++ b/src/LuaSL/LuaSL_lexer.l
@@ -0,0 +1,164 @@
1%{
2
3#define excludeLexer
4#include "LuaSL.h"
5
6int common(YYSTYPE *lval, char *text, int len, LuaSL_compiler *compiler, boolean checkIgnorable, int type);
7
8%}
9
10%option reentrant never-interactive batch
11%option bison-bridge 8bit
12%option noreject noyymore
13%option backup debug perf-report perf-report verbose warn
14%option align full
15%option extra-type="LuaSL_compiler *"
16
17HEX [[:xdigit:]]
18DECIMAL [[:digit:]]
19 /* LSL has no octal integer type. */
20INTEGER ({DECIMAL}+)|(0[xX]{HEX}+)
21EXPONANT [eE][+-]?{DECIMAL}+
22 /* Floats can be "0." or".0", but "." is not valid. At least in OpenSim. A single dot should be caught by the LSL_Dot rule first anyway.*/
23FLOAT {DECIMAL}*"."{DECIMAL}*{EXPONANT}?[fF]?
24 /* Variable identifiers can have these extra characters at the end or beginning, they will be ignored- #$`'\? */
25 /* Function identifiers can start with $ */
26IDENTIFIER (_|[[:alpha:]])(_|[[:alpha:]]|[[:digit:]])*
27CHAR '(\\.|[^\\'\n])+'
28KEY \"{HEX}{8}-{HEX}{4}-{HEX}{4}-{HEX}{4}-{HEX}{12}\"
29STRING \"(\\.|[^\\"\n])*\"
30
31%%
32
33 /* The order here is important, in mysterious ways. The more specific the lower in case of ambiguities like "floats contain integers". I think, not tested that well yet. */
34
35 /* Ignorables. */
36[[:space:]]+ %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_SPACE); %}
37 /* Yes I know this will have problems with huge comments, just being simple to get it to work for now. */
38"/*"([^"*/"]*)"*/" %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_COMMENT); %}
39"//"[^\n]* %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_COMMENT_LINE); %}
40
41 /* Operations. */
42"&&" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_AND); }
43"||" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_OR); }
44"|" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_OR); }
45"^" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_XOR); }
46"&" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_AND); }
47"!=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_NOT_EQUAL); }
48"==" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_EQUAL); }
49">=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_GREATER_EQUAL); }
50"<=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LESS_EQUAL); }
51">" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_GREATER_THAN); }
52"<" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LESS_THAN); }
53">>" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_RIGHT_SHIFT); }
54"<<" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LEFT_SHIFT); }
55"+" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ADD); }
56"-" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_SUBTRACT); }
57"*" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_MULTIPLY); }
58"%" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_MODULO); }
59"/" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DIVIDE); }
60"!" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_NOT); }
61"~" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_NOT); }
62"[" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BRACKET_OPEN); }
63"]" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BRACKET_CLOSE); }
64"(" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_PARENTHESIS_OPEN); }
65")" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_PARENTHESIS_CLOSE); }
66"+=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_ADD); }
67"-=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_SUBTRACT); }
68"*=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_MULTIPLY); }
69"%=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_MODULO); }
70"/=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_DIVIDE); }
71"=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_PLAIN); }
72"." { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DOT); }
73"--" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DECREMENT_PRE); }
74"++" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_INCREMENT_PRE); }
75"," { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_COMMA); }
76
77 /* Other symbols. */
78"@" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LABEL); %}
79"{" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BLOCK_OPEN); %}
80"}" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BLOCK_CLOSE); %}
81";" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STATEMENT); %}
82
83 /* Type keywords. */
84"float" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_FLOAT); %}
85"integer" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_INTEGER); %}
86"key" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_KEY); %}
87"list" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_LIST); %}
88"quaternion" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_ROTATION); %}
89"rotation" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_ROTATION); %}
90"string" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_STRING); %}
91"vector" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_VECTOR); %}
92
93 /* Statement keywords. */
94"default" %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DEFAULT); %}
95"do" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DO); %}
96"for" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_FOR); %}
97"else" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ELSE); %}
98"if" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_IF); %}
99"else"[[:space:]]+"if" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ELSEIF); %} /* TODO - deal with people that slap a comment in between them. */
100"jump" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_JUMP); %}
101"return" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_RETURN); %}
102"state" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STATE_CHANGE); %}
103"while" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_WHILE); %}
104
105{IDENTIFIER} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_IDENTIFIER); %}
106
107 /* Types. */
108{INTEGER} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_INTEGER); %}
109{FLOAT} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_FLOAT); %}
110{KEY} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_KEY); %}
111{STRING} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STRING); %}
112
113<<EOF>> { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_SCRIPT); }
114
115 /* Everything else */
116. %{ printf(" unexpected character.\n"); yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); common(yylval, yytext, yyleng, yyextra, TRUE, LSL_UNKNOWN); %}
117
118%%
119
120int common(YYSTYPE *lval, char *text, int len, LuaSL_compiler *compiler, boolean checkIgnorable, int type)
121{
122 char *p;
123
124 lval->toKen = tokens[type - lowestToken];
125 lval->line = compiler->line;
126 lval->column = compiler->column;
127 lval->len = len;
128
129 for (p = text; *p; p++)
130 {
131 if ('\n' == *p)
132 {
133 compiler->line++;
134 compiler->column = 1;
135 }
136 else
137 compiler->column++;
138 }
139
140#if LUASL_DIFF_CHECK
141 if (checkIgnorable)
142 {
143 lval->ignorable = compiler->ignorable;
144 compiler->ignorable = eina_strbuf_new();
145 }
146 else
147 eina_strbuf_append_length(compiler->ignorable, text, len);
148#endif
149
150 return type;
151}
152
153int yywrap(yyscan_t yyscanner) // This as actually useless for our needs, as it is called BEFORE the last token is dealt with.
154{
155#ifdef FLEX_SCANNER
156 #ifndef LL_WINDOWS
157 // Get gcc to stop complaining about lack of use of yyunput and input.
158 (void) yyunput;
159 (void) input;
160 #endif
161#endif
162
163 return 1;
164}
diff --git a/src/LuaSL/LuaSL_main.c b/src/LuaSL/LuaSL_main.c
new file mode 100644
index 0000000..0a40712
--- /dev/null
+++ b/src/LuaSL/LuaSL_main.c
@@ -0,0 +1,708 @@
1
2#include "LuaSL.h"
3
4
5static int CPUs = 4;
6static Eina_Strbuf *clientStream;
7
8
9static Eina_Bool _sleep_timer_cb(void *data)
10{
11 script *script = data;
12 gameGlobals *ourGlobals = script->game;
13
14 PD("Waking up %s", script->SID);
15 sendToChannel(ourGlobals, script->SID, "return 0.0");
16 return ECORE_CALLBACK_CANCEL;
17}
18
19static Eina_Bool _timer_timer_cb(void *data)
20{
21 script *script = data;
22 gameGlobals *ourGlobals = script->game;
23
24 PD("Timer for %s", script->SID);
25 sendToChannel(ourGlobals, script->SID, "events.timer()");
26 return ECORE_CALLBACK_RENEW;
27}
28
29static script *findThem(gameGlobals *ourGlobals, const char *base, const char *text)
30{
31 char name[PATH_MAX];
32 char *temp;
33
34 strncpy(name, base, PATH_MAX);
35 if ((temp = rindex(name, '/')))
36 temp[1] = '\0';
37 strcat(name, text);
38 if ((temp = rindex(name, '"')))
39 temp[0] = '\0';
40 strcat(name, ".lsl");
41 return eina_hash_find(ourGlobals->names, name);
42}
43
44static void resetScript(script *victim)
45{
46 gameGlobals *ourGlobals = victim->game;
47
48 PD("Resetting %s", victim->fileName);
49 // TODO - now what?
50}
51
52void scriptSendBack(void * data)
53{
54 scriptMessage *message = data;
55 gameGlobals *ourGlobals = message->script->game;
56
57 if (0 == strncmp(message->message, "llSleep(", 8))
58 ecore_timer_add(atof(&(message->message)[8]), _sleep_timer_cb, message->script);
59 else if (0 == strncmp(message->message, "llSetTimerEvent(", 16))
60 {
61 message->script->timerTime = atof(&(message->message)[16]);
62 if (0.0 == message->script->timerTime)
63 {
64 if (message->script->timer)
65 ecore_timer_del(message->script->timer);
66 message->script->timer = NULL;
67 }
68 else
69 message->script->timer = ecore_timer_add(message->script->timerTime, _timer_timer_cb, message->script);
70 }
71 else if (0 == strncmp(message->message, "llSetScriptState(", 17))
72 {
73 script *them;
74
75 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[18]))))
76 {
77 char *temp = rindex(&(message->message[18]), ',');
78
79 if (temp)
80 {
81 temp++;
82 while (isspace(*temp))
83 temp++;
84 if ('1' == *temp)
85 sendToChannel(ourGlobals, them->SID, "start()");
86 else
87 sendToChannel(ourGlobals, them->SID, "stop()");
88 PD("Stopped %s", them->fileName);
89 }
90 else
91 PE("Missing script state in llSetScriptState(%s, )", them->fileName);
92 }
93 else
94 {
95 char *temp = rindex(&(message->message[18]), '"');
96
97 if (temp)
98 *temp = '\0';
99 PE("Can't stop script, can't find %s", &(message->message[18]));
100 }
101 }
102 else if (0 == strncmp(message->message, "llResetOtherScript(", 19))
103 {
104 script *them;
105
106 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[20]))))
107 resetScript(them);
108 }
109 else if (0 == strncmp(message->message, "llResetScript(", 14))
110 resetScript(message->script);
111 else
112 sendBack(ourGlobals, message->script->client, message->script->SID, message->message);
113 free(message);
114}
115
116static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Add *ev)
117{
118 ecore_con_client_timeout_set(ev->client, 0);
119 return ECORE_CALLBACK_RENEW;
120}
121
122static Eina_Bool _data(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Data *ev)
123{
124 gameGlobals *ourGlobals = data;
125 char buf[PATH_MAX];
126 char SID[PATH_MAX];
127 const char *command;
128 char *ext;
129
130 eina_strbuf_append_length(clientStream, ev->data, ev->size);
131 command = eina_strbuf_string_get(clientStream);
132 while ((ext = index(command, '\n')))
133 {
134 int length = ext - command;
135
136 strncpy(SID, command, length + 1);
137 SID[length] = '\0';
138 eina_strbuf_remove(clientStream, 0, length + 1);
139 ext = index(SID, '.');
140 if (ext)
141 {
142 ext[0] = '\0';
143 command = ext + 1;
144 if (0 == strncmp(command, "compile(", 8))
145 {
146 char *temp;
147 char *file;
148
149 strcpy(buf, &command[8]);
150 temp = buf;
151 file = temp;
152 while (')' != temp[0])
153 temp++;
154 temp[0] = '\0';
155
156 PD("Compiling %s, %s.", SID, file);
157 if (compileLSL(ourGlobals, ev->client, SID, file, FALSE))
158 {
159 script *me = calloc(1, sizeof(script));
160
161 gettimeofday(&me->startTime, NULL);
162 strncpy(me->SID, SID, sizeof(me->SID));
163 strncpy(me->fileName, file, sizeof(me->fileName));
164 me->game = ourGlobals;
165 me->client = ev->client;
166 eina_hash_add(ourGlobals->scripts, me->SID, me);
167 eina_hash_add(ourGlobals->names, me->fileName, me);
168 sendBack(ourGlobals, ev->client, SID, "compiled(true)");
169 }
170 else
171 sendBack(ourGlobals, ev->client, SID, "compiled(false)");
172 }
173 else if (0 == strcmp(command, "run()"))
174 {
175 script *me;
176 char buf[PATH_MAX];
177
178 me = eina_hash_find(ourGlobals->scripts, SID);
179 if (me)
180 {
181 sprintf(buf, "%s.lua.out", me->fileName);
182 newProc(buf, TRUE, me);
183 }
184 }
185 else if (0 == strcmp(command, "exit()"))
186 {
187 PD("Told to exit.");
188 ecore_main_loop_quit();
189 }
190 else
191 {
192 const char *status = NULL;
193
194 status = sendToChannel(ourGlobals, SID, command);
195 if (status)
196 PE("Error sending command %s to script %s : %s", command, SID, status);
197 }
198 }
199
200 // Get the next blob to check it.
201 command = eina_strbuf_string_get(clientStream);
202 }
203
204 return ECORE_CALLBACK_RENEW;
205}
206
207static Eina_Bool _del(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Del *ev)
208{
209 gameGlobals *ourGlobals = data;
210
211 if (ev->client)
212 {
213 PD("No more clients, exiting.");
214 ecore_con_client_del(ev->client);
215 ecore_main_loop_quit();
216 }
217 return ECORE_CALLBACK_RENEW;
218}
219
220int main(int argc, char **argv)
221{
222 gameGlobals ourGlobals;
223 int result = EXIT_FAILURE;
224
225 memset(&ourGlobals, 0, sizeof(gameGlobals));
226 ourGlobals.address = "127.0.0.1";
227 ourGlobals.port = 8211;
228
229 if (eina_init())
230 {
231 ourGlobals.logDom = loggingStartup("LuaSL", ourGlobals.logDom);
232 ourGlobals.scripts = eina_hash_string_superfast_new(NULL);
233 ourGlobals.names = eina_hash_string_superfast_new(NULL);
234 if (ecore_con_init())
235 {
236 if (ecore_con_init())
237 {
238 if ((ourGlobals.server = ecore_con_server_add(ECORE_CON_REMOTE_TCP, ourGlobals.address, ourGlobals.port, &ourGlobals)))
239 {
240 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb) _add, &ourGlobals);
241 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb) _data, &ourGlobals);
242 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb) _del, &ourGlobals);
243 ecore_con_server_timeout_set(ourGlobals.server, 0);
244 ecore_con_server_client_limit_set(ourGlobals.server, -1, 0);
245 ecore_con_server_timeout_set(ourGlobals.server, 10);
246 ecore_con_server_client_limit_set(ourGlobals.server, 3, 0);
247 clientStream = eina_strbuf_new();
248
249 if (edje_init())
250 {
251 int i;
252
253 result = 0;
254 compilerSetup(&ourGlobals);
255 luaprocInit();
256 for (i = 0; i < CPUs; i++)
257 {
258 if ( sched_create_worker( ) != LUAPROC_SCHED_OK )
259 PEm("Error creating luaproc worker thread.");
260 }
261 ecore_main_loop_begin();
262
263 // TODO - this is what hangs the system, should change from raw pthreads to ecore threads.
264 sched_join_workerthreads();
265 edje_shutdown();
266 }
267 else
268 PCm("Failed to init edje!");
269 }
270 else
271 PCm("Failed to add server!");
272 ecore_con_shutdown();
273 }
274 else
275 PCm("Failed to init ecore_con!");
276 ecore_shutdown();
277 }
278 else
279 PCm("Failed to init ecore!");
280 }
281 else
282 fprintf(stderr, "Failed to init eina!");
283
284 return result;
285}
286
287
288/* Stuff to be merged in later.
289
290#ifdef _WIN32
291# define FMT_SIZE_T "%Iu"
292#else
293# define FMT_SIZE_T "%zu"
294#endif
295
296#define MAX_LUA_MEM (64 * 1024)) // LSL Mono is 64KB, edje Lua is 4MB. (4 * (1024 * 1024))
297
298#define _edje_lua2_error(L, err_code) _edje_lua2_error_full(__FILE__, __FUNCTION__, __LINE__, L, err_code)
299
300
301typedef struct _Edje_Lua_Alloc Edje_Lua_Alloc;
302
303struct _Edje_Lua_Alloc
304{
305 size_t max, cur;
306};
307
308static void *
309_elua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
310{
311 Edje_Lua_Alloc *ela = ud;
312 void *ptr2 = NULL;
313
314 if (ela)
315 {
316 ela->cur += nsize - osize;
317 if (ela->cur > ela->max)
318 {
319 printf("Lua memory limit of " FMT_SIZE_T " bytes reached (" FMT_SIZE_T " allocated)", ela->max, ela->cur);
320 }
321 else if (nsize == 0)
322 {
323 free(ptr);
324 }
325 else
326 {
327 ptr2 = realloc(ptr, nsize);
328 if (NULL == ptr2)
329 printf("Lua cannot re-allocate " FMT_SIZE_T " bytes", nsize);
330 }
331 }
332 else
333 printf("Lua cannoct allocate memory, no Edje_Lua_Alloc");
334
335 return ptr2;
336}
337
338static int panics = 0;
339static int
340_elua_custom_panic(lua_State *L) // Stack usage [-0, +0, m]
341{
342 // If we somehow manage to have multiple panics, it's likely due to being out
343 // of memory in the following lua_tostring() call.
344 panics++;
345 if (panics)
346 {
347 printf("Lua PANICS!!!!!");
348 }
349 else
350 {
351 printf("Lua PANIC!!!!!: %s", lua_tostring(L, -1)); // Stack usage [-0, +0, m]
352 }
353 // The docs say that this will cause an exit(EXIT_FAILURE) if we return,
354 // and that we we should long jump some where to avoid that. This is only
355 // called for things not called from a protected environment. We always
356 // use pcalls though, except for the library load calls. If we can't load
357 // the standard libraries, then perhaps a crash is the right thing.
358 return 0;
359}
360
361static void
362_edje_lua2_error_full(const char *file, const char *fnc, int line,
363 lua_State *L, int err_code) // Stack usage [-0, +0, m]
364{
365 const char *err_type;
366
367 switch (err_code)
368 {
369 case LUA_ERRRUN:
370 err_type = "runtime";
371 break;
372 case LUA_ERRSYNTAX:
373 err_type = "syntax";
374 break;
375 case LUA_ERRMEM:
376 err_type = "memory allocation";
377 break;
378 case LUA_ERRERR:
379 err_type = "error handler";
380 break;
381 default:
382 err_type = "unknown";
383 break;
384 }
385 printf("Lua %s error: %s\n", err_type, lua_tostring(L, -1)); // Stack usage [-0, +0, m]
386}
387
388static int errFunc(lua_State *L)
389{
390 int i = 0;
391 lua_Debug ar;
392
393 while (lua_getstack(L, i++, &ar))
394 {
395 if (lua_getinfo(L, "nSlu", &ar))
396 {
397 if (NULL == ar.name)
398 ar.name = "DUNNO";
399 printf("Lua error in the %s %s %s @ line %d in %s\n%s!", ar.what, ar.namewhat, ar.name, ar.currentline, ar.short_src, ar.source);
400 }
401 }
402 return 0;
403}
404
405void runLuaFile(gameGlobals *ourGlobals, const char *filename)
406{
407 PD("Starting %s", filename);
408 newProc(filename, TRUE);
409
410// TODO, should set up our panic and errfunc as below. Plus the other TODO stuff.
411
412
413// TODO - hack up LuaJIT so that we can limit memory per state.
414// lua_setallocf(L, _elua_alloc, &ela); // LuaJIT uses a heavily hacked up dlmalloc. Seems that standard realloc is not so thread safe?
415 lua_atpanic(L, _elua_custom_panic);
416// TODO - Sandbox out what this opens. See lib_init.c from LuaJIT.
417// Just noticed this in the LuaJIT docs - "To change or extend the list of standard libraries to load, copy src/lib_init.c to your project and modify it accordingly. Make sure the jit library is loaded or the JIT compiler will not be activated."
418 luaL_openlibs(L);
419
420 lua_pushcfunction(L, errFunc);
421 ...
422 if ((err = lua_pcall(L, 0, 0, -2)))
423 _edje_lua2_error(L, err);
424}
425*/
426
427
428
429
430/* What we need to do, and how we might do it.
431 *
432 * Get configuration info.
433 *
434 * Some of the configuration info we need is tucked away in OpenSim .ini files. So we have to read that.
435 * Eet is probably not so useful here, as we would have to write a UI tool, and there's no UI otherwise.
436 *
437 * Multi task!
438 *
439 * Luaproc starts up X worker threads. Each one takes the next ready Lua state and runs it until it blocks waiting for a channel message, or yields.
440 * The current mainloop waits for events and commands from the message channel, executes them, then goes back to waiting.
441 * So that is fine in general, so long as the LSL event handlers actually return in a reasonable time.
442 * We need to be able to yield at suitable places, or be able to force a yield. Both without slowing things down too much.
443 *
444 * Watchdog thread.
445 *
446 * It's easy enough to have a watchdog thread wake up every now and then to check if any given Lua state has been hogging it's CPU for too long.
447 * The hard part is forcing Lua states to yield cleanly, without slowing performance too much.
448 *
449 * Identifying scripts. - OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
450 *
451 * We need to be able to identify scripts so that messages can get to the right ones. Also the objects they are in.
452 * And do it all in a way that OpenSim is happy with.
453 * Copies of the same script in the same prim would have the same asset UUID, but different name, though they would run independently.
454 * Copies of the same script in different prims could have the same asset UUID AND the same name.
455 * OpenSim seems to be using a UUID to identify single scripts, and a uint to identify single prims, when sending events for either case.
456 * The UUID is from the script item in the prims inventory.
457 * There is also an asset UUID (the one printed out on the console at script startup time) that points to the source code in the prim.
458 * Which will be identical to the asset UUID for the multiple copies of the same script.
459 *
460 * script assetID
461 * UUID of the script source in the grids asset database, also the script source in the prim.
462 *
463 * script itemID
464 * UUID of this instance of the running script.
465 * UUID of the scripts binary in the prims inventory.
466 * This is the one used to identify the running script.
467 *
468 * prim uint localID
469 * Some sort of number representing the prim the script is running in.
470 * Events are sometimes sent to this.
471 *
472 * path/filename
473 * An invention of LuaSL, coz we store stuff as files.
474 *
475 * OpenSim says "compile this assetID for this itemID, in this prim uint"
476 * Current infrastructure does not allow easy sending of the script source, but we don't have ROBUST code to get it either.
477 * ROBUST is the way to go though, coz we can sneakily start to suck other stuff, like prim contents across when needed.
478 * Though that sort of thing needs access to the local sim databases to lookup the prim and it's other contents. sigh
479 * I think that new script and notecard contents get new assetIDs anyway, so keeping an eye on assets.create_time or asset_access_time wont help much.
480 *
481 * OpenSim says "start / stop this itemID"
482 * Already catered for.
483 *
484 * What does OpenSim REALLY do?
485 *
486 * Region/Framework/Scenes/Scene.Inventory.cs - CapsUpdateTaskInventoryScriptAsset(IClientAPI remoteClient, UUID itemId, UUID primId, bool isScriptRunning, byte[] data)
487 * remoteClient
488 * itemID - UUID of the script source.
489 * primID - UUID of the prim it is in.
490 * isScriptRunning
491 * data - the script source code.
492 * Called when a user saves the script. itemID stays the same, but we get a new assetID, for the new source code asset.
493 * Looks up the item in the prim.
494 * AssetBase asset = CreateAsset(item.Name, item.Description, (sbyte)AssetType.LSLText, data, remoteClient.AgentId);
495 * AssetService.Store(asset);
496 * stashes the new assetID in the item
497 * updates the item in the prim
498 * if (isScriptRunning)
499 * part.Inventory.RemoveScriptInstance(item.ItemID, false);
500 * part.Inventory.CreateScriptInstance(item.ItemID, 0, false, DefaultScriptEngine, 0);
501 * errors = part.Inventory.GetScriptErrors(item.ItemID);
502 *
503 * CreateScriptInstance() is generally called to start scripts, part.ParentGroup.ResumeScripts(); is usually called after CreateScriptInstance()
504 *
505 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource)
506 * looks up the itemID, then calls the real one -
507 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource)
508 * get the asset from the asset database using the items assetID
509 * restores script state if needed
510 * converts asset.data to a string called script
511 * m_part.ParentGroup.Scene.EventManager.TriggerRezScript(m_part.LocalId, item.ItemID, script, startParam, postOnRez, engine, stateSource);
512 * QUIRK - if it's a sim border crossing, TriggerRezScript() gets called with empty script source.
513 *
514 * Region/ScriptEngine/XEngine/XEngine.cs - AddRegion(Scene scene)
515 * m_log.InfoFormat("[XEngine] Initializing scripts in region {0}", scene.RegionInfo.RegionName);
516 * gets the script config info, which is the same damn stuff for each sim. Pffft
517 * Think it relies on the scenes event manager to call OnRezScript() -
518 * m_Scene.EventManager.OnRezScript += OnRezScript;
519 *
520 * Region/Framework/Scenes/EventManager.cs - TriggerRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
521 * Loops through Scene.EventManager.OnRezScript calling them.
522 *
523 * Region/ScriptEngine/XEngine/XEngine.cs - OnRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
524 * Looks at the script source to figure out if it's an XEngine script.
525 * Either queues the script for later, or does it direct.
526 * Region/ScriptEngine/XEngine/XEngine.cs - DoOnRezScript() is passed an array holding -
527 * localID is a uint that represents the containing prim in the current scene
528 * itemID is the UUID of the script in the prims contents
529 * script is the script source code.
530 * startParam is the scripts startParam
531 * postOnRez
532 * stateSource is an integer saying how we where started, used to trigger the appropriate startup events.
533 * uses localID to look up the prim in the scene, then looks inside that for the itemID to find the assetID.
534 * m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assembly, out linemap);
535 * Which is in Region/ScriptEngine/Shared/CodeTools/Compiler.cs
536 * instance = new ScriptInstance(this, part, itemID, assetID, assembly, m_AppDomains[appDomain], part.ParentGroup.RootPart.Name, item.Name, startParam, postOnRez, stateSource, m_MaxScriptQueue);
537 * Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - ScriptInstance(IScriptEngine engine, SceneObjectPart part, UUID itemID, UUID assetID, string assembly, AppDomain dom, string primName, string scriptName, int startParam, bool postOnRez, StateSource stateSource, int maxScriptQueue)
538 * inits all the APIs
539 * loads in any saved state if it can find one
540 * m_log.DebugFormat("[XEngine] Loaded script {0}.{1}, script UUID {2}, prim UUID {3} @ {4}.{5}", part.ParentGroup.RootPart.Name, item.Name, assetID, part.UUID, part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
541 *
542 * Soooo, when a script is saved -
543 * the new source is saved in the asset database
544 * The script item in the prim gets the new assetID
545 * if the script is running -
546 * remove the old script instance (item.ItemID)
547 * create a new one (item.ItemID)
548 * get the source code from the asset database (item.assetID)
549 * restore script state
550 * TriggerRezOnScript()
551 * Loop through all those that are interested, incuding XEngine.onRezScript()
552 *** check the first line to see if it's an XEngine script
553 * sooner or later passes it to XEngine.DoOnRezScript()
554 * looks up localID to get the prim
555 * looks inside prim to get the script from itemID
556 * gets the assetID from the script item
557 * compiles the script
558 * creates the script instance
559 * loads up the APIs
560 * restores any script state
561 * calls instance.Init() which is Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - Init()
562 * passes the usual startup events to the script.
563 * part.ParentGroup.ResumeScripts()
564 *
565 * At the *** marked point, LuaSL.onRezScript should -
566 * check the first line to see if it's an LuaSL script
567 * looks up localID to get the prim
568 * looks inside prim to get the script from itemID
569 * gets the assetID from the script item
570 * filename encode the sim name, object name, and script name
571 * replace anything less than 0x21, DEL " * / : < > ? \ | + [ ] - , . ( ) $ % # @ from - http://en.wikipedia.org/wiki/Filename plus a few more
572 * THEN reduce to 254 characters
573 * NOTE the object names might be identical, disambiguate them.
574 * write the script to a file - /script/engine/path/sim_name/objects/object_name/script_name
575 * send the itemID.compile(/script/engine/path/sim_name/objects/object_name/script_name) message to the script engine's socket
576 *
577 *
578 * Object inventory "cache".
579 *
580 * This code currently pretends that there is a local file based sim object store available.
581 * I think it would be a good idea to abuse the OpenSim cache system to produce that file based object store.
582 * It will help with the "damn OpenSim's asset database has to be a bottomless pit" monster design flaw.
583 * Prim contents must all be unique names anyway, and there are SOME constraints on contents names, so probably don't have to do much to convert an item name to a legal file name.
584 * Oops, names can have directory slashes in them. lol
585 * On the other hand, sim objects CAN have the same name.
586 *
587 * So we got sim directories, with an objects directory inside it, with object directories inside that. The object directories have object files in them. This is all like the test setup that is here.
588 * We need metadata. Sim metadata, object metadata, and object contents metadata. That can be done with a "foo.omg" file at each level.
589 * sim/index.omg - the list of object name.UUIDs, their X,Y,Z location, size, and rotation.
590 * sim/objects/objectName_UUID/index.omg - the list of contents names, item UUIDs, asset UUIDs, and types.
591 * sim/objects/objectName/subObjectName - the list of ITS contents names, item UUIDs, asset UUIDs, and types.
592 *
593 * Script start, stop, reset. - OpenSim/Region/ScriptEngine/Interfaces/IScriptEngine.cs
594 *
595 * Scripts and users have to be able to start, stop, and reset scripts.
596 * Users have to be able to see the start / stopped status of scripts from the viewer.
597 * Which means if the script does it, we have to tell OpenSim.
598 * Naturally, if OpenSim does it, it has to tell us.
599 * Should be able to do both by passing textual Lua function calls between OpenSim and LuaSL.
600 *
601 * Event handling, llDetect*() functions.
602 *
603 * OpenSim will generate events at random times and send us relevant information for llDetect*() functions and the handler calls.
604 * These should come through the scripts main loop eventually.
605 *
606 * Send messages from / to OpenSim and ROBUST.
607 *
608 * ROBUST uses HTTP for the communications, and some sort of XML, probably XML-RPC.
609 * OpenSim has some sort of generic mechanism for talking to script engines in C#. I want to turn that into a socket based, pass textual Lua function calls, type system.
610 * That assumes C# has some sort of semi decent introspection or reflection system.
611 * After a minimum of research, I have come to the conclusion that C# has suitable introspection, and will go ahead with my plans, leaving that problem to the C# coders.
612 * Looks like OpenSim is already using a bit of C# introspection for ll*() functions anyway.
613 * The scripts main loop can already deal with incoming commands as a string with a Lua function call in it.
614 *
615 * Send messages from / to Lua or C, and wait or not wait.
616 *
617 * Luaproc channels are distinct from Lua states, but some Lua state has to create them before they can be used.
618 * On the other hand, looks like broadcasting messages is not really catered for, it's first come first served.
619 * luaproc.send() can send multiple messages to a single channel. It blocks if no one is listening.
620 * This was done to simplify things for the luaproc programmers, who suggest creating more Lua states to deal with asynchronous message sending.
621 * luaprog.receive() gets a channel message. It can block waiting, or not block.
622 * I already hacked up C code to send and not block. I might have broken the luaproc.send() ability to send multiple messages.
623 * Before I hacked it up, actual message sending was done by copying the contents of the sending Lua states stack to the receiver states stack.
624 * This is the simple method for the luaproc programmers, as both states are in the context of a luaproc call, so both stacks are available.
625 * My hacked up version either takes one message from the sender, or is passed one from C. The C call just returns if there is no one waiting on that channel.
626 * luaproc.send() calls that C function after taking a single message from the stack, and block waits as usual if the C call cannot deliver.
627 * Don't think there is C to receive messages, luaproc seems to be lacking entirely in C side API.
628 * NOTE - Sending from C means that the message goes nowhere if no one is waiting for it.
629 * SOOOO, we may need to queue messages to.
630 * Just chuck them in a FIFO per channel, and destroy the FIFO when the channel get's destroyed.
631 * See if I can create the SID channel in C before I start the Lua state running.
632 * Edje messages might have to be used instead, or some hybrid.
633 *
634 * Main loop is waiting on messages, and that's the main driver. Luaproc is fine with that. Good for events.
635 * End of event handler -
636 * just wait for the next event.
637 * Stop a script from LSL -
638 * gotta find it's SID from it's name, and the prim UUID
639 * send the message
640 * wait for it to get the message - BUT we don't really want to wait.
641 * Stop a script from OpenSim -
642 * we should have it's SID from OpenSim, just send the message from C, no need to wait.
643 * Start a script -
644 * if it's stopped, it's already waiting for the message.
645 * if it's not stopped, then we don't care. BUT then we might be waiting for it to get the message if starting it from LSL.
646 * Reset a script -
647 * probably should be done from C anyway, and can reuse the libraries like luaproc likes to do.
648 * ask C to reset it.
649 * LSL calls a function we have to hand to OpenSim -
650 * send the message to C, wait.
651 * C eventually sends a message back.
652 * Sleep -
653 * tell C it's waiting for the wake up message.
654 * wait for the wake up message.
655 *
656 * C needs -
657 * Lua call for stop script.
658 * get the SID from the name, and the prim UUID.
659 * send the stop message to the SID.
660 * send something to OpenSim so it knows.
661 * return to Lua.
662 * Lua call for start script.
663 * get the SID from the name, and the prim UUID.
664 * send the start message to the SID.
665 * send something to OpenSim so it knows.
666 * return to Lua.
667 * Lua call for reset other script.
668 * get the SID from the name, and the prim UUID.
669 * figure out which Lua state it is.
670 * fall through to "reset this script", only with the script set to the found one.
671 * Lua call for reset this script.
672 * get luaproc to close this Lua state
673 * reload the script file
674 * start it again, reusing the previous Lua state, or which ever one luaproc wants to use.
675 * Lua call for sending a function to OpenSim.
676 * Lua first strings up the function call and args, with SID.
677 * C packs it off to opensim.
678 * C puts Lua state on the "waiting for message" queue if a return value is needed.
679 * OpenSim sends back the return value, business as usual.
680 * Lua call for sleep.
681 * setup an ecore timer callback
682 * put the Lua state into "waiting for message" queue.
683 * ecore timer callback sends the wake up message.
684 *
685 * Time and timers, plus deal with time dilation.
686 *
687 * Various LSL functions deal with time, that's no problem.
688 * Some might deal with time dilation, which means dealing with that info through OpenSim.
689 * There is a timer event, which should be done through ecore timers and whatever message system we end up with.
690 * Finally, a sleep call, which can be done with ecore timer and messages to.
691 *
692 * Deal directly with MySQL and SQLite databases.
693 *
694 * The script engine can run on the same computer as the sim server, that's how OpenSim does stuff. So we can directly access the database the sim server has, which gives us access to sim object metadata.
695 * Changing that metadata might require us to inform OpenSim about the changes. It's entirely possible that different changes do or do not need that.
696 * Esskyuehl may be suitable, though it's still in the prototype stage.
697 *
698 * Serialise the script state, send it somewhere.
699 *
700 * Lua can generally serialise itself as as a string of code to be executed at the destination. There might be some C side state that needs to be take care of as well. We shall see.
701 *
702 * Email, HTTP, XML-RPC?
703 *
704 * LSL has functions for using these as communications methods. We should implement them ourselves eventually, but use the existing OpenSim methods for now.
705 * Note that doing it ourselves may cause issues with OpenSim doing it for some other script engine.
706 * Azy might be suitable, but it's also in prototype.
707 *
708*/
diff --git a/src/LuaSL/LuaSL_test.c b/src/LuaSL/LuaSL_test.c
new file mode 100644
index 0000000..58d6225
--- /dev/null
+++ b/src/LuaSL/LuaSL_test.c
@@ -0,0 +1,467 @@
1
2#include "LuaSL.h"
3
4
5static Eina_Strbuf *clientStream;
6static int scriptCount = 0;
7static int compiledCount = 0;
8static float compileTime = 0.0;
9static struct timeval startTime;
10static int timedEvent = 0;
11static char *ownerKey = "12345678-1234-4321-abcd-0123456789ab";
12static char *ownerName = "onefang rejected";
13
14static const char *names[] =
15{
16 "bub1", "sh1",
17 "bub2", "sh2",
18 "bub3", "sh3",
19};
20
21
22static void
23_edje_signal_cb(void *data, Evas_Object *obj __UNUSED__, const char *emission, const char *source)
24{
25// gameGlobals *ourGlobals = data;
26}
27
28static
29Eina_Bool anim(void *data)
30{
31 gameGlobals *ourGlobals = data;
32 Evas_Object *bub, *sh;
33 Evas_Coord x, y, w, h, vw, vh;
34 double t, xx, yy, zz, r, fac;
35 double lx, ly;
36 unsigned int i;
37
38 evas_output_viewport_get(ourGlobals->canvas, 0, 0, &vw, &vh);
39 r = 48;
40 t = ecore_loop_time_get();
41 fac = 2.0 / (double)((sizeof(names) / sizeof(char *) / 2));
42 evas_pointer_canvas_xy_get(ourGlobals->canvas, &x, &y);
43 lx = x;
44 ly = y;
45
46 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
47 {
48 bub = evas_object_data_get(ourGlobals->bg, names[i * 2]);
49 sh = evas_object_data_get(ourGlobals->bg, names[(i * 2) + 1]);
50 zz = (((2 + sin(t * 6 + (M_PI * (i * fac)))) / 3) * 64) * 2;
51 xx = (cos(t * 4 + (M_PI * (i * fac))) * r) * 2;
52 yy = (sin(t * 6 + (M_PI * (i * fac))) * r) * 2;
53
54 w = zz;
55 h = zz;
56 x = (vw / 2) + xx - (w / 2);
57 y = (vh / 2) + yy - (h / 2);
58
59 evas_object_move(bub, x, y);
60 evas_object_resize(bub, w, h);
61
62 x = x - ((lx - (x + (w / 2))) / 4);
63 y = y - ((ly - (y + (h / 2))) / 4);
64
65 evas_object_move(sh, x, y);
66 evas_object_resize(sh, w, h);
67 evas_object_raise(sh);
68 evas_object_raise(bub);
69 }
70 return ECORE_CALLBACK_RENEW;
71}
72
73static void
74_on_delete(Ecore_Evas *ee __UNUSED__)
75{
76 ecore_main_loop_quit();
77}
78
79static void dirList_compile(const char *name, const char *path, void *data)
80{
81 gameGlobals *ourGlobals = data;
82
83 char *ext = rindex(name, '.');
84
85 if (ext)
86 {
87 if (0 == strcmp(ext, ".lsl"))
88 {
89 script *me = calloc(1, sizeof(script));
90
91 scriptCount++;
92 gettimeofday(&me->startTime, NULL);
93 snprintf(me->SID, sizeof(me->SID), "%08lx-%04lx-%04lx-%04lx-%012lx", random(), random() % 0xFFFF, random() % 0xFFFF, random() % 0xFFFF, random());
94 snprintf(me->fileName, sizeof(me->fileName), "%s/%s", path, name);
95 eina_hash_add(ourGlobals->scripts, me->SID, me);
96 sendForth(ourGlobals, me->SID, "compile(%s)", me->fileName);
97 }
98 }
99}
100
101static Eina_Bool _timer_cb(void *data)
102{
103 gameGlobals *ourGlobals = data;
104 Eina_Iterator *scripts;
105 script *me;
106 boolean exit = FALSE;
107
108 scripts = eina_hash_iterator_data_new(ourGlobals->scripts);
109 while(eina_iterator_next(scripts, (void **) &me))
110 {
111 switch (timedEvent)
112 {
113 case 5 :
114 {
115 sendForth(ourGlobals, me->SID, "events.detectedKeys({\"%s\"})", ownerKey);
116 sendForth(ourGlobals, me->SID, "events.detectedNames({\"%s\"})", ownerName);
117 sendForth(ourGlobals, me->SID, "events.touch_start(1)");
118 break;
119 }
120 case 9 :
121 {
122 sendForth(ourGlobals, me->SID, "quit()");
123 break;
124 }
125 case 11 :
126 {
127 exit = TRUE;
128 break;
129 }
130 }
131 }
132 timedEvent++;
133
134 if (exit)
135 {
136 sendForth(ourGlobals, ownerKey, "exit()");
137 ecore_main_loop_quit();
138 return ECORE_CALLBACK_CANCEL;
139 }
140 return ECORE_CALLBACK_RENEW;
141}
142
143static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Add *ev)
144{
145 gameGlobals *ourGlobals = data;
146 char buf[PATH_MAX];
147
148 ourGlobals->server = ev->server;
149 gettimeofday(&startTime, NULL);
150 snprintf(buf, sizeof(buf), "%s/media/Test sim/objects", PACKAGE_DATA_DIR);
151 eina_file_dir_list(buf, EINA_TRUE, dirList_compile, ourGlobals);
152 // Wait awhile, then start sending events for testing.
153 ecore_timer_add(0.5, _timer_cb, ourGlobals);
154 return ECORE_CALLBACK_RENEW;
155}
156
157static Eina_Bool _data(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Data *ev)
158{
159 gameGlobals *ourGlobals = data;
160
161 char buf[PATH_MAX];
162 char SID[PATH_MAX];
163 const char *command;
164 char *ext;
165
166 eina_strbuf_append_length(clientStream, ev->data, ev->size);
167 command = eina_strbuf_string_get(clientStream);
168 while ((ext = index(command, '\n')))
169 {
170 int length = ext - command;
171
172 strncpy(SID, command, length + 1);
173 SID[length] = '\0';
174 eina_strbuf_remove(clientStream, 0, length + 1);
175 ext = index(SID, '.');
176 if (ext)
177 {
178 script *me;
179
180 ext[0] = '\0';
181 command = ext + 1;
182 me = eina_hash_find(ourGlobals->scripts, SID);
183 if (0 == strncmp(command, "compilerWarning(", 16))
184 {
185 char *temp;
186 char *line;
187 char *column;
188 char *text;
189
190 strcpy(buf, &command[16]);
191 temp = buf;
192 line = temp;
193 while (',' != temp[0])
194 temp++;
195 temp[0] = '\0';
196 column = ++temp;
197 while (',' != temp[0])
198 temp++;
199 temp[0] = '\0';
200 text = ++temp;
201 while (')' != temp[0])
202 temp++;
203 temp[0] = '\0';
204 PW("%s @ line %s, column %s.", text, line, column);
205 if (me)
206 me->warnings++;
207 }
208 else if (0 == strncmp(command, "compilerError(", 14))
209 {
210 char *temp;
211 char *line;
212 char *column;
213 char *text;
214
215 strcpy(buf, &command[14]);
216 temp = buf;
217 line = temp;
218 while (',' != temp[0])
219 temp++;
220 temp[0] = '\0';
221 column = ++temp;
222 while (',' != temp[0])
223 temp++;
224 temp[0] = '\0';
225 text = ++temp;
226 while (')' != temp[0])
227 temp++;
228 temp[0] = '\0';
229 PE("%s @ line %s, column %s.", text, line, column);
230 if (me)
231 me->bugs++;
232 }
233 else if (0 == strcmp(command, "compiled(false)"))
234 PE("The compile of %s failed!", SID);
235 else if (0 == strcmp(command, "compiled(true)"))
236 {
237 if (me)
238 {
239 struct timeval now;
240
241 me->compileTime = timeDiff(&now, &me->startTime);
242 me->running = TRUE;
243 compiledCount++;
244 compileTime += me->compileTime;
245// PD("Average compile speed is %f scripts per second", compiledCount / compileTime);
246 if (compiledCount == scriptCount)
247 PD("TOTAL compile speed is %f scripts per second", compiledCount / timeDiff(&now, &startTime));
248 }
249// PD("The compile of %s worked, running it now.", SID);
250 sendForth(ourGlobals, SID, "run()");
251 }
252 else
253 {
254 // Send back some random or fixed values for testing.
255 if (0 == strcmp(command, "llGetKey()"))
256 sendForth(ourGlobals, SID, "return \"%08lx-%04lx-%04lx-%04lx-%012lx\"", random(), random() % 0xFFFF, random() % 0xFFFF, random() % 0xFFFF, random());
257 else if (0 == strcmp(command, "llGetOwner()"))
258 sendForth(ourGlobals, SID, "return \"%s\"", ownerKey);
259 else if (0 == strcmp(command, "llGetPos()"))
260 sendForth(ourGlobals, SID, "return {x=128.0, y=128.0, z=128.0}");
261 else if (0 == strcmp(command, "llGetRot()"))
262 sendForth(ourGlobals, SID, "return {x=0.0, y=0.0, z=0.0, s=1.0}");
263 else if (0 == strcmp(command, "llGetObjectDesc()"))
264 sendForth(ourGlobals, SID, "return \"\"");
265 else if (0 == strncmp(command, "llGetAlpha(", 11))
266 sendForth(ourGlobals, SID, "return 1.0");
267 else if (0 == strcmp(command, "llGetInventoryNumber(7)"))
268 sendForth(ourGlobals, SID, "return 3");
269 else if (0 == strcmp(command, "llGetInventoryName(7, 2)"))
270 sendForth(ourGlobals, SID, "return \".readme\"");
271 else if (0 == strcmp(command, "llGetInventoryName(7, 1)"))
272 sendForth(ourGlobals, SID, "return \".POSITIONS\"");
273 else if (0 == strcmp(command, "llGetInventoryName(7, 0)"))
274 sendForth(ourGlobals, SID, "return \".MENUITEMS\"");
275 else
276 PI("Script %s sent command %s", SID, command);
277 }
278 }
279
280 // Get the next blob to check it.
281 command = eina_strbuf_string_get(clientStream);
282 }
283
284 return ECORE_CALLBACK_RENEW;
285}
286
287static Eina_Bool _del(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Del *ev)
288{
289 gameGlobals *ourGlobals = data;
290
291 if (ev->server)
292 {
293 ourGlobals->server = NULL;
294 ecore_con_server_del(ev->server);
295 if (!ourGlobals->ui)
296 ecore_main_loop_quit();
297 }
298
299 return ECORE_CALLBACK_RENEW;
300}
301
302int main(int argc, char **argv)
303{
304 /* put here any init specific to this app like parsing args etc. */
305 gameGlobals ourGlobals;
306 char *programName = argv[0];
307 boolean badArgs = FALSE;
308 int result = EXIT_FAILURE;
309
310 memset(&ourGlobals, 0, sizeof(gameGlobals));
311 ourGlobals.address = "127.0.0.1";
312 ourGlobals.port = 8211;
313
314 if (eina_init())
315 {
316 ourGlobals.logDom = loggingStartup("LuaSL_test", ourGlobals.logDom);
317 ourGlobals.scripts = eina_hash_string_superfast_new(NULL);
318
319 if (ecore_con_init())
320 {
321 if ((ourGlobals.server = ecore_con_server_connect(ECORE_CON_REMOTE_TCP, ourGlobals.address, ourGlobals.port, &ourGlobals)))
322 {
323 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb) _add, &ourGlobals);
324 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb) _data, &ourGlobals);
325 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb) _del, &ourGlobals);
326 clientStream = eina_strbuf_new();
327
328 if (ecore_evas_init())
329 {
330 if (edje_init())
331 {
332 // get the arguments passed in
333 while (--argc > 0 && *++argv != '\0')
334 {
335 if (*argv[0] == '-')
336 {
337 // point to the characters after the '-' sign
338 char *s = argv[0] + 1;
339
340 switch (*s)
341 {
342 case 'u':
343 {
344 ourGlobals.ui = TRUE;
345 break;
346 }
347 default:
348 badArgs = TRUE;
349 }
350 }
351 else
352 badArgs = TRUE;
353 }
354
355 if (badArgs)
356 {
357 // display the program usage to the user as they have it wrong
358 printf("Usage: %s [-u]\n", programName);
359 printf(" -u: Show the test UI.\n");
360 }
361 else
362 // else if ((ourGlobals.config) && (ourGlobals.data))
363 {
364 unsigned int i;
365 Evas_Object *bub, *sh;
366 Ecore_Animator *ani;
367 char *group = "main";
368 char buf[PATH_MAX];
369
370 if (ourGlobals.ui)
371 {
372 /* this will give you a window with an Evas canvas under the first engine available */
373 ourGlobals.ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
374 if (!ourGlobals.ee)
375 {
376 PEm("You got to have at least one evas engine built and linked up to ecore-evas for this example to run properly.");
377 edje_shutdown();
378 ecore_evas_shutdown();
379 return -1;
380 }
381 ourGlobals.canvas = ecore_evas_get(ourGlobals.ee);
382 ecore_evas_title_set(ourGlobals.ee, "LuaSL test harness");
383 ecore_evas_show(ourGlobals.ee);
384
385 ourGlobals.bg = evas_object_rectangle_add(ourGlobals.canvas);
386 evas_object_color_set(ourGlobals.bg, 255, 255, 255, 255); /* white bg */
387 evas_object_move(ourGlobals.bg, 0, 0); /* at canvas' origin */
388 evas_object_size_hint_weight_set(ourGlobals.bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
389 evas_object_resize(ourGlobals.bg, WIDTH, HEIGHT); /* covers full canvas */
390 evas_object_show(ourGlobals.bg);
391 ecore_evas_object_associate(ourGlobals.ee, ourGlobals.bg, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);
392 evas_object_focus_set(ourGlobals.bg, EINA_TRUE);
393
394 ourGlobals.edje = edje_object_add(ourGlobals.canvas);
395 snprintf(buf, sizeof(buf), "%s/media/%s.edj", PACKAGE_DATA_DIR, "LuaSL");
396 if (!edje_object_file_set(ourGlobals.edje, buf, group))
397 {
398 int err = edje_object_load_error_get(ourGlobals.edje);
399 const char *errmsg = edje_load_error_str(err);
400 PEm("Could not load '%s' from %s: %s\n", group, buf, errmsg);
401
402 evas_object_del(ourGlobals.edje);
403 ecore_evas_free(ourGlobals.ee);
404 edje_shutdown();
405 ecore_evas_shutdown();
406 return -2;
407 }
408 evas_object_move(ourGlobals.edje, 0, 0);
409 evas_object_resize(ourGlobals.edje, WIDTH, HEIGHT);
410 evas_object_show(ourGlobals.edje);
411
412 snprintf(buf, sizeof(buf), "%s/media/bubble_sh.png", PACKAGE_DATA_DIR);
413 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
414 {
415 sh = evas_object_image_filled_add(ourGlobals.canvas);
416 evas_object_image_file_set(sh, buf, NULL);
417 evas_object_resize(sh, 64, 64);
418 evas_object_show(sh);
419 evas_object_data_set(ourGlobals.bg, names[(i * 2) + 1], sh);
420 }
421
422 snprintf(buf, sizeof(buf), "%s/media/bubble.png", PACKAGE_DATA_DIR);
423 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
424 {
425 bub = evas_object_image_filled_add(ourGlobals.canvas);
426 evas_object_image_file_set(bub, buf, NULL);
427 evas_object_resize(bub, 64, 64);
428 evas_object_show(bub);
429 evas_object_data_set(ourGlobals.bg, names[(i * 2)], bub);
430 }
431 ani = ecore_animator_add(anim, &ourGlobals);
432 evas_object_data_set(ourGlobals.bg, "animator", ani);
433
434 // Setup our callbacks.
435 ecore_evas_callback_delete_request_set(ourGlobals.ee, _on_delete);
436 edje_object_signal_callback_add(ourGlobals.edje, "*", "game_*", _edje_signal_cb, &ourGlobals);
437 }
438
439 ecore_main_loop_begin();
440 if (ourGlobals.ui)
441 {
442 ecore_animator_del(ani);
443 ecore_evas_free(ourGlobals.ee);
444 }
445 }
446
447 edje_shutdown();
448 }
449 else
450 PCm("Failed to init edje!");
451 ecore_evas_shutdown();
452 }
453 else
454 PCm("Failed to init ecore_evas!");
455 }
456 else
457 PCm("Failed to connect to server!");
458 ecore_con_shutdown();
459 }
460 else
461 PCm("Failed to init ecore_con!");
462 }
463 else
464 fprintf(stderr, "Failed to init eina!");
465
466 return result;
467}
diff --git a/src/LuaSL/LuaSL_threads.c b/src/LuaSL/LuaSL_threads.c
new file mode 100644
index 0000000..234a7c5
--- /dev/null
+++ b/src/LuaSL/LuaSL_threads.c
@@ -0,0 +1,496 @@
1/* This code is heavily based on luaproc.
2 *
3 * The luaproc copyright notice and license is -
4
5 ***************************************************
6
7Copyright 2008 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27 ****************************************************
28 *
29 * Additions and changes Copyright 2012 by David Seikel, using the above license.
30 */
31
32
33/* This is a redesign of luaproc. The design goals and notes -
34 *
35 * In general use EFL where it is useful.
36 * Probably one fixed unique message channel per object, which each script in the object shares.
37 * But might be better to handle that C side anyway.
38 * Better integration with LuaSL.
39 * Use ecore threads instead of raw pthreads.
40 * Ecore threads pretty much wraps pthreads on posix, but has Windows support to.
41 * Merge in the edje Lua code, and keep an eye on that, coz we might want to actually add this to edje Lua in the future.
42 * Use my coding standards, or EFL ones. Pffft.
43 *
44 */
45
46#include "LuaSL.h"
47
48
49/* ready process queue insertion status */
50#define LUAPROC_SCHED_QUEUE_PROC_OK 0
51//#define LUAPROC_SCHED_QUEUE_PROC_ERR -1
52
53/* process is idle */
54#define LUAPROC_STAT_IDLE 0
55/* process is ready to run */
56#define LUAPROC_STAT_READY 1
57/* process is blocked on send */
58#define LUAPROC_STAT_BLOCKED_SEND 2
59/* process is blocked on receive */
60#define LUAPROC_STAT_BLOCKED_RECV 3
61
62
63typedef struct
64{
65 Eina_Clist node;
66 lua_State *L;
67} recycled;
68
69/*********
70* globals
71*********/
72
73/* ready process list */
74Eina_Clist lpready;
75
76/* ready process queue access mutex */
77pthread_mutex_t mutex_queue_access = PTHREAD_MUTEX_INITIALIZER;
78
79/* wake worker up conditional variable */
80pthread_cond_t cond_wakeup_worker = PTHREAD_COND_INITIALIZER;
81
82/* active luaproc count access mutex */
83pthread_mutex_t mutex_lp_count = PTHREAD_MUTEX_INITIALIZER;
84
85/* no active luaproc conditional variable */
86pthread_cond_t cond_no_active_lp = PTHREAD_COND_INITIALIZER;
87
88/* number of active luaprocs */
89int lpcount = 0;
90
91/* no more lua processes flag */
92int no_more_processes = FALSE;
93
94/* channel operations mutex */
95pthread_mutex_t mutex_channel = PTHREAD_MUTEX_INITIALIZER;
96
97/* recycle list mutex */
98pthread_mutex_t mutex_recycle_list = PTHREAD_MUTEX_INITIALIZER;
99
100/* recycled lua process list */
101Eina_Clist recyclelp;
102
103
104/******************************
105* library functions prototypes
106******************************/
107/* send a message to a lua process */
108static int luaproc_send( lua_State *L );
109/* receive a message from a lua process */
110static int luaproc_receive( lua_State *L );
111/* send a message back to the main loop */
112static int luaproc_send_back( lua_State *L );
113
114/* luaproc function registration array - main (parent) functions */
115static const struct luaL_reg luaproc_funcs_parent[] = {
116 { "sendback", luaproc_send_back },
117 { NULL, NULL }
118};
119
120/* luaproc function registration array - newproc (child) functions */
121static const struct luaL_reg luaproc_funcs_child[] = {
122 { "send", luaproc_send },
123 { "receive", luaproc_receive },
124 { "sendback", luaproc_send_back },
125 { NULL, NULL }
126};
127
128
129/* increase active lua process count */
130static void sched_lpcount_inc(void)
131{
132 pthread_mutex_lock(&mutex_lp_count);
133 lpcount++;
134 pthread_mutex_unlock(&mutex_lp_count);
135}
136
137/* decrease active lua process count */
138static void sched_lpcount_dec(void)
139{
140 pthread_mutex_lock(&mutex_lp_count);
141 lpcount--;
142 /* if count reaches zero, signal there are no more active processes */
143 if (lpcount == 0)
144 pthread_cond_signal(&cond_no_active_lp);
145 pthread_mutex_unlock(&mutex_lp_count);
146}
147
148/* worker thread main function */
149static void *workermain( void *args ) {
150
151 script *lp;
152 int procstat;
153
154 /* detach thread so resources are freed as soon as thread exits (no further joining) */
155 pthread_detach( pthread_self( ));
156
157 /* main worker loop */
158 while ( 1 ) {
159
160 /* get exclusive access to the ready process queue */
161 pthread_mutex_lock( &mutex_queue_access );
162
163 /* wait until instructed to wake up (because there's work to do or because its time to finish) */
164 while (( eina_clist_count( &lpready ) == 0 ) && ( no_more_processes == FALSE )) {
165 pthread_cond_wait( &cond_wakeup_worker, &mutex_queue_access );
166 }
167
168 /* pop the first node from the ready process queue */
169 if ((lp = (script *) eina_clist_head(&lpready)))
170 eina_clist_remove(&(lp->node));
171 else {
172 /* free access to the process ready queue */
173 pthread_mutex_unlock( &mutex_queue_access );
174 /* finished thread */
175 pthread_exit( NULL );
176 }
177
178 /* free access to the process ready queue */
179 pthread_mutex_unlock( &mutex_queue_access );
180
181 /* execute the lua code specified in the lua process struct */
182 procstat = lua_resume(lp->L, lp->args);
183 /* reset the process argument count */
184 lp->args = 0;
185
186 /* check if process finished its whole execution, then recycle it */
187 if (procstat == 0)
188 {
189 recycled *trash = malloc(sizeof(recycled));
190
191 if (trash)
192 {
193 trash->L = lp->L;
194 pthread_mutex_lock(&mutex_recycle_list);
195 eina_clist_add_tail(&recyclelp, &(trash->node));
196 pthread_mutex_unlock(&mutex_recycle_list);
197 sched_lpcount_dec();
198 }
199 lua_close(lp->L);
200 if (lp->timer)
201 ecore_timer_del(lp->timer);
202 free(lp);
203 }
204
205 /* check if process yielded */
206 else if ( procstat == LUA_YIELD ) {
207
208 /* if so, further check if yield originated from an unmatched send/recv operation */
209 if (lp->status == LUAPROC_STAT_BLOCKED_SEND)
210 {
211 }
212 else if (lp->status == LUAPROC_STAT_BLOCKED_RECV)
213 {
214 }
215 /* or if yield resulted from an explicit call to coroutine.yield in the lua code being executed */
216 else
217 {
218 /* get exclusive access to the ready process queue */
219 pthread_mutex_lock( &mutex_queue_access );
220 /* re-insert the job at the end of the ready process queue */
221 eina_clist_add_tail(&lpready, &(lp->node));
222 /* free access to the process ready queue */
223 pthread_mutex_unlock( &mutex_queue_access );
224 }
225 }
226 /* check if there was any execution error (LUA_ERRRUN, LUA_ERRSYNTAX, LUA_ERRMEM or LUA_ERRERR) */
227 else
228 {
229 /* print error message */
230 fprintf( stderr, "close lua_State (error: %s)\n", luaL_checkstring(lp->L, -1 ));
231 /* close lua state */
232 lua_close(lp->L);
233 /* decrease active lua process count */
234 sched_lpcount_dec();
235 }
236 }
237}
238
239/* move process to ready queue (ie, schedule process) */
240static int sched_queue_proc( script *lp ) {
241
242 /* get exclusive access to the ready process queue */
243 pthread_mutex_lock( &mutex_queue_access );
244
245 /* add process to ready queue */
246 eina_clist_add_tail(&lpready, &(lp->node));
247
248 lp->status = LUAPROC_STAT_READY;
249
250 /* wake worker up */
251 pthread_cond_signal( &cond_wakeup_worker );
252 /* free access to the process ready queue */
253 pthread_mutex_unlock( &mutex_queue_access );
254
255 return LUAPROC_SCHED_QUEUE_PROC_OK;
256}
257
258/* synchronize worker threads */
259void sched_join_workerthreads( void ) {
260
261 pthread_mutex_lock( &mutex_lp_count );
262
263 /* wait until there is no more active lua processes */
264 while( lpcount != 0 ) {
265 pthread_cond_wait( &cond_no_active_lp, &mutex_lp_count );
266 }
267 /* get exclusive access to the ready process queue */
268 pthread_mutex_lock( &mutex_queue_access );
269 /* set the no more active lua processes flag to true */
270 no_more_processes = TRUE;
271 /* wake ALL workers up */
272 pthread_cond_broadcast( &cond_wakeup_worker );
273 /* free access to the process ready queue */
274 pthread_mutex_unlock( &mutex_queue_access );
275
276// We don't need this, as we only get here during shutdown. Linking this to EFL results in a hang otherwise anyway.
277 /* wait for (join) worker threads */
278// pthread_exit( NULL );
279
280 pthread_mutex_unlock( &mutex_lp_count );
281
282}
283
284/* create a new worker pthread */
285int sched_create_worker(void)
286{
287 pthread_t worker;
288
289 /* create a new pthread */
290 if (pthread_create( &worker, NULL, workermain, NULL ) != 0)
291 return LUAPROC_SCHED_PTHREAD_ERROR;
292 return LUAPROC_SCHED_OK;
293}
294
295void newProc(const char *code, int file, script *lp)
296{
297 int ret;
298 recycled *trash;
299
300 // Try to recycle a Lua state, otherwise create one from scratch.
301 pthread_mutex_lock(&mutex_recycle_list);
302 /* pop list head */
303 if ((trash = (recycled *) eina_clist_head(&recyclelp)))
304 {
305 eina_clist_remove(&(trash->node));
306 lp->L = trash->L;
307 free(trash);
308 }
309 pthread_mutex_unlock(&mutex_recycle_list);
310
311 if (NULL == lp->L)
312 {
313 lp->L = luaL_newstate();
314
315 /* store the script struct in its own Lua state */
316 lua_pushlightuserdata(lp->L, lp);
317 lua_setfield(lp->L, LUA_REGISTRYINDEX, "_SELF");
318 luaL_openlibs(lp->L);
319 luaL_register(lp->L, "luaproc", luaproc_funcs_child);
320 }
321
322 lp->status = LUAPROC_STAT_IDLE;
323 lp->args = 0;
324 eina_clist_element_init(&(lp->node));
325 eina_clist_init(&(lp->messages));
326
327 /* load process' code */
328 if (file)
329 ret = luaL_loadfile(lp->L, code);
330 else
331 ret = luaL_loadstring(lp->L, code);
332
333 /* in case of errors, destroy Lua process */
334 if (ret != 0)
335 {
336 lua_close(lp->L);
337 lp->L = NULL;
338 }
339
340 if (lp->L)
341 {
342 sched_lpcount_inc();
343
344 /* schedule luaproc */
345 if (sched_queue_proc(lp) != LUAPROC_SCHED_QUEUE_PROC_OK)
346 {
347 printf( "[luaproc] error queueing Lua process\n" );
348 sched_lpcount_dec();
349 lua_close(lp->L);
350 }
351 }
352}
353
354/* return the lua process associated with a given lua state */
355static script *luaproc_getself(lua_State *L)
356{
357 script *lp;
358
359 lua_getfield(L, LUA_REGISTRYINDEX, "_SELF");
360 lp = (script *) lua_touserdata(L, -1);
361 lua_pop(L, 1);
362 return lp;
363}
364
365/* send a message to the client process */
366static int luaproc_send_back(lua_State *L)
367{
368 script *self = luaproc_getself(L);
369 const char *message = luaL_checkstring(L, 1);
370
371 if (self)
372 {
373 scriptMessage *sm = calloc(1, sizeof(scriptMessage));
374
375 if (sm)
376 {
377 eina_clist_element_init(&(sm->node));
378 sm->script = self;
379 strcpy((char *) sm->message, message);
380 ecore_main_loop_thread_safe_call_async(scriptSendBack, sm);
381 }
382 }
383
384 return 0;
385}
386
387/* error messages for the sendToChannel function */
388const char *sendToChannelErrors[] =
389{
390 "non-existent channel",
391 "error scheduling process"
392};
393
394/* send a message to a lua process */
395const char *sendToChannel(gameGlobals *ourGlobals, const char *SID, const char *message)
396{
397 const char *result = NULL;
398 script *dstlp;
399
400 /* get exclusive access to operate on channels */
401 pthread_mutex_lock(&mutex_channel);
402
403 // Add the message to the queue.
404 if ((dstlp = eina_hash_find(ourGlobals->scripts, SID)))
405 {
406 scriptMessage *sm = NULL;
407
408 if ((sm = malloc(sizeof(scriptMessage))))
409 {
410 sm->script = dstlp;
411 strcpy((char *) sm->message, message);
412 eina_clist_add_tail(&(dstlp->messages), &(sm->node));
413 }
414
415 /* if it's already waiting, send the next message to it and (queue) wake it */
416 if (dstlp->status == LUAPROC_STAT_BLOCKED_RECV)
417 {
418 scriptMessage *msg = (scriptMessage *) eina_clist_head(&(dstlp->messages));
419
420 // See if there's a message on the queue. Note, this may not be the same as the incoming message, if there was already a queue.
421 if (msg)
422 {
423 eina_clist_remove(&(msg->node));
424 message = msg->message;
425 }
426 /* push the message onto the receivers stack */
427 lua_pushstring(dstlp->L, message);
428 dstlp->args = lua_gettop(dstlp->L) - 1;
429 if (msg)
430 free(msg);
431
432 if (sched_queue_proc(dstlp) != LUAPROC_SCHED_QUEUE_PROC_OK)
433 {
434 sched_lpcount_dec();
435 lua_close(dstlp->L);
436 result = sendToChannelErrors[1];
437 }
438 }
439 }
440
441 pthread_mutex_unlock(&mutex_channel);
442
443 return result;
444}
445
446/* send a message to a lua process */
447static int luaproc_send(lua_State *L)
448{
449 script *self = luaproc_getself(L);
450 const char *result = sendToChannel(self->game, luaL_checkstring(L, 1), luaL_checkstring(L, 2));
451
452 if (result)
453 {
454 lua_pushnil(L);
455 lua_pushstring(L, result);
456 return 2;
457 }
458
459 lua_pushboolean(L, TRUE);
460 return 1;
461}
462
463/* receive a message from a lua process */
464static int luaproc_receive(lua_State *L)
465{
466 script *self;
467 const char *chname = luaL_checkstring(L, 1);
468 scriptMessage *msg;
469
470 // First check if there are queued messages, and grab one.
471 self = luaproc_getself(L);
472 if ((msg = (scriptMessage *) eina_clist_head(&(self->messages))))
473 {
474 eina_clist_remove(&(msg->node));
475 lua_pushstring(L, msg->message);
476 free(msg);
477 return lua_gettop(L) - 1;
478 }
479
480 /* if trying an asynchronous receive, return an error */
481 if ( lua_toboolean( L, 2 ))
482 {
483 lua_pushnil(L);
484 lua_pushfstring(L, "no senders waiting on channel %s", chname);
485 return 2;
486 }
487 /* otherwise (synchronous receive) simply block process */
488 self->status = LUAPROC_STAT_BLOCKED_RECV;
489 return lua_yield(L, lua_gettop(L));
490}
491
492void luaprocInit(void)
493{
494 eina_clist_init(&recyclelp);
495 eina_clist_init(&lpready);
496}
diff --git a/src/LuaSL/LuaSL_threads.h b/src/LuaSL/LuaSL_threads.h
new file mode 100644
index 0000000..9a11b5c
--- /dev/null
+++ b/src/LuaSL/LuaSL_threads.h
@@ -0,0 +1,54 @@
1/* This code is heavily based on luaproc.
2 *
3 * The luaproc copyright notice and license is -
4
5 ***************************************************
6
7Copyright 2008 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27 ****************************************************
28 *
29 * Additions and changes Copyright 2012 by David Seikel, using the above license.
30 */
31
32#ifndef __LUASL_THREADS_H__
33#define __LUASL_THREADS_H__
34
35/* scheduler function return constants */
36#define LUAPROC_SCHED_OK 0
37#define LUAPROC_SCHED_SOCKET_ERROR -1
38#define LUAPROC_SCHED_SETSOCKOPT_ERROR -2
39#define LUAPROC_SCHED_BIND_ERROR -3
40#define LUAPROC_SCHED_LISTEN_ERROR -4
41#define LUAPROC_SCHED_FORK_ERROR -5
42#define LUAPROC_SCHED_PTHREAD_ERROR -6
43#define LUAPROC_SCHED_INIT_ERROR -7
44
45
46void luaprocInit(void);
47int sched_create_worker(void);
48void newProc(const char *code, int file, script *lp);
49const char *sendToChannel(gameGlobals *ourGlobals, const char *SID, const char *message);
50
51/* join all worker threads and exit */
52void sched_join_workerthreads(void);
53
54#endif
diff --git a/src/LuaSL/LuaSL_utilities.c b/src/LuaSL/LuaSL_utilities.c
new file mode 100644
index 0000000..40263df
--- /dev/null
+++ b/src/LuaSL/LuaSL_utilities.c
@@ -0,0 +1,60 @@
1#include "LuaSL.h"
2
3
4void sendBack(gameGlobals *ourGlobals, Ecore_Con_Client *client, const char *SID, const char *message, ...)
5{
6 va_list args;
7 char buf[PATH_MAX];
8 int length = strlen(SID);
9
10 strncpy(buf, SID, length);
11 buf[length++] = '.';
12 va_start(args, message);
13 length += vsprintf(&buf[length], message, args);
14 va_end(args);
15 buf[length++] = '\n';
16 buf[length++] = '\0';
17 ecore_con_client_send(client, buf, strlen(buf));
18 ecore_con_client_flush(client);
19}
20
21void sendForth(gameGlobals *ourGlobals, const char *SID, const char *message, ...)
22{
23 va_list args;
24 char buf[PATH_MAX];
25 int length = strlen(SID);
26
27 strncpy(buf, SID, length);
28 buf[length++] = '.';
29 va_start(args, message);
30 length += vsprintf(&buf[length], message, args);
31 va_end(args);
32 buf[length++] = '\n';
33 buf[length++] = '\0';
34 ecore_con_server_send(ourGlobals->server, buf, strlen(buf));
35 ecore_con_server_flush(ourGlobals->server);
36}
37
38float timeDiff(struct timeval *now, struct timeval *then)
39{
40 if (0 == gettimeofday(now, 0))
41 {
42 struct timeval thisTime = { 0, 0 };
43 double result = 0.0;
44
45 thisTime.tv_sec = now->tv_sec;
46 thisTime.tv_usec = now->tv_usec;
47 if (thisTime.tv_usec < then->tv_usec)
48 {
49 thisTime.tv_sec--;
50 thisTime.tv_usec += 1000000;
51 }
52 thisTime.tv_usec -= then->tv_usec;
53 thisTime.tv_sec -= then->tv_sec;
54 result = ((double) thisTime.tv_usec) / ((double) 1000000.0);
55 result += thisTime.tv_sec;
56 return result;
57 }
58 else
59 return 0.0;
60}
diff --git a/src/LuaSL/build.lua b/src/LuaSL/build.lua
new file mode 100755
index 0000000..f86715c
--- /dev/null
+++ b/src/LuaSL/build.lua
@@ -0,0 +1,23 @@
1#!/usr/bin/env lua
2
3local dir = ...
4
5if 'nil' == type(dir) then
6 local build, err = loadfile('../../build.lua')
7 if build then
8 setfenv(build, getfenv(2))
9 build(2)
10 else
11 print("ERROR - " .. err)
12 end
13 dir = workingDir
14end
15
16removeFiles(dir, {'../../LuaSL', '*.o', '*.output', '*.backup', '../../media/LuaSL.edj', 'LuaSL_lexer.h', 'LuaSL_lexer.c', 'LuaSL_lemon_yaccer.h', 'LuaSL_lemon_yaccer.c', 'LuaSL_lemon_yaccer.out'})
17
18-- Run lemon first, flex depends on it to define the symbol values.
19runCommand('lemon', dir, '../../libraries/lemon/lemon -s -T../../libraries/lemon/lempar.c LuaSL_lemon_yaccer.y')
20runCommand('flex', dir, 'flex -C --outfile=LuaSL_lexer.c --header-file=LuaSL_lexer.h LuaSL_lexer.l')
21runCommand('edje_cc', dir, 'edje_cc ' .. EDJE_FLAGS .. ' LuaSL.edc ../../media/LuaSL.edj')
22compileFiles('../../LuaSL', dir, {'LuaSL_main', 'LuaSL_compile', 'LuaSL_threads', 'LuaSL_utilities', 'LuaSL_lexer', 'LuaSL_lemon_yaccer'})
23compileFiles('LuaSL_test', dir, {'LuaSL_test', 'LuaSL_utilities'})
diff --git a/src/LuaSL/test.sh b/src/LuaSL/test.sh
new file mode 100755
index 0000000..1c26ade
--- /dev/null
+++ b/src/LuaSL/test.sh
@@ -0,0 +1,27 @@
1#! /bin/bash
2
3wd=$(pwd)
4
5# Kill any left overs.
6killall -KILL LuaSL
7export LUA_PATH="$wd/../../libraries/?.lua"
8
9case $@ in
10
11 ddd)
12 ddd ../../LuaSL
13 ;;
14
15 gdb)
16 gdb ../../LuaSL
17 ;;
18
19 *)
20 echo "_______________ STARTING LuaSL _______________"
21 ../../LuaSL &
22 sleep 1
23 echo "_______________ STARTING LuaSL_test _______________"
24 ./LuaSL_test
25 ;;
26
27esac