impl step6
authorAnotherTest <ali.mpfard@gmail.com>
Wed, 19 Feb 2020 10:29:00 +0000 (13:59 +0330)
committerAnotherTest <ali.mpfard@gmail.com>
Sat, 23 May 2020 21:57:16 +0000 (02:27 +0430)
separate replEnv from function environments, so as to decrease base size
(state size is still O(2^n))

xslt/core.xslt
xslt/env.xslt
xslt/harness.py
xslt/printer.xslt
xslt/reader.xslt
xslt/step6_file.xslt [new file with mode: 0644]
xslt/test.xslt

index 3c9bc2f..e2308ca 100644 (file)
             <malval kind="function" name="&lt;="/>
             <malval kind="function" name="&gt;"/>
             <malval kind="function" name="&gt;="/>
+            <malval kind="function" name="read-string"/>
+            <malval kind="function" name="slurp"/>
+            <malval kind="function" name="env??"/> <!-- defined in the step files -->
+            <malval kind="function" name="eval"/> <!-- defined in the step files -->
+            <malval kind="function" name="atom"/> <!-- defined in the step files -->
+            <malval kind="function" name="atom?"/>
+            <malval kind="function" name="deref"/> <!-- defined in the step files -->
+            <malval kind="function" name="swap!"/> <!-- defined in the step files -->
+            <malval kind="function" name="reset!"/> <!-- defined in the step files -->
         </xsl:sequence>
     </xsl:function>
 
             <xsl:when test="$func/malval/@name = '&gt;='">
                 <xsl:sequence select="core:makeMALType((), if (number($args/value/malval/lvalue/malval[1]/@value) ge number($args/value/malval/lvalue/malval[2]/@value)) then 'true' else 'false')"/>
             </xsl:when>
+            <xsl:when test="$func/malval/@name = 'read-string'">
+              <xsl:variable name="read-string-context">
+                <str>
+                  <xsl:value-of select="$args/value/malval/lvalue/malval[1]/@value"></xsl:value-of>
+                </str>
+              </xsl:variable>
+              <xsl:for-each select="$read-string-context">
+                <xsl:call-template name="malreader-read_str"></xsl:call-template>
+              </xsl:for-each>
+            </xsl:when>
+            <xsl:when test="$func/malval/@name = 'slurp'">
+                <xsl:sequence select="core:makeMALType(unparsed-text($args/value/malval/lvalue/malval[1]/@value), 'string')"/>
+            </xsl:when>
+            <xsl:when test="$func/malval/@name = 'atom?'">
+                <xsl:sequence select="core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'atom') then 'true' else 'false')"/>
+            </xsl:when>
             <xsl:otherwise>
               <xsl:message terminate="yes">Invalid function <xsl:sequence select="$func"/> </xsl:message>
             </xsl:otherwise>
index a45212b..2bba6dd 100644 (file)
@@ -2,17 +2,22 @@
 <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions"  xmlns:xs="http://www.w3.org/2001/XMLSchema"  xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:env="ENV">
     <!-- since I can not, for the life of me, figure out how to (de-)serialise maps from/to xml, we're gonna be storing the env as a json string -->
     
+    <xsl:function name="env:noReplEnv">
+        <xsl:param name="env"/>
+        <xsl:sequence select="map {'outer': $env('outer'), 'isReplEnv': false(), 'data': $env('data') }"/>
+    </xsl:function>
+
     <xsl:function name="env:set">
         <xsl:param name="env"/>
         <xsl:param name="name"/>
         <xsl:param name="value"/>
-        <xsl:sequence select="map { 'outer': $env('outer'), 'data': map:put(map:merge($env('data')), $name, $value => serialize(map{})) }"/>
+        <xsl:sequence select="if ($env('isReplEnv')) then map { 'outer': $env('outer'), 'replEnv': env:set($env('replEnv'), $name, $value), 'isReplEnv': true(), 'data': $env('data') } else map { 'outer': $env('outer'), 'replEnv': $env('replEnv'), 'isReplEnv': false(), 'data': map:put($env('data'), $name, $value => serialize(map{})) }"/>
     </xsl:function>
 
     <xsl:function name="env:find">
         <xsl:param name="env"/>
         <xsl:param name="name"/>
-        <xsl:sequence select="if (empty($env)) then () else if (map:contains($env('data'), $name)) then $env else env:find($env('outer'), $name)"/>
+        <xsl:sequence select="if (empty($env)) then () else if (map:contains($env('data'), $name)) then $env else (env:find($env('outer'), $name), env:find($env('replEnv'), $name))[1]"/>
     </xsl:function>
 
     <xsl:function name="env:get">
         <xsl:variable name="minus"><malval kind="function" name="-"></malval></xsl:variable>
         <xsl:variable name="mult"><malval kind="function" name="*"></malval></xsl:variable>
         <xsl:variable name="div"><malval kind="function" name="/"></malval></xsl:variable>
