Created
December 9, 2015 15:30
-
-
Save nhusher/50868fcfa8594785855c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns timeline.roller | |
(:require [instaparse.core :as insta]) | |
(:use [clojure.string :only [join]])) | |
; | |
; -- Parsing code ----------------------------------------------------------- | |
; | |
; Known limitations | |
; - no negative numbers | |
; - no functions | |
; - no floating point numbers | |
(def roll-parser | |
(insta/parser | |
"expr = sum-expr | |
<sum-expr> = prod-expr | add | subtract | |
<prod-expr> = roll-expr | multiply | divide | |
<roll-expr> = unary-expr | roll | |
<unary-expr> = integer | scope | |
(* binary operators - +, -, /, *, d *) | |
multiply = prod-expr ws* <'*'> ws* prod-expr | |
divide = prod-expr ws* <'/'> ws* prod-expr | |
add = sum-expr ws* <'+'> ws* sum-expr | |
subtract = sum-expr ws* <'-'> ws* sum-expr | |
roll = unary-expr ws* <'d'> ws* unary-expr | <'d'> unary-expr | |
(* statements that act like unary operators *) | |
scope = <'('> ws* sum-expr ws* <')'> | |
(* different kinds of value tokens *) | |
integer = #'\\d+' | |
<ws> = <#'[\\s,]+'>")) | |
(defn- parse [s] | |
(let [result (insta/parse roll-parser s)] | |
; TODO: pass along the error object for loggin and display | |
(if (insta/failure? result) [:error s result] result))) | |
(defn dice-parse [s] | |
(parse s)) | |
; | |
; -- Evaluating code ----------------------------------------------------------- | |
; | |
(defn- unwrap [result] | |
"Unwraps a result object so we can do simple math on it. The run fn will rewrap" | |
(cond | |
(map? result) (unwrap (:result result)) | |
(seq? result) (reduce + result) | |
:else result)) | |
(defn- run [f n c] | |
"Executed the provided function f and generates a result object with name n and children c" | |
(let [result (apply f (map unwrap c))] | |
{ :name n :result result :raw c })) | |
(defn- run-scope [c] { :name :scope, :result (:result (first c)), :raw c }) | |
(defn- run-expr [c] | |
{ :name :expr :result (unwrap (first c)) :raw c }) | |
(defn- roll | |
"Rolls some dice." | |
([sides] (roll 1 sides)) | |
([number sides] (sort > (repeatedly (max (unwrap number) 1) #(+ (rand-int (max (unwrap sides) 2)) 1))))) | |
(defn- parse-int [s] (Integer. (re-find #"\d+" s))) | |
(defn- convert-error [e] (.-index e)) | |
(defn- eval-expr [s] | |
(let [[n & args] s | |
continue (fn [] (map eval-expr args))] | |
(case n | |
:multiply (run * n (continue)) | |
:divide (run / n (continue)) | |
:add (run + n (continue)) | |
:subtract (run - n (continue)) | |
:expr (run-expr (continue)) | |
:error {:name :error :result (second s) :raw (convert-error (last s)) } | |
:roll (run roll n (continue)) | |
:scope (run-scope (continue)) | |
:integer (parse-int (first args))))) | |
(defn dice-eval [p] | |
"Evaluates a dice roll expression." | |
(if (string? p) | |
(dice-eval (dice-parse p)) | |
(eval-expr p))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment