Safety commit before major change - this does not work.
This commit is contained in:
parent
fd7cc71480
commit
34096ecae5
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,3 +10,5 @@ pom.xml.asc
|
|||
/.nrepl-port
|
||||
.hgignore
|
||||
.hg/
|
||||
.idea/
|
||||
*~
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# beowulf
|
||||
|
||||
LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature.
|
||||
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
|
||||
|
||||
## What this is
|
||||
|
||||
|
|
26
beowulf.iml
Normal file
26
beowulf.iml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module cursive.leiningen.project.LeiningenProjectsManager.displayName="beowulf:0.2.1-SNAPSHOT" cursive.leiningen.project.LeiningenProjectsManager.isLeinModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<output url="file://$MODULE_DIR$/target/default/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/default/classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/dev-resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/clojure" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/default" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Leiningen: clojure-complete:0.2.5" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: environ:1.1.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: instaparse:1.4.10" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: nrepl:0.6.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.clojure/clojure:1.8.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.clojure/math.numeric-tower:0.0.4" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.clojure/tools.cli:0.4.2" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.clojure/tools.trace:0.7.10" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -13,7 +13,9 @@
|
|||
[org.clojure/tools.trace "0.7.10"]
|
||||
[environ "1.1.0"]
|
||||
[instaparse "1.4.10"]]
|
||||
:java-source-paths ["src/java"]
|
||||
:main ^:skip-aot beowulf.core
|
||||
:min-lein-version "2.0.0"
|
||||
:plugins [[lein-cloverage "1.1.1"]
|
||||
[lein-codox "0.10.7"]
|
||||
[lein-environ "1.1.0"]]
|
||||
|
@ -28,7 +30,7 @@
|
|||
["uberjar"]
|
||||
["change" "version" "leiningen.release/bump-version"]
|
||||
["vcs" "commit"]]
|
||||
|
||||
:source-paths ["src/clojure"]
|
||||
:target-path "target/%s"
|
||||
:url "https://github.com/simon-brooke/the-great-game"
|
||||
:url "https://github.com/simon-brooke/beowulf"
|
||||
)
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
(ns beowulf.host
|
||||
"provides Lisp 1.5 functions which can't be (or can't efficiently
|
||||
be) implemented in Lisp 1.5, which therefore need to be implemented in the
|
||||
host language, in this case Clojure.")
|
||||
|
||||
;; these are CANDIDATES to be host-implemented. only a subset of them MUST be.
|
||||
;; those which can be implemented in Lisp should be, since that aids
|
||||
;; portability.
|
||||
|
||||
;; RPLACA
|
||||
|
||||
;; RPLACD
|
||||
|
||||
;; PLUS
|
||||
|
||||
;; MINUS
|
||||
|
||||
;; DIFFERENCE
|
||||
|
||||
;; QUOTIENT
|
||||
|
||||
;; REMAINDER
|
||||
|
||||
;; ADD1
|
||||
|
||||
;; SUB1
|
||||
|
||||
;; MAX
|
||||
|
||||
;; MIN
|
||||
|
||||
;; RECIP
|
||||
|
||||
;; FIXP
|
||||
|
||||
;; NUMBERP
|
||||
|
||||
;;
|
|
@ -3,20 +3,43 @@
|
|||
Lisp 1.5 lists do not necessarily have a sequence as their CDR, so
|
||||
cannot be implemented on top of Clojure lists.")
|
||||
|
||||
(def NIL
|
||||
"The canonical empty list symbol."
|
||||
(symbol "NIL"))
|
||||
;; (def NIL
|
||||
;; "The canonical empty list symbol."
|
||||
;; 'NIL)
|
||||
|
||||
(def T
|
||||
"The canonical true value."
|
||||
(symbol "T")) ;; true.
|
||||
;; (def T
|
||||
;; "The canonical true value."
|
||||
;; 'T) ;; true.
|
||||
|
||||
(def F
|
||||
"The canonical false value - different from `NIL`, which is not canonically
|
||||
false in Lisp 1.5."
|
||||
(symbol "F")) ;; false as distinct from nil
|
||||
;; (def F
|
||||
;; "The canonical false value - different from `NIL`, which is not canonically
|
||||
;; false in Lisp 1.5."
|
||||
;; 'F) ;; false as distinct from nil
|
||||
|
||||
(deftype ConsCell [^:unsynchronized-mutable car ^:unsynchronized-mutable cdr]
|
||||
;; Note that, because the CAR and CDR fields are unsynchronised mutable - i.e.
|
||||
;; plain old Java instance variables which can be written as well as read -
|
||||
;; ConsCells are NOT thread safe. This does not matter, since Lisp 1.5 is
|
||||
;; single threaded.
|
||||
|
||||
(CAR [this] (.car this))
|
||||
(CDR [this] (.cdr this))
|
||||
(RPLACA
|
||||
[this value]
|
||||
(if
|
||||
(or
|
||||
(instance? beowulf.cons_cell.ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(do
|
||||
(set! (. cell CAR) value)
|
||||
cell)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACA: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))))
|
||||
|
||||
(deftype ConsCell [CAR CDR]
|
||||
clojure.lang.ISeq
|
||||
(cons [this x] (ConsCell. x this))
|
||||
(first [this] (.CAR this))
|
61
src/clojure/beowulf/host.clj
Normal file
61
src/clojure/beowulf/host.clj
Normal file
|
@ -0,0 +1,61 @@
|
|||
(ns beowulf.host
|
||||
"provides Lisp 1.5 functions which can't be (or can't efficiently
|
||||
be) implemented in Lisp 1.5, which therefore need to be implemented in the
|
||||
host language, in this case Clojure."
|
||||
(:require [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]]))
|
||||
|
||||
;; these are CANDIDATES to be host-implemented. only a subset of them MUST be.
|
||||
;; those which can be implemented in Lisp should be, since that aids
|
||||
;; portability.
|
||||
|
||||
;; RPLACA
|
||||
|
||||
(defn RPLACA
|
||||
[^beowulf.cons_cell.ConsCell cell value]
|
||||
(if
|
||||
(instance? beowulf.cons_cell.ConsCell cell)
|
||||
(if
|
||||
(or
|
||||
(instance? beowulf.cons_cell.ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(do
|
||||
(set! (. cell CAR) value)
|
||||
cell)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACA: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca})))
|
||||
(throw (ex-info
|
||||
(str "Invalid cell in RPLACA: `" cell "` (" (type cell) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))))
|
||||
|
||||
;; RPLACD
|
||||
|
||||
;; PLUS
|
||||
|
||||
;; MINUS
|
||||
|
||||
;; DIFFERENCE
|
||||
|
||||
;; QUOTIENT
|
||||
|
||||
;; REMAINDER
|
||||
|
||||
;; ADD1
|
||||
|
||||
;; SUB1
|
||||
|
||||
;; MAX
|
||||
|
||||
;; MIN
|
||||
|
||||
;; RECIP
|
||||
|
||||
;; FIXP
|
||||
|
||||
;; NUMBERP
|
||||
|
||||
;;
|
246
src/java/beowulf/substrate/ConsCell.java
Normal file
246
src/java/beowulf/substrate/ConsCell.java
Normal file
|
@ -0,0 +1,246 @@
|
|||
package beowulf.substrate;
|
||||
|
||||
import clojure.lang.*;
|
||||
|
||||
import java.lang.Number;
|
||||
import beowulf.cons_cell.NIL;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A cons cell - a tuple of two pointers - is the fundamental unit of Lisp store.
|
||||
* </p>
|
||||
* <p>
|
||||
* Implementing mutable data in Clojure if <em>hard</em> - deliberately so.
|
||||
* But Lisp 1.5 cons cells need to be mutable. This class is part of thrashing
|
||||
* around trying to find a solution.
|
||||
* </p>
|
||||
*/
|
||||
public class ConsCell
|
||||
implements clojure.lang.IPersistentCollection,
|
||||
clojure.lang.ISeq,
|
||||
clojure.lang.Seqable,
|
||||
clojure.lang.Sequential {
|
||||
|
||||
/**
|
||||
* The car of a cons cell can't be just any object; it needs to be
|
||||
* a number, a symbol or a cons cell. But as there is no common superclass
|
||||
* or interface for those things, we use Object here and specify the
|
||||
* types of objects which can be stored in the constructors and setter
|
||||
* methods.
|
||||
*/
|
||||
private Object car;
|
||||
|
||||
/**
|
||||
* The car of a cons cell can't be just any object; it needs to be
|
||||
* a number, a symbol or a cons cell. But as there is no common superclass
|
||||
* or interface for those things, we use Object here and specify the
|
||||
* types of objects which can be stored in the constructors and setter
|
||||
* methods.
|
||||
*/
|
||||
private Object cdr;
|
||||
|
||||
public ConsCell(ConsCell car, ConsCell cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(ConsCell car, Symbol cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(ConsCell car, Number cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Symbol car, ConsCell cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Symbol car, Symbol cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Symbol car, Number cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Number car, ConsCell cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Number car, Symbol cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public ConsCell(Number car, Number cdr) {
|
||||
this.car = car;
|
||||
this.cdr = cdr;
|
||||
}
|
||||
|
||||
public Object getCar() {
|
||||
return this.car;
|
||||
}
|
||||
|
||||
public Object getCdr() {
|
||||
return this.cdr;
|
||||
}
|
||||
|
||||
public ConsCell setCar(ConsCell c) {
|
||||
this.car = c;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsCell setCdr(ConsCell c) {
|
||||
this.cdr = c;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsCell setCar(java.lang.Number n) {
|
||||
this.car = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsCell setCdr(java.lang.Number n) {
|
||||
this.cdr = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsCell setCar(clojure.lang.Symbol s) {
|
||||
this.car = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsCell setCdr(clojure.lang.Symbol s) {
|
||||
this.cdr = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
boolean result;
|
||||
|
||||
if (other instanceof IPersistentCollection) {
|
||||
ISeq s = ((IPersistentCollection) other).seq();
|
||||
|
||||
result = this.car.equals(s.first()) &&
|
||||
this.cdr instanceof ConsCell &&
|
||||
((ISeq) this.cdr).equiv(s.more());
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder bob = new StringBuilder("(");
|
||||
|
||||
for (Object d = this; d instanceof ConsCell; d = ((ConsCell)d).cdr) {
|
||||
ConsCell cell = (ConsCell)d;
|
||||
bob.append(cell.car.toString())
|
||||
|
||||
if ( cell.cdr instanceof ConsCell) {
|
||||
bob.append(" ");
|
||||
} else if ( cell.cdr.toString().equals("NIL")) {
|
||||
/* That's an ugly hack to work around the fact I can't currently
|
||||
* get a handle on the NIL symbol itself. In theory, nothing else
|
||||
* in Lisp 1.5 should have the print-name `NIL`.*/
|
||||
bob.append(")");
|
||||
} else {
|
||||
bob.append(" . ").append(cell.cdr.toString()).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
return bob.toString();
|
||||
}
|
||||
|
||||
/* IPersistentCollection interface implementation */
|
||||
|
||||
public int count() {
|
||||
return this.cdr instanceof ConsCell ?
|
||||
1 + ((ConsCell) this.cdr).count() :
|
||||
1;
|
||||
}
|
||||
|
||||
/**
|
||||
* `empty` is completely undocumented, I'll return `null` until something breaks.
|
||||
*/
|
||||
public IPersistentCollection empty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* God alone knows what `equiv` is intended to do; it's completely
|
||||
* undocumented. But in PersistentList it's simply a synonym for 'equals',
|
||||
* and that's what I'll implement.
|
||||
*/
|
||||
public boolean equiv(Object o) {
|
||||
return this.equals(o);
|
||||
}
|
||||
|
||||
/* ISeq interface implementation */
|
||||
|
||||
public Object first() {
|
||||
return this.car;
|
||||
}
|
||||
|
||||
public ISeq next() {
|
||||
ISeq result;
|
||||
|
||||
if (this.cdr instanceof ConsCell) {
|
||||
result = (ISeq) this.cdr;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ISeq more() {
|
||||
ISeq result;
|
||||
|
||||
if (this.cdr instanceof ConsCell) {
|
||||
result = (ISeq) this.cdr;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new cons cell comprising the object `o` as car,
|
||||
* and myself as cdr. Hopefully by declaring the return value
|
||||
* `ConsCell` I'll satisfy both the IPersistentCollection and the
|
||||
* ISeq interfaces.
|
||||
*/
|
||||
public ConsCell cons(Object o) {
|
||||
if (o instanceof ConsCell) {
|
||||
return new ConsCell((ConsCell) o, this);
|
||||
} else if (o instanceof Number) {
|
||||
return new ConsCell((Number) o, this);
|
||||
} else if (o instanceof Symbol) {
|
||||
return new ConsCell((Symbol) o, this);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unrepresentable argument passed to CONS");
|
||||
}
|
||||
}
|
||||
|
||||
/* Seqable interface */
|
||||
public ISeq seq() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/* Sequential interface is just a marker and does not require us to
|
||||
* implement anything */
|
||||
|
||||
|
||||
}
|
18
test/beowulf/host_test.clj
Normal file
18
test/beowulf/host_test.clj
Normal file
|
@ -0,0 +1,18 @@
|
|||
(ns beowulf.host-test
|
||||
(:require [clojure.math.numeric-tower :refer [abs]]
|
||||
[clojure.test :refer :all]
|
||||
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]]
|
||||
[beowulf.host :refer :all]
|
||||
[beowulf.read :refer [gsp]]))
|
||||
|
||||
(deftest destructive-change-test
|
||||
(testing "RPLACA"
|
||||
(let
|
||||
[l (make-beowulf-list '(A B C D E))
|
||||
target (.CDR l)
|
||||
expected "(A F C D E)"
|
||||
actual (print-str (RPLACA target 'F))]
|
||||
(is (= actual expected)))
|
||||
|
||||
))
|
||||
|
Loading…
Reference in a new issue