109 lines
4.2 KiB
Clojure
109 lines
4.2 KiB
Clojure
(ns beowulf.read
|
|
"This provides the reader required for boostrapping. It's not a bad
|
|
reader - it provides feedback on errors found in the input - but it isn't
|
|
the real Lisp reader.
|
|
|
|
Intended deviations from the behaviour of the real Lisp reader are as follows:
|
|
|
|
1. It reads the meta-expression language `MEXPR` in addition to the
|
|
symbolic expression language `SEXPR`, which I do not believe the Lisp 1.5
|
|
reader ever did;
|
|
2. It treats everything between a double semi-colon and an end of line as a comment,
|
|
as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
|
|
|
|
Both these extensions can be disabled by using the `--strict` command line
|
|
switch."
|
|
(:require ;; [beowulf.reader.char-reader :refer [read-chars]]
|
|
[beowulf.reader.generate :refer [generate]]
|
|
[beowulf.reader.parser :refer [parse]]
|
|
[beowulf.reader.simplify :refer [simplify]]
|
|
[clojure.string :refer [join split starts-with? trim]])
|
|
(:import [java.io InputStream]
|
|
[instaparse.gll Failure]))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;
|
|
;;; This file provides the reader required for boostrapping. It's not a bad
|
|
;;; reader - it provides feedback on errors found in the input - but it isn't
|
|
;;; the real Lisp reader.
|
|
;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;
|
|
;;; Copyright (C) 2022-2023 Simon Brooke
|
|
;;;
|
|
;;; This program is free software; you can redistribute it and/or
|
|
;;; modify it under the terms of the GNU General Public License
|
|
;;; as published by the Free Software Foundation; either version 2
|
|
;;; 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 General Public License
|
|
;;; along with this program; if not, write to the Free Software
|
|
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defn strip-line-comments
|
|
"Strip blank lines and comment lines from this string `s`, expected to
|
|
be Lisp source."
|
|
[^String s]
|
|
(join "\n"
|
|
(remove
|
|
#(or (empty? %)
|
|
(starts-with? (trim %) ";;"))
|
|
(split s #"\n"))))
|
|
|
|
(defn number-lines
|
|
([^String s]
|
|
(number-lines s nil))
|
|
([^String s ^Failure e]
|
|
(let [l (-> e :line)
|
|
c (-> e :column)]
|
|
(join "\n"
|
|
(map #(str (format "%5d %s" (inc %1) %2)
|
|
(when (= l (inc %1))
|
|
(str "\n" (apply str (repeat c " ")) "^")))
|
|
(range)
|
|
(split s #"\n"))))))
|
|
|
|
(defn gsp
|
|
"Shortcut macro - the internals of read; or, if you like, read-string.
|
|
Argument `s` should be a string representation of a valid Lisp
|
|
expression."
|
|
[s]
|
|
(let [source (strip-line-comments s)
|
|
parse-tree (parse source)]
|
|
(if (instance? Failure parse-tree)
|
|
(doall (println (number-lines source parse-tree))
|
|
(throw (ex-info "Ne can forstande " (assoc parse-tree :source source))))
|
|
(generate (simplify parse-tree)))))
|
|
|
|
(defn read-from-console
|
|
"Attempt to read a complete lisp expression from the console. NOTE that this
|
|
will only really work for S-Expressions, not M-Expressions."
|
|
[]
|
|
(loop [r (read-line)]
|
|
(if (and (= (count (re-seq #"\(" r))
|
|
(count (re-seq #"\)" r)))
|
|
(= (count (re-seq #"\[" r))
|
|
(count (re-seq #"\]" r))))
|
|
r
|
|
(recur (str r "\n" (read-line))))))
|
|
|
|
(defn READ
|
|
"An implementation of a Lisp reader sufficient for bootstrapping; not necessarily
|
|
the final Lisp reader. `input` should be either a string representation of a LISP
|
|
expression, or else an input stream. A single form will be read."
|
|
([]
|
|
(gsp (read-from-console)))
|
|
([input]
|
|
(cond
|
|
(empty? input) (READ)
|
|
(string? input) (gsp input)
|
|
(instance? InputStream input) (READ (slurp input))
|
|
:else (throw (ex-info "READ: `input` should be a string or an input stream" {})))))
|