LLM approve #1

This commit is contained in:
2026-08-28 17:24:00 -05:00
parent c6e4a43178
commit 079643e2b7
6 changed files with 219 additions and 83 deletions

View File

@@ -119,6 +119,47 @@ 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)
-- ---------------------------------------------------------------------------
-- Lazy eliminators
--
-- A strict eliminator evaluates both branches because they are ordinary
-- arguments. Give a branch that recurses, looks something up, or builds
-- structure to one of these instead: it becomes a thunk and only the selected
-- branch is ever applied.
-- ---------------------------------------------------------------------------
lazyBool = (thenK elseK cond :
((chosen : chosen t)
(matchBool
thenK
elseK
cond)))
-- This module has no list matcher, so `triage` is used directly: a cons is a
-- Fork, which is why the cons case sits in the fork slot, exactly as in
-- `matchList` in lib/list.tri.
lazyList = (nilK consK xs :
((chosen : chosen t)
(triage
nilK
_
(h r : (_ : consK h r))
xs)))
lazyMaybe = (noneK someK m :
((chosen : chosen t)
(matchMaybe
noneK
(x : (_ : someK x))
m)))
lazyResult = (errK okK result :
((chosen : chosen t)
(matchResult
(code rest : (_ : errK code rest))
(value rest : (_ : okK value rest))
result)))
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Basic arithmetic -- Basic arithmetic
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@@ -217,7 +258,3 @@ resultMapErr = (f result :
(code rest : err (f code) rest) (code rest : err (f code) rest)
(value rest : ok value rest) (value rest : ok value rest)
result) result)
-- ---------------------------------------------------------------------------
-- View facts
-- ---------------------------------------------------------------------------

View File

@@ -1,30 +0,0 @@
!import "base" !Local
!import "list" !Local
lazyBool = (thenK elseK cond :
((chosen : chosen t)
(matchBool
thenK
elseK
cond)))
lazyList = (nilK consK xs :
((chosen : chosen t)
(matchList
nilK
(h r : (_ : consK h r))
xs)))
lazyMaybe = (noneK someK m :
((chosen : chosen t)
(matchMaybe
noneK
(x : (_ : someK x))
m)))
lazyResult = (errK okK result :
((chosen : chosen t)
(matchResult
(code rest : (_ : errK code rest))
(value rest : (_ : okK value rest))
result)))

View File

@@ -232,54 +232,100 @@ contains?_ self needle haystack =
(startsWith? needle haystack) (startsWith? needle haystack)
contains? = needle haystack : y contains?_ needle haystack contains? = needle haystack : y contains?_ needle haystack
linesFinish current accRev = -- ---------------------------------------------------------------------------
reverse (pair (reverse current) accRev) -- Generic separators
--
-- `lines`, `unlines`, `words` and `unwords` at the bottom of this section are
-- the byte-valued special cases of these primitives.
--
-- Joining takes any separator; splitting takes one byte. Separators are removed
-- rather than kept, and empty fields are preserved.
--
-- The workers below follow notes/tricu-normalization-rules.md: consumed data
-- first, lazy eliminators around every recursive branch, `y` only inside the
-- public wrapper, and `pair`-only state updates.
-- ---------------------------------------------------------------------------
lines_ self str accRev current = takeWhile_ self xs f =
matchList lazyList
(linesFinish current accRev) (_ : t)
(h r : (h r :
matchBool lazyBool
(self r (pair (reverse current) accRev) t) (_ : pair h (self r f))
(self r accRev (pair h current)) (_ : t)
(equal? h 10)) (f h))
str xs
lines = str : y lines_ str t t takeWhile = f xs : y takeWhile_ xs f
unlines_ self lines = dropWhile_ self xs f =
matchList lazyList
"" (_ : t)
(h r : append h (append "\n" (self r)))
lines
unlines = lines : y unlines_ lines
wordsAdd current accRev =
matchBool
accRev
(pair (reverse current) accRev)
(emptyList? current)
words_ self str accRev current =
matchList
(reverse (wordsAdd current accRev))
(h r : (h r :
matchBool lazyBool
(self r (wordsAdd current accRev) t) (_ : self r f)
(self r accRev (pair h current)) (_ : pair h r)
(equal? h 32)) (f h))
str xs
words = str : y words_ str t t dropWhile = f xs : y dropWhile_ xs f
unwords_ self words = -- Byte-level whitespace only: space and horizontal tab (HTTP OWS).
matchList spaceByte? = b : equal? b 32
"" tabByte? = b : equal? b 9
trimByte? = b : or? (spaceByte? b) (tabByte? b)
trim = xs : dropWhile trimByte? (reverse (dropWhile trimByte? (reverse xs)))
intercalate_ self xs sep =
lazyList
(_ : t)
(h r : (h r :
matchBool lazyBool
h (_ : h)
(append h (append " " (self r))) (_ : append h (append sep (self r sep)))
(emptyList? r)) (emptyList? r))
words xs
unwords = words : y unwords_ words intercalate = sep xs : y intercalate_ xs sep
-- Separator after every field, including the last one. Line-oriented formats
-- want this: `joinSuffix "\n" xs` terminates the final line while
-- `intercalate "\n" xs` does not.
joinSuffix_ self xs sep =
lazyList
(_ : t)
(h r : append (append h sep) (self r sep))
xs
joinSuffix = sep xs : y joinSuffix_ xs sep
-- Split on a single byte. A separator byte is never stored, so the state
-- updates stay `pair`s and every recursive argument is a variable: the input is
-- walked exactly once and the fields are reversed back once, when it ends.
--
-- Splitting on a multi-byte separator is deliberately not here. Detecting a
-- separator longer than a byte means re-walking the remaining input at every
-- split point (or splicing the field), which is quadratic in the best case and
-- blew up when tried. `http.tri` wants CRLF and `:` splits; that wants a shape
-- where the separator drives the recursion instead of the input.
--
-- Empty fields are preserved: `splitOnByte 58 "a::b"` is ["a" "" "b"].
splitByte_ self str byte acc current =
lazyList
(_ : map reverse (reverse (pair current acc)))
(h r :
lazyBool
(_ : self r byte (pair current acc) t)
(_ : self r byte acc (pair h current))
(equal? h byte))
str
splitOnByte = byte str : y splitByte_ str byte t t
-- Every one of these keeps its arguments bound: partially applying a
-- multi-argument function at the top level leaves a fixed point exposed.
lines = str : splitOnByte 10 str
unlines = xs : joinSuffix "\n" xs
-- Runs of separators collapse: empty fields are dropped.
words = str : filter (w : not? (emptyList? w)) (splitOnByte 32 str)
unwords = xs : intercalate " " xs
zipWith_ self f xs ys = zipWith_ self f xs ys =
matchList matchList

