@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
$(PLATFORM): all
SRC = pluto.c rings.c bit.c
+
+ifeq ($(USE_READLINE), yes)
+SRC += editline.c
+endif
+
LIBS = $(SRC:.c=.$(LIBEXT))
libraries: $(LIBS)
.PHONY: libraries install clean
+ifeq ($(USE_READLINE), yes)
+editline.$(LIBEXT): editline.$(OBJEXT)
+ $(MKLIB) -o $@ $+ $(LDFLAGS) -lhistory -lreadline
+endif
+
%.$(LIBEXT): %.$(OBJEXT)
$(MKLIB) -o $@ $+ $(LDFLAGS)
--- /dev/null
+#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;
+}
+
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
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
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
| `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
-------------------------------------------------------------------
-- 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"
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
# Platforms currently supported: mingw, macosx.
# Feel welcome to contribute additional ones! :)
-PLATFORM = linux
+PLATFORM = macosx
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)
#########################################################
# 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
LIBEXT = so
MKLIB = gcc -bundle -undefined dynamic_lookup
LUA_BINARIES = $(COMPILE) $(RUN)
+ USE_READLINE = yes
endif
ifeq ($(PLATFORM), mingw)
--- /dev/null
+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