prelude now passes validator
This commit is contained in:
parent
5874a56090
commit
20cb689d12
|
@ -44,7 +44,7 @@
|
|||
:dict (dict-str value)
|
||||
:set
|
||||
(string/join (map stringify (keys value)) ", ")
|
||||
:ref (stringify (value :^value))
|
||||
:box (stringify (value :^value))
|
||||
:fn (string "fn " (value :name))
|
||||
:applied (string "fn " (value :name))
|
||||
:function (string "builtin " (string value))
|
||||
|
@ -68,7 +68,7 @@
|
|||
:list (string "[" (stringify x) "]")
|
||||
:dict (string "#{" (stringify x) "}")
|
||||
:set (string "${" (stringify x) "}")
|
||||
:ref (string "box " (x :name) " [ " (stringify x) " ]")
|
||||
:box (string "box " (x :name) " [ " (stringify x) " ]")
|
||||
:pkg (show-pkg x)
|
||||
(stringify x)))
|
||||
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
(import spork/json :as j)
|
||||
(import /base :as b)
|
||||
|
||||
(defn- get-line [source line]
|
||||
((string/split "\n" source) (dec line)))
|
||||
|
||||
(defn scan-error [e out] (set (out :errors) e) (j/encode out))
|
||||
|
||||
(defn parse-error [e out] (set (out :errors) e) (j/encode out))
|
||||
(defn parse-error [e]
|
||||
(def msg (e :msg))
|
||||
(def line-num (get-in e [:token :line]))
|
||||
(def source (get-in e [:token :source]))
|
||||
(def source-line (get-line source line-num))
|
||||
(print "Parsing error: " msg)
|
||||
(print "On line " line-num ":")
|
||||
(print source-line))
|
||||
|
||||
(defn validation-error [e out] (set (out :errors) e) (j/encode out))
|
||||
|
||||
(defn validation-error [e]
|
||||
(def msg (e :msg))
|
||||
(def line-num (get-in e [:node :token :line]))
|
||||
(def source (get-in e [:node :token :source]))
|
||||
(def source-line (get-line source line-num))
|
||||
(case msg
|
||||
"unbound name"
|
||||
(do
|
||||
(print "Validation error: " msg " " (get-in e [:node :data]))
|
||||
(print "on line " line-num)
|
||||
(print source-line)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defn runtime-error [e out] (set (out :errors) e) (j/encode out))
|
||||
|
|
|
@ -340,7 +340,7 @@
|
|||
(defn- ref [ast ctx]
|
||||
(def {:data value-ast :name name} ast)
|
||||
(def value (interpret value-ast ctx))
|
||||
(def box @{:^type :ref :^value value :name name})
|
||||
(def box @{:^type :box :^value value :name name})
|
||||
(set (ctx name) box)
|
||||
box)
|
||||
|
||||
|
|
|
@ -16,16 +16,16 @@ The API from the old Clojure Ludus interpreter returns an object with four field
|
|||
* `errors`: an array of errors, which are just strings
|
||||
|
||||
This new scene will have to return a JSON POJSO:
|
||||
{:console [...] :result "..." :draw [...] :errors [...]}
|
||||
{:console "..." :result "..." :draw [...] :errors [...]}
|
||||
)
|
||||
|
||||
(def console @"")
|
||||
(setdyn :out console)
|
||||
(print "foo")
|
||||
(pp {:a 1 :b 2})
|
||||
(setdyn :out stdout)
|
||||
(print "collected out")
|
||||
(print console)
|
||||
(def prelude-src (slurp "prelude.ld"))
|
||||
(def prelude-scanned (s/scan prelude-src))
|
||||
(def prelude-parsed (p/parse prelude-scanned))
|
||||
(def parse-errors (prelude-parsed :errors))
|
||||
(when (any? parse-errors) (each err parse-errors (e/parse-error err)))
|
||||
(def prelude-validated (v/valid prelude-parsed @{"base" b/base}))
|
||||
(each err (prelude-validated :errors) (e/validation-error err))
|
||||
|
||||
(defn run [source]
|
||||
(def errors @[])
|
||||
|
@ -44,7 +44,7 @@ This new scene will have to return a JSON POJSO:
|
|||
(break (-> :errors validated (e/validation-error out))))
|
||||
(setdyn :out console)
|
||||
(try
|
||||
(set result (b/show (i/interpret (parsed :ast) @{})))
|
||||
(set result (b/show (i/interpret (parsed :ast) @{:^parent b/base})))
|
||||
([err] (e/runtime-error err out)))
|
||||
(set (out :result) result)
|
||||
(j/encode out))
|
||||
|
@ -56,7 +56,7 @@ This new scene will have to return a JSON POJSO:
|
|||
(run source))
|
||||
|
||||
(def source `
|
||||
fn foo () -> :foo
|
||||
let foo = fn () -> :bar
|
||||
foo ()
|
||||
`)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
### A recursive descent parser for Ludus
|
||||
|
||||
### We still need to scan some things
|
||||
# (try (os/cd "janet") ([_] nil)) # when in repl to do relative imports
|
||||
(try (os/cd "janet") ([_] nil)) # when in repl to do relative imports
|
||||
(import ./scanner :as s)
|
||||
|
||||
(defmacro declare
|
||||
|
@ -773,7 +773,7 @@
|
|||
(defn- lambda [parser]
|
||||
(def origin (current parser))
|
||||
(expect parser :fn) (advance parser)
|
||||
@{:type :fn :data (fn-simple parser) :token origin})
|
||||
@{:type :fn :data ((fn-simple parser) :clauses) :token origin})
|
||||
|
||||
(defn- fnn [parser]
|
||||
(if (= :lparen (-> parser peek type)) (break (lambda parser)))
|
||||
|
@ -1104,6 +1104,7 @@
|
|||
(def origin (current parser))
|
||||
(def lines @[])
|
||||
(while (not (check parser :eof))
|
||||
(accept-many parser :newline)
|
||||
(array/push lines (capture toplevel parser))
|
||||
(capture terminator parser))
|
||||
{:type :script :data lines :token origin})
|
||||
|
@ -1116,10 +1117,10 @@
|
|||
|
||||
# (do
|
||||
(comment
|
||||
(def source `pkg Foo {}
|
||||
(def source `fn () -> 42
|
||||
`)
|
||||
(def scanned (s/scan source))
|
||||
# (print "\n***NEW PARSE***\n")
|
||||
(def a-parser (new-parser scanned))
|
||||
(def parsed (pkg a-parser))
|
||||
(def parsed (fnn a-parser))
|
||||
)
|
||||
|
|
273
janet/prelude.ld
273
janet/prelude.ld
|
@ -7,6 +7,29 @@
|
|||
& tuple?
|
||||
& ref?
|
||||
|
||||
& some forward declarations
|
||||
& TODO: fix this so that we don't need (as many of) them
|
||||
fn first
|
||||
fn append
|
||||
fn some?
|
||||
fn update!
|
||||
fn string
|
||||
fn join
|
||||
fn neg?
|
||||
fn atan/2
|
||||
fn mod
|
||||
fn assoc?
|
||||
fn dict
|
||||
fn get
|
||||
fn unbox
|
||||
fn store!
|
||||
fn turn/rad
|
||||
fn deg/rad
|
||||
fn floor
|
||||
fn and
|
||||
fn apply_command
|
||||
fn state/call
|
||||
|
||||
& the very base: know something's type
|
||||
fn type {
|
||||
"Returns a keyword representing the type of the value passed in."
|
||||
|
@ -28,6 +51,40 @@ fn eq? {
|
|||
else false
|
||||
}
|
||||
|
||||
&&& true & false: boolean logic (part the first)
|
||||
fn bool? {
|
||||
"Returns true if a value is of type :boolean."
|
||||
(false) -> true
|
||||
(true) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn true? {
|
||||
"Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else."
|
||||
(true) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn false? {
|
||||
"Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`."
|
||||
(false) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn bool {
|
||||
"Returns false if a value is nil or false, otherwise returns true."
|
||||
(nil) -> false
|
||||
(false) -> false
|
||||
(_) -> true
|
||||
}
|
||||
|
||||
fn not {
|
||||
"Returns false if a value is truthy, true if a value is falsy."
|
||||
(nil) -> true
|
||||
(false) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn neq? {
|
||||
"Returns true if none of the arguments have the same value."
|
||||
(x) -> false
|
||||
|
@ -73,55 +130,6 @@ fn dec {
|
|||
(x as :number) -> base :dec (x)
|
||||
}
|
||||
|
||||
fn at {
|
||||
"Returns the element at index n of a list or tuple. Zero-indexed: the first element is at index 0."
|
||||
(xs as :list, n as :number) -> when {
|
||||
neg? (n) -> nil
|
||||
gte? (n, count (xs)) -> nil
|
||||
else -> base :nth (xs, inc (n))
|
||||
}
|
||||
(xs as :tuple, n as :number) -> when {
|
||||
neg? (n) -> nil
|
||||
gte? (n, count (xs)) -> nil
|
||||
else -> base :nth (xs, inc (n))
|
||||
}
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
fn first {
|
||||
"Returns the first element of a list or tuple."
|
||||
(xs) -> at (xs, 0)
|
||||
}
|
||||
|
||||
fn second {
|
||||
"Returns the second element of a list or tuple."
|
||||
(xs) -> at (xs, 1)
|
||||
}
|
||||
|
||||
fn last {
|
||||
"Returns the last element of a list or tuple."
|
||||
(xs) -> at (xs, sub (count (xs), 1))
|
||||
}
|
||||
|
||||
fn butlast {
|
||||
"Returns a list, omitting the last element."
|
||||
(xs as :list) -> base :slice (xs, sub (count (xs), 1))
|
||||
}
|
||||
|
||||
fn slice {
|
||||
"Returns a slice of a list, representing a sub-list."
|
||||
(xs as :list, end as :number) -> slice (xs, 0, end)
|
||||
(xs as :list, start as :number, end as :number) -> when {
|
||||
gte? (start, end) -> []
|
||||
gt? (end, count (xs)) -> slice (xs, start, count (xs))
|
||||
neg? (start) -> slice (xs, 0, end)
|
||||
else -> {
|
||||
let slice = base :slice (xs, inc (start), inc (end))
|
||||
base :into ([], slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn count {
|
||||
"Returns the number of elements in a collection (including string)."
|
||||
(xs as :list) -> dec (base :count (xs))
|
||||
|
@ -142,6 +150,16 @@ fn empty? {
|
|||
(_) -> false
|
||||
}
|
||||
|
||||
fn any? {
|
||||
"Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)."
|
||||
([...]) -> true
|
||||
(#{...}) -> true
|
||||
(s as :set) -> not (empty? (s))
|
||||
((...)) -> true
|
||||
(s as :string) -> not (empty? (s))
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn list? {
|
||||
"Returns true if the value is a list."
|
||||
(l as :list) -> true
|
||||
|
@ -153,6 +171,7 @@ fn list {
|
|||
(x) -> base :to_list (x)
|
||||
}
|
||||
|
||||
& TODO: make this work with Janet base
|
||||
fn set {
|
||||
"Takes an ordered collection--list or tuple--and turns it into a set."
|
||||
(xs as :list) -> base :into (${}, xs)
|
||||
|
@ -180,6 +199,7 @@ fn fold {
|
|||
}
|
||||
}
|
||||
|
||||
& TODO: optimize these with base :conj!
|
||||
fn map {
|
||||
"Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`."
|
||||
(f as :fn, xs) -> {
|
||||
|
@ -227,13 +247,13 @@ fn concat {
|
|||
& the console: sending messages to the outside world
|
||||
& the console is *both* something we send to the host language's console
|
||||
& ...and also a list of messages.
|
||||
ref console = []
|
||||
box console = []
|
||||
|
||||
fn flush! {
|
||||
"Clears the console, and returns the messages."
|
||||
() -> {
|
||||
let msgs = deref (console)
|
||||
make! (console, [])
|
||||
let msgs = unbox (console)
|
||||
store! (console, [])
|
||||
msgs
|
||||
}
|
||||
}
|
||||
|
@ -320,28 +340,28 @@ fn join {
|
|||
|
||||
&&& references: mutable state and state changes
|
||||
|
||||
fn ref? {
|
||||
"Returns true if a value is a ref."
|
||||
(r as :ref) -> true
|
||||
fn box? {
|
||||
"Returns true if a value is a box."
|
||||
(b as :box) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn deref {
|
||||
"Resolves a ref into a value."
|
||||
(r as :ref) -> base :deref (r)
|
||||
fn unbox {
|
||||
"Returns the value that is stored in a box."
|
||||
(b as :box) -> base :unbox (b)
|
||||
}
|
||||
|
||||
fn make! {
|
||||
"Sets the value of a ref."
|
||||
(r as :ref, value) -> base :set! (r, value)
|
||||
fn store! {
|
||||
"Stores a value in a box, replacing the value that was previously there. Returns the value."
|
||||
(b as :box, value) -> base :set! (b, value)
|
||||
}
|
||||
|
||||
fn update! {
|
||||
"Updates a ref by applying a function to its value. Returns the new value."
|
||||
(r as :ref, f as :fn) -> {
|
||||
let current = deref (r)
|
||||
"Updates a box by applying a function to its value. Returns the new value."
|
||||
(b as :box, f as :fn) -> {
|
||||
let current = unbox (b)
|
||||
let new = f (current)
|
||||
make! (r, new)
|
||||
store! (b, new)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -541,6 +561,56 @@ fn max {
|
|||
(x, y, ...zs) -> fold (max, zs, max (x, y))
|
||||
}
|
||||
|
||||
& additional list operations now that we have comparitors
|
||||
fn at {
|
||||
"Returns the element at index n of a list or tuple. Zero-indexed: the first element is at index 0."
|
||||
(xs as :list, n as :number) -> when {
|
||||
neg? (n) -> nil
|
||||
gte? (n, count (xs)) -> nil
|
||||
true -> base :nth (xs, inc (n))
|
||||
}
|
||||
(xs as :tuple, n as :number) -> when {
|
||||
neg? (n) -> nil
|
||||
gte? (n, count (xs)) -> nil
|
||||
true -> base :nth (xs, inc (n))
|
||||
}
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
fn first {
|
||||
"Returns the first element of a list or tuple."
|
||||
(xs) -> at (xs, 0)
|
||||
}
|
||||
|
||||
fn second {
|
||||
"Returns the second element of a list or tuple."
|
||||
(xs) -> at (xs, 1)
|
||||
}
|
||||
|
||||
fn last {
|
||||
"Returns the last element of a list or tuple."
|
||||
(xs) -> at (xs, dec (count (xs)))
|
||||
}
|
||||
|
||||
fn butlast {
|
||||
"Returns a list, omitting the last element."
|
||||
(xs as :list) -> base :slice (xs, dec (count (xs)))
|
||||
}
|
||||
|
||||
fn slice {
|
||||
"Returns a slice of a list, representing a sub-list."
|
||||
(xs as :list, end as :number) -> slice (xs, 0, end)
|
||||
(xs as :list, start as :number, end as :number) -> when {
|
||||
gte? (start, end) -> []
|
||||
gt? (end, count (xs)) -> slice (xs, start, count (xs))
|
||||
neg? (start) -> slice (xs, 0, end)
|
||||
true -> {
|
||||
let slice = base :slice (xs, inc (start), inc (end))
|
||||
base :into ([], slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&&& keywords: funny names
|
||||
fn keyword? {
|
||||
"Returns true if a value is a keyword, otherwise returns false."
|
||||
|
@ -570,40 +640,6 @@ fn some {
|
|||
(value, _) -> value
|
||||
}
|
||||
|
||||
&&& true & false: boolean logic
|
||||
|
||||
fn bool? {
|
||||
"Returns true if a value is of type :boolean."
|
||||
(false) -> true
|
||||
(true) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn true? {
|
||||
"Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else."
|
||||
(true) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn false? {
|
||||
"Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`."
|
||||
(false) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn bool {
|
||||
"Returns false if a value is nil or false, otherwise returns true."
|
||||
(nil) -> false
|
||||
(false) -> false
|
||||
(_) -> true
|
||||
}
|
||||
|
||||
fn not {
|
||||
"Returns false if a value is truthy, true if a value is falsy."
|
||||
(nil) -> true
|
||||
(false) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
& TODO: make `and` and `or` special forms which lazily evaluate arguments
|
||||
fn and {
|
||||
|
@ -689,7 +725,6 @@ fn diff {
|
|||
fn coll? {
|
||||
"Returns true if a value is a collection: dict, struct, list, tuple, or set."
|
||||
(coll as :dict) -> true
|
||||
(coll as :struct) -> true
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(coll as :set) -> true
|
||||
|
@ -988,29 +1023,29 @@ let turtle_init = #{
|
|||
|
||||
& turtle states: refs that get modified by calls
|
||||
& turtle_commands is a list of commands, expressed as tuples
|
||||
ref turtle_commands = []
|
||||
box turtle_commands = []
|
||||
|
||||
& and a list of turtle states
|
||||
ref turtle_states = [turtle_init]
|
||||
box turtle_states = [turtle_init]
|
||||
|
||||
fn reset_turtle! {
|
||||
"Resets the turtle to its original state."
|
||||
() -> make! (turtle_states, [turtle_init])
|
||||
() -> store! (turtle_states, [turtle_init])
|
||||
}
|
||||
|
||||
& and a list of calls to p5--at least for now
|
||||
ref p5_calls = []
|
||||
box p5_calls = []
|
||||
|
||||
& ...and finally, a background color
|
||||
& we need to store this separately because, while it can be updated later,
|
||||
& it must be the first call to p5.
|
||||
ref bgcolor = colors :black
|
||||
box bgcolor = colors :black
|
||||
|
||||
fn add_call! (call) -> update! (p5_calls, append (_, call))
|
||||
|
||||
fn add_command! (command) -> {
|
||||
update! (turtle_commands, append (_, command))
|
||||
let prev = do turtle_states > deref > last
|
||||
let prev = do turtle_states > unbox > last
|
||||
let curr = apply_command (prev, command)
|
||||
update! (turtle_states, append (_, curr))
|
||||
let call = state/call ()
|
||||
|
@ -1026,7 +1061,7 @@ let turtle_angle = 0.385
|
|||
let turtle_color = (255, 255, 255, 150)
|
||||
|
||||
fn render_turtle! () -> {
|
||||
let state = do turtle_states > deref > last
|
||||
let state = do turtle_states > unbox > last
|
||||
if state :visible?
|
||||
then {
|
||||
let (r, g, b, a) = turtle_color
|
||||
|
@ -1055,8 +1090,8 @@ fn render_turtle! () -> {
|
|||
}
|
||||
|
||||
fn state/call () -> {
|
||||
let cmd = do turtle_commands > deref > last > first
|
||||
let states = deref (turtle_states)
|
||||
let cmd = do turtle_commands > unbox > last > first
|
||||
let states = unbox (turtle_states)
|
||||
let curr = last (states)
|
||||
let prev = at (states, sub (count (states), 2))
|
||||
match cmd with {
|
||||
|
@ -1078,7 +1113,7 @@ fn state/call () -> {
|
|||
(:stroke, r, g, b, a)
|
||||
}
|
||||
:clear -> (:background, 0, 0, 0, 255)
|
||||
else -> nil
|
||||
_ -> nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1144,9 +1179,9 @@ let pw! = penwidth!
|
|||
|
||||
fn background! {
|
||||
"Sets the background color behind the turtle and path. Alias: bg!"
|
||||
(gray as :number) -> make! (bgcolor, (gray, gray, gray, 255))
|
||||
((r as :number, g as :number, b as :number)) -> make! (bgcolor, (r, b, g, 255))
|
||||
((r as :number, g as :number, b as :number, a as :number)) -> make! (bgcolor, (r, g, b, a))
|
||||
(gray as :number) -> store! (bgcolor, (gray, gray, gray, 255))
|
||||
((r as :number, g as :number, b as :number)) -> store! (bgcolor, (r, b, g, 255))
|
||||
((r as :number, g as :number, b as :number, a as :number)) -> store! (bgcolor, (r, g, b, a))
|
||||
}
|
||||
|
||||
let bg! = background!
|
||||
|
@ -1205,7 +1240,7 @@ fn apply_command {
|
|||
|
||||
fn turtle_state {
|
||||
"Returns the turtle's current state."
|
||||
() -> do turtle_states > deref > last
|
||||
() -> do turtle_states > unbox > last
|
||||
}
|
||||
|
||||
& position () -> (x, y)
|
||||
|
@ -1234,12 +1269,14 @@ fn penwidth {
|
|||
() -> turtle_state () :pencolor
|
||||
}
|
||||
|
||||
ns prelude {
|
||||
pkg Prelude {
|
||||
type
|
||||
eq?
|
||||
neq?
|
||||
tuple?
|
||||
fn?
|
||||
empty?
|
||||
any?
|
||||
first
|
||||
second
|
||||
rest
|
||||
|
@ -1266,9 +1303,9 @@ ns prelude {
|
|||
report!
|
||||
doc!
|
||||
concat
|
||||
ref?
|
||||
deref
|
||||
make!
|
||||
box?
|
||||
unbox
|
||||
store!
|
||||
update!
|
||||
string
|
||||
string?
|
||||
|
|
|
@ -173,7 +173,7 @@ Deferred until a later iteration of Ludus:
|
|||
:dict
|
||||
:list
|
||||
:fn
|
||||
:ref
|
||||
:box
|
||||
:pkg
|
||||
])
|
||||
|
||||
|
@ -296,7 +296,7 @@ Deferred until a later iteration of Ludus:
|
|||
(defn- fnn [validator]
|
||||
(def ast (validator :ast))
|
||||
(def name (ast :name))
|
||||
# (print "function name: " name)
|
||||
(print "function name: " name)
|
||||
(def status (validator :status))
|
||||
(def tail? (status :tail))
|
||||
(set (status :tail) true)
|
||||
|
|
Loading…
Reference in New Issue
Block a user