lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi all,

I've been playing with the parser in order to get an useful short lambda syntax.
I'm trying to kill several birds in one stone, e.g.:

foo = {
	method = .(arg) => doSomething(arg) end, -- TK_SHORFUNC, TK_SHORTRETURN
		-- => is the alternative, short
		-- return token. Good for one liners.
					
	otherMethod = :(arg)      -- TK_SHORTMETHOD, needself = 1
		return self.method(10)
	end
}

I've gotten this to work with a minimally invasive patch.

I'd like to extend it further, in order to allow syntactic sugar for
functions that have a single function as an argument, similar to the
current behaviour with strings and tables.
For consistency, I've tried to add similar sugar for numbers too.
square 2 --> 4
test :(...) =>baz(...) end --> whatever test() returns.

The patch also tries to do that, it compiles, but it segfaults when I
attempt to use the new sugar.

This is the first time I write some C, and, even though I have some
knowledge of the syntax and semantics of the language, I've never
debugged any code nor compiled C code by myself (besides ./configure
&& make && make install, of course). I've tried to adapt some other
code in the parser to extend it. It worked for most things, but it
doesn't seem to help here, though. I don't understand the problem, and
I don't know how to debug it.

Why am I doing all this?
I'm currently writing a declarative DSL for a Löve library, but it
could be improved with the above modifications.

The goal would be to be able to turn this :

state "ingame"
     --======--
	:init    (function()   load(resources)     end)
	:update  (function(dt) update(game,dt)     end)
	:draw    (function()   draw(game)          end)
	:cleanup (function()   cleanup(resources)  end)
	:keyboard
		"esc"    :pressed (exit)
		"arrows" :held    (function(key,dt) hero[key](hero,dt) end)
		" "
			:pressed  (function()   hero:shoot()                 end)
			:held     (function(dt) hero:accumulaEtenergy(dt)    end)
			:released (function()   hero:releaseDevatativeBlow() end) () ()
-- yes, this is valid Lua. Neat eh :-)
-- () () close :keyboard and state respectively.

into this:

state "ingame"
     --======--
	:init    .()   load(resources)     end
	:update  .(dt) update(game,dt)     end
	:draw    .()   draw(game)          end
	:cleanup .()   cleanup(resources)  end
	:keyboard
		"esc"    :pressed (exit)
		"arrows" :held    .(key,dt) hero[key](hero,dt) end
		" "
			:pressed  .()   hero:shoot()                 end
			:held     .(dt) hero:accumulaEtenergy(dt)    end
			:released .()   hero:releaseDevatativeBlow() end
	()
()

Thereby approaching the expressivity and cleanliness of Ruby regarding DSLs.

To allow this, the patch also removes the "Ambiguous syntax" error.
There's no ambiguity, IMO. If you want to use (foo or bar).foo=1 at
the beginning of a line, prefix it with ";", you'll be safe no matter
what even in vanilla Lua. I've allowed a ";" as the first character of
a chunk so that ;(some_expression or some_other).foo() is valid as a
first statement too.

For the curious, the DSL parser follows the patch.

Some pointers in order to debug the code would help me a great deal :-).

Cheers,
-- Pierre-Yves

diff -u -r -w src.bak/llex.c src/llex.c
--- src.bak/llex.c	2009-11-17 17:33:38.000000000 +0100
+++ src/llex.c	2010-03-24 16:21:54.000000000 +0100
@@ -38,6 +38,7 @@
     "end", "false", "for", "function", "if",
     "in", "local", "nil", "not", "or", "repeat",
     "return", "then", "true", "until", "while",
+    ".(", ":(", "->", "=>", /* Short lambda */
     "..", "...", "==", ">=", "<=", "~=", "<eof>",
     "<number>", "<name>", "<string>"
 };
