Skip to content

Instantly share code, notes, and snippets.

@blasut
Last active November 3, 2017 17:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blasut/b4ae4d594db0c9434207c002ca463bd3 to your computer and use it in GitHub Desktop.
Save blasut/b4ae4d594db0c9434207c002ca463bd3 to your computer and use it in GitHub Desktop.
(ns playingwithsyntax.core)
;; We can define our own 'reader', which says that <> are waaay coler than Sexps.
;; We "bottom" out to clojure, so we parse the <>-exp and return a sexp that clojure understands.
;; To support list literals we can return a quoted list from read.
(comment
;; This is useful for stepping through the my-read function
(def string-to-read "(+ 1 1)")
(def string-to-read "<+ 1 1>"))
(defn my-read [string-to-read]
(cond
(= \( (first string-to-read)) (read-string (str "'" string-to-read))
(= \< (first string-to-read)) (-> string-to-read
(clojure.string/replace "<" "(")
(clojure.string/replace ">" ")")
(read-string))
:else (read-string string-to-read)))
(my-read "<+ 1 1>") ;; => (+ 1 1)
(my-read "(+ 1 1)") ;; => (quote (+ 1 1))
;; So now we dont have sexps anymore (for an very small example ofc, only works with a single form)
(eval (my-read "<+ 1 1>"))
;; => 2
(eval (my-read "(+ 1 1)"))
;; => (+ 1 1)
;; the rest of clojure works as normal
(eval (my-read "{:a 3}"))
;; => {:a 3}
;; This is because macroexpansion works by receiving forms that are READ, macros operate on clojure data not on strings.
;; We don't have access to adding new syntax to the read-table in clojure.
;; And we can't extend the tagged literals because those are read AFTER clojure read.
;; Or, we could add a tagged literal that is the <>-sexp. But that is the same thing as here, but nicer.
;; It still has to read a stream, it doesn't change the clojure-built in read-table.
;; So we could use EDN tagged literals to build our new syntax, call it "clo<ure>" and read it in with tagged literals.
;; But this is fundamentally different to changing the read-table.
;; And the syntax would be a bit different because of how the reader works I think... Should try that though
;; So one would have to use our reader and READ from some stream BEFORE the clojure reader does anything.
;; this can be achieved by overwriting which reader clojure is using, like rebinding it when starting a new repl.
;; We can't just use a the new "syntax" with a macro without putting the <> forms into a string when passing it into the macro.
;; so the macro has to take a string or a normal clojure form, which is lol. doesn't work
(defmacro my-syntax [forms]
(if (string? forms)
`(my-read ~forms)
`(my-read ,(prn-str '~forms))))
(macroexpand '(my-syntax (+ 1 1)))
(macroexpand '(my-syntax "<+ 5 5>"))
(my-syntax (+ 1 1))
(my-syntax 5)
;; this works
(my-syntax "<+ 5 5>")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment