#!/usr/bin/env nomsu -V6.15.13.8
#
    This file contains code for defining ways to upgrade code between different versions
    of Nomsu.
    
use "filesystem"

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

$UPGRADES = {}
$ACTION_UPGRADES = ({} with fallback $ -> {})
external:
    (upgrade to $version via $upgrade_fn) means:
        $UPGRADES.$version = $upgrade_fn
    
    (upgrade action $stub to $version via $upgrade_fn) means:
        $ACTION_UPGRADES.$version.$stub = $upgrade_fn
    
    (upgrade $tree to $version as $body) parses as
        upgrade to $version via (($ $end_version) -> ($, with ($tree -> $body)))
    
    (upgrade action $actions to $version as $body) compiles to:
        if ($actions is "Action" syntax tree):
            $actions = \[$actions]
        $lua = (Lua "")
        for $action in $actions:
            $replacements = {}
            for $i in 1 to (size of $action):
                if ($action.$i is "Var" syntax tree):
                    $replacements.($action.$i.1) = "\(\$tree as lua id)[\$i]"
            define mangler
            (make tree $t) means:
                when:
                    ($t is "Var" syntax tree):
                        if $replacements.($t.1):
                            return $replacements.($t.1)
                        ..else:
                            external ($needs_mangle = (yes))
                            return ("
                                SyntaxTree{type=\(quote $t.type), source=\(quote "\($t.source)"), \(quote (mangle $t.1))}
                            ")
                    
                    ($t is syntax tree):
                        $args = []
                        for $k = $v in $t:
                            if ((type of $k) == "a Number"):
                                $args, add (make tree $v)
                            ..else:
                                $args, add "\($k)=\(make tree $v)"
                        return "SyntaxTree{\($args, joined with ", ")}"
                    
                    else:
                        return (quote $t)
            
            unless ("\$lua" == ""):
                $lua, add "\n"
            
            $retval = (make tree $body)
            $lua, add
                Lua ("
                    upgrade_action_1_to_2_via(\(quote $action.stub), \($version as lua expr), function(\
                    ..\(\$tree as lua id))
                        return \$retval
                    end)
                ")
        return $lua
    
    [
        $tree upgraded from $start_version to $end_version
        $tree upgraded to $end_version from $start_version
    ] all mean:
        unless ($tree is syntax tree): return $tree
        ($ver as version list) means:
            if ($ver is "Text"):
                return (($ as number) for $ in $ver matching "[0-9]+")
            return $ver
        (Ver $) means:
            if ($ is "a List"):
                if ($.1 is "Text"):
                    return {.lib = $.1, .version = ($, from 2)}
                return {.version = $}
            [$lib, $ver] = ($, match "(.*)/([0-9.]+)")
            if $lib:
                return {.lib = $lib, .version = ($ver as version list)}
            return {.version = ($ as version list)}
        $start = (Ver $start_version)
        $end = (Ver $end_version)
        $end.lib or= $start.lib
        assume $start.lib == $end.lib
        $seen = {}
        $versions = {}
        for $v = $ in $UPGRADES:
            $versions.$v = (yes)
        
        for $v = $ in $ACTION_UPGRADES:
            $versions.$v = (yes)
        
        $versions = [: for $v = $ in $versions: if ((Ver $v).lib == $start.lib): add $v]
        sort $versions by $ -> ($ as version list)
        for $ver in $versions:
            if (($ver as version list) <= $start.version): do next $ver
            if (($ver as version list) > $end.version): stop $ver
            if $ACTION_UPGRADES.$ver:
                $tree =
                    $tree, with
                        $ ->:
                            if ($ is "Action" syntax tree):
                                $(upgrade 1 to 2) = $ACTION_UPGRADES.$ver.($.stub)
                                if $(upgrade 1 to 2):
                                    $with_upgraded_args = {
                                        : for $k = $v in $:
                                            add $k = ($v upgraded from $start_version to $end_version)
                                    }
                                    set $with_upgraded_args's metatable to ($'s metatable)
                                    return (upgrade $with_upgraded_args to $end_version)
            
            if $UPGRADES.$ver:
                $with_upgraded_args = {
                    : for $k = $v in $tree:
                        add $k = ($v upgraded from $start_version to $end_version)
                }
                set $with_upgraded_args's metatable to ($tree's metatable)
                $tree = ($UPGRADES.$ver $with_upgraded_args $end_version)
        
        if ($tree.version != $end_version):
            $tree = (SyntaxTree {: for $k = $v in $tree: add $k = $v})
            $tree.version = $end_version
            if $tree.shebang:
                $tree.shebang = "#!/usr/bin/env nomsu -V\($end_version, joined with ".")\n"
        
        return $tree
    
    ($tree upgraded from $start_version) means
        $tree upgraded from $start_version to $(NOMSU VERSION)
    
    ($tree upgraded to $end_version) means
        $tree upgraded from ($tree.version or $(NOMSU VERSION)) to $end_version
    
    ($tree upgraded) means
        $tree upgraded from ($tree.version or $(NOMSU VERSION)) to $(NOMSU VERSION)