]> git.lizzy.rs Git - metalua.git/commitdiff
added REPL loop and editline support
authorFabien Fleutot <fabien@macfabien.local>
Tue, 12 Feb 2008 23:07:53 +0000 (00:07 +0100)
committerFabien Fleutot <fabien@macfabien.local>
Tue, 12 Feb 2008 23:07:53 +0000 (00:07 +0100)
src/Makefile
src/binlibs/Makefile
src/binlibs/editline.c [new file with mode: 0644]
src/compiler/gg.lua
src/compiler/metalua.mlua
src/compiler/mlc.mlua
src/config
src/lib/metaloop.mlua [new file with mode: 0644]

index af2db68daa5b2bb3c744a51806c973c73601aa15..777e8c947b675a8b8ba1e7e1bbedd220f35ba25e 100644 (file)
@@ -29,11 +29,11 @@ compile-libraries: copy-libraries bin-libraries setenv.sh compiler
        @echo "Compiling libraries:"
        for src in $$(find $(TARGET_LUA_PATH) -name '*.mlua') ; do \
                bc="$$(dirname $$src)/$$(basename $$src .mlua).luac"; \
-               if [ $$src -nt $$bc ]; then \
+               if [ -f $$bc ]  && [ $$bc -nt $$src ]; then \
+                       echo "| up2date:  $$bc"; \
+               else \
                        echo "| COMPILING $$bc" ; \
                        $(TARGET_BIN_PATH)/metalua $$src -o $$bc ; \
-               else \
-                       echo "| up2date:  $$bc"; \
                fi \
        done
 
index 8566f9f60e1728039a66128b4c72a1ac08821a95..1c0da6d79ec081e7ea96262ba0c29528bd7015a7 100644 (file)
@@ -4,6 +4,11 @@ all: libraries install
 $(PLATFORM): all
 
 SRC  = pluto.c rings.c bit.c
+
+ifeq ($(USE_READLINE), yes)
+SRC += editline.c
+endif
+
 LIBS = $(SRC:.c=.$(LIBEXT)) 
 
 libraries: $(LIBS)
@@ -19,6 +24,11 @@ clean:
 
 .PHONY: libraries install clean
 
+ifeq ($(USE_READLINE), yes)
+editline.$(LIBEXT): editline.$(OBJEXT)
+       $(MKLIB) -o $@ $+ $(LDFLAGS) -lhistory -lreadline
+endif
+
 %.$(LIBEXT): %.$(OBJEXT)
        $(MKLIB) -o $@ $+ $(LDFLAGS)
 
