#!/usr/bin/env nomsu -V7.0.0
###
    This file contains basic error reporting code
    
use "core/metaprogramming"
use "core/operators"
use "core/control_flow"

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(fail $msg) compiles to ("
    at_1_fail(\(quote (this tree).source),
        \(($msg as lua expr) if $msg else (quote "A failure was triggered here'"))
    )
")

(assume $condition) compiles to:
    if ($condition.type == "IndexChain"):
        return Lua ("
            do -- Assumption:
                local _thing, _key = \($condition.1 as lua expr), \($condition.2.1 as lua expr)
                if not _thing[_key] then
                    _key = type_of(_key) == 'Text' and _key:as_lua() or _1_as_text(_key)
                    at_1_fail(\(quote "\($condition.source)"),
                        "Assumption failed: \($condition.1 as nomsu, text) does not have a value for ".._key..".")
                end
            end
        ")
    
    if ($condition.type == "Action"):
        when $condition.stub is:
            "1 ==":
                return Lua ("
                    do -- Assumption:
                        local _a, _b = \($condition.1 as lua expr), \($condition.3 as lua expr)
                        if _a ~= _b then
                            _a = type_of(_a) == 'Text' and _a:as_lua() or _1_as_text(_a)
                            _b = type_of(_b) == 'Text' and _b:as_lua() or _1_as_text(_b)
                            at_1_fail(\(quote "\($condition.1.source)"),
                                "Assumption failed: This value was ".._a.." but it was expected to be ".._b..".")
                        end
                    end
                ")
            
            "1 !=":
                return Lua ("
                    do -- Assumption:
                        local _a, _b = \($condition.1 as lua expr), \($condition.3 as lua expr)
                        if _a == _b then
                            _a = type_of(_a) == 'Text' and _a:as_lua() or _1_as_text(_a)
                            at_1_fail(\(quote "\($condition.1.source)"),
                                "Assumption failed: This value was ".._a.." but it wasn't expected to be.")
                        end
                    end
                ")
            
            "1 >" "1 <" "1 >=" "1 <=":
                return Lua ("
                    do -- Assumption:
                        local _a, _b = \($condition.1 as lua expr), \($condition.3 as lua expr)
                        if not (_a \($condition.2) _b) then
                            _a = type_of(_a) == 'Text' and _a:as_lua() or _1_as_text(_a)
                            _b = type_of(_b) == 'Text' and _b:as_lua() or _1_as_text(_b)
                            at_1_fail(\(quote "\($condition.1.source)"),
                                "Assumption failed: This value was ".._a..", but it was expected to be \
                    ..\($condition.2)".._b..".")
                        end
                    end
                ")
            
            "1 is":
                return Lua ("
                    do -- Assumption:
                        local _a, _b = \($condition.1 as lua expr), \($condition.3 as lua expr)
                        if not _1_is(_a, _b) then
                            _a = type_of(_a) == 'Text' and _a:as_lua() or _1_as_text(_a)
                            at_1_fail(\(quote "\($condition.1.source)"),
                                "Assumption failed: This value (".._a..") was expected to be ".._b..", but wasn't.")
                        end
                    end
                ")
            
            "1 isn ' t" "1 is not":
                return Lua ("
                    do -- Assumption:
                        local _a, _b = \($condition.1 as lua expr), \($condition.(#$condition) as lua expr)
                        if _1_is(_a, _b) then
                            _a = type_of(_a) == 'Text' and _a:as_lua() or _1_as_text(_a)
                            at_1_fail(\(quote "\($condition.1.source)"),
                                "Assumption failed: This value (".._a..") was expected to not be ".._b..", but it was.")
                        end
                    end
                ")
    
    return Lua ("
        if not \($condition as lua expr) then
            at_1_fail(\(quote "\($condition.source)"), "Assumption failed: This assumption did not hold.")
        end
    ")

(assume $a == $b) parses as (assume ($a == $b))
(assume $a != $b) parses as (assume ($a != $b))
(test that $condition) parses as (assume $condition)
test:
    try: fail
    $worked = (no)
    try:
        fail "xx"
    ..if it fails with $failure:
        $worked = (yes)
    ..if it succeeds:
        fail "'try' incorrectly ran success case."
    assume ($failure, matches "xx")
    unless $worked:
        fail "'try' failed to recover from failure"

### Try/except
[
    try $action if it succeeds $success if it fails with $msg $fallback
    try $action if it fails with $msg $fallback if it succeeds $success
] all compile to:
    $success_lua = ($success as lua)
    if (#"\$success_lua" > 0):
        $success_lua, add "\n"
    $success_lua, prepend "-- Success:\n"
    $success_lua, 
        add "if not _fell_through then return table.unpack(_result, 2) end"
    $fallback_lua = ($fallback as lua)
    if (#"\$fallback_lua" > 0):
        $msg_lua = ($msg as lua expr)
        if (#"\$msg_lua" > 0):
            $fallback_lua, prepend "\n\$msg_lua = _result[2]\n"
            if ($msg_lua, text, is lua id):
                $fallback_lua, add free vars [($msg_lua, text)]
    
    $fallback_lua, prepend "-- Failure:\n"
    return Lua ("
        do
            local _fell_through = false
            local _result = {xpcall(function()
                \($action as lua)
                _fell_through = true
            end, enhance_error)}
            if _result[1] then
                \$success_lua
            else
                \$fallback_lua
            end
        end
    ")

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(try $action) parses as
    try $action if it succeeds (do nothing) if it fails (do nothing)

(try $action if it fails $fallback) parses as
    try $action if it succeeds (do nothing) if it fails $fallback

(try $action if it fails with $msg $fallback) parses as
    try $action if it succeeds (do nothing) if it fails with $msg $fallback

(try $action if it succeeds $success) parses as
    try $action if it succeeds $success if it fails (do nothing)

(try $action if it fails $fallback if it succeeds $success) parses as
    try $action if it fails with (=lua "") $fallback if it succeeds $success

(try $action if it succeeds $success if it fails $fallback) parses as
    try $action if it succeeds $success if it fails with (=lua "") $fallback

test:
    $success = (no)
    try:
        do: fail
        ..then always:
            $success = (yes)
    ..if it succeeds:
        fail "'try ... then always ...' didn't propagate failure"
    
    unless $success:
        fail "'try ... then always ...' didn't execute the 'always' code"

(do $action then always $final_action) compiles to ("
    do -- do/then always
        local _fell_through = false
        local _results = {xpcall(function()
            \($action as lua)
            _fell_through = true
        end, enhance_error)}
        \($final_action as lua)
        if not _results[1] then error(_results[2], 0) end
        if not _fell_through then return table.unpack(_results, 2) end
    end
")