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:
2026-05-22 18:23:13 -05:00
parent 7cea3d1559
commit 2e2db07bd6
17 changed files with 1039 additions and 589 deletions

View File

@@ -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))

View File

@@ -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)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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 = ?"

View File

@@ -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

View File

@@ -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'))

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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