--------------------------------------------------------------------------------
---------------------- ##       #####    #####   ######  -----------------------
---------------------- ##      ##   ##  ##   ##  ##   ## -----------------------
---------------------- ##      ##   ##  ##   ##  ######  -----------------------
---------------------- ##      ##   ##  ##   ##  ##      -----------------------
---------------------- ######   #####    #####   ##      -----------------------
----------------------                                   -----------------------
----------------------- Lua Object-Oriented Programming ------------------------
--------------------------------------------------------------------------------
-- Project: LOOP Class Library                                                --
-- Release: 2.2 alpha                                                         --
-- Title  : Simple Expression Parser                                          --
-- Author : Renato Maia <maia@inf.puc-rio.br>                                 --
-- Date   : 25/08/2006 09:09                                                  --
--------------------------------------------------------------------------------

local luaerror = error
local pairs = pairs
local ipairs = ipairs
local unpack = unpack
local select = select

require "string"
local oo = require "loop.base"

--[[VERBOSE]] local verbose = require("loop.debug.Verbose"){
--[[VERBOSE]] 	groups = { expression = {"operator","value","parse"} }
--[[VERBOSE]] }
--[[VERBOSE]] verbose:flag("expression", false)

module("loop.compiler.Expression", oo.class)

pos = 1
count = 0
precedence = {}

local pattern = "^ *%s"
function __init(self, object)
	self = oo.rawnew(self, object)

	if not self.operands and self.values then
		local operands = {}
		for kind, spec in pairs(self.values) do
			operands[kind] = pattern:format(spec)
		end
		self.operands = operands
	end
	
	if not self.format and self.operators then
		local opformat = {}
		for name, spec in pairs(self.operators) do
			local format = {}
			local pos = 1
			while pos <= #spec do
				if spec:find("^ ", pos) then
					format[#format+1] = true
					pos = pos + 1
				else
					local keyword = spec:match("^[^ ]+", pos)
					format[#format+1] = keyword
					pos = pos + #keyword
				end
			end
			opformat[name] = format
		end
		self.format = opformat
	end

	self.values = self.values or {}
	
	return self
end

function push(self, kind, value)
	self[#self+1] = kind
	if kind == true then
		self.count = self.count + 1
		self.values[self.count] = value
	end
end

function pop(self)
	local kind = self[#self]
	self[#self] = nil
	local value
	if kind == true then
		value = self.values[self.count]
		self.count = self.count - 1
	end
	return kind, value
end

function get(self, count)
	local nvals = 0
	for _=1, count do
		if self[#self] == true then
			nvals = nvals + 1
		end
		self[#self] = nil
	end
	self.count = self.count - nvals
	return unpack(self.values, self.count + 1, self.count + nvals)
end

local errmsg = "%s at position %d"
function error(self, msg)
	return luaerror(errmsg:format(msg, self.pos))
end

function done(self)
	return (self.text:match("^%s*$", self.pos))
end

function token(self, token)
	local pos = select(2, self.text:find("^%s*[^%s]", self.pos))
	if pos and (self.text:find(token, pos, true) == pos) then
		self.pos = pos + #token
		return true
	end
end

function match(self, pattern)
	local first, last, value = self.text:find(pattern, self.pos)
	if first then
		self.pos = last + 1
		return value
	end
end

function operator(self, name, level, start)
	local format = self.format[name]
	for index, kind in ipairs(format) do
		local parsed = self[start + index]
		if parsed then
			if parsed ~= kind then                                                    --[[VERBOSE]] verbose:operator("parsed value mismatch, got '",parsed,"' ('",kind,"' expected)")
				return false                                                            --[[VERBOSE]] else verbose:operator("parsed value matched, got '",parsed,"'")
			end
		else
			if kind == true then                                                      --[[VERBOSE]] verbose:operator(true, "operand expected, parsing...")
				if not self:parse(level + 1, #self) then                                --[[VERBOSE]] verbose:operator(false, "operand parsing failed")
					return false
				end                                                                     --[[VERBOSE]] verbose:operator(false, "operand parsed successfully")
			else                                                                      --[[VERBOSE]] verbose:operator("token ",kind," expected")
				if self:token(kind) then                                                --[[VERBOSE]] verbose:operator("token found successfully")
					self:push(kind)
				else                                                                    --[[VERBOSE]] verbose:operator("token not found")
					return false
				end
			end
		end
	end                                                                           --[[VERBOSE]] verbose:operator(true, "operator ",name," matched")
	self:push(true, self[name](self, self:get(#format)))                          --[[VERBOSE]] verbose:operator(false, "operator ",name," callback called")
	return true
end

function value(self)
	if self:token("(") then                                                       --[[VERBOSE]] verbose:value(true, "'(' found at ",self.pos)
		local start = #self
		if not self:parse(1, start) then                                            --[[VERBOSE]] verbose:value(false, "error in enclosed expression")
			self:error("value expected")
		elseif #self ~= start + 1 or self[#self] ~= true then                       --[[VERBOSE]] verbose:value(false, "enclosed expression incomplete (too many parsed values left)")
			self:error("incomplete expression")
		elseif not self:token(")") then                                             --[[VERBOSE]] verbose:value(false, "')' not found")
			self:error("')' expected")
		end                                                                         --[[VERBOSE]] verbose:value(false, "matching ')' found")
		return true
	else                                                                          --[[VERBOSE]] verbose:value(true, "parsing value at ",self.pos)
		for kind, pattern in pairs(self.operands) do                                --[[VERBOSE]] verbose:value("attempt to match value as ",kind)
			local value = self:match(pattern)
			if value then                                                             --[[VERBOSE]] verbose:value(true, "value found as ",kind)
				self:push(true, self[kind](self, value))                                --[[VERBOSE]] verbose:value(false, "value evaluated to ",self.values[self.count])
				return true                                                             --[[VERBOSE]],verbose:value(false)
			end
		end                                                                         --[[VERBOSE]] verbose:value(false, "no value found at ",self.pos)
		return false
	end
end

function parse(self, level, start)
	if not self:done() then
		local ops = self.precedence[level]
		if ops then                                                                 --[[VERBOSE]] verbose:parse(true, "parsing operators of level ",level)
			local i = 1
			while ops[i] do
				local op = ops[i]                                                       --[[VERBOSE]] verbose:parse(true, "attempt to match operator ",op)
				if self:operator(ops[i], level, start) then                             --[[VERBOSE]] verbose:parse(false, "operator ",op," successfully matched")
					i = 1
				else                                                                    --[[VERBOSE]] verbose:parse(false, "operator ",op," not matched")
					i = i + 1
				end
			end
			if #self == start then                                                    --[[VERBOSE]] verbose:parse(false, "no value evaluated by operators of level ",level)
				return self:parse(level + 1, start)
			elseif self[start + 1] == true then                                       --[[VERBOSE]] verbose:parse(false, "values evaluated by operators of level ",level)
				return true
			end
		else                                                                        --[[VERBOSE]] verbose:parse(true, "parsing value")
			return self:value()                                                       --[[VERBOSE]] ,verbose:parse(false)
		end
	end
end

function evaluate(self, text, pos)
	if text then
		self.text = text
		self.pos = pos
	end
	if not self:parse(1, 0) then
		self:error("parsing failed")
	elseif not self:done() then
		self:error("malformed expression")
	elseif #self ~= 1 or self[1] ~= true then
		self:error("incomplete expression")
	end
	return self:get(1)
end