@@ -366,7 +367,8 @@
       }
       case '-': {
         next(ls);
-        if (ls->current != '-') return '-';
+        if (ls->current != '-' && ls->current != '>') return '-';
+        if (check_next(ls, ">")) return TK_SHORTRETURN;
         /* else is a comment */
         next(ls);
         if (ls->current == '[') {
@@ -394,7 +396,8 @@
       }
       case '=': {
         next(ls);
-        if (ls->current != '=') return '=';
+        if (ls->current != '='&& ls->current != '>') return '=';
+        if (check_next(ls, ">")) return TK_SHORTRETURN2;
         else { next(ls); return TK_EQ; }
       }
       case '<': {
@@ -419,6 +422,7 @@
       }
       case '.': {
         save_and_next(ls);
+        if (check_next(ls, "(")) return TK_SHORTFUNC;
         if (check_next(ls, ".")) {
           if (check_next(ls, "."))
             return TK_DOTS;   /* ... */
@@ -430,6 +434,11 @@
           return TK_NUMBER;
         }
       }
+      case ':': {
+        save_and_next(ls);
+        if (check_next(ls, "(")) return TK_SHORTMETHOD;
+		else return ':';
+      }
       case EOZ: {
         return TK_EOS;
       }
diff -u -r -w src.bak/llex.h src/llex.h
--- src.bak/llex.h	2009-10-11 22:02:19.000000000 +0200
+++ src/llex.h	2010-03-24 16:22:10.000000000 +0100
@@ -28,6 +28,7 @@
   TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
   TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
   /* other terminal symbols */
+  TK_SHORTFUNC, TK_SHORTMETHOD, TK_SHORTRETURN, TK_SHORTRETURN2
   TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_EOS,
   TK_NUMBER, TK_NAME, TK_STRING
 };
diff -u -r -w src.bak/lparser.c src/lparser.c
--- src.bak/lparser.c	2010-03-24 13:33:47.000000000 +0100
+++ src/lparser.c	2010-03-24 16:35:36.000000000 +0100
@@ -620,12 +620,12 @@
 }


