├── .gitignore
├── .travis.yml
├── README.md
├── pom.xml
└── src
├── main
├── clojure
│ └── kiss
│ │ ├── compiler.clj
│ │ └── core.clj
├── java
│ └── kiss
│ │ └── lang
│ │ ├── Analyser.java
│ │ ├── Compiler.java
│ │ ├── Environment.java
│ │ ├── Expression.java
│ │ ├── KFn.java
│ │ ├── Keywords.java
│ │ ├── RT.java
│ │ ├── Result.java
│ │ ├── Symbols.java
│ │ ├── Type.java
│ │ ├── Types.java
│ │ ├── expression
│ │ ├── Application.java
│ │ ├── Cast.java
│ │ ├── ClojureLookup.java
│ │ ├── Constant.java
│ │ ├── Def.java
│ │ ├── Do.java
│ │ ├── FieldLookup.java
│ │ ├── If.java
│ │ ├── InstanceOf.java
│ │ ├── Lambda.java
│ │ ├── Let.java
│ │ ├── Lookup.java
│ │ ├── Loop.java
│ │ ├── Map.java
│ │ ├── MethodCall.java
│ │ ├── Recur.java
│ │ ├── Return.java
│ │ └── Vector.java
│ │ ├── impl
│ │ ├── ATypedFn.java
│ │ ├── ClojureFn.java
│ │ ├── EvalResult.java
│ │ ├── ExitResult.java
│ │ ├── IExitResult.java
│ │ ├── KissException.java
│ │ ├── KissUtils.java
│ │ ├── LambdaFn.java
│ │ ├── MapEntry.java
│ │ ├── Mapping.java
│ │ ├── RecurResult.java
│ │ ├── ReturnResult.java
│ │ └── WrappedFn.java
│ │ └── type
│ │ ├── ACompoundType.java
│ │ ├── AFunctionType.java
│ │ ├── Anything.java
│ │ ├── FunctionType.java
│ │ ├── Intersection.java
│ │ ├── JavaType.java
│ │ ├── Maybe.java
│ │ ├── Not.java
│ │ ├── Nothing.java
│ │ ├── Null.java
│ │ ├── Predicate.java
│ │ ├── Reference.java
│ │ ├── Something.java
│ │ ├── Union.java
│ │ ├── Value.java
│ │ └── ValueSet.java
└── resources
│ └── kiss.png
└── test
├── clojure
└── kiss
│ ├── demo
│ ├── blank.clj
│ └── example.clj
│ └── test
│ ├── test_clojure.clj
│ ├── test_core.clj
│ ├── test_examples.clj
│ └── test_lambda.clj
└── java
└── kiss
└── test
├── EnvironmentTests.java
├── ExpressionTests.java
├── KissClojureTest.java
├── TestUtils.java
└── TypeTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /classes/
2 | /.project
3 | /target
4 | /.settings
5 | /.classpath
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk7
4 | - openjdk7
5 | - openjdk6
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## IMPORTANT NOTE: Merging with Magic
2 |
3 | Ongoing Kiss development has been merged into Magic (https://github.com/mikera/magic). Magic incorporates all of the key ideas developed in Kiss, but starts afresh with a much more solid implementation. If you are interested in Kiss, please join the Magic community going forwards, thanks!
4 |
5 | Mike.
6 |
7 | Kiss
8 | ====
9 |
10 |
11 |
12 |
13 | Kiss is Immutable, Statically compiled and Symbiotic (with Clojure).
14 |
15 | This is an **EXPERIMENT** in programming language design, combining several big ideas from different programming languages:
16 |
17 | - A smart static type system that makes it easy to write correct code without boilerplate
18 | - Lisp concepts of homoiconicity and macro-driven metaprogramming
19 | - Functional programming concepts of programming with pure functions and immutable values
20 | - The ability to run on the excellent JVM platform and take advantage of the huge library ecosystem this gives you
21 | - The (I believe novel?) concept of programming with a succession of immutable environments
22 |
23 | Kiss is designed to be identical to Clojure except where necessary to incorporate the above ideas.
24 |
25 | 
26 |
27 | ## Objectives
28 |
29 | - Productivity like **Clojure**
30 | - Performance like **Java**
31 | - Type safety like **Haskell**
32 |
33 |
34 | ## Examples
35 |
36 | ```clojure
37 | ;; NOTE: This is Clojure code, being used to bootstrap Kiss....
38 | (ns my.clojure.project
39 | (:use kiss.core))
40 |
41 | (defn call-kiss-from-clojure []
42 | (kiss
43 | (str "Hello from Kiss!")))
44 | ```
45 |
46 | For more working code examples see the [examples.clj](https://github.com/mikera/kiss/blob/master/src/test/clojure/kiss/demo/example.clj) namespace.
47 |
48 | ## Solution
49 |
50 | Kiss takes the following approach to language design:
51 |
52 | - **Immutable environments** - all Kiss code is compiled against a specific immutable environment, potentially creating a new immutable environment (with any definitions updated). Environments are *first class* and represent the complete state of the code base, making them highly amenable to static analysis.
53 | - **Statically compiled** - Kiss objects all have a static type, and the compiler will use these to generate decent bytecode. Types are *first class* and can be used both at compile time and runtime. Exact features of the type system are still to be determined, but at a minimum will take full advantage of all JVM types.
54 | - **Symbiotic with Clojure** - Kiss is bootstrapped on top of Clojure, and designed to be used within a Clojure environment. You can call Clojure functions / macros transparently, and Kiss functions will be IFn instances that are equally usable from Clojure. Kiss will just use the Clojure reader and syntax directly.
55 |
56 | ## See the Wiki for more details
57 |
58 | - [Detailed Rationale](https://github.com/mikera/kiss/wiki/Rationale)
59 | - [Differences with Typed Clojure](https://github.com/mikera/kiss/wiki/Differences-with-Typed-Clojure)
60 | - [Implementation Ideas](https://github.com/mikera/kiss/wiki/Implementation-Ideas)
61 |
62 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | kiss
5 | net.mikera
6 | 0.0.1-SNAPSHOT
7 |
8 | net.mikera
9 | clojure-pom
10 | 0.0.4
11 |
12 |
13 |
14 |
15 | clojars.org
16 | Clojars repository
17 | https://clojars.org/repo
18 |
19 |
20 |
21 |
22 | org.clojure
23 | clojure
24 | 1.7.0-alpha4
25 |
26 |
27 | net.mikera
28 | cljunit
29 | 0.3.1
30 | test
31 |
32 |
33 | net.mikera
34 | clojure-utils
35 | 0.6.1
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/main/clojure/kiss/compiler.clj:
--------------------------------------------------------------------------------
1 | (ns kiss.compiler)
2 |
3 | (set! *warn-on-reflection* true)
4 | (set! *unchecked-math* true)
5 |
6 | (defn clojure-compile
7 | "Converts kiss code to an executable Clojure form"
8 | [& body]
9 | `(do ~@body))
--------------------------------------------------------------------------------
/src/main/clojure/kiss/core.clj:
--------------------------------------------------------------------------------
1 | (ns kiss.core
2 | (:require [kiss.compiler :as compiler])
3 | (:refer-clojure :exclude [compile])
4 | (:import [kiss.lang RT Environment Analyser Expression KFn Result])
5 | (:use [mikera.cljutils error]))
6 |
7 | (set! *warn-on-reflection* true)
8 | (set! *unchecked-math* true)
9 |
10 | ;; EXPERIMENTAL - API subject to change!!!
11 | ;;
12 | ;; Don't rely on any of this stuff being here in the future
13 |
14 | (defmacro environment
15 | "Creates an Environment with the given Symbol -> Expression mappings."
16 | ([]
17 | `RT/ENVIRONMENT)
18 | ([mappings]
19 | `(environment RT/ENVIRONMENT ~mappings))
20 | ([env mappings]
21 | `(reduce
22 | (fn [^Environment e# [^Symbol k# v#]] (.define e# k# (analyse v#)))
23 | ~env
24 | (quote ~mappings))))
25 |
26 | (defn empty-environment
27 | "Returns an empty Kiss Environment"
28 | ([]
29 | Environment/EMPTY))
30 |
31 | (defn new-environment
32 | "Returns an empty Kiss Environment"
33 | ([]
34 | (environment)))
35 |
36 | (defn analyse
37 | "Analyse a form, resulting a Kiss Expression AST"
38 | (^Expression [form]
39 | (analyse (environment) form))
40 | (^Expression [^Environment env form]
41 | (Analyser/analyse env form)))
42 |
43 | (defn compile
44 | (^KFn [form]
45 | (compile (environment) form))
46 | (^KFn [^Environment env form]
47 | (let [ex (analyse env form)]
48 | (kiss.lang.Compiler/compile env ex))))
49 |
50 |
51 |
52 |
53 | (defn kmerge
54 | "Merge Kiss Environments, returning a new Environment"
55 | (^Environment [^Environment a] a)
56 | (^Environment [^Environment a ^Environment b] (.merge a b))
57 | (^Environment [^Environment a ^Environment b & more ]
58 | (reduce kmerge (kmerge a b) more)))
59 |
60 | (defn result
61 | "Returns the latest evaluation result from a given Kiss Environment"
62 | ([^Result e]
63 | (.getResult e)))
64 |
65 | (defmacro kiss
66 | "Compiles and executes Kiss code in the given Environment, returning the result"
67 | ([body]
68 | `(let [env# (environment)]
69 | (kiss env# ~body)))
70 | ([env body]
71 | `(let [env# ~env
72 | ex# (compile env# (quote ~body))]
73 | (ex#))))
74 |
75 | (defmacro kisst
76 | "Returns the type of a given expression, without executing it."
77 | ([body]
78 | `(kisst (environment) ~body))
79 | ([env body]
80 | `(let [env# ~env
81 | ex# (compile env# (quote ~body))]
82 | (.getReturnType ex#))))
83 |
84 | (defn kisse*
85 | ([^Environment env form]
86 | (let [body (analyse env form)]
87 | (.getEnvironment (.interpret body env)))))
88 |
89 | (defmacro kisse
90 | "Compiles and executes Kiss code in the given Environment, returning the new Environment."
91 | ([body]
92 | `(kisse (environment) ~body))
93 | ([env body]
94 | `(let [env# ~env
95 | body# (quote ~body)]
96 | (kisse* env# body#))))
97 |
98 | (def kiss-repl-env (atom (environment)))
99 |
100 | (defn kissify
101 | "TODO: figure out how to hack the REPL"
102 | ([]
103 | (alter-var-root #'clojure.core/eval
104 | (fn [form]
105 | (swap! kiss-repl-env (fn [env] (kisse* env form)))
106 | (.getResult ^Result @kiss-repl-env)))))
107 |
--------------------------------------------------------------------------------
/src/main/java/kiss/lang/Analyser.java:
--------------------------------------------------------------------------------
1 | package kiss.lang;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | import kiss.lang.expression.Application;
10 | import kiss.lang.expression.ClojureLookup;
11 | import kiss.lang.expression.Constant;
12 | import kiss.lang.expression.Def;
13 | import kiss.lang.expression.Do;
14 | import kiss.lang.expression.If;
15 | import kiss.lang.expression.InstanceOf;
16 | import kiss.lang.expression.Lambda;
17 | import kiss.lang.expression.Let;
18 | import kiss.lang.expression.Lookup;
19 | import kiss.lang.expression.Loop;
20 | import kiss.lang.expression.Recur;
21 | import kiss.lang.expression.Return;
22 | import kiss.lang.expression.Vector;
23 | import kiss.lang.impl.KissException;
24 | import kiss.lang.impl.KissUtils;
25 | import kiss.lang.type.Intersection;
26 | import kiss.lang.type.JavaType;
27 | import kiss.lang.type.Union;
28 | import clojure.lang.IFn;
29 | import clojure.lang.IPersistentMap;
30 | import clojure.lang.IPersistentVector;
31 | import clojure.lang.ISeq;
32 | import clojure.lang.PersistentHashMap;
33 | import clojure.lang.RT;
34 | import clojure.lang.Symbol;
35 |
36 | /**
37 | * Kiss language analyser
38 | *
39 | * Design intent:
40 | * - Converts Clojure forms to Kiss ASTs
41 | *
42 | * @author Mike
43 | */
44 | public class Analyser {
45 |
46 | /**
47 | * Converts a Kiss form into an Expression
48 | *
49 | * @param form
50 | * @return
51 | */
52 | public static Expression analyse(Environment env, Object form) {
53 | if (form instanceof Symbol) return analyseSymbol(env,(Symbol)form);
54 | if (form instanceof ISeq) return analyseSeq(env,(ISeq)form);
55 | if (form instanceof IPersistentVector) return analyseVector(env,(IPersistentVector)form);
56 | if (form instanceof IPersistentMap) return analyseMap(env,(IPersistentMap)form);
57 | return Constant.create(form);
58 | }
59 |
60 | public static Expression analyse(Object form) {
61 | return analyse(Environment.EMPTY,form);
62 | }
63 |
64 | @SuppressWarnings("unchecked")
65 | public static Type analyseType(Object form) {
66 | if (form instanceof Symbol) {
67 | return analyseTypeSymbol((Symbol)form);
68 | }
69 | if (form instanceof Class) {
70 | return JavaType.create((Class