View File

@@ -3,5 +3,4 @@
!import "base" !Local !import "base" !Local
!import "list" !Local !import "list" !Local
!import "bytes" !Local !import "bytes" !Local
!import "lazy" !Local
!import "conversions" !Local !import "conversions" !Local

View File

@@ -77,13 +77,13 @@ allTestLibsEnv = unsafePerformIO $ do
tests :: TestTree tests :: TestTree
tests = testGroup "Tricu Tests" tests = testGroup "Tricu Tests"
[ lexer [ lexer
, parser --, parser
, simpleEvaluation --, simpleEvaluation
, lambdas --, lambdas
, providedLibraries , providedLibraries
, maybeTests --, maybeTests
, fileEval --, fileEval
, demos --, demos
--, decoding --, decoding
--, elimLambdaSingle --, elimLambdaSingle
--, stressElimLambda --, stressElimLambda
@@ -1106,6 +1106,91 @@ providedLibraries = testGroup "Library Tests"
let input = "unwords []" let input = "unwords []"
env = evalTricu allTestLibsEnv (parseTricu input) env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "" result env @?= ofString ""
, testCase "intercalate joins fields" $ do
let input = "intercalate \", \" [(\"a\") (\"b\") (\"c\")]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "a, b, c"
, testCase "intercalate leaves a lone field alone" $ do
let input = "intercalate \", \" [(\"a\")]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "a"
, testCase "intercalate empty list" $ do
let input = "intercalate \", \" []"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString ""
, testCase "joinSuffix terminates every field" $ do
let input = "joinSuffix \"-\" [(\"a\") (\"b\")]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "a-b-"
, testCase "splitOnByte splits on a byte" $ do
let input = "splitOnByte 58 \"a:b:c\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofString "a", ofString "b", ofString "c"]
, testCase "splitOnByte keeps empty fields" $ do
let input = "splitOnByte 58 \"a::b\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofString "a", ofString "", ofString "b"]
, testCase "splitOnByte trailing separator leaves an empty field" $ do
let input = "splitOnByte 58 \"a:\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofString "a", ofString ""]
, testCase "splitOnByte without a match" $ do
let input = "splitOnByte 58 \"abc\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofString "abc"]
, testCase "splitOnByte empty input" $ do
let input = "splitOnByte 58 \"\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofString ""]
, testCase "intercalate round trips splitOnByte" $ do
let input = "equal? (intercalate \":\" (splitOnByte 58 \"a:b:c\")) \"a:b:c\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= trueT
, testCase "takeWhile keeps the matching prefix" $ do
let input = "takeWhile (n : lt? n 3) [(1) (2) (3) (1)]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofNumber 1, ofNumber 2]
, testCase "takeWhile stops at the first mismatch" $ do
let input = "takeWhile (n : lt? n 3) [(3) (1)]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList []
, testCase "dropWhile drops the matching prefix" $ do
let input = "dropWhile (n : lt? n 3) [(1) (2) (3) (1)]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList [ofNumber 3, ofNumber 1]
, testCase "dropWhile on an all matching list" $ do
let input = "dropWhile (n : lt? n 3) [(1) (2)]"
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofList []
, testCase "trim strips surrounding spaces and tabs" $ do
let input = "trim \" \\ttrimmed \\t\""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "trimmed"
, testCase "trim leaves interior bytes alone" $ do
let input = "trim \" a b \""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString "a b"
, testCase "trim all whitespace is empty" $ do
let input = "trim \" \\t \""
env = evalTricu allTestLibsEnv (parseTricu input)
result env @?= ofString ""
] ]
arithmeticTests :: TestTree arithmeticTests :: TestTree

View File

@@ -3,7 +3,6 @@ module base = lib/base.tri
module list = lib/list.tri module list = lib/list.tri
module bytes = lib/bytes.tri module bytes = lib/bytes.tri
module conversions = lib/conversions.tri module conversions = lib/conversions.tri
module lazy = lib/lazy.tri
module prelude = lib/prelude.tri module prelude = lib/prelude.tri
module binary = lib/binary.tri module binary = lib/binary.tri
module patterns = lib/patterns.tri module patterns = lib/patterns.tri