----------------------------------------------------------------------
-- Metalua:  $Id$
--
-- Summary: metalua parser, expression parser. This is part of the
--   definition of module [mlp].
--
----------------------------------------------------------------------
--
-- 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.expr()]
-- * [mlp.expr_list()]
-- * [mlp.func_val()]
--
--------------------------------------------------------------------------------

--require "gg"
--require "mlp_misc"
--require "mlp_table"
--require "mlp_meta"

--------------------------------------------------------------------------------
-- These function wrappers (eta-expansions ctually) are just here to break
-- some circular dependencies between mlp_xxx.lua files.
--------------------------------------------------------------------------------
local function _expr (lx) return mlp.expr (lx)  end
local function _table_content (lx) return mlp.table_content (lx) end
local function block (lx) return mlp.block (lx) end
local function stat  (lx) return mlp.stat (lx)  end

module ("mlp", package.seeall)

--------------------------------------------------------------------------------
-- Non-empty expression list. Actually, this isn't used here, but that's
-- handy to give to users.
--------------------------------------------------------------------------------
expr_list = gg.list{ _expr, separators = "," }

--------------------------------------------------------------------------------
-- Helpers for function applications / method applications
--------------------------------------------------------------------------------
local func_args_content = gg.list { 
   name = "function arguments",
   _expr, separators = ",", terminators = ")" } 

-- Used to parse methods
local method_args = gg.multisequence{
   name = "function argument(s)",
   { "{", table_content, "}" },
   { "(", func_args_content, ")", builder = fget(1) },
   default = function(lx) local r = opt_string(lx); return r and {r} or { } end }

--------------------------------------------------------------------------------
-- [func_val] parses a function, from opening parameters parenthese to
-- "end" keyword included. Used for anonymous functions as well as
-- function declaration statements (both local and global).
--
-- It's wrapped in a [_func_val] eta expansion, so that when expr
-- parser uses the latter, they will notice updates of [func_val]
-- definitions.
--------------------------------------------------------------------------------
func_params_content = gg.list{ name="function parameters",
   gg.multisequence{ { "...", builder = "Dots" }, default = id },
   separators  = ",", terminators = {")", "|"} } 

local _func_params_content = function (lx) return func_params_content(lx) end

func_val = gg.sequence { name="function body",
   "(", func_params_content, ")", block, "end", builder = "Function" }

local _func_val = function (lx) return func_val(lx) end

--------------------------------------------------------------------------------
-- Default parser for primary expressions
--------------------------------------------------------------------------------
local function id_or_literal (lx)
   local a = lx:next()
   if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then
      gg.parse_error (lx, "Unexpected expr token %s", _G.table.tostring(a))
   end
   return a
end


--------------------------------------------------------------------------------
-- Builder generators for operators. Wouldn't be worth it if "|x|" notation
-- were allowed, but then lua 5.1 wouldn't compile it 
--------------------------------------------------------------------------------
-- opf1 = |op_tag| |op, a| `Op{ {tag=op_tag}, a }
local function opf1 (op_tag) return function (op, a) 
   return {tag="Op", {tag=op_tag}, a } end end

-- opf2 = |op_tag| |a, op, b| `Op{ {tag=op_tag}, a, b }
local function opf2 (op_tag) return function (a, op, b) 
   return {tag="Op", {tag=op_tag}, a, b } end end

--------------------------------------------------------------------------------
--
-- complete expression
--
--------------------------------------------------------------------------------

-- FIXME: set line number. In [expr] transformers probably

expr = gg.expr { name = "expression",

   primary = gg.multisequence{ name="expr primary",
      { "(", _expr, ")",           builder = "One" },
      { "function", _func_val,     builder = fget(1) },
      { "-{", splice_content, "}", builder = fget(1) },
      { "+{", quote_content, "}",  builder = fget(1) }, 
      { "nil",                     builder = "Nil" },
      { "true",                    builder = "True" },
      { "false",                   builder = "False" },
      { "...",                     builder = "Dots" },
      table,
      default = id_or_literal },

   infix = { name="expr infix op",
      { "+",  prec = 60, builder = opf2 "Add"  },
      { "-",  prec = 60, builder = opf2 "Sub"  },
      { "*",  prec = 70, builder = opf2 "Mul"  },
      { "/",  prec = 70, builder = opf2 "Div"  },
      { "%",  prec = 70, builder = opf2 "Mod"  },
      { "^",  prec = 90, builder = opf2 "Pow",    assoc = "right" },
      { "..", prec = 40, builder = opf2 "Concat", assoc = "right" },
      { "==", prec = 30, builder = opf2 "Eq"  },
      { "~=", prec = 30, builder = opf2 "Ne"  },
      { ">",  prec = 30, builder = opf2 "Gt"  },
      { ">=", prec = 30, builder = opf2 "Ge"  },
      { "<",  prec = 30, builder = opf2 "Lt"  },
      { "<=", prec = 30, builder = opf2 "Le"  },
      { "and",prec = 20, builder = opf2 "And" },
      { "or", prec = 10, builder = opf2 "Or"  } },

   prefix = { name="expr prefix op",
      { "not",prec = 80, builder = opf1 "Not" },
      { "#",  prec = 80, builder = opf1 "Len" },
      { "-",  prec = 80, builder = opf1 "Sub" } },

   suffix = { name="expr suffix op",
      { "[", _expr, "]", builder = function (tab, idx) 
         return {tag="Index", tab, idx[1]} end},
      { ".", id, builder = function (tab, field) 
         return {tag="Index", tab, id2string(field[1])} end },
      { "(", func_args_content, ")", builder = function(f, args) 
         return {tag="Call", f, unpack(args[1])} end },
      { "{", _table_content, "}", builder = function (f, arg)
         return {tag="Call", f, arg[1]} end},
      { ":", id, method_args, builder = function (obj, post)
         return {tag="Method", obj, id2string(post[1]), unpack(post[2])} end},
      { "+{", quote_content, "}", builder = function (f, arg) 
         return {tag="Call", f,  arg[1] } end },
      default = { parse=mlp.opt_string, builder = function(f, arg) 
         return {tag="Call", f, arg } end } } }