wisp
 
(Arne Babenhauserheide)
2015-06-23: merge no longer wrapping blocks in begin, wisp-mode without electric stable v0.8.4

merge no longer wrapping blocks in begin, wisp-mode without electric indent and examples newbase60 and cli.

diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,15 @@
+wisp 0.8.4 (2015-06-23):
+- no longer wrap wisp blocks into begin. Fixes missing macro
+  definitions when executed as file.
+- any top-level form ends a wisp block. Required to avoid wrapping in
+  begin. In the REPL code is now executed when entering the first line
+  of the next top-level form.
+- new examples: newbase60 and cli.
+- known issue: To execute a procedure with shell-indirection, you have
+  to define a module.
+- wisp-mode: disable electric-indent-mode which reindented lines
+  wrongly when pressing enter.
+
 wisp 0.8.3 (2015-04-10):
 - add partial source-properties support: show line numbers in backtraces.
 - d20world: start of experiement to plot the results.
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 dnl run `autoreconf -i` to generate a configure script. 
 dnl Then run ./configure to generate a Makefile.
 dnl Finally run make to generate the project.
-AC_INIT([wisp], [0.8.3],
+AC_INIT([wisp], [0.8.4],
         [arne_bab@web.de])
 # Check for programs I need for my build
 AC_CANONICAL_TARGET
diff --git a/docs/srfi-from-template.html b/docs/srfi-from-template.html
--- a/docs/srfi-from-template.html
+++ b/docs/srfi-from-template.html
@@ -32,6 +32,8 @@ subscribe to the list.  You can access p
     <li>Received: <a
     href="http://srfi.schemers.org/cgi-bin/viewcvs.cgi/*checkout*/srfi/srfi-119/srfi-119.html?rev=1.1">2015/01/25</a></li>
       <li>Draft: 2015/02/03-2015/04/03</li>
+    <li>Revised: <a
+    href="http://srfi.schemers.org/cgi-bin/viewcvs.cgi/*checkout*/srfi/srfi-119/srfi-119.html?rev=1.2">2015/03/12</a></li>
 </ul>
 
 
@@ -61,24 +63,30 @@ It resolves a limitation of <a href="htt
 Wisp expressions can include any s-expressions and as such provide backwards compatibility.
 </p>
 
+<blockquote>
 <table><tr><th>wisp</th><th>s-exp</th></tr><tr><td>
-<pre><b>define</b> : <i>hello</i> who   
-         <i>format</i> #t "Hello ~A!\n"  
-                        . who   
-<i>hello</i> "Wisp"        
+<pre>
+<b>define</b> : <i>factorial</i> n
+__  <b>if</b> : <i>zero?</i> n
+____   . 1
+____   <i>*</i> n : <i>factorial</i> (- n 1)
+
+<i>display</i> : <i>factorial</i> 5 
+<i>newline</i>
 </pre>
+</blockqote>
 </td><td>
-<pre>(<b>define</b> (<i>hello</i> who)
-        (<i>format</i> #t "Hello ~A!\n"
-                          who))
-(<i>hello</i> "S-exp")
+<pre>
+(<b>define</b> (<i>factorial</i> n)
+    (<b>if</b> (<i>zero?</i> n)
+       1
+       (<i>*</i> n (<i>factorial</i> (- n 1)))))
+
+(<i>display</i> (<i>factorial</i> 5))
+(<i>newline</i>)
 </pre>
 </td></tr></table>
-
-<H1>Issues</H1>
-
-<ul>
-<li>wisp-scheme: REPL: sometimes the output of a command is only shown after typing the next non-empty line.</li></ul>
+</blockquote>
 
 <H1>Rationale</H1>
 
@@ -133,7 +141,7 @@ Wisp expressions can include any s-expre
 
 <h2>Wisp example</h2>
 
-Since an example speaks more than a hundred explanations, the following shows wisp exploiting all its features - including curly-infix from <a href="http://srfi.schemers.org/srfi-105/srfi-105.html">SRFI 105</a>:
+Since an example speaks more than a hundred explanations, the following shows wisp exploiting all its features - including compatibility with curly-infix from <a href="http://srfi.schemers.org/srfi-105/srfi-105.html">SRFI 105</a>:
 
 <blockquote>
 <pre>
@@ -750,7 +758,7 @@ DEALINGS IN THE SOFTWARE.
     <address>Editor: <a href="mailto:srfi-editors at srfi dot schemers dot org">Michael Sperber</a></address>
 <!-- Created: Tue Sep 29 19:20:08 EDT 1998 -->
 <!-- hhmts start -->
-Last modified: Tue Mar 11 21:25:26 MET 2015
+Last modified: Thu Mar 12 08:52:43 MET 2015
 <!-- hhmts end -->
   </body>
 </html>
diff --git a/examples/cli.w b/examples/cli.w
new file mode 100755
--- /dev/null
+++ b/examples/cli.w
@@ -0,0 +1,16 @@
+#!/usr/bin/env sh
+# -*- wisp -*-
+exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples cli) main)' -s "$0" "$@"
+; !#
+
+define-module : examples cli
+              . #:use-module : ice-9 match
+
+define : main args
+         match args
+           : prog ; just the program name, empty call
+             display : car args
+           else
+             display args
+         newline
+
diff --git a/examples/ensemble-estimation.w b/examples/ensemble-estimation.w
--- a/examples/ensemble-estimation.w
+++ b/examples/ensemble-estimation.w
@@ -1,4 +1,5 @@
 #!/usr/bin/env sh
+# -*- wisp -*-
 exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples ensemble-estimation) main)' -s "$0" "$@"
 ; !#
 
diff --git a/examples/newbase60.w b/examples/newbase60.w
new file mode 100755
--- /dev/null
+++ b/examples/newbase60.w
@@ -0,0 +1,149 @@
+#!/usr/bin/env sh
+# -*- wisp -*-
+exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples newbase60) main)' -s "$0" "$@"
+; !#
+
+;; Encoding and decoding numbers in New Base 60 as defined by Tantek:
+;; http://tantek.pbworks.com/NewBase60
+
+;; Based on the very elegant implementation from Kevin Marks licensed under CC0:
+;; https://github.com/indieweb/newBase60py/blob/master/newbase60.py
+
+define-module : examples newbase60
+              . #:export : integer->sxg sxg->integer date->sxg sxg->date date->sxgepochdays sxgepochdays->yeardays yeardays->sxgepochdays
+              . #:use-module : srfi srfi-1
+
+define base60letters "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz"
+define base60numbers
+       append
+         map cons (string->list base60letters) : iota : string-length base60letters
+         quote
+           : #\l . 1 ; typo lowercase l to 1
+             #\I . 1 ; typo capital I to 1
+             #\O . 0 ; typo capital O to 0
+
+define : positive-integer->sxg num
+       . "Convert a positive integer to Tanteks new base 60."
+       if : equal? 0 num
+          . "0"
+          let loop
+              : s '()
+                n num
+              if : equal? n 0
+                 list->string s
+                 loop
+                   cons (string-ref base60letters (remainder n 60)) s
+                   quotient n 60
+
+define : positive-sxg->integer string
+       . "Convert a positive new base 60 string into a positive integer."
+       let loop
+         : n 0
+           s string
+         cond 
+           : equal? "" s
+             . n
+           else
+             loop
+               + : assoc-ref base60numbers : string-ref s 0
+                 * n 60
+               string-drop s 1
+
+define : integer->sxg num
+       . "Convert an integer to Tanteks new base 60."
+       if : >= num 0
+          positive-integer->sxg num
+          string-append "-" : positive-integer->sxg : - num
+
+define : sxg->integer str
+       . "Convert a new base 60 string into an integer."
+       if : and (>= (string-length str) 1) (equal? #\- (string-ref str 0))
+          - : positive-sxg->integer : string-drop str 1
+          positive-sxg->integer str
+
+define : date->sxgepochdays year month day hour minute second
+       let 
+         : tm : gmtime 0 ; initialize 
+         set-tm:year tm : - year 1900
+         set-tm:mon tm month
+         set-tm:mday tm day
+         set-tm:hour tm hour
+         set-tm:min tm minute
+         set-tm:sec tm second
+         let* 
+           : epochseconds : car : mktime tm "+0" ; 0: UTC
+             epochdays : quotient epochseconds : * 24 60 60
+           integer->sxg epochdays
+
+define : yeardays->sxgepochdays year yeardays
+       let 
+         : tm : car : strptime "%Y %j" : string-join : map number->string : list year yeardays
+         let* 
+           : epochseconds : car : mktime tm "+0" ; 0: UTC
+             epochdays : quotient epochseconds : * 24 60 60
+           integer->sxg epochdays
+
+define : sxgepochdays->yeardays str
+       . "Turn sexagesimal days since epoch into year (YYYY) and day of year (DDD)."
+       let*
+         : epochdays : sxg->integer str
+           epochseconds : * epochdays 24 60 60
+           tm : gmtime epochseconds
+           year : + 1900 : tm:year tm
+           yeardays : tm:yday tm
+         list year (+ yeardays 1)
+
+define : date->sxg year month day hour minute second
+       . "Convert a date into new base 60 format:
+          yyyymmdd hhmmss -> YYMD-hms (can extend till 3599)
+         "
+       format #f "~A-~A" 
+           apply string-append
+             map integer->sxg
+                 list year month day
+           apply string-append 
+             map integer->sxg
+                 list hour minute second
+
+define : sxg->date str
+       . "Convert a new base 60 date into a list:
+          YYMD-hms -> (year month day hour minute second)
+         "
+       let*
+         : centeridx : string-rindex str #\- ; rindex because the year could be negative
+           getstr : lambda (s di) : string : string-ref str : + centeridx di
+         let
+           : year : substring/read-only str 0 : - centeridx 2
+             month : getstr str -2
+             day : getstr str -1
+             hour : getstr str 1
+             minute : getstr str 2
+             second : getstr str 3
+           map sxg->integer 
+             list year month day hour minute second
+
+define : main args
+       let
+         : help : lambda () : format #t "usage: ~A [integer | -d string | --datetime | --datetime year month day hour minute second | --help]\n" : list-ref args 0
+         cond
+           : or (= 1 (length args)) (member "--help" args)
+             help
+           : and (= 8 (length args)) : equal? "--datetime" : list-ref args 1
+             format #t "~A\n" : apply date->sxg : map string->number : drop args 2
+           : and (= 8 (length args)) : equal? "--sxgepochdays" : list-ref args 1
+             format #t "~A\n" : apply date->sxgepochdays : map string->number : drop args 2
+           : and (= 4 (length args)) : equal? "--sxgepochdays-from-yearday" : list-ref args 1
+             format #t "~A\n" : apply yeardays->sxgepochdays : map string->number : drop args 2
+           : and (= 2 (length args)) : equal? "--datetime" : list-ref args 1
+             let : : tm : localtime : current-time
+               format #t "~A\n" : apply date->sxg : list (+ 1900 (tm:year tm)) (+ 1 (tm:mon tm)) (tm:mday tm) (tm:hour tm) (tm:min tm) (tm:sec tm)
+           : and (= 3 (length args)) : equal? "--decode-datetime" : list-ref args 1
+             format #t "~A\n" : sxg->date : list-ref args 2
+           : and (= 3 (length args)) : equal? "--decode-sxgepochdays" : list-ref args 1
+             format #t "~A\n" : sxgepochdays->yeardays : list-ref args 2
+           : and (= 3 (length args)) : equal? "-d" : list-ref args 1
+             format #t "~A\n" : sxg->integer : list-ref args 2
+           : = 2 : length args
+             format #t "~A\n" : integer->sxg : string->number : list-ref args 1
+           else
+             help
diff --git a/examples/property.w b/examples/property.w
new file mode 100755
--- /dev/null
+++ b/examples/property.w
@@ -0,0 +1,29 @@
+#!/usr/bin/env sh
+# -*- wisp -*-
+exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples property) main)' -s "$0" "$@"
+; !#
+
+define-module : examples property
+              . #:export : main
+
+; FIXME: this does not work when called from guile, but it works when
+; first translating it to scheme and then calling the scheme file.
+
+; The following works:
+
+; guile ../wisp.scm property.w > property.scm; guile -e '(@@ (examples property) main)' -s property.scm 
+
+define y 5
+define-syntax z
+  identifier-syntax : var y
+                    : set! var val
+                      set! y : + 1 val
+
+define : main args
+         write args
+         newline
+         write z
+         newline
+         set! z 5
+         write z
+         newline
diff --git a/guildhall-packages/newbase60.scm b/guildhall-packages/newbase60.scm
new file mode 100644
--- /dev/null
+++ b/guildhall-packages/newbase60.scm
@@ -0,0 +1,151 @@
+#!/usr/bin/env sh
+# -*- scheme -*-
+exec guile -e '(@@ (examples newbase60) main)' -s "$0" "$@"
+; !#
+
+;; Encoding and decoding numbers in New Base 60 as defined by Tantek:
+;; http://tantek.pbworks.com/NewBase60
+
+;; Based on the very elegant implementation from Kevin Marks licensed under CC0:
+;; https://github.com/indieweb/newBase60py/blob/master/newbase60.py
+
+(define-module (examples newbase60)
+              #:export (integer->sxg sxg->integer date->sxg sxg->date date->sxgepochdays sxgepochdays->yeardays yeardays->sxgepochdays)
+              #:use-module (srfi srfi-1))
+
+(define base60letters "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz")
+(define base60numbers
+       (append
+         (map cons (string->list base60letters) (iota (string-length base60letters)))
+         (quote
+           ((#\l . 1 ); typo lowercase l to 1
+             (#\I . 1 ); typo capital I to 1
+             (#\O . 0 ))))); typo capital O to 0
+
+(define (positive-integer->sxg num)
+       "Convert a positive integer to Tanteks new base 60."
+       (if (equal? 0 num)
+          "0"
+          (let loop
+              ((s '())
+                (n num))
+              (if (equal? n 0)
+                 (list->string s)
+                 (loop
+                   (cons (string-ref base60letters (remainder n 60)) s)
+                   (quotient n 60))))))
+
+(define (positive-sxg->integer string)
+       "Convert a positive new base 60 string into a positive integer."
+       (let loop
+         ((n 0)
+           (s string))
+         (cond 
+           ((equal? "" s)
+             n)
+           (else
+             (loop
+               (+ (assoc-ref base60numbers (string-ref s 0))
+                 (* n 60))
+               (string-drop s 1))))))
+
+(define (integer->sxg num)
+       "Convert an integer to Tanteks new base 60."
+       (if (>= num 0)
+          (positive-integer->sxg num)
+          (string-append "-" (positive-integer->sxg (- num)))))
+
+(define (sxg->integer str)
+       "Convert a new base 60 string into an integer."
+       (if (and (>= (string-length str) 1) (equal? #\- (string-ref str 0)))
+          (- (positive-sxg->integer (string-drop str 1)))
+          (positive-sxg->integer str)))
+
+(define (date->sxgepochdays year month day hour minute second)
+       (let 
+         ((tm (gmtime 0 ))); initialize 
+         (set-tm:year tm (- year 1900))
+         (set-tm:mon tm month)
+         (set-tm:mday tm day)
+         (set-tm:hour tm hour)
+         (set-tm:min tm minute)
+         (set-tm:sec tm second)
+         (let* 
+           ((epochseconds (car (mktime tm "+0" ))); 0: UTC
+             (epochdays (quotient epochseconds (* 24 60 60))))
+           (integer->sxg epochdays))))
+
+(define (yeardays->sxgepochdays year yeardays)
+       (let 
+         ((tm (car (strptime "%Y %j" (string-join (map number->string (list year yeardays)))))))
+         (let* 
+           ((epochseconds (car (mktime tm "+0" ))); 0: UTC
+             (epochdays (quotient epochseconds (* 24 60 60))))
+           (integer->sxg epochdays))))
+
+(define (sxgepochdays->yeardays str)
+       "Turn sexagesimal days since epoch into year (YYYY) and day of year (DDD)."
+       (let*
+         ((epochdays (sxg->integer str))
+           (epochseconds (* epochdays 24 60 60))
+           (tm (gmtime epochseconds))
+           (year (+ 1900 (tm:year tm)))
+           (yeardays (tm:yday tm)))
+         (list year (+ yeardays 1))))
+
+(define (date->sxg year month day hour minute second)
+       "Convert a date into new base 60 format:
+          yyyymmdd hhmmss -> YYMD-hms (can extend till 3599)
+         "
+       (format #f "~A-~A" 
+           (apply string-append
+             (map integer->sxg
+                 (list year month day)))
+           (apply string-append 
+             (map integer->sxg
+                 (list hour minute second)))))
+
+(define (sxg->date str)
+       "Convert a new base 60 date into a list:
+          YYMD-hms -> (year month day hour minute second)
+         "
+       (let*
+         ((centeridx (string-rindex str #\- )); rindex because the year could be negative
+           (getstr (lambda (s di) (string (string-ref str (+ centeridx di))))))
+         (let
+           ((year (substring/read-only str 0 (- centeridx 2)))
+             (month (getstr str -2))
+             (day (getstr str -1))
+             (hour (getstr str 1))
+             (minute (getstr str 2))
+             (second (getstr str 3)))
+           (map sxg->integer 
+             (list year month day hour minute second)))))
+
+(define (main args)
+       (let
+         ((help (lambda () (format #t "usage: ~A [integer | -d string | --datetime | --datetime year month day hour minute second | --help]\n" (list-ref args 0)))))
+         (cond
+           ((or (= 1 (length args)) (member "--help" args))
+             (help))
+           ((and (= 8 (length args)) (equal? "--datetime" (list-ref args 1)))
+             (format #t "~A\n" (apply date->sxg (map string->number (drop args 2)))))
+           ((and (= 8 (length args)) (equal? "--sxgepochdays" (list-ref args 1)))
+             (format #t "~A\n" (apply date->sxgepochdays (map string->number (drop args 2)))))
+           ((and (= 4 (length args)) (equal? "--sxgepochdays-from-yearday" (list-ref args 1)))
+             (format #t "~A\n" (apply yeardays->sxgepochdays (map string->number (drop args 2)))))
+           ((and (= 2 (length args)) (equal? "--datetime" (list-ref args 1)))
+             (let ((tm (localtime (current-time))))
+               (format #t "~A\n" (apply date->sxg (list (+ 1900 (tm:year tm)) (+ 1 (tm:mon tm)) (tm:mday tm) (tm:hour tm) (tm:min tm) (tm:sec tm))))))
+           ((and (= 3 (length args)) (equal? "--decode-datetime" (list-ref args 1)))
+             (format #t "~A\n" (sxg->date (list-ref args 2))))
+           ((and (= 3 (length args)) (equal? "--decode-sxgepochdays" (list-ref args 1)))
+             (format #t "~A\n" (sxgepochdays->yeardays (list-ref args 2))))
+           ((and (= 3 (length args)) (equal? "-d" (list-ref args 1)))
+             (format #t "~A\n" (sxg->integer (list-ref args 2))))
+           ((= 2 (length args))
+             (format #t "~A\n" (integer->sxg (string->number (list-ref args 1)))))
+           (else
+             (help)))))
+
+
diff --git a/guildhall-packages/pkg-list.scm b/guildhall-packages/pkg-list.scm
new file mode 100644
--- /dev/null
+++ b/guildhall-packages/pkg-list.scm
@@ -0,0 +1,6 @@
+(package (newbase60 (0))
+         (synopsis "Implementation of Tanteks New Base 60")
+         (libraries
+          (scm -> "newbase60"))
+         (programs
+          (("newbase60.scm") -> "newbase60")))
diff --git a/wisp-mode.el b/wisp-mode.el
--- a/wisp-mode.el
+++ b/wisp-mode.el
@@ -49,6 +49,10 @@
 ; use this mode automatically
 ;;;###autoload
 (add-to-list 'auto-mode-alist '("\\.w\\'" . wisp-mode))
+;;;###autoload
+(add-hook 'wisp-mode-hook
+          (lambda ()
+            (electric-indent-local-mode -1)))
 
 ; see http://www.emacswiki.org/emacs/DerivedMode
 
diff --git a/wisp-reader.w b/wisp-reader.w
--- a/wisp-reader.w
+++ b/wisp-reader.w
@@ -14,6 +14,8 @@ define-module : language wisp spec
   . #:use-module : wisp-scheme
   . #:use-module : system base compile
   . #:use-module : system base language
+  . #:use-module : language scheme compile-tree-il
+  . #:use-module : language scheme decompile-tree-il
   . #:export : wisp
 
 ; Set locale to something which supports unicode. Required to avoid using fluids.
@@ -23,64 +25,26 @@ setlocale LC_ALL ""
 ;;; Language definition
 ;;;
 
-define : compile-scheme x e opts
-       values x e e
-
-define : decompile-scheme x e opts
-       values x e
-
-define wisp-pending-port : make-object-property
-
-; Code thanks to Mark Weaver
-; define : read-one-wisp-sexp port env
-;   define : read-wisp-chunk
-;     if : eof-object? : peek-char port
-;       read-char port ; return eof: we’re done
-;       let : : s : wisp2lisp : wisp-chunkreader port
-;         set! : wisp-pending-port port
-;                open-input-string s
-;         try-pending
-;   define : try-pending
-;     let : : pending-port : wisp-pending-port port
-;       if pending-port
-;           let : : x : read pending-port
-;              if : eof-object? x
-;                 read-wisp-chunk
-;                 . x
-;           read-wisp-chunk
-;   try-pending
-
-
 define wisp-pending-sexps : list
 
 define : read-one-wisp-sexp port env
-  define : wisp-scheme-read-chunk-env
-           cond 
-              : eof-object? : peek-char port
-                read-char port ; return eof: we’re done
-              else
-                set! wisp-pending-sexps
-                     append wisp-pending-sexps : wisp-scheme-read-chunk port
-                try-pending
-  define : try-pending
-    if : null? wisp-pending-sexps
-         wisp-scheme-read-chunk-env
-         let
-            : sexp : car wisp-pending-sexps
-              pending wisp-pending-sexps
-            set! wisp-pending-sexps : list ; : cdr wisp-pending-sexps
-            ; write : cons 'begin pending
-            if : = 1 : length pending
-                 car pending
-                 cons 'begin pending
-  try-pending
+         cond
+            : eof-object? : peek-char port
+              read-char port ; return eof: we’re done
+            else
+              let : : chunk : wisp-scheme-read-chunk port
+                cond
+                  : not : null? chunk
+                    car chunk
+                  else
+                    . #f
 
 define-language wisp
   . #:title "Wisp Scheme Syntax. See SRFI-119 for details. THIS IS EXPERIMENTAL, USE AT YOUR OWN RISK"
   ; . #:reader read-one-wisp-sexp
   . #:reader : lambda (port env) : let ((x (read-one-wisp-sexp port env))) x
-  . #:compilers `((scheme . ,compile-scheme))
-  . #:decompilers `((scheme . ,decompile-scheme))
+  . #:compilers `((tree-il . ,compile-tree-il))
+  . #:decompilers `((tree-il . ,decompile-tree-il))
   . #:evaluator : lambda (x module) : primitive-eval x
   . #:printer write ; TODO: backtransform to wisp? Use source-properties?
   . #:make-default-environment
diff --git a/wisp-scheme.w b/wisp-scheme.w
--- a/wisp-scheme.w
+++ b/wisp-scheme.w
@@ -1,4 +1,5 @@
 #!/bin/bash
+# -*- wisp -*-
 exec guile -L . --language=wisp -s "$0" "$@"
 ; !#
 
@@ -209,16 +210,20 @@ define : wisp-scheme-read-chunk-lines po
              currentindent 0
              currentsymbols '()
              emptylines 0
-           if : <= 2 emptylines ; the chunk end has to be checked
-                                ; before we look for new chars in the
-                                ; port to make execution in the REPL
-                                ; after two empty lines work
-                                ; (otherwise it shows one more line).
+           cond
+            : >= emptylines 2 ; the chunk end has to be checked
+                              ; before we look for new chars in the
+                              ; port to make execution in the REPL
+                              ; after two empty lines work
+                              ; (otherwise it shows one more line).
              . indent-and-symbols
+            else
              let : : next-char : peek-char port
                cond
                  : eof-object? next-char
                    append indent-and-symbols : list : append (list currentindent) currentsymbols
+                 : and inindent (zero? currentindent) (not incomment) (not (null? indent-and-symbols)) (not inunderscoreindent) (not (or (equal? #\space next-char) (equal? #\newline next-char) (equal? (string-ref ";" 0) next-char)))
+                  append indent-and-symbols ; top-level form ends chunk
                  : and inindent : equal? #\space next-char
                    read-char port ; remove char
                    loop