From 89bb73ed99fcc8de5e791e37fdcfac5a4fb9b87f Mon Sep 17 00:00:00 2001 From: James Eversole Date: Wed, 6 May 2026 18:53:17 -0500 Subject: [PATCH] Tree-native byte processing --- lib/bytes.tri | 49 ++++++++++++ src/Lexer.hs | 2 + test/Spec.hs | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 lib/bytes.tri diff --git a/lib/bytes.tri b/lib/bytes.tri new file mode 100644 index 0000000..4e40fe9 --- /dev/null +++ b/lib/bytes.tri @@ -0,0 +1,49 @@ +!import "base.tri" !Local +!import "list.tri" !Local + +nothing = t +just = x : t x + +bytesIsNil = emptyList? + +bytesHead = matchList nothing (h _ : just h) + +bytesTail = matchList nothing (_ r : just r) + +byteEq = equal? +bytesLength = length +bytesAppend = append + +bytesTake_ = y (self n i remaining : + matchBool + t + (matchList + t + (h r : pair h (self n (succ i) r)) + remaining) + (equal? i n)) + +bytesTake = n bytes : bytesTake_ n 0 bytes + +bytesDrop_ = y (self n i remaining : + matchBool + remaining + (matchList + t + (_ r : self n (succ i) r) + remaining) + (equal? i n)) + +bytesDrop = n bytes : bytesDrop_ n 0 bytes + +bytesSplitAt = n bytes : pair (bytesTake n bytes) (bytesDrop n bytes) + +bytesEq = y (self xs ys : + matchList + (matchList true (_ _ : false) ys) + (xh xt : + matchList + false + (yh yt : and? (byteEq xh yh) (self xt yt)) + ys) + xs) diff --git a/src/Lexer.hs b/src/Lexer.hs index 6026caf..0b7f74a 100644 --- a/src/Lexer.hs +++ b/src/Lexer.hs @@ -62,6 +62,7 @@ identifierWithHash = do rest <- many $ letterChar <|> digitChar <|> char '_' <|> char '-' <|> char '?' <|> char '$' <|> char '@' <|> char '%' + <|> char '\'' _ <- char '#' -- Consume '#' hashString <- some (alphaNumChar <|> char '-') -- Ensures at least one char for hash "hash characters (alphanumeric or hyphen)" @@ -83,6 +84,7 @@ identifier = do rest <- many $ letterChar <|> digitChar <|> char '_' <|> char '-' <|> char '?' <|> char '$' <|> char '@' <|> char '%' + <|> char '\'' let name = first : rest if name == "t" || name == "!result" then fail "Keywords (`t`, `!result`) cannot be used as an identifier" diff --git a/test/Spec.hs b/test/Spec.hs index 39f99fa..f0dcdea 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -46,6 +46,7 @@ tests = testGroup "Tricu Tests" , stressElimLambda , byteMarshallingTests , wireTests + , byteListUtilities ] lexer :: TestTree @@ -1020,3 +1021,207 @@ wireTests = testGroup "Wire Tests" close dstConn close srcConn ] + +-- -------------------------------------------------------------------------- +-- Byte-list utility tests +-- Expected values built with canonical Haskell-side T constructors. +-- -------------------------------------------------------------------------- + +-- | Helpers for byte-list test expectations. +trueT :: T +trueT = Stem Leaf + +falseT :: T +falseT = Leaf + +nothingT :: T +nothingT = Leaf + +justT :: T -> T +justT = Stem + +pairT :: T -> T -> T +pairT = Fork + +byteT :: Integer -> T +byteT = ofNumber + +bytesT :: [Integer] -> T +bytesT = ofList . fmap byteT + +byteListUtilities :: TestTree +byteListUtilities = testGroup "Byte List Utility Tests" + [ testCase "isNil: empty list is nil" $ do + let input = "bytesIsNil []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= trueT + + , testCase "isNil: non-empty list is not nil" $ do + let input = "bytesIsNil [(1)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + + , testCase "head: empty list is nothing" $ do + let input = "bytesHead []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= nothingT + + , testCase "head: non-empty list returns first element" $ do + let input = "bytesHead [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= justT (byteT 1) + + , testCase "tail: empty list is nothing" $ do + let input = "bytesTail []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= nothingT + + , testCase "tail: non-empty list returns rest" $ do + let input = "bytesTail [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= justT (bytesT [2]) + + , testCase "length: empty list is zero" $ do + let input = "bytesLength []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= ofNumber 0 + + , testCase "length: single element list is one" $ do + let input = "bytesLength [(1)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= ofNumber 1 + + , testCase "length: three element list is three" $ do + let input = "bytesLength [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= ofNumber 3 + + , testCase "append: empty ++ [1,2] = [1,2]" $ do + let input = "bytesAppend [] [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2] + + , testCase "append: [1,2] ++ [3] = [1,2,3]" $ do + let input = "bytesAppend [(1) (2)] [(3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2,3] + + , testCase "append: [1,2] ++ empty = [1,2]" $ do + let input = "bytesAppend [(1) (2)] []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2] + + , testCase "take: take 0 any list = empty" $ do + let input = "bytesTake 0 [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [] + + , testCase "take: take 2 [1,2,3] = [1,2]" $ do + let input = "bytesTake 2 [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2] + + , testCase "take: take 5 [1,2] = [1,2] (overlong)" $ do + let input = "bytesTake 5 [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2] + + , testCase "drop: drop 0 any list = list" $ do + let input = "bytesDrop 0 [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [1,2,3] + + , testCase "drop: drop 2 [1,2,3] = [3]" $ do + let input = "bytesDrop 2 [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [3] + + , testCase "drop: drop 5 [1,2] = empty (overlong)" $ do + let input = "bytesDrop 5 [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= bytesT [] + + , testCase "splitAt: splitAt 0 [1,2] = pair [] [1,2]" $ do + let input = "bytesSplitAt 0 [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= pairT (bytesT []) (bytesT [1,2]) + + , testCase "splitAt: splitAt 2 [1,2,3] = pair [1,2] [3]" $ do + let input = "bytesSplitAt 2 [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= pairT (bytesT [1,2]) (bytesT [3]) + + , testCase "splitAt: splitAt 5 [1,2] = pair [1,2] []" $ do + let input = "bytesSplitAt 5 [(1) (2)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= pairT (bytesT [1,2]) (bytesT []) + + , testCase "byteEq: equal bytes are equal" $ do + let input = "byteEq 1 1" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= trueT + + , testCase "byteEq: unequal bytes are not equal" $ do + let input = "byteEq 1 2" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + + , testCase "bytesEq: empty == empty" $ do + let input = "bytesEq [] []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= trueT + + , testCase "bytesEq: empty != [1]" $ do + let input = "bytesEq [] [(1)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + + , testCase "bytesEq: [1] != empty" $ do + let input = "bytesEq [(1)] []" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + + , testCase "bytesEq: equal lists are equal" $ do + let input = "bytesEq [(1) (2) (3)] [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= trueT + + , testCase "bytesEq: different last element" $ do + let input = "bytesEq [(1) (2) (3)] [(1) (2) (4)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + + , testCase "bytesEq: different lengths" $ do + let input = "bytesEq [(1) (2)] [(1) (2) (3)]" + library <- evaluateFile "./lib/bytes.tri" + let env = evalTricu library (parseTricu input) + result env @?= falseT + ]