phino 0.0.120 → 0.0.121
raw patch · 6 files changed
+56/−5 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Bytes: btsIsUtf8 :: Bytes -> Bool
+ CST: sweetString :: Bytes -> Bool
Files
- phino.cabal +1/−1
- src/Bytes.hs +17/−0
- src/CST.hs +10/−2
- src/XMIR.hs +2/−2
- test/CSTSpec.hs +11/−0
- test/PrinterSpec.hs +15/−0
phino.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: phino-version: 0.0.120+version: 0.0.121 license: MIT synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>
src/Bytes.hs view
@@ -15,6 +15,7 @@ , unescapeStr , btsToNum , btsToUnescapedStr+ , btsIsUtf8 , btsAnd , btsOr , btsNot@@ -328,6 +329,22 @@ -- "5" btsToUnescapedStr :: Bytes -> String btsToUnescapedStr bytes = T.unpack (T.decodeUtf8 (B.pack (btsToWord8 bytes)))++-- Whether the byte array is valid UTF-8. The string-side counterpart of the+-- eight-byte check on numbers: a short or malformed datum is legal, and the+-- printer keeps it in its byte form instead of aborting with an uncaught+-- 'decodeUtf8' exception (see #1138).+-- >>> btsIsUtf8 (BtMany ["77", "6F", "72", "6C", "64"])+-- True+-- >>> btsIsUtf8 (BtMany ["F0", "90", "80", "41"])+-- False+-- >>> btsIsUtf8 (BtOne "FE")+-- False+btsIsUtf8 :: Bytes -> Bool+btsIsUtf8 bytes =+ case T.decodeUtf8' (B.pack (btsToWord8 bytes)) of+ Left _ -> False+ Right _ -> True -- Bitwise conjunction of two byte arrays, byte by byte. EO's 'BytesRaw.and' -- refuses operands of different lengths, so there is nothing to yield for them
src/CST.hs view
@@ -11,7 +11,7 @@ module CST where import AST-import Bytes (NonFinite, btsSize, btsToNonFinite, btsToNum, btsToStr)+import Bytes (NonFinite, btsIsUtf8, btsSize, btsToNonFinite, btsToNum, btsToStr) import Data.Maybe (isJust) import qualified Data.Text as T import qualified Yaml as Y@@ -276,9 +276,17 @@ Right dbl | isNaN dbl || isInfinite dbl -> isJust (btsToNonFinite bts) _ -> True +-- A string can be rendered as a literal only when its bytes decode as UTF-8.+-- An arbitrary byte array is a legal datum and nothing promises it decodes, so+-- a malformed one is kept in its byte form, exactly as a payload NaN is kept+-- today (see #1138).+sweetString :: Bytes -> Bool+sweetString = btsIsUtf8+ -- Whether a data object may be collapsed into its sweet literal form. sweetCollapsible :: Expression -> Bool sweetCollapsible (DataNumber bts) = sweetNumber bts+sweetCollapsible (DataString bts) = sweetString bts sweetCollapsible _ = True attributeToCST :: Attribute -> ATTRIBUTE@@ -353,7 +361,7 @@ withoutLastVoidRho [] = [] withoutLastVoidRho [BiVoid AtRho] = [] withoutLastVoidRho (bd : bds') = bd : withoutLastVoidRho bds'- toCST (DataString bts) (tabs, _) = EX_STRING (btsToStr bts) (TAB tabs) []+ toCST (DataString bts) (tabs, _) | sweetString bts = EX_STRING (btsToStr bts) (TAB tabs) [] -- The three canonical non-finite doubles have no sweet numeric literal, so -- they become the root dispatches `Φ.nan`, `Φ.pinf` and `Φ.ninf`. Any other -- non-finite pattern is left in its byte form `Φ.number(Φ.bytes(⟦ Δ ⤍ … ⟧))`
src/XMIR.hs view
@@ -22,7 +22,7 @@ where import AST-import Bytes (btsSize, btsToNum, btsToStr, bytesToBts)+import Bytes (btsIsUtf8, btsSize, btsToNum, btsToStr, bytesToBts) import Control.Exception (Exception (displayException), throwIO) import Data.Bifunctor (bimap) import Data.Foldable (foldlM)@@ -131,7 +131,7 @@ [object [("as", "data")] [NodeContent (T.pack (printBytes bytes))]] in pure ( "Φ.string"- , if _omitComments+ , if _omitComments || not (btsIsUtf8 bytes) then [bts] else [ NodeComment (T.pack ('"' : btsToStr bytes ++ "\""))
test/CSTSpec.hs view
@@ -155,6 +155,17 @@ ] (\(desc, bts, expected) -> it desc (sweetNumber bts `shouldBe` expected)) + describe "sweetString" $+ forM_+ [ ("is true for a valid ASCII datum", BtMany ["77", "6F", "72", "6C", "64"], True)+ , ("is true for a valid multi-byte datum", BtMany ["D0", "B0"], True)+ , ("is true for empty bytes", BtEmpty, True)+ , ("is false for a lone continuation byte", BtOne "A0", False)+ , ("is false for a truncated surrogate sequence", BtMany ["F0", "90", "80", "41"], False)+ , ("is false for a lone 0xFE byte", BtOne "FE", False)+ ]+ (\(desc, bts, expected) -> it desc (sweetString bts `shouldBe` expected))+ describe "sweetCollapsible" $ forM_ [
test/PrinterSpec.hs view
@@ -116,6 +116,21 @@ parseExpression printed `shouldBe` Right expr ) + describe "printExpression keeps a string with a non-UTF-8 datum in byte form" $+ forM_+ [ ("a lone 0xFE", BtOne "FE")+ , ("a truncated surrogate sequence", BtMany ["F0", "90", "80", "41"])+ , ("a bare continuation byte", BtOne "A0")+ ]+ ( \(desc, bts) ->+ it desc $ do+ let expr = DataString bts+ printed = printExpression' expr (SWEET, ASCII, SINGLELINE, defaultMargin)+ printed `shouldContain` "string"+ printed `shouldContain` "bytes"+ parseExpression printed `shouldBe` Right expr+ )+ describe "printExpression keeps a compressed meet atomic under a narrow margin" $ -- A \phinoMeet is a single \overbracket visual unit, so its body must stay -- on one line even when the surrounding margin forces the outer formation to