(Arne Babenhauserheide)
2014-07-14: merge default into stable for release: stable v0.6.0 merge default into stable for release: - Better performance - Support for SRFI-105 - Working syntax highlighting in export via wisp-mode.el - Wisp can now execute wisp-files directly via guile -L . --language=wisp file.w. - Icosahedric Advection-Diffusion example: examples/d20world.w - Draft for an SRFI - Presentatien: Why Wisp? - Working autoreconf -i; ./configure; make distcheck - Real Testsuite (./runtests.sh . .)
diff --git a/.bugs/bugs b/.bugs/bugs --- a/.bugs/bugs +++ b/.bugs/bugs @@ -1,6 +1,7 @@ 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 -wisp-mode: export wisp to html fails in org-mode: font-lock-fontify-keywords-region: Invalid regexp | owner:, open:True, id:1e46d8c05580c961c37a32d36c987a5dd1d34943, time:1389371020.39 +wisp-mode: export wisp to html fails in org-mode: font-lock-fontify-keywords-region: Invalid regexp | owner:, open:False, id:1e46d8c05580c961c37a32d36c987a5dd1d34943, time:1389371020.39 an empty line with : should start with double parens | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:2e188ddf44d36e4605030d3c58607ebfa97d189e, time:1390328674.43 wisp-in-wisp: remove the leading . for continuation lines after inferring the brackets. | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:2e42e5b64622f0cc383eb8acc3d510912e925bf0, time:1377476687.79 wisp: handle general paren prefixes like quote, quasiquote, etc. | owner:, open:False, id:323ff94b5be635742619467e1cb44f4c0d96f63f, time:1379047798.47 @@ -12,14 +13,14 @@ linebreaks in parens still break wisp-guile.w does not yet remove the leading . which signifies a continuation. | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:7075096a323933493f42a3751da5f75a45d5d699, time:1377381700.17 inline ' : should be '( | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:72d534a8b23b4cb168017f1bb7d8816f0ea170c4, time:1366497335.26 failing test tests/shebang.w | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:74a851f83af8996465a7b24d8453161beb0f0fd5, time:1379106761.57 -multiline comments (srfi-30) | owner:, open:True, id:7a57614fa920b2ddad002d044b144d0bb7c34f84, time:1389364108.01 -support nested multi-line comments with #| ... |# | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:8cf6202873d4454f57813dd17cf60432059f7c62, time:1389569421.6 +non-nested multiline comments with #! !#. Requires restructuring. | owner:, open:True, id:7a57614fa920b2ddad002d044b144d0bb7c34f84, time:1389364108.01 +support nested multi-line comments with #| ... |#: multiline comments (srfi-30). Requires restructuring. | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:8cf6202873d4454f57813dd17cf60432059f7c62, time:1389569421.6 the repl does not require 3 returns when you use a single char as function, or rather only does so every second time | owner:, open:True, id:9cedd0bdbf4a3b17add4bfe86ad5a23e500cfc6c, time:1379064870.78 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 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 | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:d75a93ca58ade5b3e3e51f1e7ee9782e743ac131, time:1377424552.02 +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 breaks on empty files | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:e40fa7a93eb2c497dca1af7eed22ad5ed5cfbe7f, time:1390325470.91 quote as only char in a line gets parenthized instead of becoming a paren-prefix. | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:eb7468387e90bb5d13f5a5d81c6f4a704f2ca0fb, time:1390326369.6 diff --git a/.bugs/details/7a57614fa920b2ddad002d044b144d0bb7c34f84.txt b/.bugs/details/7a57614fa920b2ddad002d044b144d0bb7c34f84.txt new file mode 100644 --- /dev/null +++ b/.bugs/details/7a57614fa920b2ddad002d044b144d0bb7c34f84.txt @@ -0,0 +1,29 @@ +# 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 + + +[details] +# Additional details + + +[expected] +# The expected result + + +[actual] +# What happened instead + + +[reproduce] +# Reproduction steps + + +[comments] +# Comments and updates - leave your name +This will require restructuring of the parsing to stay maintainable. Deferred for now. + +For notes how to do it right, see http://draketo.de/light/english/recursion-wins diff --git a/.bugs/details/8cf6202873d4454f57813dd17cf60432059f7c62.txt b/.bugs/details/8cf6202873d4454f57813dd17cf60432059f7c62.txt new file mode 100644 --- /dev/null +++ b/.bugs/details/8cf6202873d4454f57813dd17cf60432059f7c62.txt @@ -0,0 +1,29 @@ +# 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 + + +[details] +# Additional details + + +[expected] +# The expected result + + +[actual] +# What happened instead + + +[reproduce] +# Reproduction steps + + +[comments] +# Comments and updates - leave your name +This will require restructuring of the parsing to stay maintainable. Deferred for now. + +For notes how to do it right, see http://draketo.de/light/english/recursion-wins diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -1,3 +1,14 @@ +wisp 0.6.0 (2014-07-14): +- Better performance +- Support for SRFI-105 +- Working syntax highlighting in export via wisp-mode.el +- Wisp can now execute wisp-files directly via `guile -L . --language=wisp file.w`. +- Icosahedric Advection-Diffusion example: examples/d20world.w +- Draft for an SRFI +- Presentatien: Why Wisp? +- Working autoreconf -i; ./configure; make distcheck +- Real Testsuite (./runtests.sh . .) + wisp 0.5.3 (2014-01-08): Started to use autotools to get make distcheck. wisp 0.5.2 (2014-01-07): Support general paren-prefixes for macros. @@ -14,4 +25,4 @@ wisp 0.5 (2013-09-13): Wisp now has a RE Voilà, you have wisp at the REPL! Caveeat: the wisp-parser is still experimental and contains known bugs. Use it for testing, but please do not rely on it for important stuff, yet. -wisp 0.4 (2013-09-10): wisp-guile.w can now parse itself! Bootstrapping: The magical feeling of seeing a language (dialect) grow up to live by itself: python3 wisp.py wisp-guile.w > 1 && guile 1 wisp-guile.w > 2 && guile 2 wisp-guile.w > 3 && diff 2 3. Starting today, wisp is implemented in wisp. \ No newline at end of file +wisp 0.4 (2013-09-10): wisp-guile.w can now parse itself! Bootstrapping: The magical feeling of seeing a language (dialect) grow up to live by itself: python3 wisp.py wisp-guile.w > 1 && guile 1 wisp-guile.w > 2 && guile 2 wisp-guile.w > 3 && diff 2 3. Starting today, wisp is implemented in wisp. diff --git a/README b/README --- a/README +++ b/README @@ -33,7 +33,7 @@ Usage ----- * Get wisp: `hg clone http://draketo.de/proj/wisp` -* Bootstrap: `autoreconf -i && ./configure && make` +* Bootstrap: `cd wisp && autoreconf -i && ./configure && make` * Preprocess files: `guile ./wisp.scm infile.wisp > outfile.scm` * Wisp at the REPL: `guile -L . --language=wisp # run this in the wisp-folder` @@ -61,6 +61,12 @@ If you want to use a curly-infix express Notes ----- +Standardization: A [SRFI](srfi.html)[^srfi][^ess] is in the works. + +[^srfi]: SRFI is the abbreviation of Scheme Request for Implementation. It is the official schemisch way of suggesting new features. SRFIs are maintained at [srfi.schemers.org/](http://srfi.schemers.org/). + +[^ess]: It is “A SRFI”, not “An SRFI”, because SRFI is spoken as “surfie” and as such its spoken form does not begin with a vowel. + Copyright: 2013--2014 Arne Babenhauserheide License: GPLv3 or later 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.5.9], +AC_INIT([wisp], [0.6.0], [arne_bab@web.de]) # Check for programs I need for my build AC_CANONICAL_TARGET @@ -21,4 +21,4 @@ AS_IF([test "x$python3" = "xno"], AM_INIT_AUTOMAKE([gnu]) AM_MAINTAINER_MODE([enable]) AC_CONFIG_FILES([Makefile]) -AC_OUTPUT \ No newline at end of file +AC_OUTPUT diff --git a/docs/srfi.org b/docs/srfi.org --- a/docs/srfi.org +++ b/docs/srfi.org @@ -1,13 +1,33 @@ #+title: SRFI: wisp: whitespace-to-lisp preprocessing +#+options: toc:nil num:t #+BEGIN_ABSTRACT -This SRFI describes a simple syntax which allows making scheme easier to read for newcomers while keeping the simplicity and elegance of s-expressions. Similar to SRFI-110, SRFI-49 and Python it uses indentation to group expressions. As SRFI-110 wisp is general and homoiconic. Different from its precedessors, wisp only uses the absolute minimum of additional syntax-elements which are required for writing and exchanging arbitrary code-structures. +This SRFI describes a simple syntax which allows making scheme easier to read for newcomers while keeping the simplicity, generality and elegance of s-expressions. Similar to SRFI-110, SRFI-49 and Python it uses indentation to group expressions. Like SRFI-110 wisp is general and homoiconic. -The expressiveness of +Different from its precedessors, wisp only uses the absolute minimum of additional syntax-elements which are required for writing and exchanging arbitrary code-structures. As syntax elements it only uses a colon surrounded by whitespace, the period as first code-character on the line and underscores at the beginning of the line. -Wisp expressions can include any s-expressions +It resolves a limitation of SRFI-110 and SRFI-49, both of which force the programmer to use a single argument per line if the arguments to a function need to be continued after a function-call. + +Wisp expressions can include any s-expressions and as such provide backwards compatibility. + +#+html: <table><tr><th>wisp</th><th>s-exp</th></tr><tr><td> +#+BEGIN_SRC wisp + define : hello who + format #t "~A ~A!\n" + . "Hello" who +#+END_SRC +#+html: </td><td> +#+BEGIN_SRC scheme + (define (hello who) + (format #t "~A ~A!\n" + "Hello" who)) +#+END_SRC +#+html: </td></tr></table> + #+END_ABSTRACT +#+toc: headlines 2 + * SRFI process :noexport: 1. Authors submit a proposal by using the http://srfi.schemers.org/ web page, or sending email to srfi minus editors at srfi dot schemers dot org. @@ -58,7 +78,1122 @@ Remember, even if a proposal becomes an - Arne Babenhauserheide +** Acknowledgments + +Thanks for lots of constructive discussions goes to Alan Manuel K. Gloria and David A. Wheeler. + * Related SRFIs -SRFI-49 (Indentation-sensitive syntax) (superceded by this SRFI), SRFI-110 (Sweet-expressions (t-expressions)) (superceded by this SRFI), … (neoteric expressions) (compatible), … (curly infix) (supported) and SRFI-30 (Nested Multi-line comments) (interacts with this SRFI). +- SRFI-49 (Indentation-sensitive syntax): superceded by this SRFI, +- SRFI-110 (Sweet-expressions (t-expressions)): alternative to this SRFI, +- SRFI-105 (neoteric expressions and curly infix): supported in this SRFI by treating curly braces like brackets and parens. +- SRFI-30 (Nested Multi-line comments): complex interaction. Should be avoided at the beginning of lines, because it can make the indentation hard to distinguish for humans. SRFI-110 includes them, so there might be value in adding them. The wisp reference implementation does not treat them specially, though, which might create arbitrary complications. +* Rationale + +A big strength of Scheme and other lisp-like languages is their minimalistic syntax. By using only the most common characters like the period, the comma, the quote and quasiquote, the hash, the semicolon and the parens for the syntax[fn:1], they are very close to natural language. Along with the minimal list-structure of the code, this gives these languages a timeless elegance. + +But as SRFI-110 explains very thoroughly (which we need not repeat here), the parentheses at the beginning of lines hurt readability and scare away newcomers. Additionally using indentation to mark the structure of the code follows naturally from the observation that most programmers use indentation, with many programmers actually letting their editor indent code automatically to fit the structure. Indentation is an important way how programmers understand code and using it directly to define the structure avoids errors due to mismatches between indentation and actual meaning. + +As a solution to this, SRFI-49 and SRFI-110 provide a way to write whitespace sensitive scheme, but both have their share of problems. + +As noted in SRFI-110, there are a number of implementation-problems in SRFI-49, as well as specification shortcomings like choosing the name “group” for the construct which is necessary to represent double parentheses. Additionally to the problems named in SRFI-110, SRFI-49 is not able to continue the arguments to a function on one line, if a prior argument was a function call. The example code in the abstract would have to be written in SRFI-49 as follows: + +#+BEGIN_SRC scheme + ,* 5 + + 4 3 + 2 + 1 +#+END_SRC + +SRFI-110 improves a lot over the implementation of SRFI-49. It resolves the group-naming and tries to reduce the need to continue the argument-list by introducing 3 different grouping-syntaxes (=$=, =\\= and =<* *>=). These additional syntax-elements however hurt readability for newcomers a lot (obviously the authors of SRFI-110 disagree with this assertion. Their point is discussed in SRFI-110 in the section about wisp). The additional syntax elements lead to structures like the following: +#+BEGIN_SRC scheme +myfunction + x: \\ original-x + y: \\ calculate-y original-y +#+END_SRC + +#+BEGIN_SRC scheme + a b $ c d e $ f g +#+END_SRC + +#+BEGIN_SRC scheme + let <* x getx() \\ y gety() *> + ! {{x * x} + {y * y}} +#+END_SRC + +This is not only hard to read, but also makes it harder to work with the code, because the programmer has to learn these additional syntax elements and keep them in mind before being able to understand the code. + +Like SRFI-49 SRFI-110 also cannot continue the argument-list without resorting to single-element lines, though it reduces this problem by advertising the use of neoteric expressions (SRFI-105). + +** Advantages of Wisp + +Wisp draws on the strength of SRFI-110 but avoids its complexities. It was actually conceived and improved in the discussions within the readable-project which preceded SRFI-110 and there is a comparison between readable in wisp in SRFI-110. + +Like SRFI-110, wisp is general and homoiconic and interacts nicely with SRFI-105 (neoteric expressions and curly infix). Like SRFI-110, the expressions are the same in the REPL and in code-files. Like SRFI-110, wisp has been used for implementing multiple smaller programs, though the biggest program in wisp is still its implementation. + +But unlike SRFI-110, wisp only uses the minimum of additional syntax-elements which are necessary to support arbitrary code-structures with indentation-sensitive code which is intended to be shared over the internet. To realize these syntax-elements, it generalizes existing syntax and draws on the most common non-letter non-math characters in prose. This allows keeping the actual representation of the code elegant and inviting to newcomers. + +** Disadvantages of Wisp + +Using the colon as syntax element keeps the code very close to written prose, but it can interfere with type definitions as for example used in Typed Racket[fn:6]. This can be mitigated in let- and lambda-forms by using the parenthesized form. When doing so, wisp avoid the double-paren for type-declarations and as such makes them easier to catch by eye. For function definitions (the only =define= call where type declarations are needed in typed-racket[fn:7]), a =declare= macro directly before the =define= should work well. + +* Specification + +The specification is separated into four parts: A general overview of the syntax, a more detailed description, justifications for each added syntax element and clarifications for technical details. + +** Overview + +The basic rules for wisp-code can be defined in 4 rules, each of which emerges directly from a requirement: + +*** Wisp syntax 1/4: function calls + +Indentation: + +#+BEGIN_SRC wisp +display + + 3 4 5 +newline +#+END_SRC + +becomes + +#+BEGIN_SRC scheme +(display + (+ 3 4 5)) +(newline) +#+END_SRC + +*** Wisp syntax 2/4: Continue Argument list + +The period: + +#+BEGIN_SRC wisp ++ 5 + * 4 3 + . 2 1 +#+END_SRC + +becomes + +#+BEGIN_SRC scheme +(+ 5 + (* 4 3) + 2 1) +#+END_SRC + +This also works with just one argument after the period. To start a line without a function call, you have to prefix it with a period followed by whitespace.[fn:2] + +*** Wisp syntax 3/4: Double Parens + +The colon:[fn:3] + +#+BEGIN_SRC wisp +let + : x 1 + y 2 + z 3 + body +#+END_SRC + +becomes + +#+BEGIN_SRC scheme +(let + ((x 1) + (y 2) + (z 3)) + (body)) +#+END_SRC + +*** Wisp syntax 4/4: Resilient Indentation + +The underscore (optional): + +#+BEGIN_SRC wisp +let +_ : x 1 +__ y 2 +__ z 3 +_ body +#+END_SRC + +becomes + +#+BEGIN_SRC scheme +(let + ((x 1) + (y 2) + (z 3)) + (body)) +#+END_SRC + + +*** Summary + +The syntax shown here is the minimal syntax required for the goal of wisp: indentation-based, general lisp with a simple preprocessor, and code which can be shared easily on the internet: + +- =.= to continue the argument list +- =:= for double parens +- =_= to survive HTML + +** More detailed: Wisp syntax rules + +*** Unindented line + +*A line without indentation is a function call*, just as if it would start with a bracket. + +#+BEGIN_SRC wisp + display "Hello World!" ; (display "Hello World!") +#+END_SRC + +*** Sibling line +*A line which is more indented than the previous line is a sibling to that line*: It opens a new bracket. + +#+BEGIN_SRC wisp + display ; (display + string-append "Hello " "World!" ; (string-append "Hello " "World!")) +#+END_SRC + +*** Closing line +*A line which is not more indented than previous line(s) closes the brackets of all previous lines which have higher or equal indentation*. You should only reduce the indentation to indentation levels which were already used by parent lines, else the behaviour is undefined. + +#+BEGIN_SRC wisp + display ; (display + string-append "Hello " "World!" ; (string-append "Hello " "World!")) + display "Hello Again!" ; (display "Hello Again!") +#+END_SRC + +*** Prefixed line + +*To add any of ' , ` #' #, #` or #@, to the first bracket on a line, just prefix the line with that symbol* followed by at least one space. Implementations are free to add more prefix symbols. + +#+BEGIN_SRC wisp + ' "Hello World!" ; '("Hello World!") +#+END_SRC + + +*** Continuing line +*A line whose first non-whitespace characters is a dot followed by a space (". ") does not open a new bracket: it is treated as simple continuation of the first less indented previous line*. In the first line this means that this line does not start with a bracket and does not end with a bracket, just as if you had directly written it in lisp without the leading ". ". + +#+BEGIN_SRC wisp + string-append "Hello" ; (string-append "Hello" + string-append " " "World" ; (string-append " " "World") + . "!" ; "!") +#+END_SRC + + +*** Empty indentation level +*A line which contains only whitespace and a colon (":") defines an indentation level at the indentation of the colon*. It opens a bracket which gets closed by the next less-indented line. If you need to use a colon by itself. you can escape it as "\:". + +#+BEGIN_SRC wisp + let ; (let + : ; ( + msg "Hello World!" ; (msg "Hello World!")) + display msg ; (display msg)) +#+END_SRC + + +*** Inline Colon +*A colon sourrounded by whitespace (" : ") starts a bracket which gets closed at the end of the line*. + +#+BEGIN_SRC wisp + define : hello who ; (define (hello who) + display ; (display + string-append "Hello " who "!" ; (string-append "Hello " who "!"))) +#+END_SRC + +If the colon starts a line, it starts a bracket which gets closed at the end of the line *and* defines an indentation level at the position of the colon. + +*** Initial Underscores +*You can replace any number of consecutive initial spaces by underscores*, as long as at least one whitespace is left between the underscores and any following character. You can escape initial underscores by prefixing the first one with \ ("\___ a" → "(___ a)"), if you have to use them as function names. + +#+BEGIN_SRC wisp + define : hello who ; (define (hello who) + _ display ; (display + ___ string-append "Hello " who "!" ; (string-append "Hello " who "!"))) +#+END_SRC + +*** Parens and Strings +*Linebreaks inside parentheses and strings are not considered linebreaks* for parsing indentation. To use parentheses at the beginning of a line without getting double parens, prefix the line with a period. + +#+BEGIN_SRC wisp +define : stringy s + string-append s "can be varied as follows: +" + string-capitalize s + string-reverse s + . (string-capitalize + (string-reverse s)) + . " +" + +#+END_SRC + +** Clarifications + +- Code-blocks end after 2 empty lines followed by a newline. Indented non-empty lines after 2 empty lines should be treated as error. A line is empty if it only contains whitespace. + +- Inside parentheses wisp parsing is disabled. Consequently linebreaks inside parentheses are not considered linebreaks for wisp-parsing. For the parser everything which happens inside parentheses is considered as a black box. + +- Square brackets and curly braces should be treated the same way as parentheses: They stop the indentation processing until they are closed. + +- Likewise linebreaks inside strings are not considered linebreaks for wisp-parsing. + +- A colon (:) at the beginning of a line adds an extra open parentheses that gets closed at end-of-line (rule 4.2.7) *and* defines an indentation level. + +* Syntax justification + +/I do not like adding any unnecessary syntax element to lisp. So I want to show explicitely why the syntax elements are required./ + +#+html: <small> +See also http://draketo.de/light/english/wisp-lisp-indentation-preprocessor#sec-4 +#+html: </small> + + +** . (the dot) + +The dot at the beginning of the line as marker of the continuation of a variable list is a generalization of using the dot as identity function - which is an implementation detail in many lisps. + +`(. a)` is just `a`. + +So for the single variable case, this would not even need additional parsing: wisp could just parse ". a" to "(. a)" and produce the correct result in most lisps. But forcing programmers to always use separate lines for each parameter would be very inconvenient, so the definition of the dot at the beginning of the line is extended to mean “take every element in this line as parameter to the parent function”. + +Essentially this dot-rule means that we mark variables in the code instead of function calls, since in Lisp variables at the beginning of a line are much rarer than in other programming languages. In lisp assigning a value to a variable is a function call while it is a syntax element in many other languages, so what would be a variable at the beginning of a line in other languages is a function call in lisp.. + +** : (the colon) + +For double brackets and for some other cases we must have a way to mark indentation levels without any code. I chose the colon, because it is the most common non-alpha-numeric character in normal prose which is not already reserved as syntax by lisp when it is surrounded by whitespace, and because it already gets used for marking keyword arguments to functions in Emacs Lisp, so it does not add completely alien characters. + +The inline function call via inline " : " is a limited generalization of using the colon to mark an indentation level: If we add a syntax-element, we should use it as widely as possible to justify adding syntax overhead. + +But if you need to use : as variable or function name, you can still do so by escaping it with a backslash, so this does not forbid using the character. + +For simple cases, the colon could be replaced by clever whitespace parsing, but there are complex cases which make this impossible. A simple example is a theoretical doublelet which does not require a body:[fn:4] + +#+BEGIN_SRC scheme +(doublelet + ((foo bar)) + ((bla foo))) +#+END_SRC + +The wisp version of this is + +#+BEGIN_SRC wisp +doublelet + : + foo bar + : ; <- this double backstep is the real issue + bla foo +#+END_SRC + +or shorter with inline colon (which you can use only if you don’t need further indentation-syntax inside the assignment). + +#+BEGIN_SRC wisp +doublelet + : foo bar + : bla foo +#+END_SRC + +The need to be able to represent things like this is the real reason, why the colon exists. The inline and start-of-line use is only a generalization of that principle (we add a syntax-element, so we should see how far we can push it to reduce the effective cost of introducing the additional syntax). + +*** Clever whitespace-parsing which would not work + +There are two alternative ways to tackle this issue: deferred level-definition and fixed-width indentation. + +Defining intermediate indentation-levels by later elements (deferred definition) would be a problem, because it would create code which is really hard to understand. An example is the following: + +#+BEGIN_SRC wisp +defun flubb + + nubb + gam +#+END_SRC + +would become + +#+BEGIN_SRC scheme +(defun flubb () + ((nubb)) + (gam)) +#+END_SRC + +Fixed indentation width (alternative option to inferring it from later lines) would make it really hard to write readable code. Stuff like this would not be possible: + +#+BEGIN_SRC wisp +if + equals wrong + isright? stuff + fixstuff +#+END_SRC + +** _ (the underscore) + +In Python the whitespace hostile html already presents problems with sharing code - for example in email list archives and forums. But in Python the indentation can mostly be inferred by looking at the previous line: If that ends with a colon, the next line must be more indented (there is nothing to clearly mark reduced indentation, though). In wisp we do not have that help, so we need a way to survive in that hostile environment. + +The underscore is commonly used to denote a space in URLs, where spaces are inconvenient, but it is rarely used in lisp (where the dash ("-") is mostly used instead), so it seems like a a natural choice. + +You can still use underscores anywhere but at the beginning of the line, and even at the beginning of the line you simply need to escape it by prefixing the first underscore with a backslash ("\____"). + +* Implementation + +This reference implementation realizes a general wisp-preprocessor which can be used for any lisp-like language. It contains special syntax-constructs for scheme, though. The reference-preprocessor uses GNU Guile and can also be used at the REPL. Due to being a simple preprocessor, wisp can also be implemented as an external program which gets called on reading. It does not actually have to understand the code itself. + +A wisp-preprocessor which is specialiized for scheme should be much easier to realize, though, by using the parsing methods from an existing scheme implementation. + +Since generality of the reference implementation makes it quite heavyweight, it would be great to have someone step up and create a more lightweight scheme-specific alternative. To allow for this, the test-suite in the next chapter only contains scheme-specific snippets. + +** The generic wisp preprocessor (code) + +TODO: Include the code from http://draketo.de/proj/wisp + +** Test Suite + +The wisp test-suite consists of a large number of wisp-snippets and the corresponding scheme-code. A wisp-implementation may call itself compliant to the wisp test-suite if it successfully converts each wisp-snippet into the corresponging scheme-snippet. Blank lines at the end of the file and non-functional white-space in the produced scheme-file do not matter for this purpose. + +This test-suite is also available in the [[http://draketo.de/proj/wisp][wisp repository]] along with a script-runner (runtests.sh) which tests the reference wisp-implementation with GNU Guile against this testsuite.[fn:5] + +The test-suite included here only contains scheme-compatible code to allow for scheme-specific wisp-implementations which use existing parsing functions to simplify the code. + +/TODO: Some of the snippets were transformed from emacs lisp to scheme by hand and this might have introduced bugs. They still need to be tested again./ + +*** tests/syntax-underscore.w +#+begin_src wisp +define : a b c +_ d e +___ f +___ g h +__ . i + +define : _ +_ display "hello\n" + +\_ +#+end_src +*** tests/syntax-underscore.scm +#+begin_src scheme +(define (a b c) + (d e + (f) + (g h) + i)) + +(define (_) + (display "hello\n")) + +(_) + + +#+end_src +*** tests/syntax-strings-parens.w +#+begin_src wisp +; Test linebreaks in strings and brackets + +. "flubbub + +flabbab" + +hrug (nadda +madda gadda "shoktom + mee" " sep +ka" + hadda) + gom + +flu + +sum [foo +bar] barz {1 + [* 2 2]} + +mara { +li ++ +lo (mabba) +} +#+end_src +*** tests/syntax-strings-parens.scm +#+begin_src scheme +; Test linebreaks in strings and brackets + +"flubbub + +flabbab" + +(hrug (nadda +madda gadda "shoktom + mee" " sep +ka" + hadda) + (gom)) + +(flu) + +(sum [foo +bar] barz {1 + [* 2 2]}) + +(mara { +li ++ +lo (mabba) +}) +#+end_src +*** tests/syntax-indent.w +#+begin_src wisp +define + hello who + format #t "Hello ~A\n" who + +define + let + : + a 1 + b 2 + c 3 + format #t "a: ~A, b: ~A, c: ~A" + + a 2 + . b c + +#+end_src +*** tests/syntax-indent.scm +#+begin_src scheme +(define + (hello who) + (format #t "Hello ~A\n" who)) + +(define + (let + ( + (a 1) + (b 2) + (c 3)) + (format #t "a: ~A, b: ~A, c: ~A" + (+ a 2) + b c))) + + + +#+end_src +*** tests/syntax-empty.w +#+begin_src wisp +#+end_src +*** tests/syntax-empty.scm +#+begin_src scheme +#+end_src +*** tests/syntax-dot.w +#+begin_src wisp +define : foo + . "bar" + +define : bar + ' 1 + . . 2 ; pair + +display : foo +newline +display : bar +newline +#+end_src +*** tests/syntax-dot.scm +#+begin_src scheme +(define (foo) + "bar") + +(define (bar) + '(1 + . 2 )); pair + +(display (foo)) +(newline) +(display (bar)) +(newline) + + +#+end_src +*** tests/syntax-colon.w +#+begin_src wisp +let + : + a 1 + b 2 + let + : + : + . c 3 + format #t "a: ~A, b: ~A, c: ~A" + . a b c + +: a + +define : hello + display "hello\n" + +let + : a 1 + b 2 + format #t "a: ~A, b: ~A" + . a b + +let : : a ' : + +let + : ; foo + a + ' + +: + a + +define : \: + hello + +\: +#+end_src +*** tests/syntax-colon.scm +#+begin_src scheme +(let + ( + (a 1) + (b 2)) + (let + ( + ( + c 3)) + (format #t "a: ~A, b: ~A, c: ~A" + a b c))) + +((a)) + +(define (hello) + (display "hello\n")) + +(let + ((a 1) + (b 2)) + (format #t "a: ~A, b: ~A" + a b)) + +(let ((a '()))) + +(let + ( ; foo + (a + '()))) + +( + (a)) + +(define (:) + (hello)) + +(:) + + +#+end_src +*** tests/sublist.w +#+begin_src wisp +; sublists allow to start single line function calls with a colon ( : ). +; +define : a b c + let : : e . f + . g +#+end_src +*** tests/sublist.scm +#+begin_src scheme +; sublists allow to start single line function calls with a colon ( : ). + +(define (a b c) + (let ((e . f)) + g)) + + +#+end_src +*** tests/shebang.w +#+begin_src wisp +#!/usr/bin/wisp.py # !# +; This tests shebang lines +#+end_src +*** tests/shebang.scm +#+begin_src scheme +#!/usr/bin/wisp.py # !# +; This tests shebang lines + + +#+end_src +*** tests/readable-tests.w +#+begin_src wisp +define : fibfast n + if : < n 2 + . n + fibup n 2 1 0 + +define : fibup maxnum count n-1 n-2 + if : = maxnum count + + n-1 n-2 + fibup maxnum + + count 1 + + n-1 n-2 + . n-1 + +define : factorial n + if : <= n 1 + . 1 + * n + factorial : - n 1 + +define (gcd x y) + if (= y 0) + . x + gcd y + rem x y + +define : add-if-all-numbers lst + call/cc + lambda : exit + let loop + : + lst lst + sum 0 + if : null? lst + . sum + if : not : number? : car lst + exit #f + + : car lst + loop : cdr lst +#+end_src +*** tests/readable-tests.scm +#+begin_src scheme +(define (fibfast n) + (if (< n 2)) + n + (fibup n 2 1 0 )) + +(define (fibup maxnum count n-1 n-2) + (if (= maxnum count) + (+ n-1 n-2) + (fibup maxnum + (+ count 1 ) + (+ n-1 n-2 ) + n-1))) + +(define (factorial n) + (if (<= n 1) + 1 + (* n + (factorial (- n 1))))) + +(define (gcd x y) + (if (= y 0)) + x + (gcd y + (rem x y))) + +(define (add-if-all-numbers lst) + (call/cc + (lambda (exit) + (let loop + ( + (lst lst ) + (sum 0)) + (if (null? lst) + sum + (if (not (number? (car lst))) + (exit #f) + (+ (car lst) + (loop (cdr lst))))))))) + +#+end_src +*** tests/range.w +#+begin_src wisp +import : rnrs + +define range + case-lambda + : n ; one-argument syntax + range 0 n 1 + : n0 n ; two-argument syntax + range n0 n 1 + : n0 n s ; three-argument syntax + assert + and + for-all number? : list n0 n s + not : zero? s + let : : cmp : if (positive? s) >= <= + let loop + : i n0 + acc '() + if + cmp i n + reverse acc + loop (+ i s) (cons i acc) + +display : apply string-append "" : map number->string : range 5 +newline +#+end_src +*** tests/range.scm +#+begin_src scheme +(import (rnrs)) + +(define range + (case-lambda + ((n ); one-argument syntax + (range 0 n 1)) + ((n0 n ); two-argument syntax + (range n0 n 1)) + ((n0 n s ); three-argument syntax + (assert + (and + (for-all number? (list n0 n s)) + (not (zero? s)))) + (let ((cmp (if (positive? s) >= <= ))) + (let loop + ((i n0 ) + (acc '())) + (if + (cmp i n ) + (reverse acc) + (loop (+ i s) (cons i acc)))))))) + +(display (apply string-append "" (map number->string (range 5)))) +(newline) + +#+end_src +*** tests/quotecolon.w +#+begin_src wisp +#!/home/arne/wisp/wisp-multiline.sh +; !# +define a 1 ; test whether ' : correctly gets turned into '( +; and whether brackets in commments are treated correctly. + +define a ' : 1 2 3 + +define + a b + c +#+end_src +*** tests/quotecolon.scm +#+begin_src scheme +#!/home/arne/wisp/wisp-multiline.sh +; !# +(define a 1 ); test whether ' : correctly gets turned into '( +; and whether brackets in commments are treated correctly. + +(define a '(1 2 3)) + +(define + (a b) + (c)) + + +#+end_src +*** tests/namedlet.w +#+begin_src wisp +#!/home/arne/wisp/wisp-multiline.sh +; !# +define : hello who + display who + +let hello + : who 0 + if : = who 5 + display who + hello : + 1 who +#+end_src +*** tests/namedlet.scm +#+begin_src scheme +#!/home/arne/wisp/wisp-multiline.sh +; !# +(define (hello who) + (display who)) + +(let hello + ((who 0)) + (if (= who 5) + (display who) + (hello (+ 1 who)))) + + +#+end_src +*** tests/mtest.w +#+begin_src wisp +#!/home/arne/wisp/wisp-multiline.sh !# + +display 1 +#+end_src +*** tests/mtest.scm +#+begin_src scheme +#!/home/arne/wisp/wisp-multiline.sh !# + +(display 1) + + +#+end_src +*** tests/flexible-parameter-list.w +#+begin_src wisp +; Test using a . as first parameter on a line by prefixing it with a second . +define + a i + . . b + unless : >= i : length b + display : number->string : length b + display : list-ref b i + newline + apply a ( + i 1 ) b + + +a 0 "123" "345" "567" +#+end_src +*** tests/flexible-parameter-list.scm +#+begin_src scheme +; Test using a . as first parameter on a line by prefixing it with a second . +(define + (a i + . b) + (unless (>= i (length b)) + (display (number->string (length b ))) + (display (list-ref b i)) + (newline) + (apply a ( + i 1 ) b))) + + +(a 0 "123" "345" "567") + + +#+end_src +*** tests/factorial.w +#+begin_src wisp +;; short version +; note: once you use one inline colon, all the following forms on that +; line will get closed at the end of the line + +define : factorial n + if : zero? n + . 1 + * n : factorial : - n 1 + +display : factorial 5 + + +;; more vertical space, less colons +define : factorial n + if : zero? n + . 1 + * n + factorial + - n 1 + +display : factorial 5 + +#+end_src +*** tests/factorial.scm +#+begin_src scheme +;; short version +; note: once you use one inline colon, all the following forms on that +; line will get closed at the end of the line + +(define (factorial n) + (if (zero? n) + 1 + (* n (factorial (- n 1))))) + +(display (factorial 5 )) + + +;; more vertical space, less colons +(define (factorial n) + (if (zero? n) + 1 + (* n + (factorial + (- n 1))))) + +(display (factorial 5 )) + + + +#+end_src +*** tests/example.w +#+begin_src wisp +define (a b c) + let + : + d "i am a string +do not break me!" + : + ; comment: 0 + f +; comment : 1 + ` g ; comment " : " 2 + : + h (I am in brackets: + do not : change "me") + . i + , ' j k + + . l + +; comment + + a c + +define : b :n o + . "second defun : with a docstring!" + message "I am here" + . t + +define : c e f + : g + : + h + i + j + ' : + k + . l + . : m + +define : _ \: +__ +__ . \: + +\_ b + +define : d + let + : a b + c d + +a : : : c + +let + : a b + c + +let : : a b + +. a + +#+end_src +*** tests/example.scm +#+begin_src scheme +(define (a b c) + (let + ( + (d "i am a string +do not break me!") + ( + ; comment: 0 + (f) +; comment : 1 + `(g )); comment " : " 2 + ( + (h (I am in brackets: + do not : change "me")) + i))) + ,(' j k) + + l + +; comment + + (a c)) + +(define (b :n o) + "second defun : with a docstring!" + (message "I am here") + t) + +(define (c e f) + ((g)) + ( + (h + (i)) + (j)) + '(()) + (k) + l + (m)) + +(define (_ :) + + :) + +(_ b) + +(define (d) + (let + ((a b) + (c d)))) + +(a (((c)))) + +(let + ((a b) + (c))) + +(let ((a b))) + +a + + + +#+end_src +*** tests/continuation.w +#+begin_src wisp +a b c d e + . f g h + . i j k + +concat "I want " + getwish from me + . " - " username + +#+end_src +*** tests/continuation.scm +#+begin_src scheme +(a b c d e + f g h + i j k) + +(concat "I want " + (getwish from me) + " - " username) + + + +#+end_src +*** tests/btest.w +#+begin_src wisp +display "b" +newline +#+end_src +*** tests/btest.scm +#+begin_src scheme +(display "b") +(newline) +#+end_src + +* Copyright + + Copyright (C) Arne Babenhauserheide (2013--2014). All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +* Footnotes + +[fn:1] The most common non-letter, non-math characters in prose are =.,":'_#?!;=, in the given order as derived from newspapers and other sources (for the ngram assembling scripts, see the [[http://bitbucket.org/ArneBab/evolve-keyboard-layout][evolve keyboard layout project]]). + +[fn:2] Conceptually, continuing the argument list with a period uses syntax to mark the rare case of not calling a function as opposed to marking the common case of calling a function. To back the claim, that calling a function is actually the common case in scheme-code, grepping the the modules in the Guile source code shows over 27000 code-lines which start with a paren and only slightly above 10000 code-lines which start with a non-paren, non-comment character. Since wisp-syntax mostly follows the regular scheme indentation guidelines (as realized for example by emacs), the whitespace in front of lines does not need to change. + +[fn:3] This special syntax for double parens cannot be replaced by clever whitespace parsing, because it is required for representing two consecutive forms which start with double parentheses. The only pure-whitespace alternative would be fixed-width indentation levels. + +[fn:4] I used a double let without action as example for the colon-syntax, even though that does nothing, because that makes it impossible to use later indentation to mark an intermediate indentation-level. Another reason why I would not use later indentation to define whether something earlier is a single or double indent is that this would call for subtle and really hard to find errors: + +[fn:5] To run the tests in the wisp testsuite with a separately built GNU Guile, you can use any given guile interpreter by adjusting the following command: =PATH=~/guile-2.0.11/meta:${PATH} ./runtests.sh= + +[fn:6] Typed Racket uses calls of the form =(: x Number)= to declare types. These forms can still be used in parenthesized form, but not in wisp-form (as the colon has to be replaced with =\:=). + +[fn:7] In most cases type-declarations are not needed in typed racket, since the type can be inferred. See [[http://docs.racket-lang.org/ts-guide/more.html?q=typed#%28part._when-annotations~3f%29][When do you need type annotations?]] diff --git a/docs/why-wisp.org b/docs/why-wisp.org --- a/docs/why-wisp.org +++ b/docs/why-wisp.org @@ -26,7 +26,6 @@ /The most common paired characters/ (From letter distributions in newspapers) - * On Words #+BEGIN_SRC elisp :exports results :results results raw @@ -70,9 +69,7 @@ - /Do you see how much harder it got?/ - /€ and ¥ escape your existing filters/ - -* Summary: Current Lisp - +* On Lisp 1. Lisp syntax uses the *most common* non-letter, non-math characters. 2. The *first and last characters* are important for text-recognition. @@ -82,6 +79,12 @@ Any fix for elegance 2 should preserve e Elegance 0: generality and homoiconicity: *code is data* +* Summary: Current Lisp Syntax + +- √ *Code is data*. +- √ Uses the *most common* characters. +- *×* The *first and last characters* are always *the same*. + * On Wisp #+html: <br /> #+BEGIN_SRC scheme @@ -152,7 +155,7 @@ myfunction /This breaks elegance 1/ -/Also the problems of SRFI-49 are preserved/ +/The problems of SRFI-49 are preserved, but their impact reduced./ * Summary: Why wisp? @@ -181,7 +184,7 @@ becomes (newline) #+END_SRC -* Wisp syntax 2/4: Continue Argument list +* Wisp syntax 2/4: Continue Arguments ** The dot @@ -218,7 +221,7 @@ becomes ((x 1) (y 2) (z 3)) - body) + (body)) #+END_SRC * Wisp syntax 4/4: Resilient Indentation @@ -240,7 +243,7 @@ becomes ((x 1) (y 2) (z 3)) - body) + (body)) #+END_SRC diff --git a/examples/d20world.w b/examples/d20world.w new file mode 100755 --- /dev/null +++ b/examples/d20world.w @@ -0,0 +1,263 @@ +#!/home/arne/wisp/wisp-multiline.sh +; !# + +; A world projected on a d20 (20-sided die, ikosaeder) + +; For this we need a vector with 20 elements, a vector which shows the +; neighboring elements and accessor functions which give us the +; relevant elements for any set of longitude and latitude as well as +; its inverse (element-id to lon+lat). For further subdivisions, just +; elevate the center of each edge and connect these centers. + +; Advection: Give each field a wind direction: target fields with an +; advection fraction: The fraction of the value which will be +; transported into the other field. Basic system: Follow the numbers. + +define-module : examples d20world + . #:export : world neighbors d20-as-text d20-diffuse + +use-modules : ice-9 format + +define world : make-vector 20 0 +define neighbors : make-vector 20 +; count from the top + +; Contains the numbers instead of the indexes, to make it easier for +; me to think about them. +; +; 7 8 +; 3 4 +; 1 +; 6 2 9 +; 5 10 +; +; 14 13 +; 18 17 +; 20 +; 15 19 12 +; 16 11 +; +define neighbors-helper + ' : 1 2 3 4 + 2 1 5 10 + 3 1 6 7 + 4 1 8 9 + 5 2 6 14 + 6 3 5 15 + 7 3 8 16 + 8 4 7 11 + 9 4 10 12 + 10 1 9 13 + 20 19 18 17 + 19 20 16 11 + 18 20 15 14 + 17 20 13 12 + 16 19 17 7 + 15 18 16 6 + 14 18 13 5 + 13 17 14 10 + 12 17 11 9 + 11 19 12 8 + +let loop : : relationships neighbors-helper + cond + : null? relationships + . neighbors + else + let* + : cur : car relationships + idx : 1- : car cur + vec : cdr cur + vector-set! world idx : 1+ idx + vector-set! neighbors idx : make-vector 3 + let setidx : : idxtoset '(0 1 2) + cond + : null? idxtoset + ; the outer loop continues here + loop : cdr relationships + else + vector-set! + vector-ref neighbors idx + car idxtoset + 1- : list-ref vec : car idxtoset + setidx : cdr idxtoset + +define advection-directions + make-vector 20 + +let loop : : index 20 + cond + : = 0 index + . advection-directions + : = 20 index + vector-set! advection-directions (1- index) (1- index) + loop : 1- index + else + vector-set! advection-directions (1- index) index + loop : 1- index + +define : d20-value-ascii-color-string letter value + . "Create an ascii color string for d20." + let + : csi "[" + color : inexact->exact : max 17 : min 230 : floor : * 12 value + format #f "~A38;5;~dm~A~Am" csi color letter csi + +define : d20-value-ascii-color-string-show-values letter value + . "Create an ascii color string for d20." + let + : csi "[" + color : inexact->exact : max 17 : min 230 : floor : * 12 value + int : inexact->exact : floor : * 12 value + format #f "~A38;5;~dm~A~Am" csi color int csi + +define : d20-as-text-base world-vector function + . "show the given d20 world as text" + let + : template " + ~A ~A + ~A ~A + ~A + ~A ~A ~A + ~A ~A + + ~A ~A + ~A ~A + ~A +~A ~A ~A + ~A ~A +" + indexes ' : 7 8 3 4 1 6 2 9 5 10 14 13 18 17 20 15 19 12 16 11 + apply format : append (list #f template) : map function indexes : map (lambda (x) (vector-ref world (1- x))) indexes + +define : d20-as-text world-vector + . "show the given d20 world as text" + d20-as-text-base world-vector d20-value-ascii-color-string-show-values + +define : d20-cursor-up-text world-vector + . "Kill each line of the text of the world vector in a terminal." + let* + : text : d20-as-text-base world-vector d20-value-ascii-color-string-show-values + lines : string-split text #\newline + format #t "[~AA" : 1- : length lines + +define : d20-diffuse world neighbors D + . "Diffuse the values on the d20 using the diffusion constant D. Step 1: Simply iterative." + let leapfrog : : targets '(0 1 2) + if : null? targets + . world + let loop : : neighbors-to-diffuse : iota : vector-length neighbors + cond + : null? neighbors-to-diffuse + leapfrog : cdr targets + else + let* + : originidx : car neighbors-to-diffuse ; index in world and in neighbors + targetleap : car targets + targetidx : vector-ref (vector-ref neighbors originidx) targetleap + originval : vector-ref world originidx + targetval : vector-ref world targetidx + diff : * (/ D 3) : - targetval originval + vector-set! world originidx : + originval diff + vector-set! world targetidx : - targetval diff + loop : cdr neighbors-to-diffuse + + +define : d20-advect world advection-directions A + . "Advect the values on the d20 using the advection constant A." + let loop : : neighbors-to-advect : iota : vector-length advection-directions + cond + : null? neighbors-to-advect + . world + else + let* + : source : car neighbors-to-advect + target : vector-ref advection-directions source + source-value : vector-ref world source + target-value : vector-ref world target + change : * A source-value + source-new : - source-value change + target-new : + target-value change + ; format #t "target: ~A, source: ~A, change: ~A\n" target source change + when : not : = source target + vector-set! world source source-new + vector-set! world target target-new + loop : cdr neighbors-to-advect + + +define φ : * (/ 1 2) : 1+ : sqrt 5 + + +display : d20-as-text world +newline +format #t "Diffuse ~A\n" 0.01 +d20-diffuse world neighbors 0.01 +display : d20-as-text world +newline +format #t "Advect ~A\n" 0.1 +d20-advect world advection-directions 0.1 +display : d20-as-text world +newline +format #t "Diffuse ~A\n" 0.1 +d20-diffuse world neighbors 0.1 +display : d20-as-text world +newline +format #t "Diffuse: ~A*(~A)\n" 100 0.1 +let loop : : steps 100 + cond + : = 0 steps + . world + else + d20-diffuse world neighbors 0.1 + loop : 1- steps +display : d20-as-text world +newline +let + : number 20 + val 1 + format #t "disturb: ~A to ~A\n" number val + vector-set! world (1- number) val + display : d20-as-text world + newline +format #t "Diffuse ~A\n" 0.1 +d20-diffuse world neighbors 0.1 +display : d20-as-text world +newline + +format #t "Advect: ~A*(~A)\n" 1000 0.001 +let loop : : steps 1000 + cond + : = 0 steps + . world + else + d20-advect world advection-directions 0.001 + display : d20-as-text world + d20-cursor-up-text world + loop : 1- steps +display : d20-as-text world +newline +format #t "Diffuse: ~A*(~A)\n" 1000 0.004 +let loop : : steps 1000 + cond + : = 0 steps + . world + else + d20-diffuse world neighbors 0.004 + display : d20-as-text world + d20-cursor-up-text world + loop : 1- steps +display : d20-as-text world +newline +format #t "Diffuse+Advect: ~A*(~A+~A)\n" 1000 0.002 0.001 +let loop : : steps 1000 + cond + : = 0 steps + . world + else + d20-diffuse world neighbors 0.002 + d20-advect world advection-directions 0.001 + display : d20-as-text world + d20-cursor-up-text world + loop : 1- steps +display : d20-as-text world +newline diff --git a/examples/fizzbuzz.w b/examples/fizzbuzz.w new file mode 100755 --- /dev/null +++ b/examples/fizzbuzz.w @@ -0,0 +1,24 @@ +#!./wisp-multiline.sh +; !# + +;; this example needs foof-loop installed via guildhall! +use-modules : guildhall ext foof-loop +;; Pseudocode adapted from +;; http://en.wikipedia.org/wiki/Pseudocode#Syntax +define : divisible? number divisor + = 0 : remainder number divisor + +define : fizzbuzz + let : : print_number #f + loop : : for i : up-from 1 : to 100 + set! print_number #t + when : divisible? i 3 + display "Fizz" + set! print_number #f + when : divisible? i 5 + display "Buzz" + set! print_number #f; + when print_number : display i + newline + +fizzbuzz diff --git a/examples/kit-encode.w b/examples/kit-encode.w --- a/examples/kit-encode.w +++ b/examples/kit-encode.w @@ -93,12 +93,11 @@ Karlsruher Institut fuer Technologie " -define : kittify numbers - . "Display a list of numbers as Text in a KIT Logo." +define : kittifytologo numbers logo + . "Display a list of numbers as Text in the given text logo." let* : base60numbers : map base60encode numbers requiredletters : + (length base60numbers) : apply + : map string-length base60numbers - logo kitlogosmall charsinlogo : string-count logo #\. requiredlogos : ceiling-quotient requiredletters charsinlogo text : xsubstring logo 0 : * requiredlogos : string-length logo @@ -146,6 +145,14 @@ define : kittify numbers +define : kittify numbers + . "Display a list of numbers as Text in a KIT Logo." + kittifytologo numbers kitlogosmall + + +define : kittifylarge numbers + . "Display a list of numbers as Text in a KIT Logo." + kittifytologo numbers kitlogo ; unkittify: first take out "Karlsruher Institut fuer Technologie" and all spaces and linebreaks, then split by . and base60decode the result. @@ -224,7 +231,7 @@ If TEXT is #t, transform the numbers to bv : get-bytevector-all file numbers : bytevector->u8-list bv numbers : if text (map shiftbytedownfortext numbers) numbers - kittify numbers + kittifylarge numbers define : kittytextfile filepath . "Kittify the contents of the file at FILEPATH, with a transformation to optimize for text files." @@ -284,5 +291,21 @@ 1o.1 r.1i . 1k.1 g.Y. f.1F Karlsruher Institut fuer Technologie " + +displaywithnewline " + + === KIT, IMK, RemoteC ===" + +displaywithnewline : kittify : map shiftbytedownfortext : bytevector->u8-list : string->utf8 "Karlsruhe Institut für Technologie (KIT), IMK-ASF, RemoteC" + + +displaywithnewline " + + === kittifyscript ===" + +displaywithnewline : kittytextfile "examples/kit-encode.w" + + + ; TODO: Final step: Add commandline handling which allows to write into files and set the text flag and so on. ; ./kit-encode [-e|--encode|-d|--decode] [--text] [--template file] [--killstring "stringtoremove" (mutliple times)] [-o|--output file] [file|-] diff --git a/tests/hello.scm b/tests/hello.scm new file mode 100644 --- /dev/null +++ b/tests/hello.scm @@ -0,0 +1,5 @@ +(define (hello who) + (format #t "~A ~A!\n" + "Hello" who)) + + diff --git a/tests/hello.w b/tests/hello.w new file mode 100644 --- /dev/null +++ b/tests/hello.w @@ -0,0 +1,3 @@ +define : hello who + format #t "~A ~A!\n" + . "Hello" who diff --git a/tests/linebreaks.scm b/tests/linebreaks.scm new file mode 100644 --- /dev/null +++ b/tests/linebreaks.scm @@ -0,0 +1,16 @@ +; Test linebreaks in strings and brackets + +("flubbub + +flabbab") + +(hrug (nadda +madda gadda "shoktom + mee" " sep +ka" + hadda) + (gom)) + +(flu) + + diff --git a/tests/sublist.scm b/tests/sublist.scm --- a/tests/sublist.scm +++ b/tests/sublist.scm @@ -1,7 +1,7 @@ ; sublists allow to start single line function calls with a colon ( : ). (defun a (b c) - (let ((e . f))) - g) + (let ((e . f)) + g)) diff --git a/tests/sublist.w b/tests/sublist.w --- a/tests/sublist.w +++ b/tests/sublist.w @@ -2,4 +2,4 @@ ; defun a : b c let : : e . f - . g + . g diff --git a/tests/websafe-indent.scm b/tests/websafe-indent.scm new file mode 100644 --- /dev/null +++ b/tests/websafe-indent.scm @@ -0,0 +1,7 @@ +(defun a (b c) + (d e + (f) + (g h) + i)) + + diff --git a/wisp-mode.el b/wisp-mode.el --- a/wisp-mode.el +++ b/wisp-mode.el @@ -74,6 +74,13 @@ `(( ("\\`#!.*" . font-lock-comment-face) ; initial hashbang ("\"\\.\\*\\?" . font-lock-string-face) ; strings (anything between "") + ; ("^_+ *$" . font-lock-default-face) ; line with only underscores + ; and whitespace shown as + ; default text. This is just + ; a bad workaround. + ; Which does not work because + ; *-default-face is not guaranteed + ; to be defined. ("^\\(?:_* +\\| *\\): *$" . font-lock-keyword-face) ; line with only a : + whitespace, not at the beginning ("^\\(?:_* +\\| *\\): \\| *\\. " . font-lock-keyword-face) ; leading : or . ( ,(regexp-opt wisp-builtin 'symbols) . font-lock-builtin-face) ; generic functions @@ -90,6 +97,8 @@ ("\\_<[+-]?[0-9]+\\_>\\|\\_<[+-][0-9]*\\.[0-9]*\\(e[+-]?[0-9]+\\)?\\_>" . font-lock-constant-face) ; numbers ("'()" . font-lock-constant-face) ; empty list ("[ ]'[^ ]+" . font-lock-constant-face) ; 'name + ; FIXME: This is too general (it will capture a . 'b, making it + ; impossible to have 'b highlighted) (" : \\| \\. " . font-lock-keyword-face) ; leading : or . )) "Default highlighting expressions for wisp mode.") diff --git a/wisp-multiline.sh b/wisp-multiline.sh --- a/wisp-multiline.sh +++ b/wisp-multiline.sh @@ -41,7 +41,7 @@ else fi PROGNAME=`basename $0` -ARGS=`getopt --name "$PN" --long help,lisp:,verbose,version,output:,wisp:interactive --options hl:vo:w:i -- "$@"` +ARGS=`getopt --name "$PROGNAME" --long help,lisp:,verbose,version,output:,wisp:interactive --options hl:vo:w:i -- "$@"` if [ $? -ne 0 ]; then exit 1 fi diff --git a/wisp-reader.w b/wisp-reader.w --- a/wisp-reader.w +++ b/wisp-reader.w @@ -30,10 +30,23 @@ define wisp-pending-port : make-object-p ; Code thanks to Mark Weaver define : read-one-wisp-sexp port env define : read-wisp-chunk - let : : s : wisp2lisp : wisp-chunkreader port - set! : wisp-pending-port port - open-input-string s - try-pending + if : eof-object? : peek-char port + read-char port ; return eof: we’re done + let + : dpe : fluid-ref %default-port-encoding + set-pending-port! + lambda () + let + : s : wisp2lisp : wisp-chunkreader port + set! : wisp-pending-port port + open-input-string s + if dpe ; default port encoding is set + set-pending-port! + ; else: we need to redefine %default-port-encoding to + ; UTF-8. Use with-fluids to avoid affecting other code. + with-fluids : : %default-port-encoding "UTF-8" + set-pending-port! + try-pending define : try-pending let : : pending-port : wisp-pending-port port if pending-port