diff --git a/src/binlibs/editline.c b/src/binlibs/editline.c
new file mode 100644 (file)
index 0000000..8ba5bdc
--- /dev/null
@@ -0,0 +1,125 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <histedit.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include <string.h>
+
+#define MOD_NAME "editline"
+#define DEFAULT_PROMPT "> "
+#define DEFAULT_HIST_NAME "metalua"
+
+struct el_userdata {
+  EditLine   *el;
+  History    *hist;
+  lua_State  *L;
+  char       *prompt;
+  HistEvent   hev;
+};
+
+/* static const lua_CFunction init, read, close, set; */
+static int init( lua_State *L);
+static int read( lua_State *L);
+static int close( lua_State *L);
+static int setf( lua_State *L);
+
+static const struct luaL_Reg REG_TABLE[] = {
+  { "init",  init  },
+  { "read",  read  },
+  { "close", close },
+  { "__gc",  close },
+  { "__newindex", setf }, 
+  { NULL,    NULL  } };
+
+int luaopen_editline( lua_State *L) {
+  /* Create the module. */
+  luaL_register( L, MOD_NAME, REG_TABLE);  
+
+  /* Set the module as editline's metatable */  
+  lua_pushvalue( L, -1);
+  lua_setfield( L, LUA_REGISTRYINDEX, MOD_NAME);
+
+  /* Set the table as its own __index metamethod */
+  lua_pushvalue( L, -1);
+  lua_setfield( L, -2, "__index");
+
+  /* printf( "Editline binary registered\n"); */
+
+  return 1;
+}
+
+static int setf( lua_State *L) {
+  struct el_userdata *u = luaL_checkudata( L, 1, MOD_NAME);
+  const  char *key     = luaL_checkstring( L, 2);
+  if( ! strcmp( key, "prompt")) {
+    const char *prompt = luaL_checkstring( L, 3);
+    realloc( u->prompt, strlen( prompt));
+    strcpy( u->prompt, prompt);
+  } else {   
+    luaL_error( L, "invalid field in editline");
+  }
+  return 0;
+}  
+
+static char *prompt( EditLine *el) {
+  /* Hack Hack Hack: the address of el_userdata is the same as
+   * its el field's. */
+  struct el_userdata *u;
+  el_get( el, EL_CLIENTDATA, &u);
+  return u->prompt;
+}
+
+#include <dlfcn.h>
+
+static int init( lua_State *L) {
+  /* Allocate the structure and initialize its fields */
+  const char *name      = luaL_optstring( L, 1, DEFAULT_HIST_NAME);
+  struct el_userdata *u = lua_newuserdata( L, sizeof( *u));
+
+  u->el = el_init( name, stdin, stdout, stderr);
+  if( ! u->el) luaL_error( L, "can't create editline object");
+  u->hist = history_init();
+  if( ! u->hist) luaL_error( L, "can't create editline history");
+  u->L = L;
+  u->prompt = (char *) malloc( sizeof( DEFAULT_PROMPT));
+  strcpy( u->prompt, DEFAULT_PROMPT);
+
+  /* Set its metatable; if necessary, create the metatable. */
+  luaL_newmetatable( L, MOD_NAME);
+  lua_setmetatable( L, -2);
+
+  /* Some basic settings */
+  history( u->hist, & u->hev, H_SETSIZE, 800);
+  el_set( u->el, EL_PROMPT, & prompt);
+  el_set( u->el, EL_EDITOR, "emacs");
+  el_set( u->el, EL_HIST,   history, u->hist);
+  el_set( u->el, EL_CLIENTDATA, u);
+  return 1;
+}
+
+static int close( lua_State *L) {
+  struct el_userdata *u = luaL_checkudata( L, 1, MOD_NAME);
+  free( u->prompt);
+  history_end( u->hist);
+  el_end( u->el);
+  return 0;
+}
+
+static int read( lua_State *L) {
+  struct el_userdata *u = luaL_checkudata( L, 1, MOD_NAME);
+  const char *p = luaL_optstring( L, 2, NULL);
+  char *old_p = NULL;
+  int count;
+  const char *line;
+  if( p) { old_p = u->prompt; u->prompt = (char*) p; }
+  line = el_gets( u->el, & count);
+  if( p) { u->prompt = old_p; }
+  if( line) {
+    if (count > 0) history(u->hist, & u->hev, H_ENTER, line);
+    lua_pushlstring( L, line, count);
+  } else {
+    lua_pushnil( L);
+  }
+  return 1;
+}
+
index 17ef25192f8c2b327b15cdf82f27abae662bf850..993bdd1ba9fe13c141e4ee535e53b39cff3f76a4 100644 (file)
@@ -51,10 +51,9 @@ function parser_metatable.__call (parser, lx, ...)
       local char = lx:peek().char\r
       local status, ast = pcall (parser.parse, parser, lx, ...)      \r
       if status then return ast else\r
-         local msg = ast\r
-         if msg then print(msg) end\r
-         printf(" - (%i) in parser %s", char, parser.name or parser.kind)\r
-         error()\r
+         local msg = ast:strmatch "gg.lua:%d+: (.*)" or ast\r
+         msg=msg..string.format("\n - (%i) in parser %s", char, parser.name or parser.kind)\r
+         error(msg)\r
       end\r
    end\r
 end\r
@@ -145,7 +144,7 @@ function parse_error(lx, fmt, ...)
       while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end      \r
       local srcline = src:sub (i+1, j-1)\r
       local idx  = string.rep (" ", char-i-1).."^"\r
-      msg = printf("%s\n>>> %s\n>>> %s", msg, srcline, idx)\r
+      msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx)\r
    end\r
    error(msg)\r
 end\r
@@ -653,7 +652,7 @@ function onkeyword (p)
       if type(x)=="string" then table.insert (p.keywords, x)\r
       else assert (not p.primary and is_parser (x)); p.primary = x end\r
    end\r
-   assert (p.primary)\r
+   assert (p.primary, 'no primary parser in gg.onkeyword')\r
    return p\r
 end --</onkeyword>\r
 \r
