----------------------------------------------------------------------
-- Metalua:  $Id$
--
-- Summary: Meta-operations: AST quasi-quoting and splicing
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
----------------------------------------------------------------------


--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.splice_content()]
-- * [mlp.quote_content()]
--
--------------------------------------------------------------------------------

--require "compile"
--require "ldump"

module ("mlp", package.seeall)

--------------------------------------------------------------------------------
-- External splicing: compile an AST into a chunk, load and evaluate
-- that chunk, and replace the chunk by its result (which must also be
-- an AST).
--------------------------------------------------------------------------------

local function x_splice (ast)
   --printf("   * Splicing %s", _G.table.tostring (ast, "nohash", 60))
   local f = mlc.function_of_ast(ast)
   if not f then error ("Invalid AST in splice: "..
                        _G.table.tostring (ast, "nohash", 60)) end
   --printf "   * Compiled."
   local r = f()   
   --printf "   * Evaled."
   return r
end

--------------------------------------------------------------------------------
-- Going from an AST to an AST representing that AST
-- the only key being lifted in this version is ["tag"]
--------------------------------------------------------------------------------
function x_quote (t)
   --print("QUOTING:", _G.table.tostring(t, 60))
   local cases = { }
   function cases.table (t)
      local mt = { tag = "Table" }
      if t.tag == "Splice" then
         assert (#t==1, "Invalid splice")
         return t[1]
      elseif t.tag then
         _G.table.insert (mt, { tag = "Key", x_quote "tag", x_quote (t.tag) })
      end
      for _, v in ipairs (t) do
         _G.table.insert (mt, x_quote(v))
      end
      return mt
   end
   function cases.number (t) return { tag = "Number", t } end
   function cases.string (t) return { tag = "String", t } end
   return cases [ type (t) ] (t)
end

--------------------------------------------------------------------------------
-- when this variable is false, code inside [-{...}] is compiled and
-- avaluated immediately. When it's true (supposedly when we're
-- parsing data inside a quasiquote), [-{foo}] is replaced by
-- [`Splice{foo}], which will be unpacked by [x_quote()].
--------------------------------------------------------------------------------
in_a_quote = false

--------------------------------------------------------------------------------
-- Parse the inside of a "-{ ... }"
--------------------------------------------------------------------------------
function splice_content (lx)
   local parser_name = "expr"
   if lx:is_keyword (lx:peek(2), ":") then
      local a = lx:next()
      lx:next() -- skip ":"
      assert (a.tag=="Id", "Invalid splice parser name")
      parser_name = a[1]
   end
   local ast = mlp[parser_name](lx)
   if in_a_quote then
      --printf("Q_SPLICE %s", disp.indent(disp.ast(ast)))
      return { tag="Splice", ast }
   else
      if parser_name == "expr" then ast = { { tag="Return", ast } }
      elseif parser_name == "stat"  then ast = { ast }
      elseif parser_name ~= "block" then
         error ("splice content must be an expr, stat or block") end
      --printf("X_SPLICE %s", disp.indent(disp.ast(ast)))
      return x_splice (ast)
   end
end

--------------------------------------------------------------------------------
-- Parse the inside of a "+{ ... }"
--------------------------------------------------------------------------------
function quote_content (lx)
   local parser = mlp.expr
   if lx:is_keyword (lx:peek(2), ":") then
      parser = mlp[id(lx)[1]]
      lx:next()
   end
   --assert(not in_a_quote, "Nested quotes not handled yet")
   in_a_quote = true
   --print("IN_A_QUOTE")
   local content = parser (lx)
   local q_content = x_quote (content)
   --printf("/IN_A_QUOTE: +{%s}", disp.ast (content))
   --printf("             %s", disp.ast (q_content))
   in_a_quote = false
   return q_content
end