-        <xsl:sequence select="env:serialise(env:set(env:set(env:set(env:set(map{'outer':(), 'data':map{}}, '+', $plus), '-', $minus), '*', $mult), '/', $div))"/>
+        <xsl:sequence select="env:serialise(env:set(env:set(env:set(env:set(map{'outer':(), 'data':map{}, 'isReplEnv': false()}, '+', $plus), '-', $minus), '*', $mult), '/', $div))"/>
+    </xsl:function>
+
+    <xsl:function name="env:swap-replEnv">
+        <xsl:param name="env"></xsl:param>
+        <xsl:param name="toRepl"></xsl:param>
+        <xsl:sequence select="if (not(empty($toRepl))) then map:put($env, 'replEnv', $toRepl) else $env"/>
+    </xsl:function>
+
+    <xsl:function name="env:replEnv">
+        <xsl:param name="env"/>
+        <xsl:sequence select="$env('replEnv')"/>
+    </xsl:function>
+
+    <xsl:function name="env:toReplEnv">
+        <xsl:param name="env"/>
+        <xsl:variable name='renv' select="map{'outer':(), 'data': map{}, 'replEnv': map{'data':env:dump($env), 'outer':(), 'isReplEnv': false()}, 'isReplEnv': true()}"/>
+        <xsl:sequence select="$renv"/>
     </xsl:function>
 
     <xsl:function name="env:empty">
-        <xsl:sequence select="map{'outer':(), 'data':map{}}"/>
+        <xsl:sequence select="map{'outer':(), 'data':map{}, 'isReplEnv': false()}"/>
     </xsl:function>
 
     <xsl:function name="env:serialise">
@@ -44,7 +66,7 @@
 
     <xsl:function name="env:close">
         <xsl:param name="env"/>
-        <xsl:sequence select="map {'outer': $env, 'data': map{}}"/>
+        <xsl:sequence select="map{'outer': env:noReplEnv($env), 'data': map{}, 'isReplEnv': false(), 'replEnv': $env('replEnv')}"/>
     </xsl:function>
 
     <xsl:function name="env:close-with-binds">
@@ -52,7 +74,7 @@
         <xsl:param name="binds" />
         <xsl:param name="exprs" />
 
-        <xsl:variable name="new-env" select="map {'outer': $env, 'data': map{}}"/>
+        <xsl:variable name="new-env" select="map {'outer': env:noReplEnv($env), 'replEnv': $env('replEnv'), 'data': map{}, 'isReplEnv': false()}"/>
         <xsl:sequence select="$new-env => env:bind-all($binds, $exprs)"/>
     </xsl:function>
     
 
         <xsl:variable name="env-items" select="env:dump($env)"></xsl:variable>
         <xsl:variable name="second-items" select="env:dump($second)"></xsl:variable>
-        <xsl:variable name="new-env" select="map {'outer': (), 'data': map:merge(($env-items, $second-items))}"></xsl:variable>
+        <xsl:variable name="new-env" select="if (empty($env)) then $second else if (empty($second)) then $env else map {'outer': $env('outer'), 'data': map:merge(($env-items, $second-items)), 'isReplEnv': false(), 'replEnv': env:merge($second('replEnv'), $env('replEnv'))}"></xsl:variable>
         <xsl:sequence select="$new-env"/> 
     </xsl:function>
 
     <xsl:function name="env:hier">
         <xsl:param name="env"/>
         <xsl:param name="over"/>
-        <xsl:sequence select="map{'outer': env:merge($over, $env), 'data': map{}}"/>
+        <xsl:variable name="newEnv" select="env:merge($over, $env)"></xsl:variable>
+        <xsl:sequence select="map{'outer': env:noReplEnv($newEnv), 'data': map{}, 'isReplEnv': false(), 'replEnv': $newEnv('replEnv')}"/>
     </xsl:function>
 
     <xsl:function name="env:bind-all">
index cdda24e..d2c7427 100644 (file)
@@ -4,8 +4,18 @@ import sys
 import xml.etree.ElementTree as ET
 
 fname = sys.argv[1]
+args = sys.argv[2:]
 tree = ET.Element('mal')
 ET.SubElement(tree, 'stdin')
+
+if len(args) > 0:
+    args0 = args[0]
+    ET.SubElement(tree, 'argv')
+    for a in tree.iter('mal'):
+        for a in a.iter('argv'):
+            for arg in args[1:]:
+                ET.SubElement(a, 'arg').text = arg
+
 tree = ET.ElementTree(tree)
 
 try:
