#!/usr/bin/env nomsu -V6.15.13.8
#
    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), 'Failure: \(
        "'..\($msg as lua expr)" if $msg else "A failure was triggered here'"
    ))
")

(assume $condition) compiles to ("
    if not \($condition as lua expr) then
        at_1_fail(\(quote "\($condition.source)"), "Assumption failed: This was not true.")
    end
")

(assume $a == $b) compiles to ("
    do
        local _a, _b = \($a as lua expr), \($b as lua expr)
        if _a ~= _b then
            at_1_fail(\(quote "\($a.source)"),
                "Assumption failed: This value was "..tostring(_a).." but it was expected to be "..tostring(_b)..".")
        end
    end
")

(assume $a != $b) compiles to ("
    do
        local _a, _b = \($a as lua expr), \($b as lua expr)
        if _a == _b then
            at_1_fail(\(quote "\($a.source)"),
                "Assumption failed: This value was "..tostring(_a).." but it wasn't expected to be.")
        end
    end
")

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
")