Skip to content

Instantly share code, notes, and snippets.

@idozorenko
Created December 7, 2020 22:09
Show Gist options
  • Save idozorenko/a0c5bb5e5d38a5ba92cca7f43d1d3392 to your computer and use it in GitHub Desktop.
Save idozorenko/a0c5bb5e5d38a5ba92cca7f43d1d3392 to your computer and use it in GitHub Desktop.
(ns example.core
(:require [clojure.walk :refer [postwalk]]
[clojure.core.match :refer [match]]
[instaparse.core :as insta]))
(def grammar
"additive_expr = additive_expr add_op multiplicative_expr | multiplicative_expr
<add_op> = '+' | '-'
multiplicative_expr = multiplicative_expr mult_op unary_expr | unary_expr
mult_op = '*' | '/' | '%'
unary_expr = unary_op primary_expr | primary_expr
unary_op = '+' | '-'
<primary_expr> = <'('> additive_expr <')'> | unsigned_number
unsigned_number = #\"[0-9]+\\.?\\d*\"")
(defn parse [expr]
(let [parser (insta/parser grammar :auto-whitespace :standard)
ast (-> expr (parser))]
(postwalk
(fn [form]
(match form
[:unsigned_number x] (read-string x)
[:unary_op op] op
[:mult_op op] (case op "%" "mod" op)
[(:or :unary_expr :additive_expr :multiplicative_expr) x] x
[:unary_expr op x] `((resolve (symbol ~op)) ~x)
[(:or :multiplicative_expr :additive_expr) x op y] `(~(resolve (symbol op)) ~x ~y)
:else form))
ast)))
(println (parse "(4.25 + 3.75) / 2")) ; (#'clojure.core// (#'clojure.core/+ 4.25 3.75) 2)
(println (eval (parse "(4.25 + 3.75) / 2"))) ; 4.0
(println (parse "8 % 3 * 10")) ; (#'clojure.core/* (#'clojure.core/mod 8 3) 10)
(println (eval (parse "8 % 3 * 10"))) ; 20
(println (parse "5 x 3")) ; Parse error at line 1, column 3:
; 5 x 3
; ^
; Expected one of:
; %
; /
; *
; -
; +
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment