----------------------------------------------------------------------
-- Metalua samples:  $Id$
--
-- Summary: Lists by comprehension
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
-------------------------------------------------------------------------------
--
-- This extension implements list comprehensions, similar to Haskell and
-- Python syntax, to easily describe lists. It badly needs more comments.
--
--------------------------------------------------------------------------------

-{ extension "match" }

require "std"

local function dots_builder (x) return `Dots{ x } end

local function for_builder (x, h)
   match x with
   | `Comp{ _, acc } -> table.insert (acc, h[1]); return x
   | `Key{ _, _ }    -> error "No explicit key in a for list generator"
   |  _              -> return `Comp{ x, {h[1]} }
   end
end

local function if_builder (x, p)
   match x with
   | `Comp{ _, acc } -> table.insert (acc, `If{ p[1] }); return x
   | `Key{ _, _ }    -> error "No explicit key in a list guard"
   |  _              -> return `Comp{ x, p[1] }
   end
end
   
local function comp_builder(core, list, no_unpack)
   printf("CB: core = %s, list = %s", 
          table.tostring(core,60,"nohash"), 
          table.tostring(list,60,"nohash"))
   local ti, v, r = mlp.gensym "table_insert", mlp.gensym "table"

   -----------------------------------------------------------------------------
   -- 1 - Build the loop's core: if it has suffix "...", every elements of the
   --     multi-return must be inserted, hence the extra [for] loop.
   -----------------------------------------------------------------------------
   match core with
   | `Dots{ x } -> 
      local w = mlp.gensym()
      r = +{stat: for -{w} in values( -{x} ) do -{ `Call{ ti, v, w } } end }
   |  x -> r = `Call{ ti, v, x }
   end

   -----------------------------------------------------------------------------
   -- 2 - Stack the if and for control structures, from outside to inside.
   --     This is done in a destructive way for the elements of [list].
   -----------------------------------------------------------------------------
   for i = #list, 1, -1 do
      table.insert (list[i], {r})
      r = list[i]
   end
   if no_unpack then
      return `Stat{ { `Local{ {ti, v}, { +{table.insert}, `Table} }, r }, v }
   else
      return +{ function() 
                   local -{ti}, -{v} = table.insert, { }
                   -{r}; return unpack(-{v}) 
                 end () }
   end
end

local function table_content_builder (list)
   match list with
   | { `Comp{ y, acc } } -> return comp_builder( y, acc, "no unpack")
   | _ ->
      local tables = { `Table }
      local ctable = tables[1]
      local function flush() ctable=`Table; table.insert(tables, ctable) end
      for x in values(list) do
         match x with
         | `Comp{ y, acc } -> table.insert(ctable, comp_builder(y, acc)); flush()
         | `Dots{ y }      -> table.insert(ctable, y); flush()
         |  _              -> table.insert (ctable, x); 
         end
      end
      match tables with
      | { x } | { x, { } } -> return x
      | _ ->
         if #tables[#tables]==0 then table.remove(tables) end --suppress empty table      
         return `Call{ +{table.cat}, unpack(tables) }
      end
   end
end

mlp.table_field = gg.expr{ name="table cell",
   primary = mlp.table_field,
   suffix  = { name="table cell suffix",
      { "...",                 builder = dots_builder },
      { "for", mlp.for_header, builder = for_builder  },
      { "if",  mlp.expr,       builder = if_builder   } } }

mlp.table_content.builder = table_content_builder

local function index_builder(a, suffix)
   match suffix[1] with
   | { { e, false } } -> return `Index{ a, e }
   | ranges ->
      print"HI!"
      local r = `Call{ +{table.isub}, a }
      local function acc(x,y) table.insert(r,x); table.insert(r,y) end
      for seq in values(ranges) do
         match seq with
         | { e, false } -> acc(e,e)
         | { e, f }     -> acc(e,f)
         end
      end
      return r
   end
end

mlp.expr.suffix:add{ name="table index/range",
   "[", gg.list{
      gg.sequence { mlp.expr, gg.onkeyword{ "...", mlp.expr } } , 
      separators = { ",", ";" } }, 
   "]", builder = index_builder }