1 --------------------------------------------------------------------------------
2 -- Copyright (c) 2006-2013 Fabien Fleutot and others.
4 -- All rights reserved.
6 -- This program and the accompanying materials are made available
7 -- under the terms of the Eclipse Public License v1.0 which
8 -- accompanies this distribution, and is available at
9 -- http://www.eclipse.org/legal/epl-v10.html
11 -- This program and the accompanying materials are also made available
12 -- under the terms of the MIT public license which accompanies this
13 -- distribution, and is available at http://www.lua.org/license.html
16 -- Fabien Fleutot - API and implementation
18 --------------------------------------------------------------------------------
20 local M = require "package" -- extend Lua's basic "package" module
22 M.metalua_extension_prefix = 'metalua.extension.'
24 -- Initialize package.mpath from package.path
25 M.mpath = M.mpath or os.getenv 'LUA_MPATH' or
26 (M.path..";") :gsub("%.(lua[:;])", ".m%1") :sub(1, -2)
28 M.mcache = M.mcache or os.getenv 'LUA_MCACHE'
30 ----------------------------------------------------------------------
31 -- resc(k) returns "%"..k if it's a special regular expression char,
32 -- or just k if it's normal.
33 ----------------------------------------------------------------------
34 local regexp_magic = { }
35 for k in ("^$()%.[]*+-?") :gmatch "." do regexp_magic[k]="%"..k end
37 local function resc(k) return regexp_magic[k] or k end
39 ----------------------------------------------------------------------
40 -- Take a Lua module name, return the open file and its name,
41 -- or <false> and an error message.
42 ----------------------------------------------------------------------
43 function M.findfile(name, path_string)
44 local config_regexp = ("([^\n])\n"):rep(5):sub(1, -2)
45 local dir_sep, path_sep, path_mark, execdir, igmark =
46 M.config :match (config_regexp)
47 name = name:gsub ('%.', dir_sep)
49 local path_pattern = string.format('[^%s]+', resc(path_sep))
50 for path in path_string:gmatch (path_pattern) do
51 --printf('path = %s, rpath_mark=%s, name=%s', path, resc(path_mark), name)
52 local filename = path:gsub (resc (path_mark), name)
53 --printf('filename = %s', filename)
54 local file = io.open (filename, 'r')
55 if file then return file, filename end
56 table.insert(errors, string.format("\tno lua file %q", filename))
58 return false, '\n'..table.concat(errors, "\n")..'\n'
61 ----------------------------------------------------------------------
62 -- Before compiling a metalua source module, try to find and load
63 -- a more recent bytecode dump. Requires lfs
64 ----------------------------------------------------------------------
65 local function metalua_cache_loader(name, src_filename, src)
66 local mlc = require 'metalua.compiler'.new()
67 local lfs = require 'lfs'
68 local dir_sep = M.config:sub(1,1)
69 local dst_filename = M.mcache :gsub ('%?', (name:gsub('%.', dir_sep)))
70 local src_a = lfs.attributes(src_filename)
71 local src_date = src_a and src_a.modification or 0
72 local dst_a = lfs.attributes(dst_filename)
73 local dst_date = dst_a and dst_a.modification or 0
74 local delta = dst_date - src_date
75 local bytecode, file, msg
77 print "NEED TO RECOMPILE"
78 bytecode = mlc :src_to_bytecode (src, name)
79 for x in dst_filename :gmatch('()'..dir_sep) do
80 lfs.mkdir(dst_filename:sub(1,x))
82 file, msg = io.open(dst_filename, 'wb')
83 if not file then error(msg) end
84 file :write (bytecode)
87 file, msg = io.open(dst_filename, 'rb')
88 if not file then error(msg) end
89 bytecode = file :read '*a'
92 return mlc :bytecode_to_function (bytecode)
95 ----------------------------------------------------------------------
96 -- Load a metalua source file.
97 ----------------------------------------------------------------------
98 function M.metalua_loader (name)
99 local file, filename_or_msg = M.findfile (name, M.mpath)
100 if not file then return filename_or_msg end
101 local luastring = file:read '*a'
103 if M.mcache and pcall(require, 'lfs') then
104 return metalua_cache_loader(name, filename_or_msg, luastring)
105 else return require 'metalua.compiler'.new() :src_to_function (luastring, name) end
109 ----------------------------------------------------------------------
110 -- Placed after lua/luac loader, so precompiled files have
111 -- higher precedence.
112 ----------------------------------------------------------------------
113 table.insert(M.loaders, M.metalua_loader)
115 ----------------------------------------------------------------------
116 -- Load an extension.
117 ----------------------------------------------------------------------
118 function extension (name, mlp)
119 local complete_name = M.metalua_extension_prefix..name
120 local extend_func = require (complete_name)
121 if not mlp.extensions[complete_name] then
122 local ast =extend_func(mlp)
123 mlp.extensions[complete_name] =extend_func