From 9afa76a988bc71737cb167b340bd2973a7aaaa2a Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Wed, 3 Feb 2021 13:36:47 +0000 Subject: Fix loot --- loot.lsp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/loot.lsp b/loot.lsp index 727f8f3..e5e9e81 100644 --- a/loot.lsp +++ b/loot.lsp @@ -1,13 +1,12 @@ ;;; Port of https://en.wikipedia.org/wiki/ModernPascal#Code_Sample[3]. -;;; And then to CL. ;;; I prefer my version. (defconstant +max-probability+ 1000) ;; Because this is a simple enum and not a full sum/product type, ;; I use symbols instead of CLOS. -(defconstant +loot-type+ (vector 'bloodstone 'copper 'emeraldite 'gold - 'heronite 'platinum 'shaownite 'silver - 'soranite 'umbrarite 'cobalt 'iron - 'nothing)) +(defconstant +loot-type+ #(bloodstone copper emeraldite gold + heronite platinum shadownite silver + soranite umbrarite cobalt iron + nothing)) (defclass () ((probabilities :accessor probabilities))) (defgeneric choose (self)) (defmethod choose ((self )) -- cgit 1.4.1-2-gfad0 From 032fb6578034edfef58d8392c4c9b468437b21c8 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Sun, 7 Feb 2021 00:53:13 +0000 Subject: Fix DBC for eisl --- dbc2.lsp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dbc2.lsp b/dbc2.lsp index 477dc38..6d29596 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -1,5 +1,8 @@ (defmacro unless (test :rest body) - `(if (not ,test) (progn ,@body))) + `(if (not ,test) (progn ,@body))) +(defmacro assert (test) + `(unless ,test + (error "assert: value is false." ',test))) ;;(defun reduce (function sequence) ;; (let ((res 0)) ;; (for ((xs sequence (cdr xs))) @@ -13,11 +16,13 @@ (setq res (+ res (car xs)))))) (defun average-of-absolutes (values) (the values) - (unless (> (length values) 0) - (error "average-of-absolutes requires non-null list" values)) + ;; (unless (> (length values) 0) + ;; (error "average-of-absolutes requires non-null list" values)) + (assert (> (length values) 0)) (let ((res (quotient (sum values) (length values)))) - (unless (>= res 0) - (error "average-of-absolutes must ensure positive result" res)) - (the res))) + ;; (unless (>= res 0) + ;; (error "average-of-absolutes must ensure positive result" res)) + (assert (>= res 0)) + (the res))) ;; (average-of-absolutes '(1 3)) ;; (average-of-absolutes '()) -- cgit 1.4.1-2-gfad0 From 922f146dfa960052ca94200a1645c5c30aa40b63 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Thu, 25 Feb 2021 12:47:54 +0000 Subject: DbC for eisl --- dbc2.lsp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dbc2.lsp b/dbc2.lsp index 6d29596..224bb1e 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -16,12 +16,10 @@ (setq res (+ res (car xs)))))) (defun average-of-absolutes (values) (the values) - ;; (unless (> (length values) 0) - ;; (error "average-of-absolutes requires non-null list" values)) + ;; requires non-null list (assert (> (length values) 0)) (let ((res (quotient (sum values) (length values)))) - ;; (unless (>= res 0) - ;; (error "average-of-absolutes must ensure positive result" res)) + ;; must ensure positive result (assert (>= res 0)) (the res))) ;; (average-of-absolutes '(1 3)) -- cgit 1.4.1-2-gfad0 From 06c451e39ff50b7646f4aa99efae67e0d4850a5f Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Thu, 25 Feb 2021 12:48:35 +0000 Subject: Avoid higher-order functions for simplicity --- dbc2.lsp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dbc2.lsp b/dbc2.lsp index 477dc38..21089ba 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -1,11 +1,5 @@ (defmacro unless (test :rest body) `(if (not ,test) (progn ,@body))) -;;(defun reduce (function sequence) -;; (let ((res 0)) -;; (for ((xs sequence (cdr xs))) -;; ((null xs) res) -;; (setq res (+ res (car xs)))))) -;;(reduce #'+ (map ' #'abs values)) (defun sum (sequence) (let ((res 0)) (for ((xs sequence (cdr xs))) -- cgit 1.4.1-2-gfad0 From b319af5ae04f825eb756c7c4aa2c4c122cf93c12 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Thu, 25 Feb 2021 13:03:47 +0000 Subject: Forgot a function --- dbc2.lsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbc2.lsp b/dbc2.lsp index 657d297..6915c4a 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -12,7 +12,7 @@ (the values) ;; requires non-null list (assert (> (length values) 0)) - (let ((res (quotient (sum values) (length values)))) + (let ((res (quotient (sum (mapcar #'abs values)) (length values)))) ;; must ensure positive result (assert (>= res 0)) (the res))) -- cgit 1.4.1-2-gfad0 From 4c637133d1afb0c1e66a20a9f0e686953f220f5b Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Thu, 25 Feb 2021 22:05:12 +0000 Subject: the -> assure --- dbc2.lsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbc2.lsp b/dbc2.lsp index 6915c4a..afc3006 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -9,12 +9,12 @@ ((null xs) res) (setq res (+ res (car xs)))))) (defun average-of-absolutes (values) - (the values) + (assure values) ;; requires non-null list (assert (> (length values) 0)) (let ((res (quotient (sum (mapcar #'abs values)) (length values)))) ;; must ensure positive result (assert (>= res 0)) - (the res))) + (assure res))) ;; (average-of-absolutes '(1 3)) ;; (average-of-absolutes '()) -- cgit 1.4.1-2-gfad0 From 5586c2093e03a3d684477c3b9ed4e7122e479255 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Fri, 26 Feb 2021 14:26:57 +0000 Subject: Return a float; for a few LOC this had a high defect density --- dbc2.lsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbc2.lsp b/dbc2.lsp index afc3006..552bc44 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -15,6 +15,6 @@ (let ((res (quotient (sum (mapcar #'abs values)) (length values)))) ;; must ensure positive result (assert (>= res 0)) - (assure res))) + (assure res))) ;; (average-of-absolutes '(1 3)) ;; (average-of-absolutes '()) -- cgit 1.4.1-2-gfad0 From 6cb2ab95bc21bc226f103445fa6a4da75a22c9e3 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Wed, 3 Mar 2021 22:51:58 +0000 Subject: Checkpointing before I dive into FFI --- cbasic.lisp | 4 ++ doc/breaking_rules.md | 109 +++++++++++++++++++++++--------------------------- loot.lsp | 2 +- xdr.lsp | 1 + 4 files changed, 55 insertions(+), 61 deletions(-) diff --git a/cbasic.lisp b/cbasic.lisp index 0f2d63c..2ddc5b4 100644 --- a/cbasic.lisp +++ b/cbasic.lisp @@ -5,6 +5,10 @@ ;;; https://github.com/Henry/BuddKaminInterpreters and *maybe* ;;; https://oleksandrmanzyuk.wordpress.com/2014/06/18/from-object-algebras-to-finally-tagless-interpreters-2/ ;;; +;;; It might be worth reading the example in "Beautiful Racket" for +;;; the extensions suggested on the OCaml page, +;;; or more likely switch to COMAL. +;;; ;;; A BASIC interpreter already exists at ;;; https://gitlab.com/com-informatimago/com-informatimago/-/tree/master/small-cl-pgms/basic ;;; but it is idiomatic CL, diff --git a/doc/breaking_rules.md b/doc/breaking_rules.md index db4f28c..31a1857 100644 --- a/doc/breaking_rules.md +++ b/doc/breaking_rules.md @@ -1,15 +1,12 @@ title: Breaking the Rules: Refining Prototypes Into Products author: Darren Bane -copyright: 2020 Darren Bane, CC BY-SA +copyright: 2021 Darren Bane, CC BY-SA # Abstract Recommendations for a process to refine prototypes into production-quality code are made. -*TODO*: Q: re-cast much of this document as Architecture -Decision Records? A: N - # Introduction The conventional wisdom is that prototypes should be discarded once the lessons @@ -19,8 +16,6 @@ In the spirit of [1] I argue that improvements in development tools have invalidated this. -*TODO*: case study - # Previous Work There is a long history of recommending prototyping as a way to construct @@ -28,16 +23,17 @@ systems. I would personally recommend [2] and [3]. -*NB*: I am almost certainly re-inventing a SmallTalk wheel. However I +The SmallTalk community probably pioneered this development process. +However I argue that Lisp's combination of imperative & OO is an easier sell to industry whereas pure OO as in SmallTalk (or logic programming as in -Prolog) is still niche. +Prolog) is, perhaps undeservedly, more niche. -A closely related are is that of "specification animation", +A closely related area is that of "specification animation", quickly writing an implementation of some subset of a formal specification in for example Z or VDM. -Prolog is a common choice for this, but I choose Lisp instead. +Prolog is a common choice for this. However, as stated in the introduction, I differ in arguing that it is possible to *refine* a prototype into a product. @@ -54,9 +50,7 @@ without earning money. ## Design Decisions -The programming language chosen is a particular style of Common Lisp. -For readability my -[ISLisp-like subset of CL](bane.20.cdr15.md) should be followed where practical[5]. +The programming language chosen is ISLisp Reasons for this decision include: * Contrary to a lot of other languages, Lisp is fairly paradigm-agnostic. @@ -64,13 +58,13 @@ Reasons for this decision include: * The imperative and object-oriented paradigms are commonly taught, used in industry, and have a small "impedence mismatch" to current hardware. -* The existence of quicklisp. - Popularity is not really a reason for choosing Common Lisp over ISLisp, - but slotting into quicklisp *is*. +* The possible migration path of running under + [core-lisp](https://github.com/ruricolist/core-lisp) + and using the quicklisp libraries. Detailed implementations, libraries, etc. are as follows: -* The SBCL compiler. +* The EISL compiler. * Avoid multi-threading at this stage, event-driven should do the job. * Not sure if this is relevant for a prototype, but you could do simple multi-user @@ -83,41 +77,43 @@ it could be difficult to satisfy expectations. ### Dependencies -For the prototyping phase, -you should *really* limit yourself to the ISLisp subset. -If absolutely necessary you can choose some of the libraries mentioned in the "Productisation" section below. +Counterintuitively, I chose ISLisp partly *because* it imposes limits in the prototyping phase. +Standard UNIX libraries like curses, xdr and dbm can still be used from compiled code using the FFI. ## Coding standards Even though this is a prototype, attention should be paid to basic craftsmanship. -* Divide the system into packages, using the subset of CL that is - supported by OpenLisp. - This can start from just "section headers" with lots of semicolons. -* Write docstrings for at least each public fun, class and package. - There are good guidelines in the Elisp manual, but for now one sentence will suffice. -* Use `declare` - to check the types of parameters in public interfaces (see below). +* Divide the system conceptually into packages. + This can be as simple as "section headers" with lots of semicolons. +* Write comments for at least each public fun, class and package. + There are guidelines in the Elisp manual, but for now one sentence will suffice. +* Dynamically + check the types of parameters in public interfaces (see below). * Indent all the source code using Emacs. * Some minimal documentation, at least an overview like in [README driven development](https://tom.preston-werner.com/2010/08/23/readme-driven-development.html) and man (actually, [mdoc](https://manpages.bsd.lv/toc.html)) pages[7]. * Certain parts of a system justify greater detail for a *complete* specification. These are (newly-designed) network protocols and complex persistent data models. - For new protocols, use JSON-RPC as a base and follow the documentation style of LSP. + For new protocols, use XDR with or without RPC but generated from rpcgen .x files. Data models should be documented as commented SQL DDL. ### Run-time type-checking As stated above, -`declare` should be used for simple run-time type-checking of public functions. +`the` should be used for simple run-time type-checking of public functions. For example, the following: ```lisp (defun f (x) - (declare (fixnum x)) - (the fixnum (+ x 1))) + (the x) + (the (+ x 1))) ``` +`assure` might be better according to the standard, +but for now only `the` is used for inference +by the eisl compiler. + # Refinement to Production-Quality Software at the level of the previous section is quite usable. @@ -135,9 +131,10 @@ obviously at a maintenance cost. Ensure that the surrounding infrastructure is in place: * Configuration management. The prototype should already have been checked into git. -* Build. Write an ASDF description, and install as a local quicklisp package. +* Build. Split sections into different files, write simple Makefile. + *TODO*: copy elisp public/private convention? * Test. - Write FiveAM + Write *library/test.lsp* test cases. Extend the simple run-time type-checking to contracts where possible. * Track. Start using a defect tracking system. @@ -145,8 +142,9 @@ Ensure that the surrounding infrastructure is in place: Then, the following code & documentation improvements should be made: * Document the system more exhaustively -* Can use any of the "starred" libraries in quicklisp. -* Maybe "lparallel" to take advantage of all cores +* Can re-implement interfaces from the OpenLisp manual using UNIX libraries. +* Can port any of the `trivial-*` libraries from quicklisp. +* Maybe multi-process to take advantage of all cores Since we have a working prototype, it may make sense to write the documentation (and contracts, and tests) "bottom-up": @@ -169,7 +167,7 @@ PlantUML *should* be used where it can replace ad-hoc text. Documents should be stored under git in a "doc" subdirectory of the project. It is recommended to keep the separation between library and UI code, -e.g. for using ltk. +e.g. for using a GUI like Tk. The following can be added as sections to the README: @@ -194,52 +192,43 @@ However, some of this documentation is better in the source code: ```lisp (defun f (x) - (declare (fixnum x)) + (the x) (assert (precondition x)) (let ((res (+ x 1))) (assert (postcondition res)) - (the fixnum res))) + (the res))) ``` -`lisp-critic` can be used to perform static analysis of the codebase. -But it's not worth writing custom rules. +I'm not aware of any static analysis tool. ## Dependencies For productisation you may want to add more features. -Although the official ANSI standard is moribund, -"community standard" (i.e. starred) libraries are recommended on the -[Awesome-CL list](https://github.com/CodyReichert/awesome-cl), -or [CL portability layers](http://portability.cl/). +OpenLisp has idiomatic interfaces for several UNIX features in its manual, which could be re-implemented. +Also quicklisp (and as a second choice non-quicklisp) `trivial-*` libraries should be easy enough to port. Dependencies should be limited to these two initially. -Ltk can implement a GUI to replace the +Tk can implement a GUI to replace the prototype command-line or terminal-based UI, if it makes sense. -A second round of productisation, -which again may never actually be required, -could include: - -* Any of the `trivial-` libraries from the Awesome-CL list. The `trivial-` libraries may be *forked* and maintained locally. -* Any other `trivial-` libraries available in Quicklisp. -* Other libraries available in Quicklisp. +The order of preference is: +1. Any UNIX interface documented in the OpenLisp manual. +2. A port of any of the `trivial-` libraries from the Awesome-CL list. +3. A port of any other `trivial-` libraries available in Quicklisp. -Now it may also be worth taking on the complexity of a Web UI, -using HTMX and the platform.sh stack. -HTMX & ReST (following Fielding) seem simpler than single-page applications -(outside the very specific case of drawing on a canvas using ParenScript). +The complexity of a Web UI should be avoided in favour of simpler protocols like IRC, Gemini and maybe XMPP. ## Testing -Unit (FiveAM) tests grow in parallel with the module interface specs. +Unit tests grow in parallel with the module interface specs. Property-based testing would be nice here, but there doesn't seem to be a readily-available library. System tests grow in parallel with the requirements spec. -It's ok for system tests to use the same interfaces as the ltk code. +It's ok for system tests to use the same interfaces as the GUI code. All tests should be automated, except possibly for the UI/view layer. -Q: These scripts could be generated from a literate test plan? A: yes, probably one of the few places to use nw2md. +These scripts could be generated from a literate test plan, one of the places where it makes sense to use nw2md. As much of the testing work should be pushed "back" in the V model to contracts for the functions, following the pattern above. @@ -247,7 +236,7 @@ following the pattern above. # Conclusion A method for developing software from an incomplete understanding of the requirements is given. -It is hoped that this is more effective than most of what is currently-used. +It is hoped that this is more effective than a lot of what is currently-used. # References diff --git a/loot.lsp b/loot.lsp index 4dfb2c8..74378fc 100644 --- a/loot.lsp +++ b/loot.lsp @@ -38,4 +38,4 @@ (for ((n 0 (+ n 1))) ((> n 99)) (format (standard-output) "~A~%" (as-string (choose loot)))))) -(main) +;; (main) diff --git a/xdr.lsp b/xdr.lsp index b3cf1ad..ffae67f 100644 --- a/xdr.lsp +++ b/xdr.lsp @@ -1,4 +1,5 @@ ;;; Use (a subset of) CL and the xpc part of the "frpc" package from QuickLisp instead? +;;; No, use eisl and ../build/xdr-tests-1.0. (require "olunit") (defpackage #:xdr -- cgit 1.4.1-2-gfad0 From a964f55b4b983b6b286ad61c02cfce819977e6bf Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Mon, 29 Mar 2021 14:56:41 +0100 Subject: Gopher mole. --- guess.lsp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 guess.lsp diff --git a/guess.lsp b/guess.lsp new file mode 100644 index 0000000..d03d2e3 --- /dev/null +++ b/guess.lsp @@ -0,0 +1,59 @@ +;;; gopherlib +;;; Distantly descended from Perl code that is (c) 2001, 2004 Cameron Kaiser. +(import "unix") + +(defconstant +tab-chr+ (convert 9 )) +(defconstant +cr-chr+ (convert 13 )) + +(defun offer (type name resc server port extent) + (format (standard-output) "~C~A~C~A~C~A~C~D" type name +tab-chr+ resc +tab-chr+ server +tab-chr+ port) + (if (not (null extent)) + (format (standard-output) "~C~A" +tab-chr+ extent)) + (format (standard-output) "~C~%" +cr-chr+)) + +(defun dirname (path) + (if (null (char-index #\/ path)) + path + (for ((prev-slash 0 next-slash) + (next-slash 0 (char-index #\/ path (+ prev-slash 1)))) + ((null next-slash) (subseq path 0 (+ prev-slash 1)))))) + +(defun offer-file (type name resc server port extent) + (assure type)(assure name)(assure resc)(assure port) + (if (null server) + (setq server (getenv "SERVER_HOST"))) + (if (= port 0) + (setq port (convert (getenv "SERVER_PORT") ))) + (if (and (> (length resc) 0) (char/= (elt resc 0) #\/)) + (setq resc (string-append (dirname (getenv "SELECTOR")) resc))) + (offer type name resc server port extent)) + +(defun print-string (msg) + (assure msg) + (offer-file #\i msg "" "null.host" 1 nil)) + +;;; Main program +(defconstant +target+ 45) + +(defun try-guess (guess) + (cond ((< guess +target+) + (let ((str (create-string-output-stream))) + (format str "Your guess of ~A was too low." guess) + (print-string (get-output-stream-string str))) + (offer-file #\7 "Guess again." "guess.cgi" nil 0 nil)) + ((= guess +target+) + (print-string "Congratulations, you won!")) + ((> guess +target+) + (let ((str (create-string-output-stream))) + (format str "Your guess of ~A was too high." guess) + (print-string (get-output-stream-string str))) + (offer-file #\7 "Guess again." "guess.cgi" nil 0 nil)))) + +(defun main () + (let ((guess-str (getenv "QUERY_STRING"))) + (if (null guess-str) + (print-string "No guess supplied") + (try-guess (convert guess-str ))))) + +(main) +(quit) -- cgit 1.4.1-2-gfad0 From 48358adb04bb37cb1630e137ec181a0b6997ca64 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Tue, 20 Apr 2021 15:46:58 +0100 Subject: Doc updates --- basic.lsp | 2 +- doc/breaking_rules.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/basic.lsp b/basic.lsp index a63ab27..09efc5c 100644 --- a/basic.lsp +++ b/basic.lsp @@ -67,7 +67,7 @@ (defgeneric ppl (pr expr)) (defmethod ppl (pr (expr )) - (convert (num expr) )) + (convert (int expr) )) (defmethod ppl (pr (expr )) (var expr)) (defmethod ppl (pr (expr )) diff --git a/doc/breaking_rules.md b/doc/breaking_rules.md index 31a1857..d9c499b 100644 --- a/doc/breaking_rules.md +++ b/doc/breaking_rules.md @@ -78,7 +78,7 @@ it could be difficult to satisfy expectations. ### Dependencies Counterintuitively, I chose ISLisp partly *because* it imposes limits in the prototyping phase. -Standard UNIX libraries like curses, xdr and dbm can still be used from compiled code using the FFI. +Standard UNIX libraries like curses, catgets, xdr and dbm can still be used from compiled code using the FFI. ## Coding standards -- cgit 1.4.1-2-gfad0 From 3e547ec218830ff391859287e404a19775aa37a1 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Tue, 20 Apr 2021 15:47:49 +0100 Subject: Doc updates --- cap-muck.lsp | 13 +++---------- doc/breaking_rules.md | 25 +++++++------------------ 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/cap-muck.lsp b/cap-muck.lsp index fd90626..8cace16 100644 --- a/cap-muck.lsp +++ b/cap-muck.lsp @@ -1,9 +1,3 @@ -(defpackage #:cap-muck - (:use #:openlisp) - (:export - #:main) - ) -(in-package #:cap-muck) (defglobal *terminate-program* nil) (defconstant +bold+ "#\esc[1m") @@ -109,9 +103,7 @@ ()) ;; TODO: Use the reader, for prototype at least? -;; Can switch to postmodern for production. -;; -;; Or dbm? +;; Or dbm? (Espcially for production.) (defun read-room-database () (setq *rooms* '()) (with-open-input-file (file +rdb+) @@ -180,4 +172,5 @@ (read-room-database) (while (not *terminate-program*) (check-for-inputs))) -(provide "cap-muck") +(main) + diff --git a/doc/breaking_rules.md b/doc/breaking_rules.md index db4f28c..694261b 100644 --- a/doc/breaking_rules.md +++ b/doc/breaking_rules.md @@ -1,15 +1,12 @@ title: Breaking the Rules: Refining Prototypes Into Products author: Darren Bane -copyright: 2020 Darren Bane, CC BY-SA +copyright: 2020-21 Darren Bane, CC BY-SA # Abstract Recommendations for a process to refine prototypes into production-quality code are made. -*TODO*: Q: re-cast much of this document as Architecture -Decision Records? A: N - # Introduction The conventional wisdom is that prototypes should be discarded once the lessons @@ -19,8 +16,6 @@ In the spirit of [1] I argue that improvements in development tools have invalidated this. -*TODO*: case study - # Previous Work There is a long history of recommending prototyping as a way to construct @@ -54,9 +49,7 @@ without earning money. ## Design Decisions -The programming language chosen is a particular style of Common Lisp. -For readability my -[ISLisp-like subset of CL](bane.20.cdr15.md) should be followed where practical[5]. +The programming language chosen is ISLisp. Reasons for this decision include: * Contrary to a lot of other languages, Lisp is fairly paradigm-agnostic. @@ -64,13 +57,10 @@ Reasons for this decision include: * The imperative and object-oriented paradigms are commonly taught, used in industry, and have a small "impedence mismatch" to current hardware. -* The existence of quicklisp. - Popularity is not really a reason for choosing Common Lisp over ISLisp, - but slotting into quicklisp *is*. Detailed implementations, libraries, etc. are as follows: -* The SBCL compiler. +* The Easy-ISLisp interpreter/compiler. * Avoid multi-threading at this stage, event-driven should do the job. * Not sure if this is relevant for a prototype, but you could do simple multi-user @@ -84,19 +74,18 @@ it could be difficult to satisfy expectations. ### Dependencies For the prototyping phase, -you should *really* limit yourself to the ISLisp subset. +you should *really* limit yourself to ISLisp. If absolutely necessary you can choose some of the libraries mentioned in the "Productisation" section below. ## Coding standards Even though this is a prototype, attention should be paid to basic craftsmanship. -* Divide the system into packages, using the subset of CL that is - supported by OpenLisp. +* Divide the system into "packages". This can start from just "section headers" with lots of semicolons. -* Write docstrings for at least each public fun, class and package. +* Write comments for at least each public fun, class and package. There are good guidelines in the Elisp manual, but for now one sentence will suffice. -* Use `declare` +* Use `assure` or `the` to check the types of parameters in public interfaces (see below). * Indent all the source code using Emacs. * Some minimal documentation, at least an overview like in [README driven development](https://tom.preston-werner.com/2010/08/23/readme-driven-development.html) -- cgit 1.4.1-2-gfad0 From a4ab4320c2f3350faf9b1854eaee5ecce6bcf1b3 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Tue, 20 Apr 2021 15:54:37 +0100 Subject: Latest updates for eisl --- doc/breaking_rules.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/breaking_rules.md b/doc/breaking_rules.md index 947ca80..a31a0a1 100644 --- a/doc/breaking_rules.md +++ b/doc/breaking_rules.md @@ -132,7 +132,7 @@ Ensure that the surrounding infrastructure is in place: * Configuration management. The prototype should already have been checked into git. * Build. Split sections into different files, write simple Makefile. - *TODO*: copy elisp public/private convention? + In the absence of a standard module system, the elisp public/private convention can be copied. * Test. Write *library/test.lsp* test cases. @@ -142,14 +142,14 @@ Ensure that the surrounding infrastructure is in place: Then, the following code & documentation improvements should be made: * Document the system more exhaustively -* Can re-implement interfaces from the OpenLisp manual using UNIX libraries. +* Can re-implement more interfaces from the OpenLisp manual using UNIX libraries. * Can port any of the `trivial-*` libraries from quicklisp. * Maybe multi-process to take advantage of all cores Since we have a working prototype, it may make sense to write the documentation (and contracts, and tests) "bottom-up": -1. Contracts, static analysis +1. Contracts 2. Test cases 3. Module interface specs 4. Module guide, uses hierarchy @@ -161,7 +161,7 @@ it may make sense to write the documentation (and contracts, and tests) "bottom- Depend only on GFM, in the same spirit as the software. The use of tools like -nw2md and Pandoc should be minised. +nw2md and Pandoc should be minimised. PlantUML *should* be used where it can replace ad-hoc text. Documents should be stored under git in a "doc" subdirectory of the project. @@ -205,7 +205,7 @@ I'm not aware of any static analysis tool. For productisation you may want to add more features. -OpenLisp has idiomatic interfaces for several UNIX features in its manual, which could be re-implemented. +OpenLisp has idiomatic interfaces for several more UNIX features in its manual, which could be re-implemented. Also quicklisp (and as a second choice non-quicklisp) `trivial-*` libraries should be easy enough to port. Dependencies should be limited to these two initially. -- cgit 1.4.1-2-gfad0 From 0c2561d21d59254669dea0b662946d82fcc7c50c Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Fri, 14 May 2021 23:42:35 +0100 Subject: Update DbC --- dbc2.lsp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/dbc2.lsp b/dbc2.lsp index 552bc44..5a2699d 100644 --- a/dbc2.lsp +++ b/dbc2.lsp @@ -1,20 +1,19 @@ -(defmacro unless (test :rest body) - `(if (not ,test) (progn ,@body))) +(import "macro") ; For unless (defmacro assert (test) - `(unless ,test - (error "assert: value is false." ',test))) -(defun sum (sequence) + `(unless ,test + (error "assert: value is false." ',test))) +(defun my-sum (sequence) (let ((res 0)) (for ((xs sequence (cdr xs))) - ((null xs) res) - (setq res (+ res (car xs)))))) + ((null xs) res) + (setq res (+ res (car xs)))))) (defun average-of-absolutes (values) (assure values) ;; requires non-null list (assert (> (length values) 0)) - (let ((res (quotient (sum (mapcar #'abs values)) (length values)))) + (let ((res (quotient (my-sum (mapcar #'abs values)) (length values)))) ;; must ensure positive result (assert (>= res 0)) - (assure res))) + (assure res))) ;; (average-of-absolutes '(1 3)) ;; (average-of-absolutes '()) -- cgit 1.4.1-2-gfad0 From af38818bb582e212b5f52566a71cede0d1675d29 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Fri, 14 May 2021 23:47:35 +0100 Subject: Making changes --- basic.lsp | 3 ++- cap-muck.lsp | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/basic.lsp b/basic.lsp index 09efc5c..a91cc1c 100644 --- a/basic.lsp +++ b/basic.lsp @@ -9,7 +9,7 @@ (defclass () ((int :accessor int))) (defclass () ((var :accessor var))) (defclass () ((str :accessor str))) -(defclass () ((op :accessor op) (exp :accessor exp))) +(defclass () ((op :accessor op) (expr :accessor expr))) (defclass () ((exp1 :accessor exp1) (op :accessor op) (exp2 :accessor exp2))) (defclass () () (:abstractp t)) @@ -268,6 +268,7 @@ ;;;; Program evaluation (defun run (state) + ) ;;; Finishing touches diff --git a/cap-muck.lsp b/cap-muck.lsp index 8cace16..36bffba 100644 --- a/cap-muck.lsp +++ b/cap-muck.lsp @@ -1,5 +1,8 @@ +;;; See https://github.com/chazu/16k_muds/tree/master/drveg%40pacbell.net + (defglobal *terminate-program* nil) +;; Hmm, I now think procedural interfaces are better than protocols (defconstant +bold+ "#\esc[1m") (defconstant +unbold+ "#\esc[0m") (defconstant +q+ #\") @@ -167,6 +170,13 @@ (with-open-output-file (file +adb+) (mapcar (lambda (a) (print-object a file)) *avatars*))) +(defun establish-connection () + (let ((c (create (class )))) + (say c "Welcome to CapMUCK!") + (say c "Commands are all upper case, like HELP.") + (say c "") + (say c +name-prompt+))) + (defun main () (read-avatar-database) (read-room-database) @@ -174,3 +184,4 @@ (check-for-inputs))) (main) + -- cgit 1.4.1-2-gfad0 From 58dce9acc5ce063e979f572e3cae040599918f8d Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Sun, 16 May 2021 00:22:22 +0100 Subject: Fix non-standard syntax --- bitmap.lsp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/bitmap.lsp b/bitmap.lsp index 41d3f64..aaef3a5 100644 --- a/bitmap.lsp +++ b/bitmap.lsp @@ -1,6 +1,8 @@ ;;; Bitmap ;;; This could be extended to a general drawing interface, but for now it's probably better to use Tk's canvas. +(import "compat") + (defconstant +black+ 0) (defconstant +white+ #xFFFFFF) (defconstant +red+ #xFF0000) @@ -81,13 +83,16 @@ (defconstant +whitespace-chars+ '(#\SPACE \#carriage-return #\TAB #\NEWLINE)) -(defun read-header-chars (stream &optional (delimiter-list +whitespace-chars+)) - (do ((c (read-char stream nil :eof) - (read-char stream nil :eof)) - (vals nil (if (or (null c) (char= c #\#)) vals (cons c vals)))) ;;don't collect comment chars - ((or (eql c :eof) (member c delimiter-list)) (map 'string #'identity (nreverse vals))) ;;return strings - (when (char= c #\#) ;;skip comments - (read-line stream)))) +(defun read-header-chars (stream &rest args) + (let ((delimiter-list (if (null args) + +whitespace-chars+ + (car args)))) + (do ((c (read-char stream nil :eof) + (read-char stream nil :eof)) + (vals nil (if (or (null c) (char= c #\#)) vals (cons c vals)))) ;;don't collect comment chars + ((or (eql c :eof) (member c delimiter-list)) (map 'string #'identity (nreverse vals))) ;;return strings + (when (char= c #\#) ;;skip comments + (read-line stream))))) (defun read-ppm-file-header (file) (with-open-file (s file :direction :input) -- cgit 1.4.1-2-gfad0 From 0da17ebd2abeef5db897143d6a43555fa4c87e27 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Tue, 18 May 2021 16:18:43 +0100 Subject: Fixing issues --- basic.lsp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/basic.lsp b/basic.lsp index a91cc1c..feec2a6 100644 --- a/basic.lsp +++ b/basic.lsp @@ -78,8 +78,8 @@ (pr2 (priority-uop op)) (res-e (ppl pr2 (expr expr)))) (if (= pr 0) - (parenthesis (string-append res-op res-e)) - (string-append res-op res-e)))) + (string-append res-op res-e) + (parenthesis (string-append res-op res-e))))) (defmethod ppl (pr (expr )) (let* ((op (op expr)) (pr2 (priority-binop op)) @@ -114,7 +114,7 @@ (defmethod pp-command ((cmd )) (string-append "GOTO " (convert (num cmd) ))) (defmethod pp-command ((cmd )) - (string-append "PRNT " (pp-expression (expr cmd)))) + (string-append "PRINT " (pp-expression (expr cmd)))) (defmethod pp-command ((cmd )) (string-append "INPUT " (var cmd))) (defmethod pp-command ((cmd )) @@ -123,7 +123,7 @@ (string-append "LET " (var cmd) " = " (pp-expression (expr cmd)))) (defun pp-line (l) - (string-append (convert (car l) ) " " (pp-command (cdr l)))) + (string-append (convert (num l) ) " " (pp-command (cmd l)))) ;;; Lexing (defclass () () (:abstractp t)) @@ -133,8 +133,8 @@ (defclass () ((lstring :reader lstring))) (defclass () ()) -(defclass () ((string :initarg s :accessor string) - (current :initform 0 :accessor current) +(defclass () ((str :initarg s :accessor str) + (curr :initform 0 :accessor curr) (size :accessor size))) (defmethod initialize-object :after ((self ) initargs) -- cgit 1.4.1-2-gfad0 From 83a8c55f433c3c18ef0b189042a0ef13ac2598b5 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Wed, 19 May 2021 00:47:13 +0100 Subject: Fixing issues --- basic.lsp | 103 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 25 deletions(-) diff --git a/basic.lsp b/basic.lsp index feec2a6..5209832 100644 --- a/basic.lsp +++ b/basic.lsp @@ -127,52 +127,97 @@ ;;; Lexing (defclass () () (:abstractp t)) -(defclass () ((int :reader int))) -(defclass () ((ident :reader ident))) -(defclass () ((lsymbol :reader lsymbol))) -(defclass () ((lstring :reader lstring))) +(defclass () ((int :initarg i :reader int))) +(defclass () ((ident :initarg i :reader ident))) +(defclass () ((lsymbol :initarg s :reader lsymbol))) +(defclass () ((lstring :initarg s :reader lstring))) (defclass () ()) -(defclass () ((str :initarg s :accessor str) - (curr :initform 0 :accessor curr) +(defclass () ((string :initarg s :accessor string) + (current :initform 0 :accessor current) (size :accessor size))) (defmethod initialize-object :after ((self ) initargs) - (setf (size self) (length (str self)))) + (setf (size self) (length (string self)))) (defgeneric forward (cl &rest args)) (defmethod forward ((cl ) &rest args) (let ((incr (if (null args) 1 (car args)))) - (setf (curr cl) (+ (curr cl) incr)))) + (setf (current cl) (+ (current cl) incr)))) (defgeneric extract (pred cl)) (defmethod extract (pred (cl )) - (let* ((st (string cl)) - (pos (current cl)) - (ext (lambda (n) - (if (and (< n (size cl)) (pred (elt st n))) - (ext (+ n 1)) - n))) - (res (ext pos))) + (flet ((ext (n) + (if (and (< n (size cl)) (apply #'pred (elt st n))) + (ext (+ n 1)) + n))) + (let* ((st (string cl)) + (pos (current cl)) + (res (ext pos))) (setf (current cl) res) - (subseq (string cl) pos (- res pos)))) + (subseq (string cl) pos (- res pos))))) + +;; Some functions from C's ctype.h: +(defun isdigit (c) + (and (char>= x #\0) (char<= x #\9))) +(defun isalpha (c) + (or (and (char>= c #\a) (char<= c #\z)) + (and (char>= c #\A) (char<= c #\Z)))) +(defun isalnum (c) + (or (isalpha c) + (isdigit c))) (defgeneric extract-int (cl)) (defmethod extract-int ((cl )) - (flet ((is-int (x) - (and (char>= x #\0) (char<= x #\9)))) - (convert (extract is-int cl) ))) + (convert (extract #'isdigit cl) )) (defgeneric extract-ident (cl)) (defmethod extract-ident ((cl )) (flet ((is-alpha-num (x) - (or (and (char>= x #\a) (char<= x #\z)) - (and (char>= x #\A) (char<= x #\Z)) - (and (char>= x #\0) (char<= x #\9)) + (or (isalnum x) (char= x #\_)))) - (extract is-alpha-num))) + (extract #'is-alpha-num))) + +(defgeneric lexer (cl)) +(defmethod lexer ((cl )) + (flet ((lexer-char (c) + (cond ((or (char= c #\space) (char= c #\tab)) + (forward cl) + (lexer cl)) + ((isalpha c) + (create (class ) 'i (extract-ident cl))) + ((isdigit c) + (create (class ) 'i (extract-int cl))) + ((char= c #\") + (forward cl) + (let ((res (create (class ) 's (extract (lambda (c) (char/= c #\")) cl)))) + (forward cl) + res)) + ((member c '(#\+ #\- #\* #\/ #\% #\& #\| #\! #\= #\( #\))) + (forward cl) + (create (class ) 's c)) + ((or (char= c #\<) (char= c #\>)) + (forward cl) + (if (>= (current cl) (size cl)) + (crate (class ) 's c) + (let ((cs (elt (string cl) (current cl)))) + (cond ((and (char= c #\<) (char= cs #\=)) + (forward cl) + (create (class ) 's "<=")) + ((and (char= c #\>) (char= cs #\=)) + (forward cl) + (create (class ) 's ">=")) + ((and (char= c #\<) (char= cs #\>)) + (forward cl) + (create (class ) 's "<>")) + (t + (create (class ) c)))))) + (t (error "Lexer error"))))) + (if (>= (current cl) (size cl)) + (create (class )) + (lexer-char (elt (string cl) (current cl)))))) ;;; Parsing (defclass () () (:abstractp t)) @@ -184,7 +229,7 @@ (defun unr-symb (s) (cond ((string= s "!") 'not) ((string= s "-") 'uminus) - (t (error "Parse error")))) + (t (throw 'parse-failure)))) (defun bin-symb (s) (cond ((string= s "+") 'plus) @@ -195,7 +240,15 @@ ((string= s "=") 'equal) ((string= s "<") 'less) ((string= s "<=") 'lesseq) - ((string= s ">") 'great))) + ((string= s ">") 'great) + ((string= s ">=") 'greateq) + ((string= s "<>") 'diff) + ((string= s "&") 'and) + ((string= s "|") 'or) + (t (throw 'parse-failure)))) + +(defun tsymb (s) + (catch 'parse-failure (lambda (defun parse (str) (let* ((cl (init-lex str)) -- cgit 1.4.1-2-gfad0