Skip to content

Commit 0536be4

Browse files
authored
Pretty-print UPLC programs with unicode chars (#7242)
1 parent 32e7df1 commit 0536be4

File tree

14 files changed

+132
-15
lines changed

14 files changed

+132
-15
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
(program 1.0.0 (con string "\955-calculus"))
1+
(program 1.0.0 (con string "λ-calculus"))

plutus-conformance/test-cases/uplc/evaluation/builtin/constant/string/string-05/string-05.uplc.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
1.0.0
33
(con
44
string
5-
"x \8712 \8477 \8658 x\178 \8805 0; z \8712 \8450\\\8477 \8658 z\178 \8713 {x \8712 \8477: x \8805 0}."
5+
"x ∈ ℝ ⇒ x² ≥ 0; z ∈ ℂ\\ℝ ⇒ z² ∉ {x ∈ ℝ: x 0}."
66
)
77
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
(program 1.0.0
2+
(con (list string)
3+
[ "z ∈ ℝ ⇒ x² ≥ 0; z ∈ ℂ"
4+
, "x \8712 \8477 \8658 x\178 \8805 0; z \8712 \8450\\\8477 \8658 z\178 \8713 {x \8712 \8477: x \8805 0}."
5+
, "\\"
6+
, "\""
7+
, "--- \"\"\"\" \\ \\ \\\\ \\\\\\ \""
8+
, "\b\n\a\t"
9+
, "\DEL"
10+
, "€"
11+
, "
12+
"
13+
, " "
14+
]
15+
)
16+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
({cpu: 16100
2+
| mem: 200})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
(program
2+
1.0.0
3+
(con
4+
(list string)
5+
[ "z ∈ ℝ ⇒ x² ≥ 0; z ∈ ℂ"
6+
, "x ∈ ℝ ⇒ x² ≥ 0; z ∈ ℂ\\ℝ ⇒ z² ∉ {x ∈ ℝ: x ≥ 0}."
7+
, "\\"
8+
, "\""
9+
, "--- \"\"\"\" \\ \\ \\\\ \\\\\\ \""
10+
, "\b\n\a\t"
11+
, "\DEL"
12+
, "€"
13+
, "\n"
14+
, "\t" ]
15+
)
16+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(program 1.0.0
2+
(con string "----- \\" hello"
3+
))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
parse error
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
parse error

plutus-core/plutus-core/src/PlutusCore/Parser/ParserCommon.hs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
{-# LANGUAGE MultiWayIf #-}
12
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE RankNTypes #-}
4+
{-# LANGUAGE RecordWildCards #-}
25
{-# LANGUAGE RecursiveDo #-}
36
{-# OPTIONS_GHC -fno-warn-orphans #-}
47

@@ -11,6 +14,7 @@ import Control.Monad.Reader (ReaderT, ask, local, runReaderT)
1114
import Control.Monad.State (StateT, evalStateT)
1215
import Data.Map qualified as M
1316
import Data.Text (Text)
17+
import Data.Text qualified as Text
1418
import Text.Megaparsec hiding (ParseError, State, parse, some)
1519
import Text.Megaparsec.Char (char, space1)
1620
import Text.Megaparsec.Char.Lexer qualified as Lex hiding (hexadecimal)
@@ -87,6 +91,69 @@ leadingWhitespace = (whitespace *>)
8791
trailingWhitespace :: Parser a -> Parser a
8892
trailingWhitespace = (<* whitespace)
8993

94+
-- This is samething from @Text.Megaparsec.Stream@.
95+
reachOffsetNoLine' ::
96+
forall s.
97+
(Stream s) =>
98+
-- | How to split input stream at given offset
99+
(Int -> s -> (Tokens s, s)) ->
100+
-- | How to fold over input stream
101+
(forall b. (b -> Token s -> b) -> b -> Tokens s -> b) ->
102+
-- | Newline token and tab token
103+
(Token s, Token s) ->
104+
-- | Offset to reach
105+
-- | Increment in column position for a token
106+
(Token s -> Pos) ->
107+
Int ->
108+
-- | Initial 'PosState' to use
109+
PosState s ->
110+
-- | Updated 'PosState'
111+
PosState s
112+
reachOffsetNoLine'
113+
splitAt'
114+
foldl''
115+
(newlineTok, tabTok)
116+
columnIncrement
117+
o
118+
PosState {..} =
119+
( PosState
120+
{ pstateInput = post,
121+
pstateOffset = max pstateOffset o,
122+
pstateSourcePos = spos,
123+
pstateTabWidth = pstateTabWidth,
124+
pstateLinePrefix = pstateLinePrefix
125+
}
126+
)
127+
where
128+
spos = foldl'' go pstateSourcePos pre
129+
(pre, post) = splitAt' (o - pstateOffset) pstateInput
130+
go (SourcePos n l c) ch =
131+
let c' = unPos c
132+
w = unPos pstateTabWidth
133+
in if
134+
| ch == newlineTok ->
135+
SourcePos n (l <> pos1) pos1
136+
| ch == tabTok ->
137+
SourcePos n l (mkPos $ c' + w - ((c' - 1) `rem` w))
138+
| otherwise ->
139+
SourcePos n l (c <> columnIncrement ch)
140+
{-# INLINE reachOffsetNoLine' #-}
141+
142+
getSourcePos' :: MonadParsec e Text m => m SourcePos
143+
getSourcePos' = do
144+
st <- getParserState
145+
let
146+
pst =
147+
reachOffsetNoLine'
148+
Text.splitAt
149+
Text.foldl'
150+
('\n', '\t')
151+
(const pos1)
152+
(stateOffset st)
153+
(statePosState st)
154+
setParserState st {statePosState = pst}
155+
return (pstateSourcePos pst)
156+
90157
{- | Returns a parser for @a@ by calling the supplied function on the starting
91158
and ending positions of @a@.
92159
@@ -96,15 +163,15 @@ trailing whitespaces.
96163
-}
97164
withSpan' :: (SrcSpan -> Parser a) -> Parser a
98165
withSpan' f = mdo
99-
start <- getSourcePos
166+
start <- getSourcePos'
100167
res <- f sp
101-
end <- getSourcePos
168+
end <- getSourcePos'
102169
let sp = toSrcSpan start end
103170
pure res
104171

105172
{- | Like `withSpan'`, but the result parser consumes whitespaces.
106173
107-
@withSpan = (<* whitespace) . withSpan'
174+
@withSpan = (<* whitespace) . withSpan'@
108175
-}
109176
withSpan :: (SrcSpan -> Parser a) -> Parser a
110177
withSpan = (<* whitespace) . withSpan'

plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import PlutusCore.Pretty.Readable
1818

1919
import Control.Lens hiding (List)
2020
import Data.ByteString qualified as BS
21+
import Data.Char qualified as Char
2122
import Data.Coerce
2223
import Data.List.NonEmpty
2324
import Data.Proxy
@@ -26,7 +27,7 @@ import Data.Typeable
2627
import Data.Vector.Strict (Vector)
2728
import Data.Word (Word8)
2829
import Numeric (showHex)
29-
import Prettyprinter
30+
import Prettyprinter as Prettyprinter
3031
import Prettyprinter.Internal (Doc (Text))
3132
import Text.PrettyBy
3233
import Text.PrettyBy.Internal (DefaultPrettyBy (..))
@@ -104,9 +105,19 @@ instance Show a => DefaultPrettyBy ConstConfig (PrettyAny a) where
104105
prettyConst :: PrettyConst a => RenderContext -> a -> Doc ann
105106
prettyConst = prettyBy . ConstConfig
106107

107-
-- This instance for String quotes control characters (which is what we want)
108-
-- but also Unicode characters (\8704 and so on).
109-
deriving via PrettyAny T.Text instance NonDefaultPrettyBy ConstConfig T.Text
108+
-- This instance for Text quotes control characters (which is what we want)
109+
-- but doesn't escape Unicode characters (\8704 and so on).
110+
instance NonDefaultPrettyBy ConstConfig T.Text where
111+
nonDefaultPrettyListBy conf = Prettyprinter.list . Prelude.map (nonDefaultPrettyBy conf)
112+
nonDefaultPrettyBy = inContextM $ \t -> pure $ pretty $ "\"" <> escape t <> "\""
113+
where
114+
escape t = T.foldr' prettyChar "" t
115+
prettyChar c acc
116+
| c == '"' = "\\\"" <> acc -- Not handled by 'showLitChar'
117+
| c == '\\' = "\\\\" <> acc -- Not handled by 'showLitChar'
118+
| Char.isPrint c = [c] <> acc
119+
| otherwise = Char.showLitChar c acc
120+
110121
deriving via PrettyAny () instance NonDefaultPrettyBy ConstConfig ()
111122
deriving via PrettyAny Bool instance NonDefaultPrettyBy ConstConfig Bool
112123
deriving via PrettyAny Integer instance NonDefaultPrettyBy ConstConfig Integer

0 commit comments

Comments
 (0)