Merge remote-tracking branch 'origin/v0.7.2'
diff --git a/README-parser.md b/README-parser.md index d5edacc..98e34ee 100644 --- a/README-parser.md +++ b/README-parser.md
@@ -85,7 +85,7 @@ `Nil | `Dots | `True | `False | `Number{ <number> } | `String{ <string> } - | `Function{ { `Id{ <string> }* `Dots? } block } + | `Function{ { ident* `Dots? } block } | `Table{ ( `Pair{ expr expr } | expr )* } | `Op{ opid expr expr? } | `Paren{ expr } -- significant to cut multiple values returns @@ -96,7 +96,9 @@ `Call{ expr expr* } | `Invoke{ expr `String{ <string> } expr* } - lhs: `Id{ <string> } | `Index{ expr expr } + ident: `Id{ <string> } + + lhs: ident | `Index{ expr expr } opid: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow' | 'concat'| 'eq'
diff --git a/metalua.lua b/bin/metalua similarity index 94% rename from metalua.lua rename to bin/metalua index eee8e6e..77e0b65 100644 --- a/metalua.lua +++ b/bin/metalua
@@ -1,5 +1,7 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2006-2013 Fabien Fleutot and others. +#!/usr/bin/env lua51 +--*-lua-*---------------------------------------------------------------------- +-- +-- Copyright (c) 2006-2014 Fabien Fleutot and others. -- -- All rights reserved. -- @@ -88,8 +90,6 @@ local argv = {...} local opts, optind, optarg = alt_getopt.get_ordered_opts({...}, alt_getopt_options, long_opts) - --pp.printf("argv=%s; opts=%s, ending at %i, with optarg=%s", - -- argv, opts, optind, optarg) local s2l = { } -- short to long option names conversion table for long, short in pairs(long_opts) do s2l[short]=long end local cfg = { chunks = { } } @@ -108,7 +108,6 @@ ------------------------------------------------------------------- -- Print messages if in verbose mode - ------------------------------------------------------------------- local function verb_print (fmt, ...) if cfg.verbose then return pp.printf ("[ "..fmt.." ]", ...) @@ -125,7 +124,7 @@ if not next(cfg.chunks) and next(cfg.params) then local the_file = table.remove(cfg.params, 1) verb_print("Param %q considered as a source file", the_file) - cfg.file={ the_file } + cfg.chunks={ { tag='file', the_file } } end ------------------------------------------------------------------- @@ -135,7 +134,6 @@ cfg.interactive=true end - ------------------------------------------------------------------- -- Run if asked to, or if no --output has been given -- if cfg.run==false it's been *forced* to false, don't override. @@ -182,8 +180,8 @@ end -- The last file returns the whole chunk's result if last_file_idx then - -- transform +{ (function(...) -{ast} end)(...) } - -- into +{ return (function(...) -{ast} end)(...) } + -- transform `+{ (function(...) -{ast} end)(...) }` + -- into `+{ return (function(...) -{ast} end)(...) }` local prv_ast = code[last_file_idx] local new_ast = { tag='Return', prv_ast } code[last_file_idx] = new_ast
diff --git a/metalua/compiler/ast_to_src.mlua b/metalua/compiler/ast_to_src.mlua index 9054088..ca80a12 100644 --- a/metalua/compiler/ast_to_src.mlua +++ b/metalua/compiler/ast_to_src.mlua
@@ -21,6 +21,7 @@ local M = { } M.__index = M +M.__call = |self, ...| self:run(...) local pp=require 'metalua.pprint' @@ -160,24 +161,34 @@ -------------------------------------------------------------------------------- function M:node (node) assert (self~=M and self._acc) - if node==nil then self:acc'<<error>>'; return end - if not node.tag then -- tagless block. - self:list (node, self.nl) - else - local f = M[node.tag] - if type (f) == "function" then -- Delegate to tag method. - f (self, node, unpack (node)) - elseif type (f) == "string" then -- tag string. - self:acc (f) - else -- No appropriate method, fall back to splice dumping. - -- This cannot happen in a plain Lua AST. - self:acc " -{ " - self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80) - self:acc " }" + if node==nil then self:acc'<<error>>' + elseif not self.custom_printer or not self.custom_printer (self, node) then + if not node.tag then -- tagless (henceunindented) block. + self:list (node, self.nl) + else + local f = M[node.tag] + if type (f) == "function" then -- Delegate to tag method. + f (self, node, unpack (node)) + elseif type (f) == "string" then -- tag string. + self:acc (f) + else -- No appropriate method, fall back to splice dumping. + -- This cannot happen in a plain Lua AST. + self:acc " -{ " + self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80) + self:acc " }" + end end end end +function M:block(body) + if not self.custom_printer or not self.custom_printer (self, body) then + self:nlindent () + self:list (body, self.nl) + self:nldedent () + end +end + -------------------------------------------------------------------------------- -- Convert every node in the AST list `list' passed as 1st arg. -- `sep' is an optional separator to be accumulated between each list element, @@ -225,9 +236,7 @@ function M:Do (node) self:acc "do" - self:nlindent () - self:list (node, self.nl) - self:nldedent () + self:block (node) self:acc "end" end @@ -244,9 +253,7 @@ self:acc " (" self:list (params, ", ", 2) self:acc ")" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) -> @@ -256,9 +263,7 @@ self:acc " (" self:list (params, ", ") self:acc ")" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs } @@ -303,17 +308,13 @@ self:acc "while " self:node (cond) self:acc " do" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" end function M:Repeat (node, body, cond) self:acc "repeat" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "until " self:node (cond) end @@ -325,16 +326,12 @@ self:acc (i==1 and "if " or "elseif ") self:node (cond) self:acc " then" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) end -- odd number of children --> last one is an `else' clause -- if #node%2 == 1 then self:acc "else" - self:nlindent () - self:list (node[#node], self.nl) - self:nldedent () + self:block (node[#node]) end self:acc "end" end @@ -352,9 +349,7 @@ self:node (node[4]) end self:acc " do" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" end @@ -364,9 +359,7 @@ self:acc " in " self:list (generators, ", ") self:acc " do" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" end @@ -406,9 +399,7 @@ self:acc " (" self:list (params, ", ") self:acc ")" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" | _ -> @@ -488,9 +479,7 @@ self:list (params, ", ") end self:acc ")" - self:nlindent () - self:list (body, self.nl) - self:nldedent () + self:block (body) self:acc "end" end @@ -690,4 +679,4 @@ end end -return (|x| M.run(x)) +return M
diff --git a/metalua/loader.lua b/metalua/loader.lua index 9c4754f..e535fef 100644 --- a/metalua/loader.lua +++ b/metalua/loader.lua
@@ -52,9 +52,9 @@ --printf('path = %s, rpath_mark=%s, name=%s', path, resc(path_mark), name) local filename = path:gsub (resc (path_mark), name) --printf('filename = %s', filename) - local file = io.open (filename, 'r') + local file = io.open (filename, 'rb') if file then return file, filename end - table.insert(errors, string.format("\tno lua file %q", filename)) + table.insert(errors, string.format("\tno file %q", filename)) end return false, '\n'..table.concat(errors, "\n")..'\n' end @@ -80,7 +80,7 @@ local bytecode, file, msg if delta <= 0 then --print ("(need to recompile "..src_filename.." into "..dst_filename..")") - bytecode = mlc :src_to_bytecode (src, name) + bytecode = mlc :src_to_bytecode (src, '@'..src_filename) for x in dst_filename :gmatch('()'..dir_sep) do lfs.mkdir(dst_filename:sub(1,x)) end @@ -94,7 +94,7 @@ bytecode = file :read '*a' file :close() end - return mlc :bytecode_to_function (bytecode) + return mlc :bytecode_to_function (bytecode, '@'..src_filename) end ---------------------------------------------------------------------- @@ -107,7 +107,7 @@ file:close() if M.mcache and pcall(require, 'lfs') then return metalua_cache_loader(name, filename_or_msg, luastring) - else return require 'metalua.compiler'.new() :src_to_function (luastring, name) end + else return require 'metalua.compiler'.new() :src_to_function (luastring, '@'..filename_or_msg) end end
diff --git a/metalua/treequery.mlua b/metalua/treequery.mlua index f5b09d2..e369b99 100755 --- a/metalua/treequery.mlua +++ b/metalua/treequery.mlua
@@ -299,7 +299,7 @@ local tag = node.tag if not tag then return false elseif STAT_TAGS[tag] then return true - elseif tag=='Call' or tag=='Invoke' then return parent.tag==nil + elseif tag=='Call' or tag=='Invoke' then return parent and parent.tag==nil else return false end end @@ -435,6 +435,27 @@ end end +--- Returns a list of the direct children of AST node `ast`. +-- Children are only expressions, statements and blocks, +-- not intermediates such as `Pair` nodes or internal lists +-- in `Local` or `Set` nodes. +-- Children are returned in parsing order, which isn't necessarily +-- the same as source code order. For instance, the right-hand-side +-- of a `Local` node is listed before the left-hand-side, because +-- semantically the right is evaluated before the variables on the +-- left enter scope. +-- +-- @param ast the node whose children are needed +-- @return a list of the direct children of `ast` +function M.children(ast) + local acc = { } + local cfg = { } + function cfg.down(x) + if x~=ast then table.insert(acc, x); return 'break' end + end + walk.guess(cfg, ast) + return acc +end -- ----------------------------------------------------------------------------- -- -----------------------------------------------------------------------------
diff --git a/metalua/treequery/walk.mlua b/metalua/treequery/walk.mlua index 67dacfd..94fc5d6 100755 --- a/metalua/treequery/walk.mlua +++ b/metalua/treequery/walk.mlua
@@ -18,35 +18,35 @@ ------------------------------------------------------------------------------- -- Low level AST traversal library. --- This library is a helper for the higher-level treequery library. +-- +-- This library is a helper for the higher-level `treequery` library. -- It walks through every node of an AST, depth-first, and executes --- some callbacks contained in its cfg config table: +-- some callbacks contained in its `cfg` config table: -- --- * cfg.down(...) is called when it walks down a node, and receive as +-- * `cfg.down(...)` is called when it walks down a node, and receive as -- parameters the node just entered, followed by its parent, grand-parent -- etc. until the root node. -- --- * cfg.up(...) is called when it walks back up a node, and receive as +-- * `cfg.up(...)` is called when it walks back up a node, and receive as -- parameters the node just entered, followed by its parent, grand-parent -- etc. until the root node. -- --- * cfg.occurrence(binder, id_node, ...) is called when it visits an `Id{ } --- node which isn't a local variable creator. binder is a reference to its --- binder with its context. The binder is the `Id{ } node which created --- this local variable. By "binder and its context", we mean a list starting --- with the `Id{ }, and followed by every ancestor of the binder node, up until --- the common root node. --- binder is nil if the variable is global. --- id_node is followed by its ancestor, up until the root node. +-- * `cfg.occurrence(binder, id_node, ...)` is called when it visits +-- an `` `Id{ }`` node which isn't a local variable creator. binder +-- is a reference to its binder with its context. The binder is the +-- `` `Id{ }`` node which created this local variable. By "binder +-- and its context", we mean a list starting with the `` `Id{ }``, +-- and followed by every ancestor of the binder node, up until the +-- common root node. `binder` is nil if the variable is global. +-- `id_node` is followed by its ancestor, up until the root node. -- --- cfg.scope is maintained during the traversal, associating a +-- `cfg.scope` is maintained during the traversal, associating a -- variable name to the binder which creates it in the context of the -- node currently visited. -- --- walk.traverse.xxx functions are in charge of the recursive descent into --- children nodes. They're private helpers. --- --- corresponding walk.xxx functions also take care of calling cfg callbacks. +-- `walk.traverse.xxx` functions are in charge of the recursive +-- descent into children nodes. They're private helpers. They are also +-- in charge of calling appropriate `cfg.xxx` callbacks. -{ extension ("match", ...) } @@ -74,6 +74,7 @@ -- These [M.traverse.xxx()] functions are in charge of actually going through -- ASTs. At each node, they make sure to call the appropriate walker. -------------------------------------------------------------------------------- + function M.traverse.stat (cfg, x, ...) if M.debug then pp.printf("traverse stat %s", x) end local ancestors = {...} @@ -180,10 +181,15 @@ local f = cfg.binder local ferror = cfg.error or cfg.malformed or cfg.unknown for i, id_node in ipairs(id_list) do + local down, up = cfg.down, cfg.up if id_node.tag == 'Id' then cfg.scope :set (id_node[1], { id_node, ... }) + if down then down(id_node, ...) end if f then f(id_node, ...) end + if up then up(id_node, ...) end elseif i==#id_list and id_node.tag=='Dots' then + if down then down(id_node, ...) end + if up then up(id_node, ...) end -- Do nothing, those are valid `Dots elseif ferror then -- Traverse error handling function @@ -213,7 +219,10 @@ end ---------------------------------------------------------------------- --- Declare [M.stat], [M.expr], [M.block] and [M.expr_list] +-- Declare [M.stat], [M.expr], [M.block]. +-- `M.binder_list` is not here, because `cfg.up` and `cfg.down` must +-- be called on individual binders, not on the list itself. +-- It's therefore handled in `traverse.binder_list()` ---------------------------------------------------------------------- for _, w in ipairs{ "stat", "expr", "block" } do --, "malformed", "unknown" } do M[w] = walker_builder (w, M.traverse[w])