src/lua/lua
src/compiler/bootstrap
src/compiler/metalua
+distrib
junk
patches
ran
-- Turn the digits of an escape sequence into the corresponding
-- character, e.g. [unesc_digits("123") == string.char(123)].
- local function unesc_digits (x)
- local k, j, i = x:reverse():byte(1, 3)
+ local function unesc_digits (backslashes, digits)
+ if #backslashes%2==0 then
+ -- Even number of backslashes, they escape each other, not the digits.
+ -- Return them so that unesc_letter() can treaat them
+ return backslashes..digits
+ else
+ -- Remove the odd backslash, which escapes the number sequence.
+ -- The rest will be returned and parsed by unesc_letter()
+ backslashes = backslashes :sub (1,-2)
+ end
+ local k, j, i = digits:reverse():byte(1, 3)
local z = _G.string.byte "0"
- return _G.string.char ((k or z) + 10*(j or z) + 100*(i or z) - 111*z)
+ local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z
+ if code > 255 then
+ error ("Illegal escape sequence '\\"..digits.."' in string: ASCII codes must be in [0..255]")
+ end
+ return backslashes .. string.char (code)
end
-- Take a letter [x], and returns the character represented by the
end
return s
+ :gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits)
:gsub ("\\(%D)",unesc_letter)
- :gsub ("\\([0-9][0-9]?[0-9]?)", unesc_digits)
end
lexer.extractors = {
require 'metalua.compiler'
require 'metalua.clopts'
-require 'serialize'
+require 'metalua.mlc_xcall'
AST_COMPILE_ERROR_NUMBER = -1
RUNTIME_ERROR_NUMBER = -3
-{ extension 'match' }
-function spring_pcall (f, arg, name)
- local pattern =
- [=[lua -l metalua.compiler -l serialize -e ]=]..
- [=["print (serialize (%s ([[%s]], [[%s]])))"]=]
- local cmd = string.format (pattern, f, arg, name)
- --print ("Running the following process: " .. cmd)
- local fd = io.popen (cmd)
- local ast_src = fd:read '*a'
- fd:close()
- --print (data)
- local ast_builder, msg = lua_loadstring(ast_src)
- if not ast_builder then
- error ("can't compile data: "..msg)
- print (ast_src)
- end
- local ast = ast_builder()
- return true, ast
-end
-
local chunks = { }
local runargs = { }
local st, ast
match x with
| `Library{ l } -> st, ast = true, `Call{ `Id 'require', `String{ l } }
- | `Literal{ e } -> st, ast = spring_pcall('mlc.ast_of_luastring', e, 'literal')
+ | `Literal{ e } -> st, ast = mlc_xcall.client_literal (e)
| `File{ f } ->
- st, ast = spring_pcall('mlc.ast_of_luafile', f, '@'..f)
- -- Isolate each file in a separate fenv
+ st, ast = mlc_xcall.client_file (f)
+ -- Isolate each file in a separate fenv
if st then
ast = +{ function (...) -{ast} end (...) }
ast.source = '@'..f -- TODO [EVE]
if not name then name = '@'..x end
local f, msg = io.open(x, "rb")
- if not f then error(msg) end
+ if not f then error("While trying to open file '"..x.."': "..msg) end
x = f:read'*a'
f:close()
lua_loadstring = loadstring
local lua_loadstring = loadstring
+lua_loadfile = loadfile
+local lua_loadfile = loadfile
function loadstring(str, name)
if type(str) ~= 'string' then error 'string expected' end
if not f then error(msg) end
return f()
end
-
-
assert (regexp.tag=="String",
"Left hand side operand for '/' in a pattern must be "..
"a literal string representing a regular expression")
- assert (sub_pattern.tag=="Table",
- "Right hand side operand for '/' in a pattern must be "..
- "an identifier or a list of identifiers")
- for x in ivalues(sub_pattern) do
- assert (x.tag=="Id" or x.tag=='Dots',
- "Right hand side operand for '/' in a pattern must be "..
- "a list of identifiers")
+ if sub_pattern.tag=="Table" then
+ for x in ivalues(sub_pattern) do
+ assert (x.tag=="Id" or x.tag=='Dots',
+ "Right hand side operand for '/' in a pattern must be "..
+ "a list of identifiers")
+ end
+ else
+ assert (sub_pattern.tag=="Id",
+ "Right hand side operand for '/' in a pattern must be "..
+ "an identifier or a list of identifiers")
end
-- Regexp patterns can only match strings
acc_stat (`Label{on_success}, cfg)
if guard then acc_test (+{not -{guard}}, cfg) end
if cfg.dots_replacement then
- eprintf ("Dots replacement required in a match")
replace_dots (block, cfg.dots_replacement)
end
block.tag = 'Do'
-{ extension 'H' }
-{ extension 'log' }
-require 'metalua.extension.match'
-
-- Get match parsers and builder, for catch cases handling:
-local match_alpha = require 'extension.match'
+local match_alpha = require 'metalua.extension.match'
local H = H:new{side='inside', alpha = match_alpha }
-- We'll need to track rogue return statements:
--- /dev/null
+-- lua -l mlc_xcall -e 'luafile_to_astfile ("/tmp/tmp12345.lua", "/tmp/tmp54321.ast")'
+-- lua -l mlc_xcall -e 'lua_to_astfile ("/tmp/tmp54321.ast")'
+
+mlc_xcall = { }
+
+function mlc_xcall.server (luafilename, astfilename)
+
+ -- We don't want these to be loaded when people only do client-side business
+ require 'metalua.compiler'
+ require 'serialize'
+
+ -- compile the content of luafile name in an AST, serialized in astfilename
+ local ast = mlc.luafile_to_ast (luafilename)
+ local out = io.open (astfilename, 'w')
+ out:write (serialize (ast))
+ out:close ()
+end
+
+function mlc_xcall.client_file (luafile)
+
+ --printf("\n\nmlc_xcall.client_file(%q)\n\n", luafile)
+
+ local tmpfilename = os.tmpname()
+ local cmd = string.format ([[lua -l metalua.mlc_xcall -e "mlc_xcall.server('%s', '%s')"]],
+ luafile :gsub ([[\]], [[\\]]),
+ tmpfilename :gsub([[\]], [[\\]]))
+
+ --printf("os.execute [[%s]]\n\n", cmd)
+
+ local ret = os.execute (cmd)
+ if ret~=0 then error "xcall failure. FIXME: transmit failure and backtrace" end
+ local ast = (lua_loadfile or loadfile) (tmpfilename) ()
+ os.remove(tmpfilename)
+ return true, ast
+end
+
+function mlc_xcall.client_literal (luasrc)
+ local srcfilename = os.tmpname()
+ local srcfile, msg = io.open (srcfilename, 'w')
+ if not srcfile then print(msg) end
+ srcfile :write (luasrc)
+ srcfile :close ()
+ local status, ast = mlc_xcall.client_file (srcfilename)
+ os.remove(srcfilename)
+ return status, ast
+end
+
+return mlc_xcall
\ No newline at end of file
local luastring = file:read '*a'
file:close()
return mlc.function_of_luastring (luastring, name)
- else
+ else
file:close()
- return spring_load (filename_or_msg)
+ require 'metalua.mlc_xcall'
+ local status, ast = mlc_xcall.client_file (filename_or_msg)
+ return mlc.function_of_ast(ast)
end
end
+++ /dev/null
-#!/bin/sh
-echo "This script is currently empty. Run ./make.sh first, then a correct make-install.sh will be generated."
-exit -1
+#! /bin/sh
+
# --- BEGINNING OF USER-EDITABLE PART ---
# Metalua sources
# Temporary building location.
# Upon installation, everything will be moved to ${INSTALL_LIB} and ${INSTALL_BIN}
-BUILD=/tmp/metalua-build
-BUILD_BIN=${BUILD}/bin
-BUILD_LIB=${BUILD}/lib
+
+if [ -z "${BUILD}" ]; then
+ BUILD=/tmp/metalua-build
+fi
+
+if [ -z "${BUILD_BIN}" ]; then
+ BUILD_BIN=${BUILD}/bin
+fi
+
+if [ -z "${BUILD_LIB}" ]; then
+ BUILD_LIB=${BUILD}/lib
+fi
# Where to place the final results
# INSTALL_BIN=/usr/local/bin
# INSTALL_LIB=/usr/local/lib/lua/5.1
-INSTALL_BIN=~/local/bin
-INSTALL_LIB=~/local/lib/lua
+if [ -z "${INSTALL_BIN}" ]; then
+ INSTALL_BIN=~/local/bin
+fi
+
+if [ -z "${INSTALL_LIB}" ]; then
+ INSTALL_LIB=~/local/lib/lua
+fi
# Where to find Lua executables.
# On many Debian-based systems, those can be installed with "sudo apt-get install lua5.1"
mkdir -p ${BUILD_BIN}
mkdir -p ${BUILD_LIB}
cp -R lib/* ${BUILD_LIB}/
-cp -R bin/* ${BUILD_BIN}/
+# cp -R bin/* ${BUILD_BIN}/ # No binaries provided for unix (for now)
echo '*** Generate a callable metalua shell script ***'
echo
echo "Build completed, proceed to installation with './make-install.sh' or 'sudo ./make-install.sh'"
-echo
\ No newline at end of file
+echo
-- Read a file, get its AST, use synth to regenerate sources
-- from that AST
--------------------------------------------------------------------------------
-require 'mlc'
+require 'metalua.compiler'
local filename = (arg[2] or arg[1]) or arg[0]
local ast = mlc.luafile_to_ast (filename)
-print(synth.run(ast))
\ No newline at end of file
+print(synth.run(ast))
try
print " Hi"
error "bang"
-catch "bang"/_ then
+catch "bang"/{_} then
print " Bang caught"
finally
print " Finally OK"
catch "some_other_error" then
assert (false, "mismatch, this must not happen")
end
-catch "some_error"/x then
+ catch "some_error"/{x} then
printf(" Successfully caught %q across a try that didn't catch", x)
catch x then
assert (false, "We shouldn't reach this catch-all")
-{ extension 'match' }
-require 'walk.id'
+require 'metalua.walk.id'
ast = +{ block:
y = type(1)
-require 'mlc'
-require 'walk'
+require 'metalua.mlc'
+require 'metalua.walk'
function weave_ast (src, ast, name)
-- the `Local{ } node, although it's not directly included in it.
-------------------------------------------------------------------
function node.down(ast, parent)
+ ----------------------------------------------------
+ -- `Do{ } blocks are processed twice:
+ -- * once as a statement
+ -- * once as a block, child of itself
+ -- This prevents them from becoming their own child.
+ ----------------------------------------------------
+ if ast==parent then return end
+
if not ast.lineinfo then
- weaveable [ast], weaveable [parent] = false, false
+ weaveable [ast] = false, false
+ if parent then weaveable [parent] = false end
else
weaveable [ast] = true
+
+ -- normalize lineinfo
+ -- TODO: FIXME
+ if ast.lineinfo.first[3] > ast.lineinfo.last[3] then
+ ast.lineinfo.first, ast.lineinfo.last = ast.lineinfo.last, ast.lineinfo.first
+ end
end
ast_children [ast] = { }
ast_parent [ast] = parent
local _acc = { }
local function acc(x) table.insert (_acc, x) end
+ if not next(ast) then -- shadow node, remove from ast_children
+ local x = ast_children[ast_parent[ast]]
+ for i,a in ipairs (x) do if a==ast then table.remove (x, i); break end end
+ return "" -- no need to continue, we know that the node is empty!
+ end
+
-- ast Can't be weaved normally, try something else --
- local function synthetize (ast)
+ local function synthetize (ast)
acc "-{expr: "
acc (table.tostring (ast, 'nohash', 80, 8))
acc " }"
-- regular weaving of chidren in the parent's sources --
local function weave (ast)
+ -- sort children in appearence order
+ local comp = |a,b| a.lineinfo.first[3] < b.lineinfo.first[3]
+ table.sort (ast_children [ast], comp)
+
local li = ast.lineinfo
if not li then return synthetize (ast) end
local a, d = li.first[3], li.last[3]
for child in ivalues (ast_children [ast]) do
local li = child.lineinfo
local b, c = li.first[3], li.last[3]
- acc (src:sub (a, b-1))
+ acc (src:sub (a, b - 1))
acc (translation [child])
- a = c+1
+ a = c + 1
end
acc (src:sub (a, d))
end
-- Get the source. If none is given, use itself as an example. --
local filename = arg[2] or arg[1] or arg[0]
-local f = io.open (filename, 'r')
+local f = assert (io.open (filename, 'r'))
local src = f:read '*a'
f:close()
local ast = mlc.luastring_to_ast (src, name)
-
-print (weave_ast (src, ast))
+if not next(ast) then
+ io.write (src) -- Empty ast, probably empty file, or comments only
+else
+ local before = src:sub (1, ast.lineinfo.first[3]-1)
+ local after = src:sub (ast.lineinfo.last[3]+1, -1)
+ io.write (before .. weave_ast (src, ast) .. after)
+end