Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/target
/classes
/checkouts
/web
pom.xml
pom.xml.asc
*.jar
*.class
/lib/
/classes/
/target/
/checkouts/
.lein-deps-sum
.lein-repl-history
.lein-plugins/
.lein-failures
.nrepl-port
/.lein-*
/.nrepl-port
.hgignore
.hg/
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: clojure
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
# psiml
a prototype language with subtyping polymorphism and type inference

*Polymorphic, Subtyped and type Infered ML* is a prototype language inspired by Stephen Dolans' [MLsub](http://www.cl.cam.ac.uk/~sd601/mlsub/).

## Usage

```sh
# On the JVM
lein run
lein repl
lein test
# On your browser
lein cljsbuild once
firefox index.html
```
3 changes: 3 additions & 0 deletions doc/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction to psiml

TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
9 changes: 9 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<header>
<meta charset="utf-8" />
<title>psiml</title>
</header>
<body>
<script src="web/main.js" type="text/javascript"></script>
</body>
</html>
18 changes: 18 additions & 0 deletions project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(defproject psiml "0.1.0-SNAPSHOT"
:description "Polymorphic, Subtyped and type Infered ML"
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/core.match "0.3.0-alpha5"]
[org.clojure/clojurescript "1.9.946"
:exclusions [org.apache.ant/ant]]]
:plugins [[lein-cljsbuild "1.1.7"
:exclusions [[org.clojure/clojure]]]]
:cljsbuild {:builds [{:source-paths ["src"]
:compiler {:main psiml.core
:output-to "web/main.js"
:output-dir "web/"
:source-map "web/main.js.map"
:optimizations :whitespace
:pretty-print true}}]}
:main ^:skip-aot psiml.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
32 changes: 32 additions & 0 deletions src/psiml/core.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(ns psiml.core
(:require [psiml.type :as t]
[psiml.parse :as p]
[clojure.core.match :refer [match]]))

#?(:cljs (enable-console-print!))

(defn eval-expr
"Evaluates an expression"
([expr] (eval-expr expr {}))
([expr env]
(match expr
[:abs n e] [:abs n (eval-expr e env)]
[:rec n e] [:rec n (eval-expr e (conj env [n e]))]
[:lit c] [:cst c]
[:var n] (or (env n) [:var n])
[:app e1 e2] (let [e2' (eval-expr e2 env)]
(or (match e1 [:abs n e1'] (eval-expr e1' (conj env [n e2'])))
[:app (eval-expr e1 env) e2']))
[:struct m] [:struct (reduce (fn [m' [l e]] (assoc m' l (eval-expr e env))) {} m)]
[:get l e] (let [e' (eval-expr e env)]
(or (match e' [:struct m] (m l))
[:get l e']))
expr)))

(defn -main
"I don't do a whole lot ... yet."
[& args]
(let [e (p/string "(fn [n1 n2] true)")]
(println e)))

#?(:cljs (-main))
44 changes: 44 additions & 0 deletions src/psiml/parse.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
(ns psiml.parse
"Parses concrete syntax, producing abstract syntax
e := 1 --> [:lit 1]
| true --> [:lit true]
| (fn [a b] e) --> [:abs :a [:abs :b e']]
| (e1 e2 e3) --> [:app [:app e1' e2'] e3']
| {:l1 e1 :l2 e2} --> [:struct {:l1 e1' :l2 e2'}]
| (:l s) --> [:get :l s']
| (let [n e1] e2) --> [:app [:abs n e2'] e1']"
(:refer-clojure :exclude [read-string])
(:require [clojure.core.match :refer [match]]
[#?(:clj clojure.edn
:cljs cljs.reader) :refer [read-string]]))

(defn data
"Parses concrete syntax from data"
[d]
(cond
(seq? d)
(match d
(['fn [a] b] :seq)
(if (symbol? a) [:abs (keyword a) (data b)])
(['fn [a & args] b] :seq)
(data (list 'fn [a] (list 'fn args b)))
(['let [n e] b] :seq)
(if (symbol? n) [:app [:abs (keyword n) (data b)] (data e)])
(['let [n e & r] b] :seq)
(data (list 'let [n e] (list 'let r b)))
([a b] :seq)
(if (keyword? a) [:get a (data b)]
[:app (data a) (data b)])
([a b & r] :seq)
(data (cons (list a b) r)))
(symbol? d)
[:var (keyword d)]
(or (integer? d) (true? d) (false? d))
[:lit d]
(map? d)
[:struct (reduce (fn [m [l v]] (assoc m l (data v))) {} d)]))

(defn string
"Parses concrete syntax from a string"
[s]
(data (read-string s)))
52 changes: 52 additions & 0 deletions src/psiml/print.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
(ns psiml.print
(:require [clojure.core.match :refer [match]])
(:require [psiml.util :refer [flatten-kw interpose-fn]]))

(defn expr
[e]
(match e
[:abs n e1] (do (print "λ") (print n) (print ".") (expr e1))
[:app e1 e2] (do (print "(") (expr e1)
(print " ") (expr e2) (print ")"))
[:lit l] (print l)
[:var n] (print n)
[:struct m]
(do (print "{")
(interpose-fn #(print " ")
(map (fn [[l e]]
#(do (print l)
(print " ")
(expr e))) m))
(print "}"))
[:get l e1] (do (print "(") (print l)
(print " ") (expr e1) (print ")"))
[:if test then else]
(do (print "(if ") (expr test)
(print " then ") (expr then)
(print " else ") (expr else) (print ")"))))

(defn type
[t]
(match t
[:top] (print "⊤")
[:bot] (print "⊥")
[:t base] (print base)
[:var n] (do (print "'") (print n))
[:abs t1 t2] (do (print "((") (type t1) (print ")")
(print " → ") (type t2) (print ")"))
[:struct m]
(do (print "{")
(interpose-fn #(print " ")
(map (fn [[l t]]
#(do (print l)
(print " ")
(type t))) m))
(print "}"))
[:meet & ts]
(interpose-fn #(print " ⊓ ")
(map #(fn [] (type %))
(flatten-kw :meet ts [])))
[:join & ts]
(interpose-fn #(print " ⊔ ")
(map #(fn [] (type %))
(flatten-kw :join ts [])))))
Loading