index 495032550093f01d9e424f234e4c9972b1a46bda..123c5e83ef4fd3e8128abdbe9e12ed86048953a7 100644 (file)
@@ -154,12 +154,17 @@ local function main (...)
       | `File{ f } -> 
          st, ast = spring_pcall('mlc.ast_of_luafile', f, '@'..f)
          -- Isolate each file in a separate fenv
-         ast = +{ function (...) -{ast} end (...)  }
-         ast.source  = '@'..f -- TODO [EVE]
-         code.source = '@'..f -- TODO [EVE]
-         last_file = ast
+         if st then
+            ast = +{ function (...) -{ast} end (...)  }
+            ast.source  = '@'..f -- TODO [EVE]
+            code.source = '@'..f -- TODO [EVE]
+            last_file = ast
+         end
+      end
+      if not st then 
+         printf ("Cannot compile %s: %s", table.tostring(x), ast)
+         os.exit (AST_COMPILE_ERROR_NUMBER)
       end
-      if not st then os.exit (AST_COMPILE_ERROR_NUMBER) end
       ast.origin = x
       table.insert(code, ast)
    end
@@ -233,9 +238,12 @@ local function main (...)
    -------------------------------------------------------------------
    -- Run REPL loop
    if cfg.interactive then
-      print ("*":rep(70))
-      print "*** !!! Interactive loop not implemented !!!"
-      print ("*":rep(70))
+      verb_print "Starting REPL loop"
+      require 'metaloop'
+      metaloop.run()
+--       print ("*":rep(70))
+--       print "*** !!! Interactive loop not implemented !!!"
+--       print ("*":rep(70))
    end
 
    verb_print "Done"
index 48a1d778546886f91bd4ee2dcadc654fde923fb9..d2525a5acf1da063092ef4ef33256617dd486b3c 100644 (file)
@@ -102,8 +102,8 @@ function mlc.convert (x, src_fmt, dst_fmt, name)
    if not status and x then 
       -- x = error msg; get rid of ???
       x = x:strmatch "[^:]+:[0-9]+: (.*)" or x
-      printf("Parsing error in %s line %s, char %s: \n%s",
-             filename or "?", lx.line, lx.i, x)
+      error(string.format("Parsing error in %s line %s, char %s: \n%s",
+                          filename or "?", lx.line, lx.i, x))
       return nil
    end
    
index f99438c5bcdde900df4ba1b5145bca5cdce5b8b4..1e0c1cadb2c3c49520a6dfad86716a6cf2998658 100644 (file)
@@ -4,7 +4,7 @@
 # Platforms currently supported: mingw, macosx.
 # Feel welcome to contribute additional ones! :)
 
-PLATFORM  = linux
+PLATFORM  = macosx
 PLATFORMS = macosx mingw linux
 
 
@@ -13,6 +13,8 @@ PLATFORMS = macosx mingw linux
 # You might want to first build & install in a temporary
 # directory, to check that everything works fine,
 # before targetting /usr/local/*
+#
+# Beware: only absolute paths work.
 
 TARGET_LUA_PATH  = /tmp/metalua
 TARGET_LUA_CPATH = $(TARGET_LUA_PATH)
@@ -20,7 +22,7 @@ TARGET_BIN_PATH  = /tmp/bin
 
 #########################################################
 # Lua VM settings
-# LUA_VM_DIR is relative to $(METALUA_DIR)/src
+# LUA_VM_DIR is relative to the 'src' directory.
 
 LUA_VM_DIR       = lua
 COMPILE          = luac
@@ -40,6 +42,7 @@ ifeq ($(PLATFORM), macosx)
   LIBEXT        = so
   MKLIB         = gcc -bundle -undefined dynamic_lookup
   LUA_BINARIES  = $(COMPILE) $(RUN)
+  USE_READLINE  = yes
 endif
 
 ifeq ($(PLATFORM), mingw)
diff --git a/src/lib/metaloop.mlua b/src/lib/metaloop.mlua
new file mode 100644 (file)
index 0000000..88fcbdc
--- /dev/null
@@ -0,0 +1,72 @@
+require 'metalua.compiler'
+
+module ('metaloop', package.seeall)
+
+PRINT_AST  = true
+LINE_WIDTH = 60
+PROMPT     = "M> "
+PROMPT2    = ">> "
+
+do -- set readline() to a line reader, either editline otr a default
+   local status, _ = pcall(require, 'editline')
+   if status then
+      local rl_handle = editline.init 'metalua'
+      readline = |p| rl_handle:read(p) 
+   else
+      readline = |p| io.write(p) and io.read '*l'
+   end
+end
+
+function reached_eof(lx, msg)
+   return lx:peek().tag=='Eof' or msg:find "token `Eof"
+end
+
+printf ("Metalua, interactive REPLoop.\n"..
+        "(c) 2006-2008 <metalua@gmail.com>")
+
+function run()
+   local lines = { }
+   while true do
+      local src, lx, ast, f, results, success
+      repeat
+         local line = readline(next(lines) and PROMPT2 or PROMPT)
+         if not line then print(); os.exit(0) end -- line==nil iff eof on stdin
+         if not next(lines) then
+            line = line:gsub('^%s*=', 'return ')
+         end
+         table.insert(lines, line)
+         src = table.concat (lines, "\n")
+      until #line>0
+      
+      lx  = mlc.lexstream_of_luastring(src) 
+      success, ast = pcall(mlc.ast_of_lexstream, lx)
+      if success then
+         success, f = pcall(mlc.function_of_ast, ast, '=stdin')
+         if success then
+            results = { pcall(f) }
+            success = table.remove (results, 1)
+            if success then
+               -- Success!
+               table.iforeach(|x| table.print(x, LINE_WIDTH), results)
+               lines = { }
+            else
+               print "Evaluation error:"
+               print (results[1])
+               lines = { }
+            end
+         else
+            print "Can't compile into bytecode:"
+            print (f)
+            lines = { }
+         end
+      else
+         -- If lx has been read entirely, try to read another
+         -- line before failing.
+         if not reached_eof(lx, ast) then
+            print "Can't compile source into AST:"
+            print (ast)
+            lines = { } 
+         end
+      end
+   end
+end
\ No newline at end of file