@@ -13,34 +23,47 @@ try:
 except:
     pass
 
-while True:
-    try:
-        x = input('user> ')
-    except EOFError:
-        break
-    except KeyboardInterrupt:
-        break
-
-    for a in tree.iter('mal'):
-        for a in a.iter('stdin'):
-            a.text = x
+def transform(do_print=True):
+    global tree
 
     tree.write('xslt_input.xml')
     if os.system(f'saxon -xsl:"{fname}" -s:xslt_input.xml > xslt_output.xml 2> xsl_error.xml'):
         with open('xsl_error.xml', 'r') as f:
             print(f.readlines()[0])
-        continue
+        return
     else:
         with open('xsl_error.xml', 'r') as f:
             print(f.read(), end='')
 
     tree = ET.parse('xslt_output.xml')
-    stdout = ''
+    if do_print:
+        stdout = ''
+        for a in tree.iter('mal'):
+            for a in a.iter('stdout'):
+                stdout = a
+        print(stdout.text)
+        stdout.clear()
+        del stdout
+
+
+if len(args) > 0:
     for a in tree.iter('mal'):
-        for a in a.iter('stdout'):
-            stdout = a
-    print(stdout.text)
-    stdout.clear()
-    del stdout
+        for a in tree.iter('stdin'):
+            a.text = f'(load-file "{args0}")'
+    transform(False)
+else:
+    while True:
+        try:
+            x = input('user> ')
+        except EOFError:
+            break
+        except KeyboardInterrupt:
+            break
+
+        for a in tree.iter('mal'):
+            for a in a.iter('stdin'):
+                a.text = x
+        
+        transform()
 
-readline.write_history_file('.xslt_mal_history')
+    readline.write_history_file('.xslt_mal_history')
index 67606aa..10472de 100644 (file)
@@ -6,7 +6,8 @@
         <xsl:param name="readably" as="xs:boolean" />
 
         <xsl:variable name="value">
-            <xsl:copy-of select="value/malval" />
+            <xsl:sequence select="value/malval" />
+            <xsl:sequence select="atoms"/>
         </xsl:variable> 
         <xsl:for-each select="$value">
             <xsl:choose>
                     <xsl:value-of select="concat('#', $lt, 'function', $gt)" />
                 </value>
             </xsl:when>
+            <xsl:when test="malval/@kind = 'atom'">
+                <xsl:variable name="val" select="malval"></xsl:variable>
+                <xsl:variable name="inner">
+                  <xsl:variable name="ctx">
+                    <value>
+                        <xsl:sequence select="atoms/atom[@identity = $val/@value]/malval"/>
+                    </value>
+                  </xsl:variable>
+                  <xsl:for-each select="$ctx">
+                    <xsl:call-template name="malprinter-pr_str"><xsl:with-param name="readably" select="$readably"/></xsl:call-template>
+                  </xsl:for-each>
+                </xsl:variable>
+                <value>
+                    <xsl:value-of select="concat('(atom ', $inner/value, ')')" />
+                </value>
+            </xsl:when>
             <xsl:otherwise>
                 <value>Unknown <xsl:copy-of select="." /></value>
             </xsl:otherwise>
index 33247b8..9569824 100644 (file)
@@ -74,7 +74,7 @@
                                     <token type="error" text="EOF while reading string or invalid escape in string"></token>
                                 </xsl:when>
                                 <xsl:when test="ends-with($match, '&quot;')">
-                                    <token type="string" text="{fn:process-string(replace($match, '&quot;(.*)&quot;', '$1'))}"> </token>
+                                    <token type="string" text="{fn:process-string(replace($match, '&quot;((.|\s)*)&quot;', '$1'))}"> </token>
                                 </xsl:when>
                                 <xsl:otherwise>
                                     <token type="error" text="EOF while reading string"></token>
