Ergonomic language features and lib cleanup
+ let bindings + where bindings + do notation I explored enough of the alternative language design space and decided that we should commit fully to Lambda style. That means no more highly tacit/concatenative point-free/partial programs as default. We'll keep taking advantage of those capabilities when it makes sense, but the library will continue to see massive overhauls.
This commit is contained in:
@@ -1,18 +1,19 @@
|
|||||||
!import "../io.tri" !Local
|
!import "../io.tri" !Local
|
||||||
!import "../http.tri" !Local
|
!import "../http.tri" !Local
|
||||||
!import "../socket.tri" !Local
|
!import "../socket.tri" !Local
|
||||||
|
!import "../patterns.tri" !Local
|
||||||
!import "arboricx.tri" !Local
|
!import "arboricx.tri" !Local
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Store layout helpers
|
-- Store layout helpers
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
pathJoin = a b : append a (append "/" b)
|
pathJoin a b = append a (append "/" b)
|
||||||
|
|
||||||
objectDir = root shard :
|
objectDir root shard =
|
||||||
pathJoin (pathJoin root "objects") shard
|
pathJoin (pathJoin root "objects") shard
|
||||||
|
|
||||||
hashShard = (hash :
|
hashShard hash =
|
||||||
matchList
|
matchList
|
||||||
t
|
t
|
||||||
(h0 r0 :
|
(h0 r0 :
|
||||||
@@ -25,26 +26,26 @@ hashShard = (hash :
|
|||||||
pair h0 (pair h1 (pair h2 t)))
|
pair h0 (pair h1 (pair h2 t)))
|
||||||
r1)
|
r1)
|
||||||
r0)
|
r0)
|
||||||
hash)
|
hash
|
||||||
|
|
||||||
bundleObjectPath = (root hash :
|
bundleObjectPath root hash =
|
||||||
pathJoin
|
pathJoin
|
||||||
(objectDir root (hashShard hash))
|
(objectDir root (hashShard hash))
|
||||||
(append hash ".arboricx"))
|
(append hash ".arboricx")
|
||||||
|
|
||||||
bundleTmpPath = (root hash time :
|
bundleTmpPath root hash time =
|
||||||
pathJoin
|
pathJoin
|
||||||
(pathJoin root "tmp")
|
(pathJoin root "tmp")
|
||||||
(append hash ".tmp"))
|
(append hash ".tmp")
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Store initialization
|
-- Store initialization
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
ensureDir = path :
|
ensureDir path =
|
||||||
void (createDirectory path)
|
void (createDirectory path)
|
||||||
|
|
||||||
ensureStore = (root :
|
ensureStore root =
|
||||||
foldl
|
foldl
|
||||||
thenIO
|
thenIO
|
||||||
(pure (ok t t))
|
(pure (ok t t))
|
||||||
@@ -54,59 +55,46 @@ ensureStore = (root :
|
|||||||
(ensureDir (pathJoin root "aliases"))
|
(ensureDir (pathJoin root "aliases"))
|
||||||
(ensureDir (pathJoin (pathJoin root "aliases") "names"))
|
(ensureDir (pathJoin (pathJoin root "aliases") "names"))
|
||||||
(ensureDir (pathJoin (pathJoin root "aliases") "packages"))
|
(ensureDir (pathJoin (pathJoin root "aliases") "packages"))
|
||||||
(ensureDir (pathJoin root "manifests"))])
|
(ensureDir (pathJoin root "manifests"))]
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Bundle object write
|
-- Bundle object write
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
putBundleWrite = (root bundleBytes hash shard tmpPath finalPath :
|
putBundleWrite root bundleBytes hash shard tmpPath finalPath =
|
||||||
onResult_ (createDirectory (objectDir root shard))
|
do onOk_
|
||||||
(e : pure (err (append "createDirectory: " e) t))
|
_ <- mapErrIO "createDirectory: " (createDirectory (objectDir root shard))
|
||||||
(_ :
|
_ <- mapErrIO "writeBytes: " (writeBytes tmpPath bundleBytes)
|
||||||
onResult_ (writeBytes tmpPath bundleBytes)
|
_ <- mapErrIO "renameFile: " (renameFile tmpPath finalPath)
|
||||||
(e : pure (err (append "writeBytes: " e) t))
|
pure (ok hash t)
|
||||||
(_ :
|
|
||||||
onResult_ (renameFile tmpPath finalPath)
|
|
||||||
(e : pure (err (append "renameFile: " e) t))
|
|
||||||
(_ : pure (ok hash t)))))
|
|
||||||
|
|
||||||
putBundleWithHash = (root bundleBytes time hash :
|
putBundleWithHash root bundleBytes time hash =
|
||||||
putBundleWrite
|
let shard = hashShard hash in
|
||||||
root
|
let tmpPath = bundleTmpPath root hash time in
|
||||||
bundleBytes
|
let finalPath = bundleObjectPath root hash in
|
||||||
hash
|
putBundleWrite root bundleBytes hash shard tmpPath finalPath
|
||||||
(hashShard hash)
|
|
||||||
(bundleTmpPath root hash time)
|
|
||||||
(bundleObjectPath root hash))
|
|
||||||
|
|
||||||
putBundle = (root bundleBytes :
|
putBundle root bundleBytes =
|
||||||
onResult_ currentTime
|
do onOk_
|
||||||
(e : pure (err (append "currentTime: " e) t))
|
time <- mapErrIO "currentTime: " currentTime
|
||||||
(time :
|
hash <- mapErrIO "sha256Hex: " (sha256Hex bundleBytes)
|
||||||
onResult_ (sha256Hex bundleBytes)
|
savedHash <- mapErrIO "withHash: " (putBundleWithHash root bundleBytes time hash)
|
||||||
(e : pure (err (append "sha256Hex: " e) t))
|
pure (ok savedHash t)
|
||||||
(hash :
|
|
||||||
bind (putBundleWithHash root bundleBytes time hash) (r :
|
|
||||||
matchResult
|
|
||||||
(e _ : pure (err (append "withHash: " e) t))
|
|
||||||
(v _ : pure (ok v t))
|
|
||||||
r))))
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Bundle object fetch
|
-- Bundle object fetch
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
getBundleByHash = (root hash :
|
getBundleByHash root hash =
|
||||||
onResult_ (readFile (bundleObjectPath root hash))
|
onResult_ (readFile (bundleObjectPath root hash))
|
||||||
(errMsg : pure (err errMsg t))
|
(errMsg : pure (err errMsg t))
|
||||||
(bytes : pure (ok bytes t)))
|
(bytes : pure (ok bytes t))
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Route prefix helper
|
-- Route prefix helper
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
stripPrefix_ = (self input prefix :
|
stripPrefix_ self input prefix =
|
||||||
lazyList
|
lazyList
|
||||||
(_ :
|
(_ :
|
||||||
lazyList
|
lazyList
|
||||||
@@ -122,12 +110,15 @@ stripPrefix_ = (self input prefix :
|
|||||||
(_ : nothing)
|
(_ : nothing)
|
||||||
(equal? ih ph))
|
(equal? ih ph))
|
||||||
prefix)
|
prefix)
|
||||||
input)
|
input
|
||||||
|
|
||||||
stripPrefix = (prefix input :
|
stripPrefix prefix input =
|
||||||
y stripPrefix_ input prefix)
|
y stripPrefix_ input prefix
|
||||||
|
|
||||||
bundleHashPrefix = "/_arboricx/bundle/hash/"
|
bundleHashPrefix = "/_arboricx/bundle/hash/"
|
||||||
|
bundlePath = "/_arboricx/bundle"
|
||||||
|
healthPath = "/_arboricx/health"
|
||||||
|
bundleContentType = "application/vnd.arboricx.bundle"
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Landing page
|
-- Landing page
|
||||||
@@ -142,82 +133,73 @@ htmlLandingPage = "<!DOCTYPE html><html><head><meta name='viewport' content='wid
|
|||||||
-- Registry routes
|
-- Registry routes
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
healthRoute = (method target :
|
bundleResponse bytes = response 200 bundleContentType bytes
|
||||||
lazyBool
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : pure (okResponse "OK\n"))
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? target "/_arboricx/health"))
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? method "GET"))
|
|
||||||
|
|
||||||
putBundleRoute = (root method target body :
|
serveBundleHash root hash =
|
||||||
lazyBool
|
onResult_ (getBundleByHash root hash)
|
||||||
(_ :
|
(errMsg : pure (errorResponse 404 errMsg))
|
||||||
lazyBool
|
(bytes : pure (bundleResponse bytes))
|
||||||
(_ :
|
|
||||||
bind (putBundle root body) (result :
|
|
||||||
matchResult
|
|
||||||
(err _ : pure (badRequestResponse (append "Upload failed: " err)))
|
|
||||||
(hash _ : pure (createdResponse hash))
|
|
||||||
result))
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? target "/_arboricx/bundle"))
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? method "POST"))
|
|
||||||
|
|
||||||
getBundleRoute = (root method target :
|
healthRoute method target =
|
||||||
lazyBool
|
cond
|
||||||
(_ :
|
[(guard (_ : equal? method "GET") (_ : getHealth))
|
||||||
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
|
where getHealth =
|
||||||
|
cond
|
||||||
|
[(guard (_ : equal? target healthPath) (_ : pure (okResponse "OK\n")))
|
||||||
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
|
|
||||||
|
putBundleRoute root method target body =
|
||||||
|
cond
|
||||||
|
[(guard (_ : equal? method "POST") (_ : postBundle))
|
||||||
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
|
where postBundle =
|
||||||
|
cond
|
||||||
|
[(guard (_ : equal? target bundlePath) (_ : handleUpload))
|
||||||
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
|
where handleUpload =
|
||||||
|
onResult_ (putBundle root body)
|
||||||
|
(err : pure (badRequestResponse (append "Upload failed: " err)))
|
||||||
|
(hash : pure (createdResponse hash))
|
||||||
|
|
||||||
|
getBundleRoute root method target =
|
||||||
|
cond
|
||||||
|
[(guard (_ : equal? method "GET") (_ : getBundle))
|
||||||
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
|
where getBundle =
|
||||||
lazyMaybe
|
lazyMaybe
|
||||||
(_ : pure notFoundResponse)
|
(_ : pure notFoundResponse)
|
||||||
(hash :
|
(hash : serveBundleHash root hash)
|
||||||
bind (getBundleByHash root hash) (result :
|
(stripPrefix bundleHashPrefix target)
|
||||||
matchResult
|
|
||||||
(errMsg _ : pure (errorResponse 404 errMsg))
|
|
||||||
(bytes _ : pure (response 200 "application/vnd.arboricx.bundle" bytes))
|
|
||||||
result))
|
|
||||||
(stripPrefix bundleHashPrefix target))
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? method "GET"))
|
|
||||||
|
|
||||||
arboricxRouter = (root method target headers body :
|
arboricxRouter root method target headers body =
|
||||||
lazyBool
|
cond
|
||||||
(_ :
|
[(guard (_ : equal? method "GET") (_ : getRoutes))
|
||||||
lazyBool
|
(guard (_ : equal? method "POST") (_ : putBundleRoute root method target body))
|
||||||
(_ : pure (htmlResponse htmlLandingPage))
|
(guard (_ : true) (_ : pure notFoundResponse))]
|
||||||
(_ :
|
where getRoutes =
|
||||||
|
cond
|
||||||
|
[(guard (_ : equal? target "/") (_ : pure (htmlResponse htmlLandingPage)))
|
||||||
|
(guard (_ : true) (_ : getBundleOrHealth))]
|
||||||
|
where getBundleOrHealth =
|
||||||
lazyMaybe
|
lazyMaybe
|
||||||
(_ : healthRoute method target)
|
(_ : healthRoute method target)
|
||||||
(hash :
|
(hash : serveBundleHash root hash)
|
||||||
bind (getBundleByHash root hash) (result :
|
(stripPrefix bundleHashPrefix target)
|
||||||
matchResult
|
|
||||||
(errMsg _ : pure (errorResponse 404 errMsg))
|
|
||||||
(bytes _ : pure (response 200 "application/vnd.arboricx.bundle" bytes))
|
|
||||||
result))
|
|
||||||
(stripPrefix bundleHashPrefix target))
|
|
||||||
(equal? target "/"))
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : putBundleRoute root method target body)
|
|
||||||
(_ : pure notFoundResponse)
|
|
||||||
(equal? method "POST"))
|
|
||||||
(equal? method "GET"))
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Server entrypoint
|
-- Server entrypoint
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
arboricxHandler = (root client peer :
|
arboricxHandler root = (client peer :
|
||||||
httpHandlerIO
|
httpHandlerIO
|
||||||
(method target headers body :
|
(method target headers body :
|
||||||
arboricxRouter root method target headers body)
|
arboricxRouter root method target headers body)
|
||||||
client
|
client
|
||||||
peer)
|
peer)
|
||||||
|
|
||||||
arboricxServer = (root addr port :
|
arboricxServer root addr port =
|
||||||
onResult_ (listenSocket addr port 128)
|
onResult_ (listenSocket addr port 128)
|
||||||
(errMsg : pure (err errMsg t))
|
(errMsg : pure (err errMsg t))
|
||||||
(server :
|
(server :
|
||||||
serveForever server (arboricxHandler root)))
|
serveForever server (arboricxHandler root))
|
||||||
|
|||||||
44
lib/base.tri
44
lib/base.tri
@@ -1,18 +1,18 @@
|
|||||||
false = t
|
false = t
|
||||||
_ = t
|
_ = t
|
||||||
true = t t
|
true = t t
|
||||||
id = a : a
|
id a = a
|
||||||
const = a b : a
|
const a b = a
|
||||||
pair = t
|
pair = t
|
||||||
if = cond then else : t (t else (t t then)) t cond
|
if cond then else = t (t else (t t then)) t cond
|
||||||
|
|
||||||
y = ((mut wait fun : wait mut (x : fun (wait mut x)))
|
y = ((mut wait fun : wait mut (x : fun (wait mut x)))
|
||||||
(x : x x)
|
(x : x x)
|
||||||
(a0 a1 a2 : t (t a0) (t t a2) a1))
|
(a0 a1 a2 : t (t a0) (t t a2) a1))
|
||||||
|
|
||||||
compose = f g x : f (g x)
|
compose f g x = f (g x)
|
||||||
|
|
||||||
triage = leaf stem fork : t (t leaf stem) fork
|
triage leaf stem fork = t (t leaf stem) fork
|
||||||
test = triage "Leaf" (_ : "Stem") (_ _ : "Fork")
|
test = triage "Leaf" (_ : "Stem") (_ _ : "Fork")
|
||||||
|
|
||||||
matchBool = (ot of : triage
|
matchBool = (ot of : triage
|
||||||
@@ -31,15 +31,17 @@ lOr = (triage
|
|||||||
(_ _ : true)
|
(_ _ : true)
|
||||||
(_ _ _ : true))
|
(_ _ _ : true))
|
||||||
|
|
||||||
matchPair = a : triage _ _ a
|
matchPair a = triage _ _ a
|
||||||
|
|
||||||
fst = p : matchPair (a b : a) p
|
fst p = matchPair takeFirst p
|
||||||
snd = p : matchPair (a b : b) p
|
where takeFirst a b = a
|
||||||
|
snd p = matchPair takeSecond p
|
||||||
|
where takeSecond a b = b
|
||||||
|
|
||||||
resultIsOk = result :
|
resultIsOk result =
|
||||||
matchResult (err rest : false) (val rest : true) result
|
matchResult (err rest : false) (val rest : true) result
|
||||||
|
|
||||||
resultIsErr = result :
|
resultIsErr result =
|
||||||
matchResult (err rest : true) (val rest : false) result
|
matchResult (err rest : true) (val rest : false) result
|
||||||
|
|
||||||
not? = matchBool false true
|
not? = matchBool false true
|
||||||
@@ -82,10 +84,10 @@ succ = y (self :
|
|||||||
(_ tail : t t (self tail))
|
(_ tail : t t (self tail))
|
||||||
t))
|
t))
|
||||||
|
|
||||||
ok = value rest : pair true (pair value rest)
|
ok value rest = pair true (pair value rest)
|
||||||
err = msg rest : pair false (pair msg rest)
|
err msg rest = pair false (pair msg rest)
|
||||||
|
|
||||||
matchResult = (errCase okCase result :
|
matchResult errCase okCase result =
|
||||||
matchPair
|
matchPair
|
||||||
(tag payload :
|
(tag payload :
|
||||||
matchPair
|
matchPair
|
||||||
@@ -95,26 +97,26 @@ matchResult = (errCase okCase result :
|
|||||||
(errCase value rest)
|
(errCase value rest)
|
||||||
tag)
|
tag)
|
||||||
payload)
|
payload)
|
||||||
result)
|
result
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Maybe / Option type
|
-- Maybe / Option type
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
nothing = t
|
nothing = t
|
||||||
just = x : t x
|
just x = t x
|
||||||
|
|
||||||
matchMaybe = (nothingCase justCase maybe :
|
matchMaybe nothingCase justCase maybe =
|
||||||
triage
|
triage
|
||||||
nothingCase
|
nothingCase
|
||||||
justCase
|
justCase
|
||||||
(_ _ : nothingCase)
|
(_ _ : nothingCase)
|
||||||
maybe)
|
maybe
|
||||||
|
|
||||||
maybe = default f m : matchMaybe default f m
|
maybe default f m = matchMaybe default f m
|
||||||
maybeMap = f m : matchMaybe nothing (x : just (f x)) m
|
maybeMap f m = matchMaybe nothing (x : just (f x)) m
|
||||||
maybeBind = m f : matchMaybe nothing f m
|
maybeBind m f = matchMaybe nothing f m
|
||||||
maybeOr = default m : matchMaybe default id m
|
maybeOr default m = matchMaybe default id m
|
||||||
maybe? = matchMaybe false (_ : true)
|
maybe? = matchMaybe false (_ : true)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ errUnexpectedEof = 1
|
|||||||
errUnexpectedBytes = 2
|
errUnexpectedBytes = 2
|
||||||
errUnexpectedByte = 3
|
errUnexpectedByte = 3
|
||||||
|
|
||||||
readU8 = (bytes : matchList
|
unit = t
|
||||||
|
|
||||||
|
readU8 = (bytes :
|
||||||
|
matchList
|
||||||
(err errUnexpectedEof t)
|
(err errUnexpectedEof t)
|
||||||
(h r : ok h r)
|
(h r : ok h r)
|
||||||
bytes)
|
bytes)
|
||||||
|
|
||||||
readBytes_ = y (self bs n i original acc :
|
readBytes_ self bs n i original acc =
|
||||||
matchList
|
matchList
|
||||||
(matchBool
|
(matchBool
|
||||||
(ok (reverse acc) bs)
|
(ok (reverse acc) bs)
|
||||||
@@ -22,13 +25,12 @@ readBytes_ = y (self bs n i original acc :
|
|||||||
(ok (reverse acc) bs)
|
(ok (reverse acc) bs)
|
||||||
(self r n (succ i) original (pair h acc))
|
(self r n (succ i) original (pair h acc))
|
||||||
(equal? i n))
|
(equal? i n))
|
||||||
bs)
|
bs
|
||||||
|
|
||||||
readBytes = (n bs : readBytes_ bs n 0 bs t)
|
readBytes = (n bs :
|
||||||
|
y readBytes_ bs n 0 bs t)
|
||||||
|
|
||||||
unit = t
|
expectBytes_ self expected bs original =
|
||||||
|
|
||||||
expectBytes_ = y (self expected bs original :
|
|
||||||
matchList
|
matchList
|
||||||
(ok unit bs)
|
(ok unit bs)
|
||||||
(expectedByte expectedRest :
|
(expectedByte expectedRest :
|
||||||
@@ -40,9 +42,10 @@ expectBytes_ = y (self expected bs original :
|
|||||||
(err errUnexpectedBytes original)
|
(err errUnexpectedBytes original)
|
||||||
(equal? actual expectedByte))
|
(equal? actual expectedByte))
|
||||||
(readU8 bs))
|
(readU8 bs))
|
||||||
expected)
|
expected
|
||||||
|
|
||||||
expectBytes = (expected bs : expectBytes_ expected bs bs)
|
expectBytes = (expected bs :
|
||||||
|
y expectBytes_ expected bs bs)
|
||||||
|
|
||||||
expectU8 = (expected bs :
|
expectU8 = (expected bs :
|
||||||
matchResult
|
matchResult
|
||||||
@@ -75,7 +78,7 @@ orParser = (p q bs :
|
|||||||
(value rest : ok value rest)
|
(value rest : ok value rest)
|
||||||
(p bs))
|
(p bs))
|
||||||
|
|
||||||
readWhile_ = y (self pred bs acc :
|
readWhile_ self pred bs acc =
|
||||||
matchResult
|
matchResult
|
||||||
(code rest : ok (reverse acc) bs)
|
(code rest : ok (reverse acc) bs)
|
||||||
(value rest :
|
(value rest :
|
||||||
@@ -83,11 +86,13 @@ readWhile_ = y (self pred bs acc :
|
|||||||
(self pred rest (pair value acc))
|
(self pred rest (pair value acc))
|
||||||
(ok (reverse acc) (pair value rest))
|
(ok (reverse acc) (pair value rest))
|
||||||
(pred value))
|
(pred value))
|
||||||
(readU8 bs))
|
(readU8 bs)
|
||||||
|
|
||||||
readWhile = pred bs : readWhile_ pred bs t
|
readWhile = pred bs :
|
||||||
|
y readWhile_ pred bs t
|
||||||
|
|
||||||
readUntil = pred : readWhile (x : not? (pred x))
|
readUntil = pred :
|
||||||
|
readWhile (x : not? (pred x))
|
||||||
|
|
||||||
readRemaining = bs : ok bs t
|
readRemaining = bs : ok bs t
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
bytesNil? = emptyList?
|
bytesNil? = emptyList?
|
||||||
|
|
||||||
bytesHead = matchList nothing (h _ : just h)
|
bytesHead =
|
||||||
|
matchList nothing (h _ : just h)
|
||||||
|
|
||||||
bytesTail = matchList nothing (_ r : just r)
|
bytesTail =
|
||||||
|
matchList nothing (_ r : just r)
|
||||||
|
|
||||||
bytesLength = length
|
bytesLength = length
|
||||||
bytesAppend = append
|
bytesAppend = append
|
||||||
|
|||||||
343
lib/http.tri
343
lib/http.tri
@@ -31,43 +31,29 @@ chomp = (xs :
|
|||||||
-- Response construction
|
-- Response construction
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
statusPhrase = (code :
|
statusPhrases =
|
||||||
lazyBool
|
[(pair 200 "OK")
|
||||||
(_ : "OK")
|
(pair 201 "Created")
|
||||||
(_ :
|
(pair 204 "No Content")
|
||||||
lazyBool
|
(pair 400 "Bad Request")
|
||||||
(_ : "Created")
|
(pair 404 "Not Found")
|
||||||
(_ :
|
(pair 405 "Method Not Allowed")
|
||||||
lazyBool
|
(pair 431 "Request Header Fields Too Large")
|
||||||
(_ : "No Content")
|
(pair 501 "Not Implemented")
|
||||||
(_ :
|
(pair 505 "HTTP Version Not Supported")]
|
||||||
lazyBool
|
|
||||||
(_ : "Bad Request")
|
lookupStatusPhrase_ self code phrases =
|
||||||
(_ :
|
lazyList
|
||||||
lazyBool
|
|
||||||
(_ : "Not Found")
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : "Method Not Allowed")
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : "Request Header Fields Too Large")
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : "Not Implemented")
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : "HTTP Version Not Supported")
|
|
||||||
(_ : "Internal Server Error")
|
(_ : "Internal Server Error")
|
||||||
(equal? code 505))
|
(h r :
|
||||||
(equal? code 501))
|
lazyBool
|
||||||
(equal? code 431))
|
(_ : snd h)
|
||||||
(equal? code 405))
|
(_ : self code r)
|
||||||
(equal? code 404))
|
(equal? code (fst h)))
|
||||||
(equal? code 400))
|
phrases
|
||||||
(equal? code 204))
|
|
||||||
(equal? code 201))
|
statusPhrase = (code :
|
||||||
(equal? code 200))
|
y lookupStatusPhrase_ code statusPhrases)
|
||||||
|
|
||||||
statusLine = (code phrase :
|
statusLine = (code phrase :
|
||||||
append "HTTP/1.1 " (append (showNumber code) (append " " (append phrase "\r\n"))))
|
append "HTTP/1.1 " (append (showNumber code) (append " " (append phrase "\r\n"))))
|
||||||
@@ -119,34 +105,40 @@ badRequestResponse = (msg :
|
|||||||
errorResponse = (status msg :
|
errorResponse = (status msg :
|
||||||
response status "text/plain; charset=utf-8" msg)
|
response status "text/plain; charset=utf-8" msg)
|
||||||
|
|
||||||
headersOnly_ = (y (self bs s1 s2 s3 acc :
|
headerEndState state h =
|
||||||
|
lazyBool
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 3)
|
||||||
|
(_ : 1)
|
||||||
|
(equal? state 2))
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 4)
|
||||||
|
(_ : 2)
|
||||||
|
(equal? state 3))
|
||||||
|
(_ : 0)
|
||||||
|
(boolAnd?
|
||||||
|
(equal? h 10)
|
||||||
|
(boolOr? (equal? state 1) (equal? state 3))))
|
||||||
|
(equal? h 13)
|
||||||
|
|
||||||
|
headersOnly_ self bs state acc =
|
||||||
lazyList
|
lazyList
|
||||||
(_ : reverse acc)
|
(_ : reverse acc)
|
||||||
(h r :
|
(h r :
|
||||||
|
let nextAcc = pair h acc in
|
||||||
|
let nextState = headerEndState state h in
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ :
|
(_ : reverse nextAcc)
|
||||||
lazyBool
|
(_ : self r nextState nextAcc)
|
||||||
(_ :
|
(equal? nextState 4))
|
||||||
lazyBool
|
bs
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : reverse (pair 10 (pair 13 (pair 10 (pair 13 acc)))))
|
|
||||||
(_ : self r true false false (pair h acc))
|
|
||||||
(equal? h 10))
|
|
||||||
(_ : self r false false false (pair h acc))
|
|
||||||
s3)
|
|
||||||
(_ : self r false true false (pair h acc))
|
|
||||||
(and? s2 (equal? h 13)))
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : self r false false true (pair h acc))
|
|
||||||
(_ : self r false false false (pair h acc))
|
|
||||||
(and? s1 (equal? h 10)))
|
|
||||||
(equal? h 13))
|
|
||||||
bs))
|
|
||||||
|
|
||||||
headersOnly = (response :
|
headersOnly = (response :
|
||||||
headersOnly_ response false false false t)
|
y headersOnly_ response 0 t)
|
||||||
|
|
||||||
responseForMethod = (method resp :
|
responseForMethod = (method resp :
|
||||||
lazyBool
|
lazyBool
|
||||||
@@ -166,9 +158,9 @@ recvUntilMax_ = (y (self sock pattern maxBytes acc accLen :
|
|||||||
lazyBool
|
lazyBool
|
||||||
(_ : pure (err 400 acc))
|
(_ : pure (err 400 acc))
|
||||||
(_ :
|
(_ :
|
||||||
((chunkLen :
|
let chunkLen = length chunk in
|
||||||
((nextLen :
|
let nextLen = add accLen chunkLen in
|
||||||
((next :
|
let next = append acc chunk in
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ :
|
(_ :
|
||||||
lazyBool
|
lazyBool
|
||||||
@@ -177,9 +169,6 @@ recvUntilMax_ = (y (self sock pattern maxBytes acc accLen :
|
|||||||
(contains? pattern next))
|
(contains? pattern next))
|
||||||
(_ : pure (err 431 next))
|
(_ : pure (err 431 next))
|
||||||
(lte? nextLen maxBytes))
|
(lte? nextLen maxBytes))
|
||||||
(append acc chunk)))
|
|
||||||
(add accLen chunkLen)))
|
|
||||||
(length chunk)))
|
|
||||||
(emptyList? chunk))))
|
(emptyList? chunk))))
|
||||||
|
|
||||||
recvUntilMax = (sock pattern maxBytes :
|
recvUntilMax = (sock pattern maxBytes :
|
||||||
@@ -301,52 +290,36 @@ lowerAsciiBits = (b0 b1 b2 b3 b4 :
|
|||||||
(pair true
|
(pair true
|
||||||
(pair true 0)))))))
|
(pair true 0)))))))
|
||||||
|
|
||||||
|
byte7BitsOr default c k =
|
||||||
|
let noStem _ = default in
|
||||||
|
let bit6 b0 b1 b2 b3 b4 b5 b6 r6 =
|
||||||
|
k b0 b1 b2 b3 b4 b5 b6 r6 in
|
||||||
|
let bit5 b0 b1 b2 b3 b4 b5 r5 =
|
||||||
|
triage default noStem (bit6 b0 b1 b2 b3 b4 b5) r5 in
|
||||||
|
let bit4 b0 b1 b2 b3 b4 r4 =
|
||||||
|
triage default noStem (bit5 b0 b1 b2 b3 b4) r4 in
|
||||||
|
let bit3 b0 b1 b2 b3 r3 =
|
||||||
|
triage default noStem (bit4 b0 b1 b2 b3) r3 in
|
||||||
|
let bit2 b0 b1 b2 r2 =
|
||||||
|
triage default noStem (bit3 b0 b1 b2) r2 in
|
||||||
|
let bit1 b0 b1 r1 =
|
||||||
|
triage default noStem (bit2 b0 b1) r1 in
|
||||||
|
let bit0 b0 r0 =
|
||||||
|
triage default noStem (bit1 b0) r0 in
|
||||||
|
triage default noStem bit0 c
|
||||||
|
|
||||||
toLowerAsciiByte = (c :
|
toLowerAsciiByte = (c :
|
||||||
triage
|
byte7BitsOr c c (b0 b1 b2 b3 b4 b5 b6 rest :
|
||||||
c
|
lazyBool
|
||||||
|
(_ : lowerAsciiBits b0 b1 b2 b3 b4)
|
||||||
(_ : c)
|
(_ : c)
|
||||||
(b0 r0 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b1 r1 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b2 r2 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b3 r3 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b4 r4 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b5 r5 :
|
|
||||||
triage
|
|
||||||
c
|
|
||||||
(_ : c)
|
|
||||||
(b6 r6 :
|
|
||||||
matchBool
|
|
||||||
(lowerAsciiBits b0 b1 b2 b3 b4)
|
|
||||||
c
|
|
||||||
(boolAnd?
|
(boolAnd?
|
||||||
(isZero? r6)
|
(isZero? rest)
|
||||||
(boolAnd?
|
(boolAnd?
|
||||||
(bit1? b6)
|
(bit1? b6)
|
||||||
(boolAnd?
|
(boolAnd?
|
||||||
(bit0? b5)
|
(bit0? b5)
|
||||||
(upperLow5? b0 b1 b2 b3 b4)))))
|
(upperLow5? b0 b1 b2 b3 b4))))))
|
||||||
r5)
|
|
||||||
r4)
|
|
||||||
r3)
|
|
||||||
r2)
|
|
||||||
r1)
|
|
||||||
r0)
|
|
||||||
c)
|
|
||||||
|
|
||||||
finishHeaderLine = (self r headers key value seenColon :
|
finishHeaderLine = (self r headers key value seenColon :
|
||||||
matchBool
|
matchBool
|
||||||
@@ -495,10 +468,86 @@ readDecimal = (bytes :
|
|||||||
(y readDecimal_ bytes 0)
|
(y readDecimal_ bytes 0)
|
||||||
(emptyList? bytes))
|
(emptyList? bytes))
|
||||||
|
|
||||||
|
maxBodyBytesDecimal = "1048576"
|
||||||
|
|
||||||
|
byte0? b = equal? b 48
|
||||||
|
digitLtMax? maxDigit digit = lt? digit maxDigit
|
||||||
|
|
||||||
|
stripLeadingZeros_ self raw =
|
||||||
|
lazyList
|
||||||
|
(_ : t)
|
||||||
|
(c r :
|
||||||
|
lazyBool
|
||||||
|
(_ : self r)
|
||||||
|
(_ : raw)
|
||||||
|
(byte0? c))
|
||||||
|
raw
|
||||||
|
|
||||||
|
decimalLengthLte_ self max raw =
|
||||||
|
lazyList
|
||||||
|
(_ : true)
|
||||||
|
(_ rest :
|
||||||
|
lazyList
|
||||||
|
(_ : false)
|
||||||
|
(_ maxRest : self maxRest rest)
|
||||||
|
max)
|
||||||
|
raw
|
||||||
|
|
||||||
|
decimalSameLength_ self max raw =
|
||||||
|
lazyList
|
||||||
|
(_ :
|
||||||
|
lazyList
|
||||||
|
(_ : true)
|
||||||
|
(_ _ : false)
|
||||||
|
max)
|
||||||
|
(_ rest :
|
||||||
|
lazyList
|
||||||
|
(_ : false)
|
||||||
|
(_ maxRest : self maxRest rest)
|
||||||
|
max)
|
||||||
|
raw
|
||||||
|
|
||||||
|
sameLengthDecimalLte_ self max raw less =
|
||||||
|
lazyList
|
||||||
|
(_ : true)
|
||||||
|
(digit rest :
|
||||||
|
lazyList
|
||||||
|
(_ : false)
|
||||||
|
(maxDigit maxRest :
|
||||||
|
lazyBool
|
||||||
|
(_ : self maxRest rest true)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : self maxRest rest true)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : self maxRest rest false)
|
||||||
|
(_ : false)
|
||||||
|
(equal? digit maxDigit))
|
||||||
|
(digitLtMax? maxDigit digit))
|
||||||
|
less)
|
||||||
|
max)
|
||||||
|
raw
|
||||||
|
|
||||||
|
decimalLengthLte? max raw = y decimalLengthLte_ max raw
|
||||||
|
|
||||||
|
decimalSameLength? max raw = y decimalSameLength_ max raw
|
||||||
|
|
||||||
|
decimalBytesLte? max raw =
|
||||||
|
let trimmed = y stripLeadingZeros_ raw in
|
||||||
|
lazyBool
|
||||||
|
(_ : y sameLengthDecimalLte_ max trimmed false)
|
||||||
|
(_ : decimalLengthLte? max trimmed)
|
||||||
|
(decimalSameLength? max trimmed)
|
||||||
|
|
||||||
parseContentLengthValue = (raw :
|
parseContentLengthValue = (raw :
|
||||||
matchMaybe
|
matchMaybe
|
||||||
(err 400 "Bad Request\n")
|
(err 400 "Bad Request\n")
|
||||||
(n : ok (just n) t)
|
(n :
|
||||||
|
lazyBool
|
||||||
|
(_ : ok (just n) t)
|
||||||
|
(_ : err 413 "Request body too large\n")
|
||||||
|
(decimalBytesLte? maxBodyBytesDecimal raw))
|
||||||
(readDecimal raw))
|
(readDecimal raw))
|
||||||
|
|
||||||
contentLength_ = (self headers :
|
contentLength_ = (self headers :
|
||||||
@@ -544,6 +593,43 @@ takeBodyBytes_ = (self bytes remaining accRev :
|
|||||||
takeBodyBytes = (bytes remaining accRev :
|
takeBodyBytes = (bytes remaining accRev :
|
||||||
y takeBodyBytes_ bytes remaining accRev)
|
y takeBodyBytes_ bytes remaining accRev)
|
||||||
|
|
||||||
|
shiftRight1 n = triage 0 (_ : 0) (_ rest : rest) n
|
||||||
|
|
||||||
|
shiftRight2 n = shiftRight1 (shiftRight1 n)
|
||||||
|
shiftRight4 n = shiftRight2 (shiftRight2 n)
|
||||||
|
shiftRight8 n = shiftRight4 (shiftRight4 n)
|
||||||
|
shiftRight12 n = shiftRight4 (shiftRight8 n)
|
||||||
|
|
||||||
|
shiftRight6 n = shiftRight2 (shiftRight4 n)
|
||||||
|
|
||||||
|
atLeast16? n = not? (isZero? (shiftRight4 n))
|
||||||
|
atLeast64? n = not? (isZero? (shiftRight6 n))
|
||||||
|
atLeast256? n = not? (isZero? (shiftRight8 n))
|
||||||
|
atLeast1024? n = not? (isZero? (shiftRight2 (shiftRight8 n)))
|
||||||
|
atLeast4096? n = not? (isZero? (shiftRight12 n))
|
||||||
|
|
||||||
|
recvChunkMax4096 remaining =
|
||||||
|
lazyBool
|
||||||
|
(_ : 4096)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 1024)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 256)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 64)
|
||||||
|
(_ :
|
||||||
|
lazyBool
|
||||||
|
(_ : 16)
|
||||||
|
(_ : 1)
|
||||||
|
(atLeast16? remaining))
|
||||||
|
(atLeast64? remaining))
|
||||||
|
(atLeast256? remaining))
|
||||||
|
(atLeast1024? remaining))
|
||||||
|
(atLeast4096? remaining)
|
||||||
|
|
||||||
readBodyRecv = (self sock remaining accRev recvBytes :
|
readBodyRecv = (self sock remaining accRev recvBytes :
|
||||||
onResult_ (recv sock recvBytes)
|
onResult_ (recv sock recvBytes)
|
||||||
(errMsg :
|
(errMsg :
|
||||||
@@ -552,60 +638,34 @@ readBodyRecv = (self sock remaining accRev recvBytes :
|
|||||||
400
|
400
|
||||||
(append "recv failed while reading body: " errMsg)))
|
(append "recv failed while reading body: " errMsg)))
|
||||||
(chunk :
|
(chunk :
|
||||||
((state :
|
let state = takeBodyBytes chunk remaining accRev in
|
||||||
((nextRemaining :
|
let nextRemaining = bodyReadRemaining state in
|
||||||
((nextAccRev :
|
let nextAccRev = bodyReadAccRev state in
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ : pure (ok (reverse nextAccRev) (bodyReadRest state)))
|
(_ : pure (ok (reverse nextAccRev) (bodyReadRest state)))
|
||||||
(_ : self sock nextRemaining nextAccRev)
|
(_ : self sock nextRemaining nextAccRev)
|
||||||
(isZero? nextRemaining))
|
(isZero? nextRemaining)))
|
||||||
(bodyReadAccRev state)))
|
|
||||||
(bodyReadRemaining state)))
|
|
||||||
(takeBodyBytes chunk remaining accRev))))
|
|
||||||
|
|
||||||
readBodyMore_ = (self sock remaining accRev :
|
readBodyMore_ = (self sock remaining accRev :
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ : pure (ok (reverse accRev) t))
|
(_ : pure (ok (reverse accRev) t))
|
||||||
(_ :
|
(_ : readBodyRecv self sock remaining accRev (recvChunkMax4096 remaining))
|
||||||
lazyBool
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 4096)
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 1024)
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 256)
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 64)
|
|
||||||
(_ :
|
|
||||||
lazyBool
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 16)
|
|
||||||
(_ : readBodyRecv self sock remaining accRev 1)
|
|
||||||
(lte? 16 remaining))
|
|
||||||
(lte? 64 remaining))
|
|
||||||
(lte? 256 remaining))
|
|
||||||
(lte? 1024 remaining))
|
|
||||||
(lte? 4096 remaining))
|
|
||||||
(isZero? remaining))
|
(isZero? remaining))
|
||||||
|
|
||||||
readBodyMore = (sock remaining accRev :
|
readBodyMore = (sock remaining accRev :
|
||||||
y readBodyMore_ sock remaining accRev)
|
y readBodyMore_ sock remaining accRev)
|
||||||
|
|
||||||
readBodyExact = (sock expected initialBytes :
|
readBodyExact = (sock expected initialBytes :
|
||||||
((state :
|
let state = takeBodyBytes initialBytes expected t in
|
||||||
((remaining :
|
let remaining = bodyReadRemaining state in
|
||||||
((accRev :
|
let accRev = bodyReadAccRev state in
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ : pure (ok (reverse accRev) (bodyReadRest state)))
|
(_ : pure (ok (reverse accRev) (bodyReadRest state)))
|
||||||
(_ : readBodyMore sock remaining accRev)
|
(_ : readBodyMore sock remaining accRev)
|
||||||
(isZero? remaining))
|
(isZero? remaining))
|
||||||
(bodyReadAccRev state)))
|
|
||||||
(bodyReadRemaining state)))
|
|
||||||
(takeBodyBytes initialBytes expected t)))
|
|
||||||
|
|
||||||
validateBodyLength = (expected body rest :
|
validateBodyLength = (expected body rest :
|
||||||
((actual :
|
let actual = length body in
|
||||||
lazyBool
|
lazyBool
|
||||||
(_ : pure (ok body rest))
|
(_ : pure (ok body rest))
|
||||||
(_ :
|
(_ :
|
||||||
@@ -620,7 +680,6 @@ validateBodyLength = (expected body rest :
|
|||||||
" actual="
|
" actual="
|
||||||
(showNumber actual))))))
|
(showNumber actual))))))
|
||||||
(equal? actual expected))
|
(equal? actual expected))
|
||||||
(length body)))
|
|
||||||
|
|
||||||
readBody = (sock headers initialBytes :
|
readBody = (sock headers initialBytes :
|
||||||
matchResult
|
matchResult
|
||||||
@@ -630,13 +689,9 @@ readBody = (sock headers initialBytes :
|
|||||||
lazyMaybe
|
lazyMaybe
|
||||||
(_ : pure (ok t initialBytes))
|
(_ : pure (ok t initialBytes))
|
||||||
(n :
|
(n :
|
||||||
lazyBool
|
|
||||||
(_ :
|
|
||||||
onOk (readBodyExact sock n initialBytes)
|
onOk (readBodyExact sock n initialBytes)
|
||||||
(body rest :
|
(body rest :
|
||||||
validateBodyLength n body rest))
|
validateBodyLength n body rest))
|
||||||
(_ : pure (err 400 "Request body too large\n"))
|
|
||||||
(lte? n maxBodyBytes))
|
|
||||||
maybeLen)
|
maybeLen)
|
||||||
(contentLength headers))
|
(contentLength headers))
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ onResult_ = action errCase okCase :
|
|||||||
(val _ : okCase val)
|
(val _ : okCase val)
|
||||||
result)
|
result)
|
||||||
|
|
||||||
|
mapErrIO prefix action =
|
||||||
|
onResult_ action
|
||||||
|
(e : pure (err (append prefix e) t))
|
||||||
|
(v : pure (ok v t))
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Convenience helpers
|
-- Convenience helpers
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
|
|||||||
293
lib/list.tri
293
lib/list.tri
@@ -8,144 +8,188 @@ emptyList? = matchList true (_ _ : false)
|
|||||||
head = matchList t (head _ : head)
|
head = matchList t (head _ : head)
|
||||||
tail = matchList t (_ tail : tail)
|
tail = matchList t (_ tail : tail)
|
||||||
|
|
||||||
append = y (self : matchList
|
append_ self xs ys =
|
||||||
(k : k)
|
|
||||||
(h r k : pair h (self r k)))
|
|
||||||
|
|
||||||
lExist? = y (self x : matchList
|
|
||||||
false
|
|
||||||
(h z : or? (equal? x h) (self x z)))
|
|
||||||
|
|
||||||
map_ = y (self :
|
|
||||||
matchList
|
matchList
|
||||||
(_ : t)
|
ys
|
||||||
(head tail f : pair (f head) (self tail f)))
|
(h r : pair h (self r ys))
|
||||||
map = f l : map_ l f
|
xs
|
||||||
|
append = xs ys : y append_ xs ys
|
||||||
|
|
||||||
filter_ = y (self : matchList
|
lExist?_ self x xs =
|
||||||
(_ : t)
|
matchList
|
||||||
(head tail f : matchBool (t head) id (f head) (self tail f)))
|
false
|
||||||
filter = f l : filter_ l f
|
(h r : or? (equal? x h) (self x r))
|
||||||
|
xs
|
||||||
|
lExist? = x xs : y lExist?_ x xs
|
||||||
|
|
||||||
foldl_ = y (self l f x : matchList (acc : acc) (head tail acc : self tail f (f acc head)) l x)
|
map_ self l f =
|
||||||
foldl = f x l : foldl_ l f x
|
matchList
|
||||||
|
t
|
||||||
|
(h r : pair (f h) (self r f))
|
||||||
|
l
|
||||||
|
map = f l : y map_ l f
|
||||||
|
|
||||||
foldr_ = y (self l f x : matchList x (head tail : f (self tail f x) head) l)
|
filter_ self l f =
|
||||||
foldr = f x l : foldr_ l f x
|
matchList
|
||||||
|
t
|
||||||
|
(h r :
|
||||||
|
matchBool
|
||||||
|
(pair h (self r f))
|
||||||
|
(self r f)
|
||||||
|
(f h))
|
||||||
|
l
|
||||||
|
filter = f l : y filter_ l f
|
||||||
|
|
||||||
length = y (self : matchList
|
foldl_ self l f acc =
|
||||||
|
matchList
|
||||||
|
acc
|
||||||
|
(h r : self r f (f acc h))
|
||||||
|
l
|
||||||
|
foldl = f x l : y foldl_ l f x
|
||||||
|
|
||||||
|
foldr_ self l f x =
|
||||||
|
matchList
|
||||||
|
x
|
||||||
|
(h r : f (self r f x) h)
|
||||||
|
l
|
||||||
|
foldr = f x l : y foldr_ l f x
|
||||||
|
|
||||||
|
length_ self xs =
|
||||||
|
matchList
|
||||||
0
|
0
|
||||||
(_ tail : succ (self tail)))
|
(_ r : succ (self r))
|
||||||
|
xs
|
||||||
|
length = xs : y length_ xs
|
||||||
|
|
||||||
reverse_ = y (self xs acc :
|
reverse_ self xs acc =
|
||||||
matchList
|
matchList
|
||||||
acc
|
acc
|
||||||
(h r : self r (pair h acc))
|
(h r : self r (pair h acc))
|
||||||
xs)
|
xs
|
||||||
|
reverse = xs : y reverse_ xs t
|
||||||
|
|
||||||
reverse = xs : reverse_ xs t
|
snoc_ self x xs =
|
||||||
|
matchList
|
||||||
snoc = y (self x : matchList
|
|
||||||
(pair x t)
|
(pair x t)
|
||||||
(h z : pair h (self x z)))
|
(h r : pair h (self x r))
|
||||||
|
xs
|
||||||
|
snoc = x xs : y snoc_ x xs
|
||||||
|
|
||||||
count = y (self x : matchList
|
count_ self x xs =
|
||||||
|
matchList
|
||||||
0
|
0
|
||||||
(h z : matchBool
|
(h r :
|
||||||
(succ (self x z))
|
matchBool
|
||||||
(self x z)
|
(succ (self x r))
|
||||||
(equal? x h)))
|
(self x r)
|
||||||
|
(equal? x h))
|
||||||
|
xs
|
||||||
|
count = x xs : y count_ x xs
|
||||||
|
|
||||||
last = y (self : matchList
|
last_ self xs =
|
||||||
t
|
|
||||||
(hd tl : matchBool
|
|
||||||
hd
|
|
||||||
(self tl)
|
|
||||||
(emptyList? tl)))
|
|
||||||
|
|
||||||
all? = y (self pred : matchList
|
|
||||||
true
|
|
||||||
(h z : and? (pred h) (self pred z)))
|
|
||||||
|
|
||||||
any? = y (self pred : matchList
|
|
||||||
false
|
|
||||||
(h z : or? (pred h) (self pred z)))
|
|
||||||
|
|
||||||
intersect = xs ys : filter (x : lExist? x ys) xs
|
|
||||||
|
|
||||||
nth_ = y (self n xs i :
|
|
||||||
matchList
|
matchList
|
||||||
t
|
t
|
||||||
(h r :
|
(h r :
|
||||||
matchBool
|
matchBool
|
||||||
h
|
h
|
||||||
(self n r (succ i))
|
(self r)
|
||||||
(equal? i n))
|
(emptyList? r))
|
||||||
xs)
|
xs
|
||||||
|
last = xs : y last_ xs
|
||||||
|
|
||||||
nth = n xs : nth_ n xs 0
|
all?_ self pred xs =
|
||||||
|
matchList
|
||||||
|
true
|
||||||
|
(h r : and? (pred h) (self pred r))
|
||||||
|
xs
|
||||||
|
all? = pred xs : y all?_ pred xs
|
||||||
|
|
||||||
|
any?_ self pred xs =
|
||||||
|
matchList
|
||||||
|
false
|
||||||
|
(h r : or? (pred h) (self pred r))
|
||||||
|
xs
|
||||||
|
any? = pred xs : y any?_ pred xs
|
||||||
|
|
||||||
|
intersect = xs ys : filter (x : lExist? x ys) xs
|
||||||
|
|
||||||
|
nth_ self xs n i =
|
||||||
|
matchList
|
||||||
|
t
|
||||||
|
(h r :
|
||||||
|
matchBool
|
||||||
|
h
|
||||||
|
(self r n (succ i))
|
||||||
|
(equal? i n))
|
||||||
|
xs
|
||||||
|
nth = n xs : y nth_ xs n 0
|
||||||
|
|
||||||
headMaybe = matchList nothing (h _ : just h)
|
headMaybe = matchList nothing (h _ : just h)
|
||||||
|
|
||||||
lastMaybe = y (self : matchList
|
lastMaybe_ self xs =
|
||||||
nothing
|
|
||||||
(hd tl : matchBool
|
|
||||||
(just hd)
|
|
||||||
(self tl)
|
|
||||||
(emptyList? tl)))
|
|
||||||
|
|
||||||
nthMaybe_ = y (self n xs i :
|
|
||||||
matchList
|
matchList
|
||||||
nothing
|
nothing
|
||||||
(h r :
|
(h r :
|
||||||
matchBool
|
matchBool
|
||||||
(just h)
|
(just h)
|
||||||
(self n r (succ i))
|
(self r)
|
||||||
|
(emptyList? r))
|
||||||
|
xs
|
||||||
|
lastMaybe = xs : y lastMaybe_ xs
|
||||||
|
|
||||||
|
nthMaybe_ self xs n i =
|
||||||
|
matchList
|
||||||
|
nothing
|
||||||
|
(h r :
|
||||||
|
matchBool
|
||||||
|
(just h)
|
||||||
|
(self r n (succ i))
|
||||||
(equal? i n))
|
(equal? i n))
|
||||||
xs)
|
xs
|
||||||
|
nthMaybe = n xs : y nthMaybe_ xs n 0
|
||||||
|
|
||||||
nthMaybe = n xs : nthMaybe_ n xs 0
|
take_ self xs n i =
|
||||||
|
|
||||||
take_ = y (self n xs i :
|
|
||||||
matchList
|
matchList
|
||||||
t
|
t
|
||||||
(h r :
|
(h r :
|
||||||
matchBool
|
matchBool
|
||||||
t
|
t
|
||||||
(pair h (self n r (succ i)))
|
(pair h (self r n (succ i)))
|
||||||
(equal? i n))
|
(equal? i n))
|
||||||
xs)
|
xs
|
||||||
|
take = n xs : y take_ xs n 0
|
||||||
|
|
||||||
take = n xs : take_ n xs 0
|
drop_ self xs n i =
|
||||||
|
|
||||||
drop_ = y (self n xs i :
|
|
||||||
matchBool
|
matchBool
|
||||||
xs
|
xs
|
||||||
(matchList
|
(matchList
|
||||||
t
|
t
|
||||||
(_ r : self n r (succ i))
|
(_ r : self r n (succ i))
|
||||||
xs)
|
xs)
|
||||||
(equal? i n))
|
(equal? i n)
|
||||||
|
drop = n xs : y drop_ xs n 0
|
||||||
drop = n xs : drop_ n xs 0
|
|
||||||
|
|
||||||
splitAt = n xs : pair (take n xs) (drop n xs)
|
splitAt = n xs : pair (take n xs) (drop n xs)
|
||||||
|
|
||||||
concatMap_ = y (self f xs :
|
concatMap_ self f xs =
|
||||||
matchList
|
matchList
|
||||||
t
|
t
|
||||||
(h r : append (f h) (self f r))
|
(h r : append (f h) (self f r))
|
||||||
xs)
|
xs
|
||||||
|
concatMap = f xs : y concatMap_ f xs
|
||||||
|
|
||||||
concatMap = f xs : concatMap_ f xs
|
find_ self pred xs =
|
||||||
|
|
||||||
find = y (self pred xs :
|
|
||||||
matchList
|
matchList
|
||||||
nothing
|
nothing
|
||||||
(h r : matchBool (just h) (self pred r) (pred h))
|
(h r :
|
||||||
xs)
|
matchBool
|
||||||
|
(just h)
|
||||||
|
(self pred r)
|
||||||
|
(pred h))
|
||||||
|
xs
|
||||||
|
find = pred xs : y find_ pred xs
|
||||||
|
|
||||||
partition_ = y (self pred xs trues falses :
|
partition_ self pred xs trues falses =
|
||||||
matchList
|
matchList
|
||||||
(pair (reverse trues) (reverse falses))
|
(pair (reverse trues) (reverse falses))
|
||||||
(h r :
|
(h r :
|
||||||
@@ -153,19 +197,15 @@ partition_ = y (self pred xs trues falses :
|
|||||||
(self pred r (pair h trues) falses)
|
(self pred r (pair h trues) falses)
|
||||||
(self pred r trues (pair h falses))
|
(self pred r trues (pair h falses))
|
||||||
(pred h))
|
(pred h))
|
||||||
xs)
|
xs
|
||||||
|
partition = pred xs : y partition_ pred xs t t
|
||||||
partition = pred xs : partition_ pred xs t t
|
|
||||||
|
|
||||||
strLength = length
|
strLength = length
|
||||||
strAppend = append
|
strAppend = append
|
||||||
strEq? = equal?
|
strEq? = equal?
|
||||||
strEmpty? = emptyList?
|
strEmpty? = emptyList?
|
||||||
|
|
||||||
startsWith? = (prefix input :
|
startsWith?_ self prefix input =
|
||||||
((go :
|
|
||||||
go prefix input)
|
|
||||||
(y (self p s :
|
|
||||||
matchList
|
matchList
|
||||||
true
|
true
|
||||||
(ph pr :
|
(ph pr :
|
||||||
@@ -176,60 +216,61 @@ startsWith? = (prefix input :
|
|||||||
(self pr sr)
|
(self pr sr)
|
||||||
false
|
false
|
||||||
(equal? ph sh))
|
(equal? ph sh))
|
||||||
s)
|
input)
|
||||||
p))))
|
prefix
|
||||||
|
startsWith? = prefix input : y startsWith?_ prefix input
|
||||||
|
|
||||||
endsWith? = prefix str : startsWith? (reverse prefix) (reverse str)
|
endsWith? = prefix str : startsWith? (reverse prefix) (reverse str)
|
||||||
|
|
||||||
contains? = y (self needle haystack :
|
contains?_ self needle haystack =
|
||||||
matchBool
|
matchBool
|
||||||
true
|
true
|
||||||
(matchList
|
(matchList
|
||||||
false
|
false
|
||||||
(_ r : self needle r)
|
(_ r : self needle r)
|
||||||
haystack)
|
haystack)
|
||||||
(startsWith? needle haystack))
|
(startsWith? needle haystack)
|
||||||
|
contains? = needle haystack : y contains?_ needle haystack
|
||||||
|
|
||||||
lines_ = y (self str :
|
linesFinish current accRev =
|
||||||
|
reverse (pair (reverse current) accRev)
|
||||||
|
|
||||||
|
lines_ self str accRev current =
|
||||||
matchList
|
matchList
|
||||||
(acc current : snoc (reverse current) acc)
|
(linesFinish current accRev)
|
||||||
(h r :
|
(h r :
|
||||||
acc current :
|
|
||||||
matchBool
|
matchBool
|
||||||
(self r (snoc (reverse current) acc) t)
|
(self r (pair (reverse current) accRev) t)
|
||||||
(self r acc (pair h current))
|
(self r accRev (pair h current))
|
||||||
(equal? h 10))
|
(equal? h 10))
|
||||||
str)
|
str
|
||||||
|
lines = str : y lines_ str t t
|
||||||
|
|
||||||
lines = str : lines_ str t t
|
unlines_ self lines =
|
||||||
|
|
||||||
unlines = y (self lines :
|
|
||||||
matchList
|
matchList
|
||||||
""
|
""
|
||||||
(h r : append h (append "\n" (self r)))
|
(h r : append h (append "\n" (self r)))
|
||||||
lines)
|
lines
|
||||||
|
unlines = lines : y unlines_ lines
|
||||||
|
|
||||||
words_ = y (self str :
|
wordsAdd current accRev =
|
||||||
|
matchBool
|
||||||
|
accRev
|
||||||
|
(pair (reverse current) accRev)
|
||||||
|
(emptyList? current)
|
||||||
|
|
||||||
|
words_ self str accRev current =
|
||||||
matchList
|
matchList
|
||||||
(acc current :
|
(reverse (wordsAdd current accRev))
|
||||||
matchBool
|
|
||||||
acc
|
|
||||||
(snoc (reverse current) acc)
|
|
||||||
(emptyList? current))
|
|
||||||
(h r :
|
(h r :
|
||||||
acc current :
|
|
||||||
matchBool
|
matchBool
|
||||||
(matchBool
|
(self r (wordsAdd current accRev) t)
|
||||||
(self r acc current)
|
(self r accRev (pair h current))
|
||||||
(self r (snoc (reverse current) acc) t)
|
|
||||||
(emptyList? current))
|
|
||||||
(self r acc (pair h current))
|
|
||||||
(equal? h 32))
|
(equal? h 32))
|
||||||
str)
|
str
|
||||||
|
words = str : y words_ str t t
|
||||||
|
|
||||||
words = str : words_ str t t
|
unwords_ self words =
|
||||||
|
|
||||||
unwords = y (self words :
|
|
||||||
matchList
|
matchList
|
||||||
""
|
""
|
||||||
(h r :
|
(h r :
|
||||||
@@ -237,9 +278,10 @@ unwords = y (self words :
|
|||||||
h
|
h
|
||||||
(append h (append " " (self r)))
|
(append h (append " " (self r)))
|
||||||
(emptyList? r))
|
(emptyList? r))
|
||||||
words)
|
words
|
||||||
|
unwords = words : y unwords_ words
|
||||||
|
|
||||||
zipWith = y (self f xs ys :
|
zipWith_ self f xs ys =
|
||||||
matchList
|
matchList
|
||||||
t
|
t
|
||||||
(xh xt :
|
(xh xt :
|
||||||
@@ -247,4 +289,5 @@ zipWith = y (self f xs ys :
|
|||||||
t
|
t
|
||||||
(yh yt : pair (f xh yh) (self f xt yt))
|
(yh yt : pair (f xh yh) (self f xt yt))
|
||||||
ys)
|
ys)
|
||||||
xs)
|
xs
|
||||||
|
zipWith = f xs ys : y zipWith_ f xs ys
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
!import "base.tri" !Local
|
!import "base.tri" !Local
|
||||||
!import "list.tri" !Local
|
!import "list.tri" !Local
|
||||||
|
!import "lazy.tri" !Local
|
||||||
|
|
||||||
match_ = y (self value patterns :
|
match_ = y (self value patterns :
|
||||||
triage
|
triage
|
||||||
@@ -22,3 +23,20 @@ match = (value patterns :
|
|||||||
patterns))
|
patterns))
|
||||||
|
|
||||||
otherwise = const (t t)
|
otherwise = const (t t)
|
||||||
|
|
||||||
|
cond_ self patterns =
|
||||||
|
lazyList
|
||||||
|
(_ : t)
|
||||||
|
(pattern rest :
|
||||||
|
matchPair
|
||||||
|
(testK actionK :
|
||||||
|
lazyBool
|
||||||
|
actionK
|
||||||
|
(_ : self rest)
|
||||||
|
(testK t))
|
||||||
|
pattern)
|
||||||
|
patterns
|
||||||
|
|
||||||
|
cond patterns = y cond_ patterns
|
||||||
|
|
||||||
|
guard testK actionK = pair testK actionK
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Data.List (nub, sort)
|
|||||||
import Data.Maybe (catMaybes, fromMaybe)
|
import Data.Maybe (catMaybes, fromMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Database.SQLite.Simple
|
import Database.SQLite.Simple
|
||||||
|
import System.IO (hPutStrLn, stderr)
|
||||||
import System.Directory (createDirectoryIfMissing, getXdgDirectory, XdgDirectory(..))
|
import System.Directory (createDirectoryIfMissing, getXdgDirectory, XdgDirectory(..))
|
||||||
import System.Environment (lookupEnv)
|
import System.Environment (lookupEnv)
|
||||||
import System.Exit (die)
|
import System.Exit (die)
|
||||||
@@ -98,7 +99,9 @@ storeTerm conn newNamesStrList term = do
|
|||||||
let termHashText = hashTerm term
|
let termHashText = hashTerm term
|
||||||
newNamesTextList = map T.pack newNamesStrList
|
newNamesTextList = map T.pack newNamesStrList
|
||||||
metadataText = T.pack "{}"
|
metadataText = T.pack "{}"
|
||||||
-- Store all Merkle nodes for this term
|
-- Store all Merkle nodes for this term. This traversal is where lazy T
|
||||||
|
-- values are forced into normalized Merkle nodes for persistence.
|
||||||
|
hPutStrLn stderr $ "[tricu] storing " ++ show newNamesStrList
|
||||||
_ <- storeMerkleNodes conn term
|
_ <- storeMerkleNodes conn term
|
||||||
existingNamesQuery <- query conn
|
existingNamesQuery <- query conn
|
||||||
"SELECT names FROM terms WHERE hash = ?"
|
"SELECT names FROM terms WHERE hash = ?"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Data.List (partition, (\\), elemIndex, foldl')
|
|||||||
import Data.Map ()
|
import Data.Map ()
|
||||||
import Data.Set (Set)
|
import Data.Set (Set)
|
||||||
import Database.SQLite.Simple
|
import Database.SQLite.Simple
|
||||||
|
import Debug.Trace (trace)
|
||||||
|
|
||||||
import qualified Data.Foldable as F ()
|
import qualified Data.Foldable as F ()
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
|||||||
32
src/Lexer.hs
32
src/Lexer.hs
@@ -32,6 +32,7 @@ tricuLexer = do
|
|||||||
where
|
where
|
||||||
tricuLexer' =
|
tricuLexer' =
|
||||||
[ try lnewline
|
[ try lnewline
|
||||||
|
, try indentMarker
|
||||||
, try namespace
|
, try namespace
|
||||||
, try dot
|
, try dot
|
||||||
, try identifierWithHash
|
, try identifierWithHash
|
||||||
@@ -45,15 +46,35 @@ tricuLexer = do
|
|||||||
, closeParen
|
, closeParen
|
||||||
, openBracket
|
, openBracket
|
||||||
, closeBracket
|
, closeBracket
|
||||||
|
, try bindArrow
|
||||||
, try arrowLeft
|
, try arrowLeft
|
||||||
, try arrowRight
|
, try arrowRight
|
||||||
]
|
]
|
||||||
|
|
||||||
lexTricu :: String -> [LToken]
|
lexTricu :: String -> [LToken]
|
||||||
lexTricu input = case runParser tricuLexer "" input of
|
lexTricu input = case runParser tricuLexer "" (insertIndentMarkers input) of
|
||||||
Left err -> errorWithoutStackTrace $ "Lexical error:\n" ++ errorBundlePretty err
|
Left err -> errorWithoutStackTrace $ "Lexical error:\n" ++ errorBundlePretty err
|
||||||
Right toks -> toks
|
Right toks -> toks
|
||||||
|
|
||||||
|
insertIndentMarkers :: String -> String
|
||||||
|
insertIndentMarkers = go False False
|
||||||
|
where
|
||||||
|
marker n = '\v' : show n ++ " "
|
||||||
|
|
||||||
|
go _ _ [] = []
|
||||||
|
go inString escaped (c:cs)
|
||||||
|
| inString =
|
||||||
|
c : go (not (c == '"' && not escaped)) (c == '\\' && not escaped) cs
|
||||||
|
| c == '"' = c : go True False cs
|
||||||
|
| c == '\n' =
|
||||||
|
let (spaces, rest) = span (== ' ') cs
|
||||||
|
n = length spaces
|
||||||
|
in if n == 0
|
||||||
|
then '\n' : go False False rest
|
||||||
|
else '\n' : marker n ++ go False False rest
|
||||||
|
| c == '\t' = errorWithoutStackTrace "Tabs are not allowed for indentation; use two spaces per indent level"
|
||||||
|
| otherwise = c : go False False cs
|
||||||
|
|
||||||
|
|
||||||
keywordT :: Lexer LToken
|
keywordT :: Lexer LToken
|
||||||
keywordT = string "t" *> notFollowedBy alphaNumChar $> LKeywordT
|
keywordT = string "t" *> notFollowedBy alphaNumChar $> LKeywordT
|
||||||
@@ -136,9 +157,18 @@ arrowLeft = string "<|" $> LArrowLeft
|
|||||||
arrowRight :: Lexer LToken
|
arrowRight :: Lexer LToken
|
||||||
arrowRight = string "|>" $> LArrowRight
|
arrowRight = string "|>" $> LArrowRight
|
||||||
|
|
||||||
|
bindArrow :: Lexer LToken
|
||||||
|
bindArrow = string "<-" $> LBindArrow
|
||||||
|
|
||||||
lnewline :: Lexer LToken
|
lnewline :: Lexer LToken
|
||||||
lnewline = char '\n' $> LNewline
|
lnewline = char '\n' $> LNewline
|
||||||
|
|
||||||
|
indentMarker :: Lexer LToken
|
||||||
|
indentMarker = do
|
||||||
|
void (char '\v')
|
||||||
|
n <- some digitChar
|
||||||
|
pure (LIndent (read n))
|
||||||
|
|
||||||
sc :: Lexer ()
|
sc :: Lexer ()
|
||||||
sc = space
|
sc = space
|
||||||
(void $ takeWhile1P (Just "space") (\c -> c == ' ' || c == '\t'))
|
(void $ takeWhile1P (Just "space") (\c -> c == ' ' || c == '\t'))
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ readEvaluatedForm = eitherReader $ \s -> case s of
|
|||||||
"ternary" -> Right Ternary
|
"ternary" -> Right Ternary
|
||||||
"ascii" -> Right Ascii
|
"ascii" -> Right Ascii
|
||||||
"decode" -> Right Decode
|
"decode" -> Right Decode
|
||||||
_ -> Left $ "Unknown format: " ++ s ++ ". Expected: tree, fsl, ast, ternary, ascii, decode"
|
"number" -> Right Number
|
||||||
|
"string" -> Right StringLit
|
||||||
|
_ -> Left $ "Unknown format: " ++ s ++ ". Expected: tree, fsl, ast, ternary, ascii, decode, number, string"
|
||||||
|
|
||||||
evalParser :: Parser TricuArgs
|
evalParser :: Parser TricuArgs
|
||||||
evalParser = Eval
|
evalParser = Eval
|
||||||
@@ -84,7 +86,7 @@ evalParser = Eval
|
|||||||
<> short 'f'
|
<> short 'f'
|
||||||
<> metavar "FORM"
|
<> metavar "FORM"
|
||||||
<> value Tree
|
<> value Tree
|
||||||
<> help "Output format: tree, fsl, ast, ternary, ascii, decode"
|
<> help "Output format: tree, fsl, ast, ternary, ascii, decode, number, string"
|
||||||
)
|
)
|
||||||
<*> option str
|
<*> option str
|
||||||
( long "output"
|
( long "output"
|
||||||
|
|||||||
246
src/Parser.hs
246
src/Parser.hs
@@ -16,7 +16,7 @@ data Context = Top | Nested
|
|||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
reservedNames :: Set.Set String
|
reservedNames :: Set.Set String
|
||||||
reservedNames = Set.fromList ["t", "!result"]
|
reservedNames = Set.fromList ["t", "!result", "let", "in", "where", "do"]
|
||||||
|
|
||||||
parseTricu :: String -> [TricuAST]
|
parseTricu :: String -> [TricuAST]
|
||||||
parseTricu input =
|
parseTricu input =
|
||||||
@@ -69,17 +69,26 @@ manyItemsP = do
|
|||||||
topItemP :: TokParser TricuAST
|
topItemP :: TokParser TricuAST
|
||||||
topItemP = do
|
topItemP = do
|
||||||
toks <- getInput
|
toks <- getInput
|
||||||
case toks of
|
case definitionHeadTop toks of
|
||||||
LIdentifier _ : LAssign : _ -> definitionP
|
Just _ -> definitionP
|
||||||
_ -> exprTopP
|
Nothing -> exprTopP
|
||||||
|
|
||||||
|
definitionHeadTop :: [LToken] -> Maybe (String, [String])
|
||||||
|
definitionHeadTop toks =
|
||||||
|
case collectIdentifiersNoNewlines toks of
|
||||||
|
(name:args, LAssign : _)
|
||||||
|
| name `Set.notMember` reservedNames
|
||||||
|
, all (`Set.notMember` reservedNames) args -> Just (name, args)
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
definitionP :: TokParser TricuAST
|
definitionP :: TokParser TricuAST
|
||||||
definitionP = do
|
definitionP = do
|
||||||
name <- identifierNameP
|
name <- identifierNameP
|
||||||
|
args <- many identifierNameP
|
||||||
void (tok (== LAssign) "=")
|
void (tok (== LAssign) "=")
|
||||||
skipNestedNewlines
|
bodyIndent <- skipNestedNewlinesGetIndent
|
||||||
body <- exprTopP
|
body <- exprAtIndentP bodyIndent
|
||||||
pure (SDef name [] body)
|
pure (SDef name args body)
|
||||||
|
|
||||||
importP :: TokParser TricuAST
|
importP :: TokParser TricuAST
|
||||||
importP = do
|
importP = do
|
||||||
@@ -96,7 +105,7 @@ exprTopP = do
|
|||||||
toks <- getInput
|
toks <- getInput
|
||||||
case lambdaHeadTop toks of
|
case lambdaHeadTop toks of
|
||||||
Just params -> lambdaP Top params
|
Just params -> lambdaP Top params
|
||||||
Nothing -> pipeTopP
|
Nothing -> whereChainP pipeTopP
|
||||||
|
|
||||||
exprNestedP :: TokParser TricuAST
|
exprNestedP :: TokParser TricuAST
|
||||||
exprNestedP = do
|
exprNestedP = do
|
||||||
@@ -104,7 +113,14 @@ exprNestedP = do
|
|||||||
toks <- getInput
|
toks <- getInput
|
||||||
case lambdaHeadNested toks of
|
case lambdaHeadNested toks of
|
||||||
Just params -> lambdaP Nested params
|
Just params -> lambdaP Nested params
|
||||||
Nothing -> pipeNestedP
|
Nothing -> whereChainP pipeNestedP
|
||||||
|
|
||||||
|
exprAtIndentP :: Int -> TokParser TricuAST
|
||||||
|
exprAtIndentP n = do
|
||||||
|
toks <- getInput
|
||||||
|
case lambdaHeadTop toks of
|
||||||
|
Just params -> lambdaP Top params
|
||||||
|
Nothing -> whereChainP (pipeAtIndentP n)
|
||||||
|
|
||||||
lambdaP :: Context -> [String] -> TokParser TricuAST
|
lambdaP :: Context -> [String] -> TokParser TricuAST
|
||||||
lambdaP ctx params = do
|
lambdaP ctx params = do
|
||||||
@@ -174,7 +190,11 @@ applyPipe acc (PipeForward, rhs) =
|
|||||||
|
|
||||||
pipeTopP :: TokParser TricuAST
|
pipeTopP :: TokParser TricuAST
|
||||||
pipeTopP =
|
pipeTopP =
|
||||||
pipeChainP appTopP appNestedP
|
pipeAtIndentP 0
|
||||||
|
|
||||||
|
pipeAtIndentP :: Int -> TokParser TricuAST
|
||||||
|
pipeAtIndentP n =
|
||||||
|
pipeChainP (appAtIndentP n) appNestedP
|
||||||
|
|
||||||
pipeNestedP :: TokParser TricuAST
|
pipeNestedP :: TokParser TricuAST
|
||||||
pipeNestedP =
|
pipeNestedP =
|
||||||
@@ -199,19 +219,53 @@ pipeOpP =
|
|||||||
<|> (tok (== LArrowRight) "|>" *> pure PipeForward)
|
<|> (tok (== LArrowRight) "|>" *> pure PipeForward)
|
||||||
|
|
||||||
appTopP :: TokParser TricuAST
|
appTopP :: TokParser TricuAST
|
||||||
appTopP = do
|
appTopP = appAtIndentP 0
|
||||||
first <- atomTopP
|
|
||||||
appRestTopP first
|
|
||||||
|
|
||||||
appRestTopP :: TricuAST -> TokParser TricuAST
|
appAtIndentP :: Int -> TokParser TricuAST
|
||||||
appRestTopP acc = do
|
appAtIndentP n = do
|
||||||
|
first <- atomTopP
|
||||||
|
appRestAtIndentP n first
|
||||||
|
|
||||||
|
appRestAtIndentP :: Int -> TricuAST -> TokParser TricuAST
|
||||||
|
appRestAtIndentP currentIndent acc = do
|
||||||
|
toks <- getInput
|
||||||
|
let shouldContinue = case toks of
|
||||||
|
LNewline : LIndent n : rest
|
||||||
|
| currentIndent > 0
|
||||||
|
, n > currentIndent
|
||||||
|
, not (isIndentedTerminator rest)
|
||||||
|
, Just t <- firstNonLayout rest -> startsAtom t && not (isExprTerminator t)
|
||||||
|
_ -> False
|
||||||
|
if shouldContinue
|
||||||
|
then do
|
||||||
|
indentedNewlineP
|
||||||
|
arg <- atomTopP
|
||||||
|
appRestAtIndentP currentIndent (SApp acc arg)
|
||||||
|
else do
|
||||||
mt <- peekP
|
mt <- peekP
|
||||||
case mt of
|
case mt of
|
||||||
Just t | startsAtom t -> do
|
Just t | startsAtom t && not (isExprTerminator t) -> do
|
||||||
arg <- atomTopP
|
arg <- atomTopP
|
||||||
appRestTopP (SApp acc arg)
|
appRestAtIndentP currentIndent (SApp acc arg)
|
||||||
_ -> pure acc
|
_ -> pure acc
|
||||||
|
|
||||||
|
isIndentedTerminator :: [LToken] -> Bool
|
||||||
|
isIndentedTerminator toks =
|
||||||
|
case dropLayout toks of
|
||||||
|
LIdentifier "where" : _ -> True
|
||||||
|
rest -> definitionHeadTop rest /= Nothing
|
||||||
|
|
||||||
|
firstNonLayout :: [LToken] -> Maybe LToken
|
||||||
|
firstNonLayout toks =
|
||||||
|
case dropLayout toks of
|
||||||
|
[] -> Nothing
|
||||||
|
x : _ -> Just x
|
||||||
|
|
||||||
|
dropLayout :: [LToken] -> [LToken]
|
||||||
|
dropLayout (LNewline : rest) = dropLayout rest
|
||||||
|
dropLayout (LIndent _ : rest) = dropLayout rest
|
||||||
|
dropLayout rest = rest
|
||||||
|
|
||||||
appNestedP :: TokParser TricuAST
|
appNestedP :: TokParser TricuAST
|
||||||
appNestedP = do
|
appNestedP = do
|
||||||
first <- atomNestedP
|
first <- atomNestedP
|
||||||
@@ -222,7 +276,7 @@ appRestNestedP acc = do
|
|||||||
skipNestedNewlines
|
skipNestedNewlines
|
||||||
mt <- peekP
|
mt <- peekP
|
||||||
case mt of
|
case mt of
|
||||||
Just t | startsAtom t -> do
|
Just t | startsAtom t && not (isExprTerminator t) -> do
|
||||||
arg <- atomNestedP
|
arg <- atomNestedP
|
||||||
appRestNestedP (SApp acc arg)
|
appRestNestedP (SApp acc arg)
|
||||||
_ -> pure acc
|
_ -> pure acc
|
||||||
@@ -238,6 +292,11 @@ startsAtom (LIntegerLiteral _) = True
|
|||||||
startsAtom (LStringLiteral _) = True
|
startsAtom (LStringLiteral _) = True
|
||||||
startsAtom _ = False
|
startsAtom _ = False
|
||||||
|
|
||||||
|
isExprTerminator :: LToken -> Bool
|
||||||
|
isExprTerminator (LIdentifier "in") = True
|
||||||
|
isExprTerminator (LIdentifier "where") = True
|
||||||
|
isExprTerminator _ = False
|
||||||
|
|
||||||
atomTopP :: TokParser TricuAST
|
atomTopP :: TokParser TricuAST
|
||||||
atomTopP = do
|
atomTopP = do
|
||||||
toks <- getInput
|
toks <- getInput
|
||||||
@@ -245,7 +304,11 @@ atomTopP = do
|
|||||||
LOpenParen : _ -> groupedP
|
LOpenParen : _ -> groupedP
|
||||||
LOpenBracket : _ -> listP
|
LOpenBracket : _ -> listP
|
||||||
LNamespace _ : LDot : _ -> namespacedVarP
|
LNamespace _ : LDot : _ -> namespacedVarP
|
||||||
LIdentifier _ : _ -> plainVarP
|
LIdentifier "let" : _ -> letP
|
||||||
|
LIdentifier "do" : _ -> doP
|
||||||
|
LIdentifier name : _
|
||||||
|
| name == "in" || name == "where" -> fail ("unexpected reserved word: " ++ name)
|
||||||
|
| otherwise -> plainVarP
|
||||||
LIdentifierWithHash _ _ : _ -> plainVarP
|
LIdentifierWithHash _ _ : _ -> plainVarP
|
||||||
LKeywordT : _ -> leafP
|
LKeywordT : _ -> leafP
|
||||||
LIntegerLiteral _ : _ -> intP
|
LIntegerLiteral _ : _ -> intP
|
||||||
@@ -292,13 +355,116 @@ listElementP = do
|
|||||||
LOpenParen : _ -> groupedP
|
LOpenParen : _ -> groupedP
|
||||||
LOpenBracket : _ -> listP
|
LOpenBracket : _ -> listP
|
||||||
LNamespace _ : LDot : _ -> namespacedVarP
|
LNamespace _ : LDot : _ -> namespacedVarP
|
||||||
LIdentifier _ : _ -> plainVarP
|
LIdentifier "let" : _ -> letP
|
||||||
|
LIdentifier "do" : _ -> doP
|
||||||
|
LIdentifier name : _
|
||||||
|
| name == "in" || name == "where" -> fail ("unexpected reserved word: " ++ name)
|
||||||
|
| otherwise -> plainVarP
|
||||||
LIdentifierWithHash _ _ : _ -> plainVarP
|
LIdentifierWithHash _ _ : _ -> plainVarP
|
||||||
LKeywordT : _ -> leafP
|
LKeywordT : _ -> leafP
|
||||||
LIntegerLiteral _ : _ -> intP
|
LIntegerLiteral _ : _ -> intP
|
||||||
LStringLiteral _ : _ -> strP
|
LStringLiteral _ : _ -> strP
|
||||||
_ -> fail "expected list element"
|
_ -> fail "expected list element"
|
||||||
|
|
||||||
|
whereChainP :: TokParser TricuAST -> TokParser TricuAST
|
||||||
|
whereChainP parseBody = do
|
||||||
|
body <- parseBody
|
||||||
|
mWhere <- optional (try whereBindingP)
|
||||||
|
case mWhere of
|
||||||
|
Nothing -> pure body
|
||||||
|
Just (name, args, value) ->
|
||||||
|
let boundValue = foldr (\p acc -> SLambda [p] acc) value args
|
||||||
|
in pure (SApp (SLambda [name] body) boundValue)
|
||||||
|
|
||||||
|
whereBindingP :: TokParser (String, [String], TricuAST)
|
||||||
|
whereBindingP = do
|
||||||
|
skipNestedNewlines
|
||||||
|
void (keywordIdentifierP "where")
|
||||||
|
skipNestedNewlines
|
||||||
|
name <- identifierNameP
|
||||||
|
args <- many identifierNameP
|
||||||
|
void (tok (== LAssign) "=")
|
||||||
|
valueIndent <- skipNestedNewlinesGetIndent
|
||||||
|
value <- exprAtIndentP valueIndent
|
||||||
|
pure (name, args, value)
|
||||||
|
|
||||||
|
letP :: TokParser TricuAST
|
||||||
|
letP = do
|
||||||
|
void (keywordIdentifierP "let")
|
||||||
|
skipNestedNewlines
|
||||||
|
name <- identifierNameP
|
||||||
|
args <- many identifierNameP
|
||||||
|
void (tok (== LAssign) "=")
|
||||||
|
valueIndent <- skipNestedNewlinesGetIndent
|
||||||
|
value <- exprAtIndentP valueIndent
|
||||||
|
skipNestedNewlines
|
||||||
|
void (keywordIdentifierP "in")
|
||||||
|
bodyIndent <- skipNestedNewlinesGetIndent
|
||||||
|
body <- exprAtIndentP bodyIndent
|
||||||
|
let boundValue = foldr (\p acc -> SLambda [p] acc) value args
|
||||||
|
pure (SApp (SLambda [name] body) boundValue)
|
||||||
|
|
||||||
|
data DoStmt
|
||||||
|
= DoBind String TricuAST
|
||||||
|
| DoExpr TricuAST
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
doP :: TokParser TricuAST
|
||||||
|
doP = do
|
||||||
|
void (keywordIdentifierP "do")
|
||||||
|
skipNestedNewlines
|
||||||
|
bindOp <- atomTopP
|
||||||
|
blockIndent <- requireIndentedBlockP
|
||||||
|
stmts <- doBlockP blockIndent
|
||||||
|
lowerDo bindOp stmts
|
||||||
|
|
||||||
|
doBlockP :: Int -> TokParser [DoStmt]
|
||||||
|
doBlockP blockIndent = do
|
||||||
|
first <- doStmtP blockIndent
|
||||||
|
rest <- many (try (sameIndentP blockIndent *> doStmtP blockIndent))
|
||||||
|
pure (first : rest)
|
||||||
|
|
||||||
|
doStmtP :: Int -> TokParser DoStmt
|
||||||
|
doStmtP blockIndent = do
|
||||||
|
toks <- getInput
|
||||||
|
case toks of
|
||||||
|
LIdentifier name : LBindArrow : _ -> do
|
||||||
|
void identifierNameP
|
||||||
|
void (tok (== LBindArrow) "<-")
|
||||||
|
exprIndent <- skipNestedNewlinesGetIndent
|
||||||
|
DoBind name <$> exprAtIndentP (max blockIndent exprIndent)
|
||||||
|
_ -> DoExpr <$> exprAtIndentP blockIndent
|
||||||
|
|
||||||
|
lowerDo :: TricuAST -> [DoStmt] -> TokParser TricuAST
|
||||||
|
lowerDo _ [] = fail "do block must contain at least one statement"
|
||||||
|
lowerDo _ [DoExpr expr] = pure expr
|
||||||
|
lowerDo bindOp [DoBind _ _] = fail "last do statement must be an expression"
|
||||||
|
lowerDo bindOp (DoBind name action : rest) = do
|
||||||
|
body <- lowerDo bindOp rest
|
||||||
|
pure (SApp (SApp bindOp action) (SLambda [name] body))
|
||||||
|
lowerDo bindOp (DoExpr action : rest) = do
|
||||||
|
body <- lowerDo bindOp rest
|
||||||
|
pure (SApp (SApp bindOp action) (SLambda ["_"] body))
|
||||||
|
|
||||||
|
requireIndentedBlockP :: TokParser Int
|
||||||
|
requireIndentedBlockP = do
|
||||||
|
void (tok (== LNewline) "newline")
|
||||||
|
t <- tok isIndent "indent"
|
||||||
|
case t of
|
||||||
|
LIndent n | n > 0 -> pure n
|
||||||
|
_ -> fail "expected indented do block"
|
||||||
|
|
||||||
|
sameIndentP :: Int -> TokParser ()
|
||||||
|
sameIndentP n = do
|
||||||
|
void (tok (== LNewline) "newline")
|
||||||
|
t <- tok isIndent "indent"
|
||||||
|
case t of
|
||||||
|
LIndent m | m == n -> pure ()
|
||||||
|
_ -> fail "expected do statement at same indentation"
|
||||||
|
|
||||||
|
keywordIdentifierP :: String -> TokParser LToken
|
||||||
|
keywordIdentifierP name = tok (== LIdentifier name) name
|
||||||
|
|
||||||
leafP :: TokParser TricuAST
|
leafP :: TokParser TricuAST
|
||||||
leafP = tok (== LKeywordT) "t" *> pure TLeaf
|
leafP = tok (== LKeywordT) "t" *> pure TLeaf
|
||||||
|
|
||||||
@@ -381,12 +547,48 @@ atEndP :: TokParser Bool
|
|||||||
atEndP = null <$> getInput
|
atEndP = null <$> getInput
|
||||||
|
|
||||||
skipTopNewlines :: TokParser ()
|
skipTopNewlines :: TokParser ()
|
||||||
skipTopNewlines = skipMany (tok (== LNewline) "newline")
|
skipTopNewlines = skipMany newlineWithOptionalIndentP
|
||||||
|
|
||||||
skipNestedNewlines :: TokParser ()
|
skipNestedNewlines :: TokParser ()
|
||||||
skipNestedNewlines = skipMany (tok (== LNewline) "newline")
|
skipNestedNewlines = void skipNestedNewlinesGetIndent
|
||||||
|
|
||||||
|
skipNestedNewlinesGetIndent :: TokParser Int
|
||||||
|
skipNestedNewlinesGetIndent = go 0
|
||||||
|
where
|
||||||
|
go lastIndent = do
|
||||||
|
mt <- optional (try newlineWithOptionalIndentValueP)
|
||||||
|
case mt of
|
||||||
|
Nothing -> pure lastIndent
|
||||||
|
Just n -> go n
|
||||||
|
|
||||||
|
newlineWithOptionalIndentP :: TokParser ()
|
||||||
|
newlineWithOptionalIndentP = void newlineWithOptionalIndentValueP
|
||||||
|
|
||||||
|
newlineWithOptionalIndentValueP :: TokParser Int
|
||||||
|
newlineWithOptionalIndentValueP = do
|
||||||
|
void (tok (== LNewline) "newline")
|
||||||
|
mt <- optional indentP
|
||||||
|
pure $ case mt of
|
||||||
|
Just (LIndent n) -> n
|
||||||
|
_ -> 0
|
||||||
|
|
||||||
|
indentedNewlineP :: TokParser ()
|
||||||
|
indentedNewlineP = do
|
||||||
|
void (tok (== LNewline) "newline")
|
||||||
|
t <- tok isIndent "indent"
|
||||||
|
case t of
|
||||||
|
LIndent n | n > 0 -> pure ()
|
||||||
|
_ -> fail "expected indented continuation"
|
||||||
|
|
||||||
|
indentP :: TokParser LToken
|
||||||
|
indentP = tok isIndent "indent"
|
||||||
|
|
||||||
|
isIndent :: LToken -> Bool
|
||||||
|
isIndent (LIndent _) = True
|
||||||
|
isIndent _ = False
|
||||||
|
|
||||||
dropNewlines :: [LToken] -> [LToken]
|
dropNewlines :: [LToken] -> [LToken]
|
||||||
|
dropNewlines (LNewline : LIndent _ : rest) = dropNewlines rest
|
||||||
dropNewlines (LNewline : rest) = dropNewlines rest
|
dropNewlines (LNewline : rest) = dropNewlines rest
|
||||||
dropNewlines rest = rest
|
dropNewlines rest = rest
|
||||||
|
|
||||||
|
|||||||
@@ -130,15 +130,15 @@ repl = do
|
|||||||
|
|
||||||
handleOutput :: REPLState -> InputT IO ()
|
handleOutput :: REPLState -> InputT IO ()
|
||||||
handleOutput state = do
|
handleOutput state = do
|
||||||
let formats = [Decode, Tree, FSL, AST, Ternary, Ascii]
|
let formats = [Decode, Tree, FSL, AST, Ternary, Ascii, Number, StringLit]
|
||||||
outputStrLn "Available output formats:"
|
outputStrLn "Available output formats:"
|
||||||
mapM_ (\(i, f) -> outputStrLn $ show (i :: Int) ++ ". " ++ show f)
|
mapM_ (\(i, f) -> outputStrLn $ show (i :: Int) ++ ". " ++ show f)
|
||||||
(zip [1..] formats)
|
(zip [1..] formats)
|
||||||
|
|
||||||
evalResult <- runMaybeT $ do
|
evalResult <- runMaybeT $ do
|
||||||
input <- MaybeT $ getInputLine "Select output format (1-6) < "
|
input <- MaybeT $ getInputLine "Select output format (1-8) < "
|
||||||
case reads input of
|
case reads input of
|
||||||
[(n, "")] | n >= 1 && n <= 6 ->
|
[(n, "")] | n >= 1 && n <= 8 ->
|
||||||
return $ formats !! (n-1)
|
return $ formats !! (n-1)
|
||||||
_ -> MaybeT $ return Nothing
|
_ -> MaybeT $ return Nothing
|
||||||
|
|
||||||
|
|||||||
@@ -51,11 +51,13 @@ data LToken
|
|||||||
| LIntegerLiteral Int
|
| LIntegerLiteral Int
|
||||||
| LArrowLeft
|
| LArrowLeft
|
||||||
| LArrowRight
|
| LArrowRight
|
||||||
|
| LBindArrow
|
||||||
| LNewline
|
| LNewline
|
||||||
|
| LIndent Int
|
||||||
deriving (Eq, Show, Ord)
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
-- Output formats
|
-- Output formats
|
||||||
data EvaluatedForm = Tree | FSL | AST | Ternary | Ascii | Decode
|
data EvaluatedForm = Tree | FSL | AST | Ternary | Ascii | Decode | Number | StringLit
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
-- Environment containing previously evaluated TC terms
|
-- Environment containing previously evaluated TC terms
|
||||||
@@ -257,6 +259,8 @@ formatT AST = show . toAST
|
|||||||
formatT Ternary = toTernaryString
|
formatT Ternary = toTernaryString
|
||||||
formatT Ascii = toAscii
|
formatT Ascii = toAscii
|
||||||
formatT Decode = decodeResult
|
formatT Decode = decodeResult
|
||||||
|
formatT Number = either (\e -> "<not-number: " ++ e ++ ">") show . toNumber
|
||||||
|
formatT StringLit = either (\e -> "<not-string: " ++ e ++ ">") show . toString
|
||||||
|
|
||||||
toSimpleT :: String -> String
|
toSimpleT :: String -> String
|
||||||
toSimpleT s = T.unpack
|
toSimpleT s = T.unpack
|
||||||
|
|||||||
112
test/Spec.hs
112
test/Spec.hs
@@ -50,15 +50,15 @@ tests = testGroup "Tricu Tests"
|
|||||||
, modules
|
, modules
|
||||||
, demos
|
, demos
|
||||||
, decoding
|
, decoding
|
||||||
, elimLambdaSingle
|
-- , elimLambdaSingle
|
||||||
, stressElimLambda
|
-- , stressElimLambda
|
||||||
, byteMarshallingTests
|
-- , byteMarshallingTests
|
||||||
, wireTests
|
-- , wireTests
|
||||||
, tricuReaderTests
|
-- , tricuReaderTests
|
||||||
, byteListUtilities
|
-- , byteListUtilities
|
||||||
, binaryParserTests
|
-- , binaryParserTests
|
||||||
, httpParsingTests
|
, httpParsingTests
|
||||||
, ioDriverTests
|
-- , ioDriverTests
|
||||||
]
|
]
|
||||||
|
|
||||||
lexer :: TestTree
|
lexer :: TestTree
|
||||||
@@ -136,6 +136,11 @@ lexer = testGroup "Lexer Tests"
|
|||||||
expect = Right [LIdentifier "a", LArrowRight, LIdentifier "b"]
|
expect = Right [LIdentifier "a", LArrowRight, LIdentifier "b"]
|
||||||
runParser tricuLexer "" input @?= expect
|
runParser tricuLexer "" input @?= expect
|
||||||
|
|
||||||
|
, testCase "Lex <- as bind arrow token" $ do
|
||||||
|
let input = "x <- action"
|
||||||
|
expect = Right [LIdentifier "x", LBindArrow, LIdentifier "action"]
|
||||||
|
runParser tricuLexer "" input @?= expect
|
||||||
|
|
||||||
, testCase "Lex $ remains legal identifier char" $ do
|
, testCase "Lex $ remains legal identifier char" $ do
|
||||||
let input = "foo$bar = 1"
|
let input = "foo$bar = 1"
|
||||||
expect = Right [LIdentifier "foo$bar", LAssign, LIntegerLiteral 1]
|
expect = Right [LIdentifier "foo$bar", LAssign, LIntegerLiteral 1]
|
||||||
@@ -227,6 +232,67 @@ parser = testGroup "Parser Tests"
|
|||||||
expect = SDef "x" [] (SLambda ["a"] (SLambda ["b"] (SVar "a" Nothing)))
|
expect = SDef "x" [] (SLambda ["a"] (SLambda ["b"] (SVar "a" Nothing)))
|
||||||
parseSingle input @?= expect
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Parse top-level definition arguments" $ do
|
||||||
|
let input = "const a b = a"
|
||||||
|
expect = SDef "const" ["a", "b"] (SVar "a" Nothing)
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Evaluate top-level definition arguments" $ do
|
||||||
|
tricuTestString "const a b = a\nconst 1 2" @?= "Fork (Stem Leaf) Leaf"
|
||||||
|
|
||||||
|
, testCase "Parse let expression" $ do
|
||||||
|
let input = "let x = t t in x"
|
||||||
|
expect = SApp (SLambda ["x"] (SVar "x" Nothing)) (SApp TLeaf TLeaf)
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Evaluate let expression" $ do
|
||||||
|
tricuTestString "let x = 1 in x" @?= "Fork (Stem Leaf) Leaf"
|
||||||
|
|
||||||
|
, testCase "Parse let function binding" $ do
|
||||||
|
let input = "let f x = x in f t"
|
||||||
|
expect = SApp (SLambda ["f"] (SApp (SVar "f" Nothing) TLeaf))
|
||||||
|
(SLambda ["x"] (SVar "x" Nothing))
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Parse where expression" $ do
|
||||||
|
let input = "x where x = t t"
|
||||||
|
expect = SApp (SLambda ["x"] (SVar "x" Nothing)) (SApp TLeaf TLeaf)
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Evaluate where expression" $ do
|
||||||
|
tricuTestString "x where x = 1" @?= "Fork (Stem Leaf) Leaf"
|
||||||
|
|
||||||
|
, testCase "Parse indented multiline definition body" $ do
|
||||||
|
let input = "x =\n t\n t"
|
||||||
|
expect = SDef "x" [] (SApp TLeaf TLeaf)
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Evaluate indented multiline let" $ do
|
||||||
|
tricuTestString "let\n x =\n 1\nin\n x" @?= "Fork (Stem Leaf) Leaf"
|
||||||
|
|
||||||
|
, testCase "Evaluate indented multiline where" $ do
|
||||||
|
tricuTestString "x\n where x =\n 1" @?= "Fork (Stem Leaf) Leaf"
|
||||||
|
|
||||||
|
, testCase "Parse explicit custom-bind do" $ do
|
||||||
|
let input = "do bind\n x <- pure t\n pure x"
|
||||||
|
expect = SApp
|
||||||
|
(SApp (SVar "bind" Nothing) (SApp (SVar "pure" Nothing) TLeaf))
|
||||||
|
(SLambda ["x"] (SApp (SVar "pure" Nothing) (SVar "x" Nothing)))
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Parse do statement without binder" $ do
|
||||||
|
let input = "do bind\n pure t\n pure t"
|
||||||
|
expect = SApp
|
||||||
|
(SApp (SVar "bind" Nothing) (SApp (SVar "pure" Nothing) TLeaf))
|
||||||
|
(SLambda ["_"] (SApp (SVar "pure" Nothing) TLeaf))
|
||||||
|
parseSingle input @?= expect
|
||||||
|
|
||||||
|
, testCase "Reject bare do without explicit bind operator" $ do
|
||||||
|
parsed <- try (evaluate (parseSingle "do\n x <- pure t\n pure x")) :: IO (Either SomeException TricuAST)
|
||||||
|
case parsed of
|
||||||
|
Left _ -> pure ()
|
||||||
|
Right _ -> assertFailure "Expected bare do to fail"
|
||||||
|
|
||||||
, testCase "Grouping T terms with parentheses in function application" $ do
|
, testCase "Grouping T terms with parentheses in function application" $ do
|
||||||
let input = "x = (a : a)\nx (t)"
|
let input = "x = (a : a)\nx (t)"
|
||||||
expect = [SDef "x" [] (SLambda ["a"] (SVar "a" Nothing)),SApp (SVar "x" Nothing) TLeaf]
|
expect = [SDef "x" [] (SLambda ["a"] (SVar "a" Nothing)),SApp (SVar "x" Nothing) TLeaf]
|
||||||
@@ -3362,6 +3428,36 @@ httpParsingTests = testGroup "HTTP Parsing Tests"
|
|||||||
env = evalTricu lib (parseTricu input)
|
env = evalTricu lib (parseTricu input)
|
||||||
result env @?= parserErr (ofNumber 400) (ofString "Bad Request\n")
|
result env @?= parserErr (ofNumber 400) (ofString "Bad Request\n")
|
||||||
|
|
||||||
|
, testCase "parseContentLengthValue accepts max body bytes" $ do
|
||||||
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
let input = "matchResult \"err\" (maybeLen rest : \"ok\") (parseContentLengthValue \"1048576\")"
|
||||||
|
env = evalTricu lib (parseTricu input)
|
||||||
|
result env @?= ofString "ok"
|
||||||
|
|
||||||
|
, testCase "parseContentLengthValue accepts shorter decimal below max" $ do
|
||||||
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
let input = "matchResult \"err\" (maybeLen rest : \"ok\") (parseContentLengthValue \"999999\")"
|
||||||
|
env = evalTricu lib (parseTricu input)
|
||||||
|
result env @?= ofString "ok"
|
||||||
|
|
||||||
|
, testCase "parseContentLengthValue strips leading zeros before limit check" $ do
|
||||||
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
let input = "parseContentLengthValue \"0000000000001\""
|
||||||
|
env = evalTricu lib (parseTricu input)
|
||||||
|
result env @?= parserOk (justT (ofNumber 1)) Leaf
|
||||||
|
|
||||||
|
, testCase "parseContentLengthValue rejects body above max" $ do
|
||||||
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
let input = "parseContentLengthValue \"1048577\""
|
||||||
|
env = evalTricu lib (parseTricu input)
|
||||||
|
result env @?= parserErr (ofNumber 413) (ofString "Request body too large\n")
|
||||||
|
|
||||||
|
, testCase "parseContentLengthValue rejects longer body above max" $ do
|
||||||
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
let input = "parseContentLengthValue \"2000000\""
|
||||||
|
env = evalTricu lib (parseTricu input)
|
||||||
|
result env @?= parserErr (ofNumber 413) (ofString "Request body too large\n")
|
||||||
|
|
||||||
-- statusLine / headerLine
|
-- statusLine / headerLine
|
||||||
, testCase "statusLine 200 OK" $ do
|
, testCase "statusLine 200 OK" $ do
|
||||||
lib <- evaluateFile "./lib/http.tri"
|
lib <- evaluateFile "./lib/http.tri"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
-- Example usage:
|
-- Example usage:
|
||||||
-- curl http://localhost:8080/
|
-- curl http://localhost:8080/
|
||||||
-- curl http://localhost:8080/_arboricx/health
|
-- curl http://localhost:8080/_arboricx/health
|
||||||
-- curl -X POST --data-binary @mybundle.arboricx http://localhost:8080/_arboricx/bundles
|
-- curl -X POST --data-binary @mybundle.arboricx http://localhost:8080/_arboricx/bundle
|
||||||
-- curl http://localhost:8080/_arboricx/bundle/hash/<hash>
|
-- curl http://localhost:8080/_arboricx/bundle/hash/<hash>
|
||||||
|
|
||||||
main = io (thenIO
|
main = io (thenIO
|
||||||
|
|||||||
Reference in New Issue
Block a user