// Copyright (c) 2007 Laurence Tratt <laurie@tratt.net>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.


//
// This file might seem a slightly gratuitous example, but people seem to be very interested in it.
// It defines the Converge equivalent of a "macro" which generates a "macro". More accurately, it
// uses nested quasi-quoting, so what we effectively have is an ITree which when spliced in and
// evaluated creates another ITree. To fully resolve a nested quasi-quote n levels deep, one has to
// splice it n times.
//
// In this example, we only use two levels of nested quasi-quoting, but the general principle should
// be fairly clear.
//

import CEI, Strings, Sys



//
// Given a string s, returns a quasi-quoted dynamically scoped function, whose result, when called
// and then spliced in, is a function which takes a string and returns a string.
//
// If this makes your head hurt, the best way to understand things is to run this file as
// "converge -v nested.cv" and then analyse the output. There's a lot of stuff in there, but you
// can eventually work out that your leg isn't being pulled.
//

func f(s):

    it := [|
        func &g(s2):
            it2 := [|
                func &h(s3):
                    return Strings::format("Nested '%s' '%s' '%s'.", \
                      ${CEI::lift(${CEI::lift(s)})}, ${CEI::lift(s2)}, s3)
            |]
            Sys::println("Elided ITree generated by g:\n", CEI::elided_pp_itree(it2, self_module.mod_id))
            return it2
    |]

    Sys::println("Elided ITree generated by f:\n", CEI::elided_pp_itree(it, self_module.mod_id), "\n")
    return it



// Splice in the result from calling f(); essentially, this creates a function called g at this
// location which will look a bit like this:
//
// func g(s2):
//   it := [|
//     func &h(s3):
//       return Strings::format("Nested '%s'.", ${CEI::lift("foo")}, ${CEI::lift(s2)}, s3)
//   |]
//   Sys::println("Elided ITree generated by g:\n", CEI::pp_itree(it, self_module.mod_id))
//   return it
//
// The actual function which gets created has a mountain of ickiness in it to ensure that scoping
// and so on work as expected (most of the variables above get renamed to something else to
// ensure hygiene); you can see the real tree by running this file.

$c<f("foo")>



// Now when we splice the result of calling g, we cancel out what was originally nested
// quasi-quoting.
//
// In other words, splicing in the result of calling g will create a function which looks roughly
// like:
//
//   func h(s3):
//     return Strings::format("Nested '%s' '%s' '%s'.", "foo", "woo", s3))

$c<g("woo")>


func main():

    Sys::println("Spliced g value = ", h("zoo"))