diff --git a/xslt/step6_file.xslt b/xslt/step6_file.xslt
new file mode 100644 (file)
index 0000000..9518e64
--- /dev/null
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Step 6: File -->
+<!-- input document must be in the following format -->
+<!--
+<mal>
+    <stdin>...stdin text...</stdin>
+    <stdout> ... ignored, omitted ... </stdout>
+    <state> contains env and atoms </state>
+</mal>
+-->
+<xsl:stylesheet
+    version="3.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:env="ENV" xmlns:core="CORE" xmlns:xs="http://www.w3.org/2001/XMLSchema"  xmlns:map="http://www.w3.org/2005/xpath-functions/map" exclude-result-prefixes="fn xs map env core">
+    <xsl:import href="reader.xslt" />
+    <xsl:import href="printer.xslt" />
+    <xsl:import href="env.xslt" />
+    <xsl:import href="core.xslt" />
+    
+    <xsl:output method='xml' encoding='utf-8' indent='yes'/>
+    <xsl:template match="mal" name="rep">
+      <xsl:choose>
+        <xsl:when test="string(state/env/@data) = ''">
+          <xsl:variable name="argv">
+            <malval kind="list">
+              <lvalue>
+                <xsl:for-each select="argv/arg/text()">
+                  <malval kind="string" value="{.}" />
+                </xsl:for-each>
+              </lvalue>
+            </malval>
+          </xsl:variable>
+          <xsl:variable name="vstate">
+            <mal>
+              <state>
+                <env data="{env:serialise(env:empty() => env:bind-all(core:ns()/@name, core:ns()) => env:set('*ARGV*', $argv) => env:toReplEnv())}"></env>
+                <atoms />
+              </state>
+              <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)"))))))</stdin>
+            </mal>
+          </xsl:variable>
+          <xsl:variable name="new-state">
+            <xsl:for-each select="$vstate/mal">
+              <xsl:call-template name="rep"></xsl:call-template>
+            </xsl:for-each>
+          </xsl:variable>
+          <xsl:variable name="state-v">
+            <xsl:sequence select="$new-state/mal/state"/>
+            <xsl:sequence select="stdin"/>
+          </xsl:variable>
+          <xsl:for-each select="$state-v">
+            <xsl:call-template name="rep"></xsl:call-template>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:otherwise>
+          <mal>
+            <xsl:variable name="env" as="map(*)">
+              <xsl:sequence select="env:deserialise(state/env/@data)"/>
+            </xsl:variable>
+            <xsl:sequence select="stdin"/>
+            <xsl:variable name="_read">
+              <xsl:call-template name="READ" />
+            </xsl:variable>
+            <xsl:variable name="_eval">
+              <xsl:for-each select="$_read">
+                <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+              </xsl:for-each>
+            </xsl:variable>
+            <xsl:for-each select="$_eval">
+              <stdout>
+                <xsl:variable name="data">
+                  <xsl:sequence select="data/value"/>
+                  <xsl:sequence select="atoms"/>
+                </xsl:variable>
+                <xsl:for-each select="$data">
+                  <xsl:call-template name="PRINT"></xsl:call-template>
+                </xsl:for-each>
+              </stdout>
+              <state>
+                <env data="{env/@data}"/>
+                <xsl:sequence select="atoms"/>
+              </state>
+            </xsl:for-each>
+          </mal>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:template>
+
+    <xsl:template name="PRINT">
+      <xsl:variable name="str">
+        <xsl:call-template name="malprinter-pr_str"><xsl:with-param name="readably" select="true()"/></xsl:call-template>
+      </xsl:variable>
+      <xsl:value-of select="$str" />
+    </xsl:template>
+
+    <xsl:template name="eval_ast">
+      <xsl:param name="env" />
+      <xsl:choose>
+        <xsl:when test="value/malval/@kind = 'symbol'">
+          <xsl:variable name="val">
+            <xsl:sequence select="env:get($env, value/malval/@value)" />
+          </xsl:variable>
+          <value>
+            <xsl:sequence select="$val"/>
+          </value>
+        </xsl:when>
+        <xsl:when test="value/malval/@kind = 'list'">
+          <value>
+            <malval kind="list">
+              <lvalue>
+                <xsl:for-each select="value/malval/lvalue/malval">
+                  <xsl:variable name="ctx">
+                    <value>
+                      <xsl:sequence select="."/>
+                    </value>
+                  </xsl:variable>
+                  <xsl:variable name="xctx">
+                    <xsl:for-each select="$ctx">
+                      <xsl:variable name="val">
+                        <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                      </xsl:variable>
+                      <xsl:sequence select="$val/data/value"/>
+                    </xsl:for-each>
+                  </xsl:variable>
+                  <xsl:sequence select="$xctx/value/malval"/>
+                </xsl:for-each>
+              </lvalue>
+            </malval>
+          </value>
+        </xsl:when>
+        <xsl:when test="value/malval/@kind = 'vector'">
+          <value>
+            <malval kind="vector">
+              <lvalue>
+                <xsl:for-each select="value/malval/lvalue/malval">
+                  <xsl:variable name="ctx">
+                    <value>
+                      <xsl:sequence select="."/>
+                    </value>
+                  </xsl:variable>
+                  <xsl:variable name="xctx">
+                    <xsl:for-each select="$ctx">
+                      <xsl:variable name="val">
+                        <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                      </xsl:variable>
+                      <xsl:sequence select="$val/data/value"/>
+                    </xsl:for-each>
+                  </xsl:variable>
+                  <xsl:sequence select="$xctx/value/malval"/>
+                </xsl:for-each>
+              </lvalue>
+            </malval>
+          </value>
+        </xsl:when>
+        <xsl:when test="value/malval/@kind = 'hash'">
+          <value>
+            <malval kind="hash">
+              <lvalue>
+                <xsl:for-each select="value/malval/lvalue/malval">
+                  <xsl:variable name="ctx">
+                    <value>
+                      <xsl:sequence select="."/>
+                    </value>
+                  </xsl:variable>
+                  <xsl:variable name="xctx">
+                    <xsl:for-each select="$ctx">
+                      <xsl:variable name="val">
+                        <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                      </xsl:variable>
+                      <xsl:sequence select="$val/data/value"/>
+                    </xsl:for-each>
+                  </xsl:variable>
+                  <xsl:sequence select="$xctx/value/malval"/>
+                </xsl:for-each>
+              </lvalue>
+            </malval>
+          </value>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:sequence select="." />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:template>
+
+    <!-- uapply[env, fn, args] -->
+    <xsl:template name="uapply">
+      <xsl:param name="func" />
+      <xsl:param name="args" />
+      <xsl:param name="env" />
+
+      <xsl:variable name="nenv" select="$env => env:hier(env:deserialise($func/malval/env/@data)) => env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)" />
+      <xsl:variable name="body">
+        <value>
+          <xsl:sequence select="$func/malval/body/malval"/>
+        </value>
+        <xsl:sequence select="atoms"/>
+      </xsl:variable>
+      <xsl:variable name="result">
+        <xsl:for-each select="$body">
+          <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$nenv"/></xsl:call-template>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:sequence select="$result/data/value"/>
+      <env data="{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) => env:replEnv()))}" />
+      <xsl:sequence select="$result/atoms"/>
+    </xsl:template>
+
+    <xsl:template name="EVAL">
+      <xsl:param name="env" />
+      <xsl:variable name="atoms" select="atoms"></xsl:variable>
+      <xsl:variable name="data">
+        <xsl:choose>
+          <xsl:when test="value/malval/@kind = 'list'">
+            <xsl:choose>
+              <xsl:when test="count(value/malval/lvalue/malval) = 0">
+                <xsl:sequence select="."/>
+                <env data="{env:serialise($env)}" />
+                <xsl:sequence select="$atoms"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:choose>
+                  <xsl:when test="let $fn := value/malval/lvalue/malval[1] return $fn/@kind = 'symbol' and $fn/@value = 'def!'">
+                    <xsl:variable name="name">
+                      <xsl:value-of select="value/malval/lvalue/malval[2]/@value"/>
+                    </xsl:variable>
+                    <xsl:variable name="xvalue">
+                      <value>
+                        <xsl:sequence select="value/malval/lvalue/malval[3]"/>
+                      </value>
+                      <xsl:sequence select="$atoms"/>
+                    </xsl:variable>
+                    <xsl:variable name="value">
+                      <xsl:for-each select="$xvalue">
+                        <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:sequence select="$value/data/value"/>
+                    <env data="{env:serialise(env:set($env, $name, $value/data/value/malval))}"/>
+                    <xsl:sequence select="$value/atoms"/>
+                  </xsl:when>
+                  <xsl:when test="let $fn := value/malval/lvalue/malval[1] return $fn/@kind = 'symbol' and $fn/@value = 'let*'">
+                    <xsl:variable name="xvalue">
+                     <value>
+                       <xsl:sequence select="value/malval/lvalue/malval[3]"/>
+                     </value>
+                     <xsl:sequence select="$atoms"/>
+                    </xsl:variable>
+                    
+                    <xsl:iterate select="fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)">
+                      <xsl:param name="new_env" select="env:close($env)"/>
+                      <xsl:param name="new_atoms" select="$atoms"/>
+
+                      <xsl:on-completion>
+                      <xsl:variable name="xvalue">
+                        <xsl:sequence select="$xvalue/value"/>
+                        <xsl:sequence select="$new_atoms"/>
+                      </xsl:variable>
+                        <xsl:variable name="value">
+                          <xsl:for-each select="$xvalue">
+                            <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$new_env"/></xsl:call-template>
+                          </xsl:for-each>
+                        </xsl:variable>
+                        <xsl:sequence select="$value/data/value"></xsl:sequence>
+                        <env data="{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) => env:replEnv()))}" />
+
+                        <xsl:sequence select="$value/atoms"/>
+                      </xsl:on-completion>
+
+                      <xsl:variable name="name">
+                        <xsl:value-of select="node()[name() = 'first']/malval/@value"/>
+                      </xsl:variable>
+
+                      <xsl:variable name="xvalue">
+                        <value>
+                          <xsl:sequence select="node()[name() = 'second']/malval"/>
+                        </value>
+                        <xsl:sequence select="$new_atoms"/>
+                      </xsl:variable>
+
+                      <xsl:variable name="value">
+                        <xsl:for-each select="$xvalue">
+                          <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$new_env"/></xsl:call-template>
+                        </xsl:for-each>
+                      </xsl:variable>
+
+                      <xsl:next-iteration>
+                        <xsl:with-param name="new_env" select="env:set($new_env, $name, $value/data/value/malval)"/>
+                        <xsl:with-param name="new_atoms" select="$value/atoms"/>
+                      </xsl:next-iteration>
+                    </xsl:iterate>
+                  </xsl:when>
+                  <xsl:when test="let $fn := value/malval/lvalue/malval[1] return $fn/@kind = 'symbol' and $fn/@value = 'do'">
+                    <xsl:iterate select="value/malval/lvalue/malval[position() > 1]">
+                      <xsl:param name="new_env" select="$env"/>
+                      <xsl:param name="atoms" select="$atoms"/>
+                      <xsl:param name="previous_res" select="()"/>
+                      <xsl:on-completion>
+                        <xsl:sequence select="$previous_res"/>
+                        <env data="{env:serialise($new_env)}" />
+                        <xsl:sequence select="$atoms"/>
+                      </xsl:on-completion>
+                      <xsl:variable name="xvalue">
+                        <value>
+                          <xsl:sequence select="."/>
+                        </value>
+                        <xsl:sequence select="$atoms"/>
+                      </xsl:variable>
+                      <xsl:variable name="value">
+                        <xsl:for-each select="$xvalue">
+                          <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$new_env"/></xsl:call-template>
+                        </xsl:for-each>
+                      </xsl:variable>
+                      <xsl:next-iteration>
+                        <xsl:with-param name="new_env" select="env:deserialise($value/env/@data)"/>
+                        <xsl:with-param name="previous_res" select="$value/data/value"/>
+                        <xsl:with-param name="atoms" select="$value/atoms"/>
+                      </xsl:next-iteration>
+                    </xsl:iterate>
+                  </xsl:when>
+                  <xsl:when test="let $fn := value/malval/lvalue/malval[1] return $fn/@kind = 'symbol' and $fn/@value = 'if'">
+                    <xsl:variable name="cond">
+                      <xsl:for-each select="value/malval/lvalue/malval[2]">
+                        <xsl:variable name="context">
+                          <value>
+                            <xsl:sequence select="."/>
+                          </value>
+                          <xsl:sequence select="$atoms"/>
+                        </xsl:variable>
+                        <xsl:for-each select="$context">
+                          <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                        </xsl:for-each>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:variable name="ptrue">
+                      <xsl:for-each select="value/malval/lvalue/malval[3]">
+                        <value>
+                          <xsl:sequence select="."/>
+                        </value>
+                        <xsl:sequence select="$cond/atoms"/>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:variable name="pfalse">
+                      <xsl:for-each select="value/malval/lvalue/malval[4]">
+                        <value>
+                          <xsl:sequence select="."/>
+                        </value>
+                        <xsl:sequence select="$cond/atoms"/>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:variable name="xfalse">
+                      <xsl:choose>
+                        <xsl:when test="empty($pfalse/value)">
+                          <value>
+                            <malval kind='nil' />
+                          </value>
+                          <xsl:sequence select="$cond/atoms"/>
+                        </xsl:when>
+                        <xsl:otherwise>
+                          <xsl:sequence select="$pfalse/value"/>
+                          <xsl:sequence select="$pfalse/atoms"/>
+                        </xsl:otherwise>
+                      </xsl:choose>
+                    </xsl:variable>
+                    <xsl:variable name="res">
+                      <xsl:choose>
+                        <xsl:when test="let $kind := $cond/data/value/malval/@kind return $kind = 'nil' or $kind = 'false'">
+                          <xsl:for-each select="$xfalse">
+                            <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                          </xsl:for-each>
+                        </xsl:when>
+                        <xsl:otherwise>
+                          <xsl:for-each select="$ptrue">
+                            <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                          </xsl:for-each>
+                        </xsl:otherwise>
+                      </xsl:choose>
+                    </xsl:variable>
+                    <xsl:sequence select="$res/data/value"/>
+                    <env data="{env:serialise($env)}" />
+                    <xsl:sequence select="$res/atoms"/>
+                  </xsl:when>
+                  <xsl:when test="let $fn := value/malval/lvalue/malval[1] return $fn/@kind = 'symbol' and $fn/@value = 'fn*'">
+                    <value>
+                      <malval kind="userfunction">
+                        <binds>
+                         <xsl:sequence select="value/malval/lvalue/malval[2]/lvalue/malval" />
+                        </binds>
+                        <body>
+                          <xsl:sequence select="value/malval/lvalue/malval[3]"/>
+                        </body>
+                        <env data="{env:serialise(env:noReplEnv($env))}"/> <!-- capture current env -->
+                      </malval>
+                    </value>
+                    <env data="{env:serialise($env)}" />
+                    <xsl:sequence select="$atoms"/>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:variable name="new_list">
+                      <xsl:call-template name="eval_ast"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                    </xsl:variable>
+                    <xsl:variable name="func">
+                      <xsl:for-each select="$new_list">
+                        <xsl:sequence select="value/malval/lvalue/malval[1]"/>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:variable name="args">
+                      <xsl:for-each select="$new_list">
+                        <value>
+                          <malval kind="list">
+                            <lvalue>
+                              <xsl:for-each select="value/malval/lvalue/node()[position() != 1]">
+                                <xsl:sequence select="."/>
+                              </xsl:for-each>
+                            </lvalue>
+                          </malval>
+                        </value>
+                        <xsl:sequence select="$atoms"/>
+                      </xsl:for-each>
+                    </xsl:variable>
+                    <xsl:variable name="resultv">
+                      <xsl:choose>
+                        <xsl:when test="$func/malval/@kind = 'userfunction'">
+                          <xsl:call-template name="uapply"><xsl:with-param name="env" select="$env"/><xsl:with-param name="func" select="$func"/><xsl:with-param name="args" select="$args"/></xsl:call-template>
+                        </xsl:when>
+                        <xsl:otherwise>
+                          <xsl:choose>
+                            <xsl:when test="$func/malval/@name = 'env??'"> <!-- needs access to env -->
+                              <xsl:variable name="nev" select="env:dump($env)"></xsl:variable>
+                              <xsl:variable name="value">
+                                <malval kind="string" value="{$env => serialize(map{'method':'json'})}"/>
+                              </xsl:variable>
+                              <value>
+                                <xsl:sequence select="$value"/>
+                              </value>
+                              <env data="{env:serialise($env)}"/>
+                              <xsl:sequence select="$value/atoms"/>
+                            </xsl:when>
+                            <xsl:when test="$func/malval/@name = 'eval'"> <!-- needs access to env -->
+                              <xsl:variable name="venv" select="env:replEnv($env)"></xsl:variable>
+                              <xsl:variable name="form">
+                                <value>
+                                  <xsl:sequence select="$args/value/malval/lvalue/malval[1]"/>
+                                </value>
+                                <xsl:sequence select="$atoms"/>
+                              </xsl:variable>
+                              <xsl:variable name="value">
+                                <xsl:for-each select="$form">
+                                  <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$venv"/></xsl:call-template>
+                                </xsl:for-each>
+                              </xsl:variable>
+                              <xsl:sequence select="$value/data/value"/>
+                              <env data="{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data)))}"/>
+                              <xsl:sequence select="$value/atoms"/>
+                            </xsl:when>
+                            <xsl:when test="$func/malval/@name = 'atom'"> <!-- needs access to atoms -->
+                              <xsl:variable name="atom-ident" select="current-dateTime()"></xsl:variable>
+                              <value>
+                                <malval kind="atom" value="{$atom-ident}"/>
+                              </value>
+                              <atoms>
+                                <xsl:for-each select="$atoms/atom">
+                                  <xsl:sequence select="."/>
+                                </xsl:for-each>
+                                <atom identity="{$atom-ident}">
+                                  <xsl:sequence select="$args/value/malval/lvalue/malval[1]"/>
+                                </atom>
+                              </atoms>
+                            </xsl:when>
+                            <xsl:when test="$func/malval/@name = 'deref'"> <!-- needs access to atoms -->
+                              <value>
+                                <xsl:sequence select="$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval"/>
+                              </value>
+                              <xsl:sequence select="$atoms"/>
+                            </xsl:when>
+                            <xsl:when test="$func/malval/@name = 'reset!'"> <!-- needs access to atoms -->
+                              <xsl:variable name="atom-ident" select="$args/value/malval/lvalue/malval[1]/@value"></xsl:variable>
+                              <xsl:variable name="newv" select="$args/value/malval/lvalue/malval[2]"></xsl:variable>
+                              <value>
+                                <xsl:sequence select="$newv"/>
+                              </value>
+                              <atoms>
+                                <xsl:for-each select="$atoms/atom[@identity != $atom-ident]">
+                                  <xsl:sequence select="."/>
+                                </xsl:for-each>
+                                <atom identity="{$atom-ident}">
+                                  <xsl:sequence select="$newv"/>
+                                </atom>
+                              </atoms>
+                            </xsl:when>
+                            <xsl:when test="$func/malval/@name = 'swap!'"> <!-- needs access to atoms -->
+                              <xsl:variable name="atom-ident" select="$args/value/malval/lvalue/malval[1]/@value"></xsl:variable>
+                              <xsl:variable name="atom-value" select="$atoms/atom[@identity = $atom-ident]/malval"></xsl:variable>
+                              <xsl:variable name="fn" select="$args/value/malval/lvalue/malval[2]"></xsl:variable>
+                              <xsl:variable name="newlist">
+                                <value>
+                                  <malval kind="list">
+                                    <lvalue>
+                                      <xsl:sequence select="$fn"/>
+                                      <xsl:sequence select="$atom-value"/>
+                                      <xsl:sequence select="$args/value/malval/lvalue/malval[position() > 2]"/>
+                                    </lvalue>
+                                  </malval>
+                                </value>
+                                <xsl:sequence select="$atoms"/>
+                              </xsl:variable>
+                              <xsl:variable name="newv">
+                                <xsl:for-each select="$newlist">
+                                  <xsl:call-template name="EVAL"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+                                </xsl:for-each>
+                              </xsl:variable>
+                              <xsl:sequence select="$newv/data/value"/>
+                              <atoms>
+                                <xsl:for-each select="$newv/atoms/atom[@identity != $atom-ident]">
+                                  <xsl:sequence select="."/>
+                                </xsl:for-each>
+                                <atom identity="{$atom-ident}">
+                                  <xsl:sequence select="$newv/data/value/malval"/>
+                                </atom>
+                              </atoms>
+                              <xsl:sequence select="$newv/env"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                              <xsl:call-template name="core-apply"><xsl:with-param name="func" select="$func"/><xsl:with-param name="args" select="$args"/></xsl:call-template>
+                              <xsl:sequence select="$atoms"/>
+                            </xsl:otherwise>
+                          </xsl:choose>
+                        </xsl:otherwise>
+                      </xsl:choose>
+                    </xsl:variable>
+                    <xsl:for-each select="$resultv">
+                      <xsl:choose>
+                        <xsl:when test="empty(env)">
+                          <env data="{env:serialise($env)}" />
+                        </xsl:when>
+                        <xsl:otherwise>
+                          <xsl:sequence select="env"/>
+                        </xsl:otherwise>
+                      </xsl:choose>
+                      <xsl:sequence select="atoms"/>
+                      <xsl:sequence select="value"/>
+                    </xsl:for-each>
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="eval_ast"><xsl:with-param name="env" select="$env"/></xsl:call-template>
+            <env data="{env:serialise($env)}" />
+            <xsl:sequence select="$atoms"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <data><xsl:sequence select="$data/value"/></data>
+      <env data="{$data/env/@data}" />
+      <xsl:sequence select="$data/atoms"/>
+    </xsl:template>
+
+    <xsl:template name="READ">
+      <xsl:variable name="context">
+          <str>
+              <xsl:copy-of select="stdin/text()" />
+          </str>
+      </xsl:variable>
+      <xsl:variable name="form">
+        <xsl:sequence select="state/atoms"/>
+        <xsl:for-each select="$context">
+            <xsl:call-template name="malreader-read_str"></xsl:call-template>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:for-each select="$form">
+        <xsl:if test="error">
+          <xsl:message terminate="yes">
+            <xsl:value-of select="error" />
+          </xsl:message>
+        </xsl:if>
+        <xsl:sequence select="." />
+      </xsl:for-each>
+    </xsl:template>
+
+    <xsl:function name="fn:group_consec">
+      <xsl:param name="nodes" />
+      <xsl:variable name="groups">
+        <xsl:for-each-group select="$nodes" group-by="position() mod 2">
+          <xsl:choose>
+            <xsl:when test="position() = 1">
+              <first>
+                <xsl:sequence select="current-group()"/>
+              </first>
+            </xsl:when>
+            <xsl:otherwise>
+              <second>
+                <xsl:sequence select="current-group()"/>
+              </second>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each-group>
+      </xsl:variable>
+      <xsl:iterate select="1 to count($groups/first/*)">
+        <element>
+        <xsl:variable name="idx" select="number(.)"></xsl:variable>
+          <first><xsl:sequence select="$groups/first/node()[position() = $idx]"/></first>
+          <second><xsl:sequence select="$groups/second/node()[position() = $idx]"/></second>
+        </element>
+      </xsl:iterate>
+    </xsl:function>
+</xsl:stylesheet>
index 6c060d4..65b331c 100644 (file)
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:template match="/">
-      <xsl:for-each select="tokens/token[last()]">
-        <xsl:copy-of select="."></xsl:copy-of>
-      </xsl:for-each>
+        <xsl:value-of select="env/@data" />
     </xsl:template>
-</xsl:stylesheet>
\ No newline at end of file
+</xsl:stylesheet>