-static void body (LexState *ls, expdesc *e, int needself, int line) {
+static void body (LexState *ls, expdesc *e, int needself, int line,
int shortfunc) {
   /* body ->  `(' parlist `)' chunk END */
   FuncState new_fs;
   open_func(ls, &new_fs);
   new_fs.f->linedefined = line;
-  checknext(ls, '(');
+  if (!shortfunc) checknext(ls, '(');
   if (needself) {
     new_localvarliteral(ls, "self");
     adjustlocalvars(ls, 1);
@@ -634,7 +634,9 @@
   checknext(ls, ')');
   chunk(ls);
   new_fs.f->lastlinedefined = ls->linenumber;
-  check_match(ls, TK_END, TK_FUNCTION, line);
+  if (shortfunc && needself) check_match(ls, TK_END, TK_SHORTMETHOD, line);
+  else if (shortfunc) check_match(ls, TK_END, TK_SHORTFUNC, line);
+  else check_match(ls, TK_END, TK_FUNCTION, line);
   pushclosure(ls, new_fs.f, e);
   close_func(ls);
 }
@@ -657,11 +659,21 @@
   FuncState *fs = ls->fs;
   expdesc args;
   int base, nparams;
+int needself = 0;
   int line = ls->linenumber;
   switch (ls->t.token) {
+    case TK_SHORTMETHOD: needself = 1;
+    case TK_SHORTFUNC: {
+      luaX_next(ls);
+      body(ls, f, needself, ls->linenumber, 1);
+      break;
+    }
+    case TK_NUMBER:{
+      init_exp(f, VKNUM, 0);
+      f->u.nval = ls->t.seminfo.r;
+      break;
+    }
     case '(': {  /* funcargs -> `(' [ explist1 ] `)' */
-      if (line != ls->lastline)
-        luaX_syntaxerror(ls,"ambiguous syntax (function call x new
statement)");
       luaX_next(ls);
       if (ls->t.token == ')')  /* arg list is empty? */
         args.k = VVOID;
@@ -760,7 +772,8 @@
         funcargs(ls, v);
         break;
       }
-      case '(': case TK_STRING: case '{': {  /* funcargs */
+      case '(': case TK_STRING: case '{':
+      case TK_SHORTFUNC: case TK_SHORTMETHOD: case TK_NUMBER: {  /* funcargs */
         luaK_exp2nextreg(fs, v);
         funcargs(ls, v);
         break;
@@ -774,6 +787,7 @@
 static void simpleexp (LexState *ls, expdesc *v) {
   /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
                   constructor | FUNCTION body | primaryexp */
+  int needself=0;
   switch (ls->t.token) {
     case TK_NUMBER: {
       init_exp(v, VKNUM, 0);
@@ -809,7 +823,13 @@
     }
     case TK_FUNCTION: {
       luaX_next(ls);
-      body(ls, v, 0, ls->linenumber);
+      body(ls, v, 0, ls->linenumber, 0);
+      return;
+    }
+    case TK_SHORTMETHOD: needself = 1;
+    case TK_SHORTFUNC: {
+      luaX_next(ls);
+      body(ls, v, needself, ls->linenumber, 1);
       return;
     }
     default: {
@@ -1223,7 +1243,7 @@
   init_exp(&v, VLOCAL, fs->freereg);
   luaK_reserveregs(fs, 1);
   adjustlocalvars(ls, 1);
-  body(ls, &b, 0, ls->linenumber);
+  body(ls, &b, 0, ls->linenumber, 0);
   luaK_storevar(fs, &v, &b);
 }

@@ -1268,7 +1288,7 @@
   expdesc v, b;
   luaX_next(ls);  /* skip FUNCTION */
   needself = funcname(ls, &v);
-  body(ls, &b, needself, line);
+  body(ls, &b, needself, line, 0);
   luaK_storevar(ls->fs, &v, &b);
   luaK_fixline(ls->fs, line);  /* definition `happens' in the first line */
 }
@@ -1380,7 +1400,7 @@
         localstat(ls);
       return 0;
     }
-    case TK_RETURN: {  /* stat -> retstat */
+    case TK_RETURN: case TK_SHORTRETURN: case TK_SHORTRETURN2 :{  /*
stat -> retstat */
       retstat(ls);
       return 1;  /* must be last statement */
     }
@@ -1401,6 +1421,7 @@
   /* chunk -> { stat [`;'] } */
   int islast = 0;
   enterlevel(ls);
+  testnext(ls, ';');
   while (!islast && !block_follow(ls->t.token)) {
     islast = statement(ls);
     testnext(ls, ';');
diff -u -r -w src.bak/lua.h src/lua.h
--- src.bak/lua.h	2010-01-08 21:40:29.000000000 +0100
+++ src/lua.h	2010-03-24 14:22:17.000000000 +0100
@@ -17,7 +17,7 @@


 #define LUA_VERSION	"Lua 5.2"
-#define LUA_RELEASE	"Lua 5.2.0 (work2)"
+#define LUA_RELEASE	"Lua 5.2.0 (work2) Pygy ShortFunc"
 #define LUA_VERSION_NUM	502
 #define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2008 Lua.org, PUC-Rio"
 #define LUA_AUTHORS	"R. Ierusalimschy, L. H. de Figueiredo, W. Celes"


keyboard = function(self, key)
	local t, keymt = {}
	self.keyconfig=t
	local actions = {pressed=true, held=true, released=true}	

	local eatkey=function(key)
		if key then
			return setmetatable({key},keymt)
		else
			for k,v in pairs(t) do print(k,v) end
			return self
		end
	end
	keymt={
		__index = function(_,action)
			assert(actions[action],"bad action")
			return function(self,callback)
				t[action] = t[action] or {}
				t[action][self[1]]=callback
				return self
			end
		end,
		__call = function(_,k) return eatkey(k) end
	}
	return eatkey(key)
end

state = function(name)
	local s = {name=name}
	local actions={init = true, draw=true, update=true, cleanup=true}
	local statemt={
		__index = function( _, action )
			assert(actions[action],"bad action ".. action)
			return function( self, callback)
				self[action]=callback
				return self
			end
		end,
		__call = function() for k,v in pairs(s) do print(k,v) end end
		TheState = s -- should actually update an upvalue.
	}
	setmetatable(s,statemt)
	s.keyboard = keyboard
	return s
end