ulambda/bootstrap/rebirth.scm

113 lines
4.7 KiB
Scheme
Raw Permalink Normal View History

;;;; Rebirth Lisp implemented in Birth and Rebirth Lisp (self-hosting)
;;;;
;;;; 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 TEMPORARY CODE that will be REWRITTEN IN ULAMBDA SCHEME after a
;;; very basic bootstrap is complete. It is retained as an important
;;; artifact for those who wish to build Ulambda Scheme from scratch without
;;; using another version of Ulambda itself. This is called "self-hosting".
;;;
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
;;; REBIRTH SHOULD NOT BE USED AS A GENERAL-PURPOSE LISP. It is very
;;; narrowly tested and absolutely has bugs or misfeatures that were
;;; explicitly ignored (some of which are explicitly noted).
;;;
;;; This is the compiler for Rebirth Lisp---it builds off of Birth by
;;; first eliminating the need for libprebirth; this allows _all_
;;; development to happen in a Lisp dialect, which liberates the last
;;; remaining process that isn't technically self-hosted. So, Rebirth
;;; completes the raw, self-hosting bootstrapping process.
;;;
;;; To continue with the creepy birthing puns, you can consider libprebirth
;;; to be the umbilical cord. After Birth, it's still attached---here we
;;; cut it.
;;;
;;; Of course, bootstrapping can't end there: we need a fully functioning
;;; Scheme compiler. Rebirth may as well be called Rerebirth, or
rebirth: Add define-macro This provides preliminary support for traditional Lisp macros, _not_ Scheme macros as we know them today; this implementation is easy to implement, and gives us a great foundation for moving forward. There are caveats to this simple and naive implementation, documented ad nauseam. It's probably worth a read if you're studying Rebirth in any level of detail for whatever reason. This is a pretty exciting change---it liberates us from rigid compiler changes and will allow us to rewrite fnmap into macros almost as-is. The change is fairly elegant, all things considered---the amount of code is minimal; most of the change consists of comments describing it and its caveats. This is a defining step in Rebirth, and brings it a step closer to being an actual Lisp rather than a fragment of one. (Though I still don't know if Rebirth Lisp will ever actually be a full Lisp. Please hold the arguments about Scheme not being a Lisp or I'll respond very immaturely with "your mom is not a Lisp", and we'll both be very confused and somewhat offended by one-another, with a net loss overall. Oh, wait, you're actually reading this?) Anyway, changes: * build-aux/bootstrap/rebirth.scm: Some rephrasing of toplevel comments, and addition of macro comments. (_macros): New ECMAScript variable. (cdfn-macro, macro-compile-result, list->ast): New procedures. Little full of comments. Lots of sap. (parse-lisp): Fix typo. Add third argument to `cdfn-proc' (#f). (cdfn-proc): Add third argument `id-override'. Use it in place of token value, if set. Remove semicolon from generated ES function (it was unneeded to begin with) so that it can be used in macro ES expressions. (macro?): New procedure, conditional based on availability of `string->es'. (apply-proc-or-macro): New procedure. Conditionally apply macro during compiler runtime or compile output for a procedure application. (fnmap)[define-macro]: Apply `cdfn-macro'. This will try to apply it even if the procedure isn't defined yet (e.g. first Rebirth pass), so don't call it until then! [else]: Use `apply-proc-or-macro'.
2017-12-11 22:43:29 -05:00
;;; Rererebirth, or Re*birth, or Reⁿbirth---it is a recursively self-hosting
;;; compiler. It adds features to itself each time it compiles itself.
;;;
;;; Note that we're dealing with a small subset of Scheme here, so certain
;;; things might be done differently given a proper implementation.
;;;
;;; This started as an exact copy of `birth.scm', modified to introduce
;;; additional features. This is important, since Birth is mostly a 1:1
;;; translation of the Prebirth compiler and needs to stay that way. This
;;; fork allows us to vary as much as we want from the initial
;;; implementation. See the commit history for this file for more
;;; information as to how it evolved (the first commit is the direct copy
;;; before actual code changes).
;;;
;;; This file follows a narrative (from Birth to Reⁿbirth), but it's more of
;;; a choose-your-adventure-book-style narrative: order of certain
;;; definitions unfortunately matters in this simple implementation. For
;;; example, primitive macros (e.g. `if') must be defined before they are
;;; used, so those appear at the top of this file, despite their definitions
;;; not being supported until future passes.
;;;
;;; Code:
;; So, to begin, goto STEP 0! -------------,
;; V
(include "rebirth/es.scm") ;; STEP 2 (start at STEP 0) <--,
(include "rebirth/relibprebirth.scm") ;; STEP 0 (start here) /
(include "rebirth/macro.scm") ;; STEP 1 (then go to STEP 2) -`
;; The runtime is then initialized and we can proceed with defining the
;; compiler.
;;
;; Most of the compiler has been extracted into a separate file so that it
;; can be used by other programs; this is necessary because the macro system
;; uses those definitions, so naturally any program using macros will
;; require that the compiler definitions be imported.
(include "rebirth/compiler.scm")
;; Compile Rebirth Lisp AST into ECMAScript.
;;
;; The AST can be generated with `parse-lisp'.
(define (rebirth->ecmascript ast)
(join "\n\n" (map sexp->es ast)))
;; Compile Rebirth Lisp AST into an ECMAScript program.
;;
;; This compiles the AST into ECMAScript using `rebirth->ecmascript' and
;; then wraps it in a self-executing function to limit scope and create the
;; toplevel environment.
(define (rebirth->ecmascript-prog ast env-es)
;; (note that we no longer depend on libprebirth)
rebirth: Primitive environment support Note that this doesn't yet provide any useful abstractions for creating discrete environments---it merely provides support for them moving forward. Hopefully. This is the next big step toward rewriting the compiler as a series of macro passes. I'll write more on this later; it has profound consequences (well, as a jump from Rebirth into Ulambda, which is a name that I haven't mentioned until now). It was yesterday that I also got word that I'll be speaking at LibrePlanet 2018 in March. This is great news, but unfortunate news for this project---it has had very little of time time to begin with, and now it's going to have even less until after the conference. * build-aux/bootstrap/rebirth.scm: Add toplevel note about Ulambda. Add notes to "Step 2" regarding environments. (es:envf, %es:env): Add macros. (lambda, let*, let, set!): Macro forms use environments. (%es:has-own-prop, %es:proto-of, %es:envobj-for, %es:setenv): New procedures for `set!' macro. (es:null?): Add procedure. (_macros): Remove ES global (now using `_env.macros'). (cdfn-macro, macro?): Use `_env.macros' in place of `macros'. (tparam->es, tname-verbatim?, env-ref, env-params): Add procedures. (tname->id): Use `tname-verbatim?', which extracts the digit check. (cdfn-var, cdfn-proc): Also assign to current environment. (apply-proc-or-macro): Use `_env.macros'. Apply function from environment. (sexp->es)[symbol]: Generate environment reference. (rebirth->ecmascript): Generate toplevel environment.
2018-01-07 00:11:17 -05:00
(string-append "(function(_env){"
(rebirth->ecmascript ast)
"})(" env-es ");"))
;; at this point, this program can parse itself and output a CST (sans
;; whitespace)
(es:console (rebirth->ecmascript-prog
(parse-lisp
(es:file->string "/dev/stdin"))
(es:empty-env)))