diff --git a/.gitignore b/.gitignore index b41fa4f..dee6131 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,4 @@ pom.xml.asc **/.DS_Store /sandbox ludus.sublime-workspace -ludus \ No newline at end of file +/ludus \ No newline at end of file diff --git a/project.clj b/project.clj index b040fc4..a9696ab 100644 --- a/project.clj +++ b/project.clj @@ -4,8 +4,15 @@ :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.11.1"] +<<<<<<< HEAD [babashka/fs "0.1.6"] [quil "4.0.0-SNAPSHOT"]] +||||||| 1c2ab51 + [babashka/fs "0.1.6"]] +======= + [babashka/fs "0.1.6"] + [quil "4.0.0-SNAPSHOT-1"]] +>>>>>>> 55d76f6854bf67119873d98e2c9c18d8390ab90a :plugins [[lein-cljfmt "0.8.0"]] :repl-options {:init-ns ludus.core} :main ludus.core diff --git a/src/ludus/draw.clj b/src/ludus/draw.clj new file mode 100644 index 0000000..5bc82e8 --- /dev/null +++ b/src/ludus/draw.clj @@ -0,0 +1,32 @@ +(ns ludus.draw + (:require [quil.core :as q] + [quil.middleware :as m])) + +(defn setup [] + (q/frame-rate 60) + (q/color-mode :hsb) + {:color 0 :angle 0}) + +(defn update-state [state] + {:color (mod (+ (:color state) 0.7) 255) + :angle (+ (:angle state) 0.1)}) + +(defn draw-state [state] + (q/background 240) + (q/fill (:color state) 255 255) + (let [angle (:angle state) + x (* 150 (q/cos angle)) + y (* 150 (q/sin angle))] + (q/with-translation [(/ (q/width) 2) + (/ (q/height) 2)] + (q/ellipse x y 100 100)))) + +(defn ludus-draw [] + (q/defsketch sketch + :title "Hello Ludus" + :size [500 500] + :setup setup + :update update-state + :draw draw-state + :features [] + :middleware [m/fun-mode])) diff --git a/src/ludus/ludus.ebnf b/src/ludus/ludus.ebnf new file mode 100644 index 0000000..3427f8a --- /dev/null +++ b/src/ludus/ludus.ebnf @@ -0,0 +1,135 @@ +(* + ludus.ebnf + + An Instaparse-style EBNF grammer for Ludus. +*) + +script = toplevel { toplevel } + +terminator = (";" | <{comment}> "\n")+ + +ws = (" " | "\t" | "\r")+ + +wsnl = (ws | <{comment}> "\n")+ + +reserved = "cond" | "let" | "if" | "then" | "else" | "nil" | "true" | "false" | "as" | "match" | "with" | "NaN" | "recur" + +comment = "&" not_nl* + +not_nl = #"[^\n]" + +toplevel = expression | import | test | ns + +test = <"test" ws> string expression + +import = <"import" ws> string name + +ns = <"ns" ws> name entries + +entries = [(name | entry) { [(name | entry)]}] + +expression = if | cond | let | tuple | atom | synthetic | block | match | fn | do | loop | dict | struct | list | ref | spawn | send | receive | repeat + +(* TODO: is this right? *) +repeat = <"repeat" ws> (number | name) fn_clause + +spawn = <"spawn" ws> expression + +receive = <"receive" ws? "{" wsnl?> match_clause {terminator [match_clause]} + +ref = <"ref" ws> name tuple (fn_clause + | (<"{" wsnl?> fn_clause {terminator [fn_clause]} )) + +do = <"do" ws> expression { expression} + +pipe = wsnl? "|>" wsnl? + +fn = lambda | named | complex + +lambda = <"fn" ws?> fn_clause + +named = <"fn" ws?> name fn_clause + +complex = <"fn" ws?> name "{" string? fn_clause {terminator [fn_clause]} + +fn_clause = tuple_pattern expression + +match = <"match" ws> expression match_clause {terminator [match_clause]} + +match_clause = pattern constraint? expression + +constraint = <"when" ws> expression + +let = <"let" ws> pattern [pattern { [pattern]}] <{separator} ws? ")"> + +struct_pattern = <"@{" wsnl?> [(name | pattern_entry | splattern) { [(name | pattern_entry | splattern)]}] <{separator} ws? "}"> + +dict_pattern = <"#{" wsnl?> [(name | pattern_entry | splattern) { [(name | pattern_entry | splattern)]}] <{separator} ws? "}"> + +pattern_entry = keyword pattern + +splattern = <"..."> name | ignored | placeholder + +block = <"{" wsnl?> expression { expression } + +cond = "cond" expression cond_clause {terminator [cond_clause]} + +cond_clause = expression expression + +arrow = " wsnl?> + +if = <"if" ws> expression expression <"else" ws> expression + +synthetic = (name | keyword | recur) (( (args | keyword))+) + +recur = <"recur"> + +separator = ("," | "\n") + +args = <"(" ws? {separator}> [arg_expr { [arg_expr]}] <{separator} ws? ")"> + +arg_expr = expression | placeholder + +placeholder = <"_"> + +tuple = <"(" wsnl?> [expression { [expression]}] <{separator} ws? ")"> + +list = <"[" wsnl?> [(expression | splat) { [(expression | splat)]}] <{separator} ws? "]"> + +struct = <"@{" wsnl?> [(name | entry) { [(name | entry)]}] <{separator} ws? "}"> + +dict = <"#{" wsnl?> [(name | entry | splat) { [(name | entry | splat)]}] <{separator} ws? "}"> + +entry = keyword expression + +splat = <"..."> name + +atom = name | ignored | keyword | number | string | boolean | nil + +boolean = true | false + +true = <"true"> + +false = <"false"> + +nil = <"nil"> + +string = <'"'> {escaped_quote | nonquote} <'"'> + +escaped_quote = "\\" '\"' +nonquote = #'[^"]' + +keyword = #":[a-zA-Z][a-zA-Z0-9\/\-_!\*\?]*" +ignored = #"_[a-z][a-zA-Z0-9\/\-_!\*\?]*" + +name = !reserved #"[a-z][a-zA-Z0-9\/\-_!\*\?]*" + +(* TODO: Debug this to reject things starting with 0, eg 012. *) +number = #"\-?[1-9][0-9]*" | #"\-?(0|[1-9][0-9]*).[0-9]+" | ["-"] "0" | "NaN" + diff --git a/src/ludus/prelude.clj b/src/ludus/prelude.clj index 3e03e31..4936f0a 100644 --- a/src/ludus/prelude.clj +++ b/src/ludus/prelude.clj @@ -2,8 +2,7 @@ (:require [ludus.data :as data] [ludus.show :as show] - [ludus.draw :as d] - )) + [ludus.draw :as d])) ;; TODO: make eq, and, or special forms that short-circuit ;; Right now, they evaluate all their args @@ -84,16 +83,20 @@ ::data/type ::data/clj :body (fn [ms] (Thread/sleep ms))}) (def conj- {:name "conj" - ::data/type ::data/clj - :body conj}) + ::data/type ::data/clj + :body conj}) (def assoc- {:name "assoc" - ::data/type ::data/clj - :body assoc}) + ::data/type ::data/clj + :body assoc}) (def get- {:name "get" - ::data/type ::data/clj - :body get}) + ::data/type ::data/clj + :body get}) + +(def draw {:name "draw" + ::data/type ::data/clj + :body draw/ludus-draw}) (def draw {:name "draw" ::data/type ::data/clj