(Arne Babenhauserheide)
2014-09-02: release 0.6.2 - mainly an actually working wisp implementation for stable v0.6.2 release 0.6.2 - mainly an actually working wisp implementation for scheme (only) which uses the guile reader. This should be actually correct for scheme. And match-magic ☺
diff --git a/.bugs/bugs b/.bugs/bugs
--- a/.bugs/bugs
+++ b/.bugs/bugs
@@ -1,3 +1,4 @@
+testsuite: to pass, the tree-il has to match, not the emitted string. This allows for reader-only implementations. | owner:, open:True, id:00b74a730bbf076e73166e817ca7b0a273b376d4, time:1408224636.42
fails when I add stuff at the end of end of example.w | owner:, open:False, id:08c68e1ce0c9798184c01806d2661a3220bff3cd, time:1363789693.79
wisp-mode in quoted lists only the first item is colorized as data, but all words up to the last paren should be colorized. | owner:, open:True, id:1675ca3f894ed8470fa292149a476a2fa0d17140, time:1397196957.45
add a testsuite for wisp parsers. | owner:, open:False, id:1c05d27ac916e1a823b8985a094947907c3c19af, time:1379064922.74
@@ -19,6 +20,7 @@ the repl does not require 3 returns when
wisp-guile.w breaks on ";" and complex brackets with bracket char literals. See wisp-guile.w::91 | owner:, open:False, id:9d8b6f87fa5365733fc8655614dbf2a9ba5bd054, time:1377533321.27
FIX regression: empty line with only : does not remove the :. It transforms to (:, it should transform to ( | owner:, open:False, id:a2323d347612425bc5af577c939916c8b60ec1c9, time:1389631450.78
wisp-mode: handle lines starting with underscores: currently sees the underscores as function call. | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:b2c3755e1deb8355655a334d569679e2e62d2836, time:1376612093.55
+parens in comments can throw off the parser. | owner:, open:True, id:ce28d6c0d1f9894c9b946e56b17934473800edfe, time:1408224406.79
make this work: let : : origfile ( open-file : nth 1 : command-line ) r | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:d6de2074a5017f1f29f34d142ce797981ed270a0, time:1366529287.67
wisp.py breaks on \ - quote, escaped backslash, quote. Ignored, because wisp.py is only needed for bootstrapping. | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:d75a93ca58ade5b3e3e51f1e7ee9782e743ac131, time:1377424552.02
comments containing a closing parenthesis can break the parser. | owner:, open:False, id:d9147504868960e5fbc2648474d48ce5c9bd1a02, time:1374838747.22
diff --git a/.bugs/details/ce28d6c0d1f9894c9b946e56b17934473800edfe.txt b/.bugs/details/ce28d6c0d1f9894c9b946e56b17934473800edfe.txt
new file mode 100644
--- /dev/null
+++ b/.bugs/details/ce28d6c0d1f9894c9b946e56b17934473800edfe.txt
@@ -0,0 +1,26 @@
+# Lines starting with '#' and sections without content
+# are not displayed by a call to 'details'
+#
+[paths]
+# Paths related to this bug.
+# suggested format: REPO_PATH:LINENUMBERS
+see tests/strangecomments.w tests/strangecomments.scm
+
+[details]
+# Additional details
+
+
+[expected]
+# The expected result
+
+
+[actual]
+# What happened instead
+
+
+[reproduce]
+# Reproduction steps
+
+
+[comments]
+# Comments and updates - leave your name
diff --git a/bootstrap.sh b/bootstrap.sh
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -9,7 +9,7 @@ fi
# Bootstrap wisp-guile with wisp.py
if [[ x"$2" == x"" ]]; then
- guile="guile"
+ guile='guile'
else
guile="$2"
fi
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.6.1],
+AC_INIT([wisp], [0.6.2],
[arne_bab@web.de])
# Check for programs I need for my build
AC_CANONICAL_TARGET
diff --git a/examples/d20world.w b/examples/d20world.w
--- a/examples/d20world.w
+++ b/examples/d20world.w
@@ -187,6 +187,27 @@ define : d20-advect world advection-dire
define φ : * (/ 1 2) : 1+ : sqrt 5
+define : latlon2cellidx lat lon
+ . "Convert a position given as latitude and longitude into the correct cell index."
+ ; cell 1 (index 0) is on top, cell 20 at the bottom. The right
+ ; border of cell 2 is situated at longitude 0. With that, the
+ ; left corner of cell 19 is at longitude 180. Top and bottom
+ ; are point-symmetric. We can cleanly divide the upper part of
+ ; the icosaeder into 3 regions by longitude. Let's do that.
+ let*
+ : upper : > lat 0
+ ; we start by switching to a symmetric longitude
+ slon : if upper lon : + lon 180
+ ; the sector number is defined by the uppermost triangle
+ ; in it.
+ sector : if (< slon 120) 4 (if (< slon 270) 3 2)
+ ; we start by calculating the fraction inside the sector
+ lonsectorfraction : modulo slon 120
+ ; we can further subdivide the sector by longitude into two subsectors
+ subseclon : if (< lon 60) lon (-120 lon)
+ ; TODO find some more symmetry or start nontrivial geometry.
+ . #t
+
display : d20-as-text world
newline
diff --git a/tests/strangecomments.scm b/tests/strangecomments.scm
new file mode 100644
--- /dev/null
+++ b/tests/strangecomments.scm
@@ -0,0 +1,19 @@
+; works
+(display
+ (call-with-input-string " foo ; bar\n ; nop \n\n; nup\n; nup \n \n\n\n foo : moo \"\n\" \n___ . goo . hoo" wisp-scheme-read-chunk))
+(newline)
+(display
+ (call-with-input-string " foo \n___. goo . hoo" wisp-scheme-read-chunk))
+(newline)
+
+; broken
+; expected:
+; ((2 (foo)) (2) (0) (0) (2 foo : moo
+; ) (4 #{.}# [goo #{.}# hoo]))
+(display
+ (call-with-input-string " (foo) ; bar\n ; nop \n\n; nup\n; nup \n \n\n\n foo : moo \"\n\" \n___ . [goo . hoo]" wisp-scheme-read-chunk))
+(newline)
+(display
+ (call-with-input-string " foo \n___. [goo . hoo]" wisp-scheme-read-chunk))
+(newline)
+
diff --git a/tests/strangecomments.w b/tests/strangecomments.w
new file mode 100644
--- /dev/null
+++ b/tests/strangecomments.w
@@ -0,0 +1,18 @@
+; works
+display
+ call-with-input-string " foo ; bar\n ; nop \n\n; nup\n; nup \n \n\n\n foo : moo \"\n\" \n___ . goo . hoo" wisp-scheme-read-chunk
+newline
+display
+ call-with-input-string " foo \n___. goo . hoo" wisp-scheme-read-chunk
+newline
+
+; broken
+; expected:
+; ((2 (foo)) (2) (0) (0) (2 foo : moo
+; ) (4 #{.}# [goo #{.}# hoo]))
+display
+ call-with-input-string " (foo) ; bar\n ; nop \n\n; nup\n; nup \n \n\n\n foo : moo \"\n\" \n___ . [goo . hoo]" wisp-scheme-read-chunk
+newline
+display
+ call-with-input-string " foo \n___. [goo . hoo]" wisp-scheme-read-chunk
+newline
diff --git a/wisp-guile.w b/wisp-guile.w
--- a/wisp-guile.w
+++ b/wisp-guile.w
@@ -69,8 +69,8 @@ Ends with three consecutive linebreaks o
not
or : eof-object? nextchar
and
- or (char=? nextchar #\linefeed ) (char=? nextchar #\newline )
- or (char=? lastchar #\linefeed ) (char=? lastchar #\newline )
+ or (char=? nextchar #\newline ) (char=? nextchar #\return )
+ or (char=? lastchar #\newline ) (char=? lastchar #\return )
string-suffix? "\n\n" text ; text includes lastchar
; incommentfirstchar is only valid for exactly one char
when incommentfirstchar : set! incommentfirstchar #f
@@ -90,7 +90,7 @@ Ends with three consecutive linebreaks o
when : >= incharform 2
if : or (char=? nextchar #\space) (char=?
- nextchar #\linefeed ) (char=? nextchar #\newline )
+ nextchar #\newline ) (char=? nextchar #\return )
begin
; format #t "2: set incharform 0: lastchar ~a nextchar ~a instring ~a incomment ~a incharform ~a" lastchar nextchar instring incomment incharform
; newline
@@ -113,8 +113,8 @@ Ends with three consecutive linebreaks o
; not : equal? #f : string-match "\\([^\\]\\)+\\(\\\\\\\\\\)*[\\]$" text ; matches [^\](\\)*\$ - non-backslash + arbitrary number of pairs of backslashes + final backslash which undoes the escaping from the lastchar (by actually escaping the lastchar)
endsinunevenbackslashes text
char=? lastchar #\space ; when the last char was a space, I can get into a string
- char=? lastchar #\linefeed ; same for newline chars
- char=? lastchar #\newline
+ char=? lastchar #\newline ; same for newline chars
+ char=? lastchar #\return
and : not instring ; outside of strings, brackets are pseudo-whitespace, too
or
char=? lastchar #\(
@@ -141,8 +141,8 @@ Ends with three consecutive linebreaks o
when
and incomment
or
+ char=? nextchar #\return
char=? nextchar #\newline
- char=? nextchar #\linefeed
set! incomment #f
; check for the beginning of a charform
@@ -176,11 +176,11 @@ Ends with three consecutive linebreaks o
when : or (equal? "}" (string nextchar)) (equal? ")" (string nextchar)) (equal? "]" (string nextchar))
set! inbrackets : - inbrackets 1
if : or instring : > inbrackets 0
- if : char=? nextchar #\linefeed
+ if : char=? nextchar #\newline
; we have to actually construct the escape
; sequence here to be able to parse ourselves.
set! text : string-append text : string-append "\\LINE_" "BREAK_N"
- if : char=? nextchar #\newline
+ if : char=? nextchar #\return
set! text : string-append text : string-append "\\LINE_" "BREAK_R"
; else
set! text : string-append text : string nextchar
@@ -207,7 +207,7 @@ define : splitlines inport
nextchar : read-char inport
nextline ""
while : not : eof-object? nextchar
- if : not : or (char=? nextchar #\newline ) (char=? nextchar #\linefeed )
+ if : not : or (char=? nextchar #\return ) (char=? nextchar #\newline )
set! nextline : string-append nextline : string nextchar
begin
set! lines : append lines (list nextline)
@@ -239,7 +239,7 @@ define : line-only-colon? line
equal? ":" : string-trim-right : line-content line
define : line-only-prefix? line prefix
- . "Check whether the line content consists only of a colon and whitespace."
+ . "Check whether the line content consists only of a given prefix and whitespace."
equal? prefix : string-trim-right : line-content line
define : line-merge-comment line
@@ -490,7 +490,7 @@ Also unescape \\: to :.
linebracketizer instring inbrackets bracketstoadd
. (string-append (string-drop-right unprocessed 5) "#,@,")
. processed
- : . else ; just go on
+ else ; just go on
linebracketizer instring inbrackets bracketstoadd
. (string-drop-right unprocessed 1)
. (string-append lastletter processed)
@@ -746,9 +746,9 @@ define : unescape-linebreaks text
. "unescape linebreaks"
string-replace-substring
; we have to construct the placeholders here to avoid unescaping them when we parse ourselves…
- string-replace-substring text (string-append "\\LINE_" "BREAK_N") : string #\linefeed
+ string-replace-substring text (string-append "\\LINE_" "BREAK_N") : string #\newline
string-append "\\LINE_" "BREAK_R"
- string #\newline
+ string #\return
define : unescape-comments text
diff --git a/wisp-scheme.w b/wisp-scheme.w
new file mode 100755
--- /dev/null
+++ b/wisp-scheme.w
@@ -0,0 +1,570 @@
+#!/home/arne/wisp/wisp-multiline.sh
+; !#
+
+;; Scheme-only implementation of a wisp-preprocessor which output a
+;; scheme Tree IL to feed to a scheme interpreter instead of a
+;; preprocessed file.
+
+;; Plan:
+;; read reads the first expression from a string. It ignores comments,
+;; so we have to treat these specially. Our wisp-reader only needs to
+;; worry about whitespace.
+;;
+;; So we can skip all the string and bracket linebreak escaping and
+;; directly create a list of codelines with indentation. For this we
+;; then simply reuse the appropriate function from the generic wisp
+;; preprocessor.
+
+
+define-module : wisp-scheme
+ . #:export (wisp-scheme-read-chunk wisp-scheme-read-all
+ wisp-scheme-read-file-chunk wisp-scheme-read-file
+ wisp-scheme-read-string)
+
+use-modules
+ srfi srfi-1
+ srfi srfi-11 ; for let-values
+ ice-9 rw ; for write-string/partial
+
+;; Helper functions for the indent-and-symbols data structure: '((indent token token ...) ...)
+define : line-indent line
+ car line
+
+define : line-real-indent line
+ . "Get the indentation without the comment-marker for unindented lines (-1 is treated as 0)."
+ let : : indent : line-indent line
+ if : = -1 indent
+ . 0
+ . indent
+
+define : line-code line
+ cdr line
+
+; literal values I need
+define readcolon
+ call-with-input-string ":" read
+define readdot
+ call-with-input-string "." read
+
+define : line-continues? line
+ equal? readdot : car : line-code line
+
+define : line-only-colon? line
+ and
+ equal? ":" : car : line-code line
+ null? : cdr : line-code line
+
+define : line-empty-code? line
+ null? : line-code line
+
+define : line-empty? line
+ and
+ ; if indent is -1, we stripped a comment, so the line was not really empty.
+ = 0 : line-indent line
+ line-empty-code? line
+
+define : line-strip-continuation line
+ if : line-continues? line
+ append
+ list
+ line-indent line
+ cdr : line-code line
+ . line
+
+define : line-strip-indentation-marker line
+ ' "Strip the indentation markers from the beginning of the line"
+ cdr line
+
+define : indent-level-reduction indentation-levels level select-fun
+ . "Reduce the INDENTATION-LEVELS to the given LEVEL and return the value selected by SELECT-FUN"
+ let loop
+ : newlevels indentation-levels
+ diff 0
+ cond
+ : = level : car newlevels
+ select-fun : list diff indentation-levels
+ : < level : car newlevels
+ loop
+ cdr newlevels
+ 1+ diff
+ else
+ throw 'wisp-syntax-error "Level ~A not found in the indentation-levels ~A."
+
+define : indent-level-difference indentation-levels level
+ . "Find how many indentation levels need to be popped off to find the given level."
+ indent-level-reduction indentation-levels level
+ lambda : x ; get the count
+ car x
+
+define : indent-reduce-to-level indentation-levels level
+ . "Find how many indentation levels need to be popped off to find the given level."
+ indent-level-reduction indentation-levels level
+ lambda : x ; get the levels
+ car : cdr x
+
+
+define : wisp-scheme-read-chunk-lines port
+ let loop
+ : indent-and-symbols : list ; '((5 "(foobar)" "\"yobble\"")(3 "#t"))
+ inindent #t
+ inunderscoreindent : equal? #\_ : peek-char port
+ incomment #f
+ currentindent 0
+ currentsymbols '()
+ emptylines 0
+ let : : next-char : peek-char port
+ cond
+ : eof-object? next-char
+ append indent-and-symbols : list : append (list currentindent) currentsymbols
+ : <= 2 emptylines
+ . indent-and-symbols
+ : and inindent : equal? #\space next-char
+ read-char port ; remove char
+ loop
+ . indent-and-symbols
+ . #t ; inindent
+ . #f ; inunderscoreindent
+ . #f ; incomment
+ 1+ currentindent
+ . currentsymbols
+ . emptylines
+ : and inunderscoreindent : equal? #\_ next-char
+ read-char port ; remove char
+ loop
+ . indent-and-symbols
+ . #t ; inindent
+ . #t ; inunderscoreindent
+ . #f ; incomment
+ 1+ currentindent
+ . currentsymbols
+ . emptylines
+ ; any char but whitespace *after* underscoreindent is
+ ; an error. This is stricter than the current wisp
+ ; syntax definition. TODO: Fix the definition. Better
+ ; start too strict.
+ : and inunderscoreindent : not : equal? #\space next-char
+ throw 'wisp-syntax-error "initial underscores without following whitespace at beginning of the line after" : last indent-and-symbols
+ : or (equal? #\newline next-char) (equal? #\return next-char)
+ read-char port ; remove the newline
+ ; TODO: Check whether when or if should be preferred here. guile 1.8 only has if.
+ if : and (equal? #\newline next-char) : equal? #\return : peek-char port
+ read-char port ; remove a full \n\r. Damn special cases...
+ let* ; distinguish pure whitespace lines and lines
+ ; with comment by giving the former zero
+ ; indent. Lines with a comment at zero indent
+ ; get indent -1 for the same reason - meaning
+ ; not actually empty.
+ :
+ indent
+ cond
+ incomment
+ if : = 0 currentindent ; specialcase
+ . -1
+ . currentindent
+ : not : null? currentsymbols ; pure whitespace
+ . currentindent
+ else
+ . 0
+ parsedline : append (list indent) currentsymbols
+ ; TODO: If the line is empty. Either do it here and do not add it, just
+ ; increment the empty line counter, or strip it later. Replace indent
+ ; -1 by indent 0 afterwards.
+ loop
+ append indent-and-symbols : list parsedline
+ . #t ; inindent
+ equal? #\_ : peek-char port
+ . #f ; incomment
+ . 0
+ . '()
+ if : line-empty? parsedline
+ 1+ emptylines
+ . 0
+ : equal? #t incomment
+ read-char port ; remove one comment character
+ loop
+ . indent-and-symbols
+ . #f ; inindent
+ . #f ; inunderscoreindent
+ . #t ; incomment
+ . currentindent
+ . currentsymbols
+ . emptylines
+ : or (equal? #\space next-char) (equal? #\tab next-char) ; remove whitespace when not in indent
+ read-char port ; remove char
+ loop
+ . indent-and-symbols
+ . #f ; inindent
+ . #f ; inunderscoreindent
+ . #f ; incomment
+ . currentindent
+ . currentsymbols
+ . emptylines
+ ; | cludge to appease the former wisp parser
+ ; | which had a prblem with the literal comment
+ ; v char.
+ : equal? (string-ref ";" 0) next-char
+ loop
+ . indent-and-symbols
+ . #f ; inindent
+ . #f ; inunderscoreindent
+ . #t ; incomment
+ . currentindent
+ . currentsymbols
+ . emptylines
+ else ; use the reader
+ loop
+ . indent-and-symbols
+ . #f ; inindent
+ . #f ; inunderscoreindent
+ . #f ; incomment
+ . currentindent
+ ; this also takes care of the hashbang and leading comments.
+ ; TODO: If used from Guile, activate curly infix via read-options.
+ append currentsymbols : list : read port
+ . emptylines
+
+define : line-append-n-parens n line
+ . "Append N parens at the end of the line"
+ let loop : (rest n) (l line)
+ cond
+ : = 0 rest
+ . l
+ else
+ loop (1- rest) (append l '(")"))
+
+define : line-prepend-n-parens n line
+ . "Prepend N parens at the beginning of the line, but after the indentation-marker"
+ let loop : (rest n) (l line)
+ cond
+ : = 0 rest
+ . l
+ else
+ loop
+ 1- rest
+ append
+ list : car l
+ . '("(")
+ cdr l
+
+
+define : line-code-replace-inline-colons line
+ ' "Replace inline colons by opening parens which close at the end of the line"
+ ; format #t "replace inline colons for line ~A\n" line
+ let loop
+ : processed '()
+ unprocessed line
+ cond
+ : null? unprocessed
+ ; format #t "inline-colons processed line: ~A\n" processed
+ . processed
+ : equal? readcolon : car unprocessed
+ loop
+ ; FIXME: This should turn unprocessed into a list.
+ append processed
+ list : loop '() (cdr unprocessed)
+ . '()
+ else
+ loop
+ append processed
+ list : car unprocessed
+ cdr unprocessed
+
+define : line-replace-inline-colons line
+ cons
+ line-indent line
+ line-code-replace-inline-colons : line-code line
+
+define : line-strip-lone-colon line
+ . "A line consisting only of a colon is just a marked indentation level. We need to kill the colon before replacing inline colons."
+ if
+ equal?
+ line-code line
+ list readcolon
+ list : line-indent line
+ . line
+
+define : line-finalize line
+ . "Process all wisp-specific information in a line and strip it"
+ line-code-replace-inline-colons
+ line-strip-indentation-marker
+ line-strip-lone-colon
+ line-strip-continuation line
+
+
+define : wisp-scheme-indentation-to-parens lines
+ . "Add parentheses to lines and remove the indentation markers"
+ ; FIXME: Find new algorithm which mostly uses current-line
+ ; and the indentation-levels for tracking. The try I have in
+ ; here right now is wrong.
+ when
+ and
+ not : null? lines
+ not : line-empty-code? : car lines
+ not : = 0 : line-real-indent : car lines ; -1 is a line with a comment
+ throw 'wisp-syntax-error
+ format #f "The first symbol in a chunk must start at zero indentation. Indentation and line: ~A"
+ car lines
+ let loop
+ : processed '()
+ unprocessed lines
+ indentation-levels '(0)
+ let*
+ :
+ current-line
+ if : <= 1 : length unprocessed
+ car unprocessed
+ list 0 ; empty code
+ next-line
+ if : <= 2 : length unprocessed
+ car : cdr unprocessed
+ list 0 ; empty code
+ current-indentation
+ car indentation-levels
+ current-line-indentation : line-real-indent current-line
+ ; format #t "processed: ~A\ncurrent-line: ~A\nnext-line: ~A\nunprocessed: ~A\nindentation-levels: ~A\ncurrent-indentation: ~A\n\n"
+ ; . processed current-line next-line unprocessed indentation-levels current-indentation
+ cond
+ ; the real end: this is reported to the outside world.
+ : and (null? unprocessed) (not (null? indentation-levels)) (null? (cdr indentation-levels))
+ ; display "done\n"
+ ; reverse the processed lines, because I use cons.
+ . processed
+ ; the recursion end-condition
+ : and (null? unprocessed)
+ ; display "last step\n"
+ ; this is the last step. Nothing more to do except
+ ; for rolling up the indentation levels. return the
+ ; new processed and unprocessed lists: this is a
+ ; side-recursion
+ values processed unprocessed
+ : null? indentation-levels
+ ; display "indentation-levels null\n"
+ throw 'wisp-programming-error "The indentation-levels are null but the current-line is null: Something killed the indentation-levels."
+ else ; now we come to the line-comparisons and indentation-counting.
+ cond
+ : line-empty-code? current-line
+ ; display "current-line empty\n"
+ ; We cannot process indentation without
+ ; code. Just switch to the next line. This should
+ ; only happen at the start of the recursion.
+ ; TODO: Somehow preserve the line-numbers.
+ loop
+ . processed
+ cdr unprocessed
+ . indentation-levels
+ : and (line-empty-code? next-line) : <= 2 : length unprocessed
+ ; display "next-line empty\n"
+ ; TODO: Somehow preserve the line-numbers.
+ ; take out the next-line from unprocessed.
+ loop
+ . processed
+ cons current-line
+ cdr : cdr unprocessed
+ . indentation-levels
+ : > current-indentation current-line-indentation
+ ; display "current-indent > next-line\n"
+ ; this just steps back one level via the side-recursion.
+ values processed unprocessed
+ : = current-indentation current-line-indentation
+ ; display "current-indent = next-line\n"
+ let
+ : line : line-finalize current-line
+ next-line-indentation : line-real-indent next-line
+ cond
+ : >= current-line-indentation next-line-indentation
+ ; simple recursiive step to the next line
+ ; display "current-line-indent >= next-line-indent\n"
+ loop
+ append processed
+ if : line-continues? current-line
+ . line
+ list line
+ cdr unprocessed ; recursion here
+ . indentation-levels
+ : < current-line-indentation next-line-indentation
+ ; display "current-line-indent < next-line-indent\n"
+ ; format #t "line: ~A\n" line
+ ; side-recursion via a sublist
+ let-values
+ :
+ : sub-processed sub-unprocessed
+ loop
+ . line
+ cdr unprocessed ; recursion here
+ . indentation-levels
+ ; format #t "side-recursion:\n sub-processed: ~A\n processed: ~A\n\n" sub-processed processed
+ loop
+ append processed : list sub-processed
+ . sub-unprocessed ; simply use the recursion from the sub-recursion
+ . indentation-levels
+ : < current-indentation current-line-indentation
+ ; display "current-indent < next-line\n"
+ loop
+ . processed
+ . unprocessed
+ cons ; recursion via the indentation-levels
+ . current-line-indentation
+ . indentation-levels
+ else
+ throw 'wisp-not-implemented
+ format #f "Need to implement further line comparison: current: ~A, next: ~A, processed: ~A."
+ . current-line next-line processed
+
+
+define : wisp-scheme-replace-inline-colons lines
+ ' "Replace inline colons by opening parens which close at the end of the line"
+ let loop
+ : processed '()
+ unprocessed lines
+ if : null? unprocessed
+ . processed
+ loop
+ append processed : list : line-replace-inline-colons : car unprocessed
+ cdr unprocessed
+
+
+define : wisp-scheme-strip-indentation-markers lines
+ ' "Strip the indentation markers from the beginning of the lines"
+ let loop
+ : processed '()
+ unprocessed lines
+ if : null? unprocessed
+ . processed
+ loop
+ append processed : cdr : car unprocessed
+ cdr unprocessed
+
+define : wisp-scheme-recreate-incomeplete-lists expressions
+ . "Turn (a #{.}# b) into the correct (a . b).
+
+read called on a single dot creates a variable named #{.}# (|.|
+in r7rs). Due to parsing the indentation before the list
+structure is known, the reader cannot create incomplete lists
+when it reads a dot. So we have to take another pass over the
+code to recreate the incomplete lists.
+
+Traverse each list and sublist backwards, and if it contains a
+readdot, cons every element in the list on the last element.
+
+TODO: Find out how I can do that, when the second element is a
+function call (a list). Problem: (cons 1 '(2)) -> '(1 2).
+
+TODO: Find out whether this would actually be legal scheme code.
+ (write (1 . (+ 1 2))) -> error
+ (write . (+ 1 2)) -> strange
+ (write (list 1 . (+ 1 2))) -> (1 #<procedure + (#:optional _ _ . _)> 1 2) ???
+ (list 1 . (list 2 3)) -> (1 #<procedure list _> 2 3)
+ (list . (list 2 3)) -> (#<procedure list _> 2 3) == (list list 2 3)"
+ ; FIXME: Implement recreating incomplete lists!
+ let loop
+ : processed '()
+ unprocessed-reversed expressions
+ cond
+ : null? unprocessed-reversed
+ . processed
+ : not : list? unprocessed-reversed
+ ; FIXME: This requires unlimited amounts of memory.
+ cons unprocessed-reversed processed
+ : not : member readdot unprocessed-reversed
+ cond
+ : list? : car unprocessed-reversed
+ loop
+ cons
+ loop '() : car unprocessed-reversed
+ . processed
+ . unprocessed-reversed
+ else
+ loop
+ cons (car unprocessed-reversed) processed
+ cdr unprocessed-reversed
+ else ; cons unprocessed on its tail
+ let conser
+ : proc-reversed : car unprocessed-reversed
+ unproc : cdr unprocessed-reversed
+ cond
+ : null? unproc
+ ; back to the main loop
+ loop
+ . processed
+ . proc-reversed
+ : equal? readdot : car unproc ; just skip the dot. It is why we cons.
+ conser
+ . proc-reversed
+ cdr unproc
+ else
+ conser
+ cons (car unproc) proc-reversed
+ cdr unproc
+
+define : wisp-scheme-read-chunk port
+ . "Read and parse one chunk of wisp-code"
+ let : : lines : wisp-scheme-read-chunk-lines port
+ ; display lines
+ ; newline
+ ; FIXME: incmoplete list recreation does not work yet
+ ; wisp-scheme-recreate-incomeplete-lists
+ wisp-scheme-indentation-to-parens lines
+
+define : wisp-scheme-read-all port
+ . "Read all chunks from the given port"
+ let loop
+ : tokens '()
+ cond
+ : eof-object? : peek-char port
+ ; TODO: Join as string.
+ . tokens
+ else
+ loop
+ append tokens : wisp-scheme-read-chunk port
+
+define : wisp-scheme-read-file path
+ call-with-input-file path wisp-scheme-read-all
+
+define : wisp-scheme-read-file-chunk path
+ call-with-input-file path wisp-scheme-read-chunk
+
+define : wisp-scheme-read-string str
+ call-with-input-string str wisp-scheme-read-all
+
+
+; TODO: Recreate incomplete lists.
+write
+ wisp-scheme-read-string "foo . bar"
+newline
+write
+ wisp-scheme-read-string "foo .
+ . bar"
+newline
+write
+ wisp-scheme-read-string "foo
+ . . bar"
+newline
+write
+ wisp-scheme-read-string "moo
+ foo
+ . . bar
+baz waz"
+newline
+; systax error
+write
+ wisp-scheme-read-string "foo .
+ . . bar"
+newline
+; write
+; wisp-scheme-read-string "foo ; bar\n ; nop \n\n; nup\n; nup \n \n\n\nfoo : moo \"\n\" \n___ . goo . hoo"
+; newline
+; display
+; wisp-scheme-read-string " foo ; bar\n ; nop \n\n; nup\n; nup \n \n\n\nfoo : moo"
+; newline
+; write : wisp-scheme-read-file-chunk "wisp-scheme.w"
+; newline
+; run all chunks in wisp-guile.w as parsed by wisp-scheme.w. Give wisp-guile.w to parse as argument.
+; map primitive-eval : wisp-scheme-read-file "wisp-guile.w"
+; call-with-output-file "wisp-guile.scm"
+; lambda : port
+; map
+; lambda : chunk
+; write chunk port
+; wisp-scheme-read-file "wisp-guile.w"
+; pipe the output into 1, then compare it with the output of wisp.scm. If it is equal, this parser works!
+; guile wisp.scm wisp-scheme.w > wisp-scheme.scm; guile wisp-scheme.scm wisp-guile.w > 1; guile wisp.scm wisp-guile.w > 2; diff 1 2