ulambda/bootstrap/rebirth/relibprebirth.scm

209 lines
6.2 KiB
Scheme
Raw Normal View History

;;;; libprebirth Replacement for Rebirth Lisp
;;;;
;;;; Copyright (C) 2017, 2018 Mike Gerwitz
;;;;
;;;; This file is part of Ulambda Scheme.
;;;;
;;;; Ulambda Scheme is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU Affero General Public License as
;;;; published by the Free Software Foundation, either version 3 of the
;;;; License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Affero General Public License
;;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;;;
;;; Commentary:
;;; THIS IS BOOTSTRAP CODE INTENDED FOR USE ONLY IN REBIRTH.
;;;
;;;
;;; === STEP 0 ===
;;;
;;; The first step in the Rebirth process is to liberate ourselves from
;;; libprebirth.
;;;
;;; Here we define the libprebirth primitives. When we first compile
;;; Rebirth with Birth, `string->es' is not yet available, because it is
;;; only implemented in Rebirth. Further, Birth includes libprebirth in
;;; its output, so we cannot blindly redefine the procedures without
;;; producing an error.
;;;
;;; Once Rebirth is compiled with Birth, Rebirth can then compile
;;; itself. Since Rebirth _does_ implement `string->es', and further _does
;;; not_ include libprebirth in its output, we can define the libprebirth
;;; primitives ourselves in Rebirth Lisp. Cut the cord.
;;;
;;; Some of these definitions aren't valid: variable arguments, for
;;; example, aren't represented _at all_---the `define' form will be
;;; properly implemented in the future to correct this.
;;;
;;; Code:
(cond-expand
(string->es
(define #t (string->es "true"))
(define #f (string->es "false"))
;; _truep is used only internally and is still defined as a JS function
;; for brevity
(string->es "const _truep = x => x !== false")
;; intended for whether a procedure is defined, mostly
(define (es:defined? x)
(let ((id (tname->id x)))
(string->es "eval('typeof ' + $$id) !== 'undefined'")))
rebirth: Add viability test suite This is an initial test suite that is by no means comprehensive, but is a pretty good starting point. At this point, it's important that we be confident in certain aspects of the system to preempt difficulty in debugging problems when using 3rd-party code (we'll be using psyntax at some point, for example). We also need to be able to have confidence in refactoring. We do gain limited confidence when Rebirth is able to successfully compile itself, but finer details are missed, as has been demonstrated by recent commits (which fix bugs found by this test suite). This is also the first test of writing another program in Rebirth that isn't Rebirth itself. It's exciting, and helps make obvious certain pain points, like the need to include core packages. Further, we can't include important definitions in `rebith.scm' itself. Consequently, I think it may be in my best interest to just add support to Birth for `include' so that the first pass of Rebirth can benefit from it. I didn't want to do so because I wanted Birth to be a faithful re-implementation of Prebirth, but it's not worth it. This would be a minor addition and would save so much trouble. I saw a couple mentions of JS Lisps today on HN (one a tiny Lisp that compiles to JS, one an interpreter)---neither of them come close to what Ulambda will be (and in many ways with Rebirth already is), so I'm excited to keep development going. There is a niche to be filled. And I intend to keep this project secret until it can actually be called "Scheme" (and maybe beyond that too, since I have Gibble to work on as well). Anyway, enjoy the first non-Rebirth Rebirth program! Hello, world! * bootstrap/Bootstrap.js (bootstrap): Include `#_testViable' call after Rebirth compilation. (_birth): Throw any error received during Birth compilation. (_makeCompiler): Accumulate output for each `console.log' in compiler, rather than overwriting with each call (that latter behavior was never intended in the first place). (_compileRebirth): Throw error on compilation failure. (_testViable): New method. (_strmap)[rebirthTestCompiling, rebirthTestCompiled, rebirthTestFailed] [rebirthTestDone]: New functions. * bootstrap/rebirth.scm: Add comment clarifying that Rebirth should _not_ be used as a general-purpose little Lisp. * bootstrap/rebirth/.dir-locals.el: New file. * bootstrap/rebirth/es.scm: Add comment comparing environment implementation to a spaghetti stack (a term which I was unfamiliar with until recently while researching some Lisp history). (es:json-stringify): New procedure. (es:try): New macro. * bootstrap/rebirth/relibprebirth.scm (es:null): New procedure for use by test suite. (fold): Fix argument naming order. (zero?): Correctly use `=' instead of `eq?'. * bootstrap/rebirth/test.scm: New viability test suite.
2018-09-11 22:30:01 -04:00
(define es:null (string->es "null"))
(define (es:null? x)
(string->es "$$x === null"))
(define (es:typeof x)
(string->es "typeof $$x"))
(define (symbol=? x y)
(and (string=? (es:typeof x) "symbol")
(eq? x y)))
(define (es:arg->arr args)
(string->es "Array.prototype.slice.call($$args)"))
(define (list . xs) xs)
;; warning: only compares two values
(define (= x y)
(string->es "+$$x === +$$y"))
(define (> x y)
(string->es "+$$x > +$$y"))
(define (< x y)
(string->es "+$$x < +$$y"))
;; warning: doesn't verify that it's a pair
(define (length xs)
(string->es "$$xs.length"))
(define (es:array? xs)
(string->es "Array.isArray($$xs)"))
(define (es:-assert-list xs)
(or (es:array? xs)
(error "expecting list")))
(define (es:-assert-pair xs)
(es:-assert-list xs)
(if (= 0 (length xs))
(error "expecting pair")
#t))
;; ignore obj for now
(define (error msg obj)
(string->es "throw Error($$msg)")
#f) ; prevent above from being in tail position and prefixing "return"
;; warning: these only operate on arrays
(define (cons obj1 obj2)
(es:-assert-list obj2)
(string->es "[$$obj1].concat($$obj2)"))
(define (car pair)
(es:-assert-pair pair)
(string->es "$$pair[0]"))
(define (cdr pair)
(es:-assert-pair pair)
(string->es "$$pair.slice(1)"))
(define (append . args)
(fold (lambda (x xs)
(es:-assert-list x)
(string->es "$$xs.concat($$x)"))
(list)
args))
;; warning: these two are wholly inadequate
(define (list? xs)
(string->es "Array.isArray($$xs)"))
(define (pair? xs)
(and (list? xs)
(not (zero? (length xs)))))
;; R7RS string
(define (substring s start end)
(string->es "$$s.substring($$start, $$end)"))
(define (string-length s)
(string->es "$$s.length"))
(define (string=? s1 s2)
(string->es "typeof $$s1 === 'string' && $$s1 === $$s2"))
(define (string-append . xs)
(string->es "$$xs.join('')"))
(define (eq? x y)
(string->es "$$x === $$y"))
;; R7RS math
(define (+ . xs)
rebirth: Add viability test suite This is an initial test suite that is by no means comprehensive, but is a pretty good starting point. At this point, it's important that we be confident in certain aspects of the system to preempt difficulty in debugging problems when using 3rd-party code (we'll be using psyntax at some point, for example). We also need to be able to have confidence in refactoring. We do gain limited confidence when Rebirth is able to successfully compile itself, but finer details are missed, as has been demonstrated by recent commits (which fix bugs found by this test suite). This is also the first test of writing another program in Rebirth that isn't Rebirth itself. It's exciting, and helps make obvious certain pain points, like the need to include core packages. Further, we can't include important definitions in `rebith.scm' itself. Consequently, I think it may be in my best interest to just add support to Birth for `include' so that the first pass of Rebirth can benefit from it. I didn't want to do so because I wanted Birth to be a faithful re-implementation of Prebirth, but it's not worth it. This would be a minor addition and would save so much trouble. I saw a couple mentions of JS Lisps today on HN (one a tiny Lisp that compiles to JS, one an interpreter)---neither of them come close to what Ulambda will be (and in many ways with Rebirth already is), so I'm excited to keep development going. There is a niche to be filled. And I intend to keep this project secret until it can actually be called "Scheme" (and maybe beyond that too, since I have Gibble to work on as well). Anyway, enjoy the first non-Rebirth Rebirth program! Hello, world! * bootstrap/Bootstrap.js (bootstrap): Include `#_testViable' call after Rebirth compilation. (_birth): Throw any error received during Birth compilation. (_makeCompiler): Accumulate output for each `console.log' in compiler, rather than overwriting with each call (that latter behavior was never intended in the first place). (_compileRebirth): Throw error on compilation failure. (_testViable): New method. (_strmap)[rebirthTestCompiling, rebirthTestCompiled, rebirthTestFailed] [rebirthTestDone]: New functions. * bootstrap/rebirth.scm: Add comment clarifying that Rebirth should _not_ be used as a general-purpose little Lisp. * bootstrap/rebirth/.dir-locals.el: New file. * bootstrap/rebirth/es.scm: Add comment comparing environment implementation to a spaghetti stack (a term which I was unfamiliar with until recently while researching some Lisp history). (es:json-stringify): New procedure. (es:try): New macro. * bootstrap/rebirth/relibprebirth.scm (es:null): New procedure for use by test suite. (fold): Fix argument naming order. (zero?): Correctly use `=' instead of `eq?'. * bootstrap/rebirth/test.scm: New viability test suite.
2018-09-11 22:30:01 -04:00
(fold (lambda (x y)
(string->es "$$x + $$y"))
0
xs))
(define (- . xs)
(if (= (length xs) 1)
(- 0 (car xs))
(fold (lambda (y x)
(string->es "$$x - $$y"))
(car xs)
(cdr xs))))
(define (zero? x)
rebirth: Add viability test suite This is an initial test suite that is by no means comprehensive, but is a pretty good starting point. At this point, it's important that we be confident in certain aspects of the system to preempt difficulty in debugging problems when using 3rd-party code (we'll be using psyntax at some point, for example). We also need to be able to have confidence in refactoring. We do gain limited confidence when Rebirth is able to successfully compile itself, but finer details are missed, as has been demonstrated by recent commits (which fix bugs found by this test suite). This is also the first test of writing another program in Rebirth that isn't Rebirth itself. It's exciting, and helps make obvious certain pain points, like the need to include core packages. Further, we can't include important definitions in `rebith.scm' itself. Consequently, I think it may be in my best interest to just add support to Birth for `include' so that the first pass of Rebirth can benefit from it. I didn't want to do so because I wanted Birth to be a faithful re-implementation of Prebirth, but it's not worth it. This would be a minor addition and would save so much trouble. I saw a couple mentions of JS Lisps today on HN (one a tiny Lisp that compiles to JS, one an interpreter)---neither of them come close to what Ulambda will be (and in many ways with Rebirth already is), so I'm excited to keep development going. There is a niche to be filled. And I intend to keep this project secret until it can actually be called "Scheme" (and maybe beyond that too, since I have Gibble to work on as well). Anyway, enjoy the first non-Rebirth Rebirth program! Hello, world! * bootstrap/Bootstrap.js (bootstrap): Include `#_testViable' call after Rebirth compilation. (_birth): Throw any error received during Birth compilation. (_makeCompiler): Accumulate output for each `console.log' in compiler, rather than overwriting with each call (that latter behavior was never intended in the first place). (_compileRebirth): Throw error on compilation failure. (_testViable): New method. (_strmap)[rebirthTestCompiling, rebirthTestCompiled, rebirthTestFailed] [rebirthTestDone]: New functions. * bootstrap/rebirth.scm: Add comment clarifying that Rebirth should _not_ be used as a general-purpose little Lisp. * bootstrap/rebirth/.dir-locals.el: New file. * bootstrap/rebirth/es.scm: Add comment comparing environment implementation to a spaghetti stack (a term which I was unfamiliar with until recently while researching some Lisp history). (es:json-stringify): New procedure. (es:try): New macro. * bootstrap/rebirth/relibprebirth.scm (es:null): New procedure for use by test suite. (fold): Fix argument naming order. (zero?): Correctly use `=' instead of `eq?'. * bootstrap/rebirth/test.scm: New viability test suite.
2018-09-11 22:30:01 -04:00
(= x 0))
;; SRFI-1
;; warning: fold here only supports one list
(define (fold f init xs)
(string->es "$$xs.reduce((prev, x) => $$f(x, prev), $$init)"))
;; warning: map here uses the length of the first list, not the shortest
(define (map f . xs)
(string->es
"$$xs[0].map((_, i) => $$f.apply(null, $$xs.map(x => x[i])))"))
(define (es:regexp s opts)
(string->es "new RegExp($$s, $$opts)"))
(define (es:match r s)
(string->es "$$s.match($$r) || false"))
(define (es:replace r repl s)
(string->es "$$s.replace($$r, $$repl)"))
(define *fsdata*
(if (string->es "typeof __fsinit === 'undefined'")
(string->es "{}")
(string->es "__fsinit")))
(define *fs*
(if (string->es "typeof require === 'undefined'")
(string->es
"{
readFileSync(path)
{
throw Error(`Cannot load ${path} (no fs module)`);
},
}")
(string->es "require('fs')")))
;; so that we do not have to modify existing compiler output (which would
;; break the first round of compilation before these are defined)
(string->es "const fsdata = $$$k$fsdata$k$")
(string->es "const fs = $$$k$fs$k$")
(define (es:file->string path)
(if (string->es "fsdata[$$path] === undefined")
(string->es
"fsdata[$$path] = fs.readFileSync($$path).toString()")
(string->es "fsdata[$$path]")))))