require 'walk.id'
-{ extension 'log' }
+--------------------------------------------------------------------------------
+--
-- H params:
-
-- * H.alpha is the `Local{ } (or `Set{ }) statement which will
-- receive the alpha-conversions required to restore the free
-- variables of the transformed term. For instance,
--
-- * H:reset(field) sets a field to nil, and returns the value of that
-- field prior to nilification.
+--------------------------------------------------------------------------------
-H = { } setmetatable(H, H)
+H = { } --setmetatable(H, H)
+H.__index=H
+H.template = { }
+--------------------------------------------------------------------------------
+--
+--------------------------------------------------------------------------------
+function H:new(x)
+ local instance = table.deep_copy(self.template)
+ if x then instance <- x end
+ setmetatable(instance, self)
+ return instance
+end
+
+--------------------------------------------------------------------------------
+--
+--------------------------------------------------------------------------------
function H:__call (ast)
assert (type(ast)=='table', "H expects an AST")
local local_renames -- only set if inside hygienization's required
- -- kind of hygienization(s) to perform
+ -----------------------------------------------------------------------------
+ -- kind of hygienization(s) to perform: h_inseide and/or h_outside
+ -----------------------------------------------------------------------------
local h_inside, h_outside do
local side = self.side or 'both'
h_inside = side=='inside' or side=='both'
h_outside = side=='outside' or side=='both'
end
+ -----------------------------------------------------------------------------
+ -- Initialize self.keep:
-- self.keep is a dictionary of free var names to be protected from capture
+ -----------------------------------------------------------------------------
do
local k = self.keep
-- If there's no self.keep, that's an empty dictionary
else for i, v in ipairs(k) do k[v], k[i] = true, nil end end
end
- -- Config for the id walker
+ -----------------------------------------------------------------------------
+ -- Config skeleton for the id walker
+ -----------------------------------------------------------------------------
local cfg = { expr = { }, stat = { }, id = { } }
+ -----------------------------------------------------------------------------
-- Outside hygienization: all free variables are renamed to fresh ones,
-- and self.alpha is updated to contain the assignments required to keep
-- the AST's semantics.
+ -----------------------------------------------------------------------------
if h_outside then
local alpha = self.alpha
end
end
+ -----------------------------------------------------------------------------
+ -- Inside hygienization: rename all local variables and their ocurrences.
+ -----------------------------------------------------------------------------
if h_inside then
+ ----------------------------------------------------------------
-- Renamings can't performed on-the-spot, as it would
-- transiently break the link between binders and bound vars,
-- thus preventing the algo to work. They're therefore stored
-- in local_renames, and performed after the whole tree has been
-- walked.
+ ----------------------------------------------------------------
local_renames = { } -- `Id{ old_name } -> new_name
local bound_vars = { } -- binding statement -> old_name -> new_name
- -- Give a new name to newly created local vars, store it in bound_vars
+ ----------------------------------------------------------------
+ -- Give a new name to newly created local vars, store it in
+ -- bound_vars
+ ----------------------------------------------------------------
function cfg.binder (id, binder)
if id.h_boundary then return end
local old_name = id[1]
local_renames[id] = new_name
end
- -- List a bound var for renaming.
- -- The new name has already been chosen and put in bound_vars by cfg.binder().
+ ----------------------------------------------------------------
+ -- List a bound var for renaming. The new name has already been
+ -- chosen and put in bound_vars by cfg.binder().
+ ----------------------------------------------------------------
function cfg.id.bound (id, binder)
if id.h_boundary then return end
local old_name = id[1]
end
end
+ -----------------------------------------------------------------------------
-- Don't traverse subtrees marked by '!'
+ -----------------------------------------------------------------------------
local cut_boundaries = |x| x.h_boundary and 'break' or nil
cfg.stat.down, cfg.expr.down = cut_boundaries, cut_boundaries
+ -----------------------------------------------------------------------------
-- The walker's config is ready, let's go.
-- After that, ids are renamed in ast, free_vars and bound_vars are set.
+ -----------------------------------------------------------------------------
walk_id [self.kind or 'guess'] (cfg, ast)
if h_inside then -- Apply local name changes
return ast
end
+--------------------------------------------------------------------------------
-- Return H to allow call chainings
+--------------------------------------------------------------------------------
function H:set(field, val)
local t = type(field)
if t=='string' then self[field]=val
- elseif t=='table' then table.override(self, field)
+ elseif t=='table' then self <- field
else error("Can't set H, field arg can't be of type "..t) end
return self
end
+--------------------------------------------------------------------------------
-- Return the value before reset
+--------------------------------------------------------------------------------
function H:reset(field)
if type(field) ~= 'string' then error "Can only reset H string fields" end
local r = H[field]
return r
end
-local function commit_locals_to_chunk(x)
- local alpha = H:reset 'alpha'
- --$log ('commit locals', x, alpha, 'nohash')
- if not alpha or not alpha[1][1] then return end
- if not x then return alpha end
- table.insert(x, 1, alpha)
-end
+-- local function commit_locals_to_chunk(x)
+-- local alpha = H:reset 'alpha'
+-- --$log ('commit locals', x, alpha, 'nohash')
+-- if not alpha or not alpha[1][1] then return end
+-- if not x then return alpha end
+-- table.insert(x, 1, alpha)
+-- end
-mlp.chunk.transformers:add (commit_locals_to_chunk)
\ No newline at end of file
+-- mlp.chunk.transformers:add (commit_locals_to_chunk)
\ No newline at end of file
-- Get match parsers and builder, for catch cases handling:\r
require 'extension.match' \r
\r
+-{ extension 'H' }\r
+-{ extension 'log' }\r
+\r
-- We'll need to track rogue return statements:\r
require 'walk'\r
\r
local try_code, catch_cases, finally_code = unpack(x)\r
local insert_return_catcher = false\r
\r
+ local H = H:new{side='inside'}\r
+ !try_code; !(catch_code or { }); !(finally_code or { })\r
+\r
--$log(try_code, catch_cases, finally_code)\r
\r
----------------------------------------------------------------\r
match x with \r
| `Return{...} -> \r
insert_return_catcher = true\r
- local setvar = \r
- +{stat:caught_return = -{ `Table{ unpack(x) } } }\r
- x.tag = nil\r
- x <- { setvar; `Return }\r
+-- FIXME: caught_return won't be tranformed because it's inside \r
+-- a user-bounded block\r
+ local setvar = +{stat:caught_return = -{`Table{ unpack(x) }}}\r
+ x <- { setvar; `Return }; x.tag = nil;\r
+ $log('transformed return stat:', x, 60)\r
return 'break'\r
| `Function{...} -> return 'break' \r
- -- inside this, returns would be the function's, not ours.\r
+ -- inside this, returns would be the nested function's, not ours.\r
| _ -> -- pass\r
end\r
end\r
\r
-- code handling the error catching process:\r
local catch_result do\r
- if catch_cases then\r
+ if #catch_cases>0 then\r
----------------------------------------------------------\r
-- Protect catch code against failures: they run in a pcall(), and\r
-- the result is kept in catch_* vars so that it can be used to\r
if not user_success and not catch_success then error(catch_error) end \r
-{ caught_return_rethrow }\r
end }\r
+\r
+ H(result)\r
+\r
return result\r
end\r
\r