This commit is contained in:
2026-08-22 21:55:16 -06:00
parent bbcc924b34
commit bf5595f185
9 changed files with 318 additions and 14 deletions
+3
View File
@@ -62,6 +62,7 @@ library
Gyehoek.GenSym
Gyehoek.Jalmot
Gyehoek.Language
Gyehoek.Lift1
Gyehoek.Options
Gyehoek.Prelude
Gyehoek.Scheme.Syntax
@@ -69,6 +70,7 @@ library
Gyehoek.Sexp.Grammar
Gyehoek.Sexp.Grammar.Base
Gyehoek.Sexp.Print
Gyehoek.Sexp.QQ
Gyehoek.Sexp.Read
Gyehoek.Sexp.Syntax
Gyehoek.Stack.Syntax
@@ -131,6 +133,7 @@ test-suite test
Gyehoek.Test.Scheme.Syntax
Gyehoek.Test.Sexp
Gyehoek.Test.Sexp.Print
Gyehoek.Test.Sexp.QQ
Gyehoek.Test.Sexp.Read
Gyehoek.Test.Stack.VM
Gyehoek.TestUtil
+30
View File
@@ -0,0 +1,30 @@
{-# LANGUAGE TemplateHaskell #-}
module Gyehoek.Lift1
( Lift1(..)
, lift1
) where
import Gyehoek.Prelude hiding ((:<))
import Language.Haskell.TH (Quote, Exp, listE)
import Language.Haskell.TH.Syntax (Lift (..))
import Control.Comonad.Cofree (Cofree(..))
-- 뻘짓뻘짓뻘짓뻘짓뻘짓
class Lift1 f where
liftLift :: Quote m => (a -> m Exp) -> f a -> m Exp
lift1 :: (Lift1 f, Lift a, Quote m) => f a -> m Exp
lift1 = liftLift lift
--- instances
instance Lift1 f => Lift1 (Cofree f) where
liftLift l (a :< e) = [|(:<) $(l a) $(liftLift (liftLift l) e)|]
instance Lift1 List where
liftLift l xs = listE $ l <$> xs
instance Lift1 NonEmpty where
liftLift l (x :| xs) = [|(:|) $(l x) $(liftLift l xs)|]
+1 -1
View File
@@ -21,7 +21,7 @@ module Gyehoek.Prelude
, (<=<)
) where
import Control.Lens hiding (List)
import Control.Lens hiding (List, (:<))
import Data.List (List)
import Data.Text (Text)
import Effectful
+7 -1
View File
@@ -22,7 +22,7 @@ module Gyehoek.Sexp.Grammar
where
import Gyehoek.Sexp.Grammar.Base
import Gyehoek.Prelude
import Gyehoek.Prelude hiding (traversed, iso)
import Data.InvertibleGrammar (backward, sealed, forward, runGrammar)
import Gyehoek.Sexp.Print (printDatum, printDatum', printData)
import Gyehoek.Jalmot
@@ -31,6 +31,7 @@ import qualified Gyehoek.Sexp.Read as Read
import qualified Data.Text.IO as TIO
import Text.Pretty.Simple (pPrintNoColor)
import Data.InvertibleGrammar.Generic
import qualified Control.Category
toDatum :: Jalmot :> es => DatumGrammar a -> a -> Eff es Datum
@@ -95,3 +96,8 @@ instance DatumIso a => DatumIso (List a) where
instance DatumIso Bool where datumIso = boolean
instance DatumIso Int where datumIso = int
instance DatumIso Datum where datumIso = Control.Category.id
instance DatumIso a => DataIso (List a) where
dataIso = onHead . traversed . sealed $ datumIso @a
+1
View File
@@ -65,6 +65,7 @@ prettyDatum depth datum = case datum of
Ordinary; NSpecial _ -> pparen depth $
group . align . vsep $
prettyDatum (depth+1) <$> xs
_ -> error [i|unimplemented: #{datum}|]
pparen depth = enclose (delim depth "(") (delim depth ")")
delim depth = annotate (SynParen depth)
+128
View File
@@ -0,0 +1,128 @@
{-# LANGUAGE TemplateHaskell #-}
module Gyehoek.Sexp.QQ
( makeSxs
, makeSx
, makeSx'
, sx
, sxs
) where
import Data.Data (Typeable, cast)
import Gyehoek.Prelude
import Gyehoek.Sexp.Syntax
import Language.Haskell.TH
import qualified Data.Text as T
import Data.List (groupBy)
import Language.Haskell.TH.Syntax (liftData, Lift (lift))
import Gyehoek.Jalmot
import Gyehoek.Sexp.Grammar
import Control.Exception (throw)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (dataToExpQ)
import qualified Gyehoek.Sexp.Read as Read
import Text.Megaparsec.Pos (mkPos)
import Gyehoek.Lift1
import Data.Foldable (toList)
extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
extQ f g a = maybe (f a) g (cast a)
spliceMeta :: (HasCallStack, DataIso a) => a -> List Datum
spliceMeta x =
case runPureEff . runJalmot . toData dataIso $ x of
Left (cs,e) -> throw $ MkAJalmotCS cs e
Right xs -> xs
meta :: (HasCallStack, DatumIso a) => a -> Datum
meta x =
case runPureEff . runJalmot . toDatum datumIso $ x of
Left (cs,e) -> throw $ MkAJalmotCS cs e
Right xs -> xs
unquoteSplicingRecursive :: List Datum -> ExpQ
unquoteSplicingRecursive xs = [| mconcat $(spans) |]
where
spans = xs
& groupBy \cases
(MetaSplice _) _ -> False
_ (MetaSplice _) -> False
_ _ -> True
& fmap \case
[MetaSplice x] ->
[| spliceMeta $(varE (mkName (T.unpack x))) |]
es -> listE $ unquoteRecursive <$> es
& listE
unquoteRecursive :: Datum -> ExpQ
unquoteRecursive = \case
Meta x -> [| meta $(varE (mkName (T.unpack x))) |]
a :< CompoundF x -> [| $(liftData a) :< CompoundF $c|]
where
c = case x of
ListF ind xs ->
[| ListF $(lift ind) $(unquoteSplicingRecursive xs) |]
VectorF xs ->
[| VectorF $(unquoteSplicingRecursive xs) |]
DotListF xs t ->
[| DotListF $(liftLift unquoteRecursive xs) $(unquoteRecursive t) |]
AbbrevF p t ->
[| AbbrevF $(lift p) $(unquoteRecursive t) |]
e -> liftData e
getPos :: Q SourcePos
getPos = do
Loc {loc_filename,loc_start} <- location
pure $ SourcePos
{ sourceName = loc_filename
, sourceLine = mkPos $ fst loc_start
, sourceColumn = mkPos $ snd loc_start
}
readq
:: (SourcePos -> Text -> Eff '[Jalmot, IOE] a)
-> String -> Q a
readq f s = do
pos <- getPos
liftIO . runJalmotIO . f pos . T.pack $ s
makeSxs :: Data r => Code Q (List Datum -> r) -> QuasiQuoter
makeSxs f = QuasiQuoter
{ quoteExp = \str -> do
xs <- readq Read.readStringWithPos str
let e = dataToExpQ
(const Nothing
`extQ` (Just . unquoteRecursive)
`extQ` (Just . unquoteSplicingRecursive))
xs
[| $(unTypeCode f) $e |]
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
-- | An untyped variant of 'makeSx', useful when the user function is
-- polymorphic in its return value.
makeSx' :: ExpQ -> QuasiQuoter
makeSx' f = QuasiQuoter
{ quoteExp = \str -> do
x <- readq Read.readStringWithPos1 str
let e = dataToExpQ
(const Nothing
`extQ` (Just . unquoteRecursive)
`extQ` (Just . unquoteSplicingRecursive))
x
[| $f $e |]
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
makeSx :: Data r => Code Q (Datum -> r) -> QuasiQuoter
makeSx = makeSx' . unTypeCode
sx, sxs :: QuasiQuoter
sxs = makeSxs [|| id @(List Datum) ||]
sx = makeSx [|| id @Datum ||]
+50 -6
View File
@@ -2,6 +2,9 @@ module Gyehoek.Sexp.Read
( readFile
, readString
, readString1
, SourcePos(..)
, readStringWithPos
, readStringWithPos1
) where
import Text.Megaparsec
@@ -34,10 +37,48 @@ readString s =
readString1 :: Jalmot :> es => Text -> Eff es Datum
readString1 s =
case runParser (sc *> datum <* eof) "<none>" s of
case runParser file1 "<none>" s of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
initialStateFromSourcePos :: SourcePos -> s -> State s e
initialStateFromSourcePos pos s = State
{ stateInput = s
, stateOffset = 0
, stateParseErrors = []
, statePosState = PosState
{ pstateInput = s
, pstateOffset = 0
, pstateSourcePos = pos
, pstateTabWidth = defaultTabWidth
, pstateLinePrefix = ""
}
}
readStringWithPos1
:: Jalmot :> es
=> SourcePos
-> Text
-> Eff es Datum
readStringWithPos1 pos s =
case snd $ runParser' file1 st of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
where
st = initialStateFromSourcePos pos s
readStringWithPos
:: Jalmot :> es
=> SourcePos
-> Text
-> Eff es (List Datum)
readStringWithPos pos s =
case snd $ runParser' file st of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
where
st = initialStateFromSourcePos pos s
type P = Parsec Void Text
@@ -134,16 +175,19 @@ string = label "string" . lexeme $
metaSplice :: P Text
metaSplice = label "splicing meta" . lexeme . between "##{" "}" $
takeWhileP Nothing (/= '}')
takeWhile1P Nothing (/= '}')
meta :: P Text
meta = label "meta" . lexeme . between "#{" "}" $
takeWhileP Nothing (/= '}')
takeWhile1P Nothing (/= '}')
file :: P (List Datum)
file = many datum <* eof
file = sc *> many datum <* eof
file1 :: P Datum
file1 = sc *> datum <* eof
datum :: P Datum
datum = do
@@ -153,6 +197,8 @@ datum = do
, Simple <$> simpleDatum
-- , labeled
-- , labelRef
, MetaSplice <$> metaSplice
, Meta <$> meta
]
simpleDatum :: P Simple
@@ -163,8 +209,6 @@ simpleDatum = choice
, SimpleString <$> string
, SimpleSymbol <$> symbol
-- , SimpleBytevector <$> bytevector
, SimpleMetaSplice <$> metaSplice
, SimpleMeta <$> meta
]
compoundDatum :: P Compound
+51 -6
View File
@@ -19,6 +19,8 @@ module Gyehoek.Sexp.Syntax
, pattern Compound
, pattern Labeled
, pattern LabelRef
, pattern Meta
, pattern MetaSplice
, pattern Abbrev
, pattern Vector
, pattern DotList
@@ -38,19 +40,26 @@ module Gyehoek.Sexp.Syntax
, ann
, pattern List'
, position
, stripAnn
) where
import Language.Haskell.TH.Syntax (Lift)
import Language.Haskell.TH.Syntax (Lift (lift), liftData)
import Data.Scientific (Scientific)
import Data.ByteString (ByteString)
import Gyehoek.Prelude hiding ((:<), Simple)
import Text.Megaparsec.Pos (SourcePos(..), sourcePosPretty)
import Control.Comonad.Cofree (Cofree((:<)), _extract)
import Control.Comonad.Cofree (Cofree((:<)), _extract, _unwrap)
import Data.Fix (Fix (..))
import Data.Functor.Foldable
import Text.Show.Deriving (deriveShow1)
import Data.Eq.Deriving (deriveEq1)
import qualified Control.Comonad.Trans.Cofree as F
import Prettyprinter (Pretty (pretty), viaShow)
import Gyehoek.Lift1 (Lift1 (liftLift))
import Data.Data (Typeable, cast)
import Language.Haskell.TH
import qualified Data.Text as T
import Control.Comonad.Trans.Cofree (tailF)
data DatumF a
@@ -58,6 +67,8 @@ data DatumF a
| CompoundF (CompoundF a)
| LabeledF Label a
| LabelRefF Label
| MetaF Text
| MetaSpliceF Text
deriving stock (Show, Eq, Data, Generic, Lift, Functor, Foldable, Traversable)
deriving anyclass (NFData)
@@ -68,8 +79,6 @@ data Simple
| SimpleString Text
| SimpleSymbol Text
| SimpleBytevector ByteString
| SimpleMeta Text
| SimpleMetaSplice Text
deriving stock (Show, Eq, Data, Generic, Lift)
deriving anyclass (NFData)
@@ -135,10 +144,13 @@ noAnn = MkAnn
instance Pretty Ann where
pretty = pretty . maybe "<unknown>" sourcePosPretty . view #position
deriveShow1 ''CompoundF
deriveEq1 ''CompoundF
deriveShow1 ''DatumF
deriveEq1 ''DatumF
--- modification and extraction of annotations
ann :: Lens' Datum Ann
ann = _extract
@@ -161,6 +173,12 @@ adorn = set syntax
indentWith :: Indentation -> Datum -> Datum
indentWith = set indentation
stripAnn :: Datum -> Fix DatumF
stripAnn = hoist tailF
--- pattern synonyms
pattern Simple :: Simple -> Datum
pattern Simple a <- _ :< SimpleF a
where Simple a = noAnn :< SimpleF a
@@ -177,6 +195,14 @@ pattern LabelRef :: Label -> Datum
pattern LabelRef l <- _ :< LabelRefF l
where LabelRef l = noAnn :< LabelRefF l
pattern MetaSplice :: Text -> Datum
pattern MetaSplice x <- _ :< MetaSpliceF x
where MetaSplice x = noAnn :< MetaSpliceF x
pattern Meta :: Text -> Datum
pattern Meta x <- _ :< MetaF x
where Meta x = noAnn :< MetaF x
pattern List :: List Datum -> Datum
pattern List a <- _ :< CompoundF (ListF _ a)
where List a = noAnn :< CompoundF (ListF Ordinary a)
@@ -203,3 +229,22 @@ pattern Character a = Simple (SimpleCharacter a)
pattern String a = Simple (SimpleString a)
pattern Symbol a = Simple (SimpleSymbol a)
pattern Bytevector a = Simple (SimpleBytevector a)
--- Lift1 instances
instance Lift1 DatumF where
liftLift l = \case
SimpleF s -> [|SimpleF $(lift s)|]
CompoundF c -> [|CompoundF $(liftLift l c)|]
LabeledF lbl x -> [|LabeledF $(lift lbl) $(l x)|]
LabelRefF lbl -> [|LabelRefF $(lift lbl)|]
MetaF x -> [|MetaF $(lift x)|]
MetaSpliceF x -> [|MetaSpliceF $(lift x)|]
instance Lift1 CompoundF where
liftLift l = \case
ListF ind xs -> [|ListF $(lift ind) $(liftLift l xs)|]
DotListF xs t -> [|DotListF $(liftLift l xs) $(l t)|]
VectorF xs -> [|VectorF $(liftLift l xs)|]
AbbrevF p x -> [|AbbrevF $(lift p) $(l x)|]
+47
View File
@@ -0,0 +1,47 @@
module Gyehoek.Test.Sexp.QQ where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Gyehoek.Sexp.QQ (sx)
import Data.Function (on)
import Gyehoek.Sexp.Syntax
import Data.Coerce (coerce)
newtype EquivDatum = MkEquiv Datum
deriving newtype (Show)
instance Eq EquivDatum where
(==) = (==) `on` (stripAnn . coerce)
assertEquiv
:: HasCallStack
=> String -> Datum -> Datum -> Assertion
assertEquiv prefix = assertEqual prefix `on` MkEquiv
equivto :: HasCallStack => Datum -> Datum -> Assertion
equivto = assertEquiv ""
test_qq :: TestTree
test_qq = testGroup "sexp quasiquoter"
[ testCase "quotation" do
equivto (Symbol "abc") [sx|abc|]
equivto (List [Symbol "a", Symbol "b"]) [sx|(a b)|]
, testCase "antiquotation" do
equivto [sx|123|]
let meta = 123 :: Int
in [sx|#{meta}|]
equivto [sx|(blah (blah blah) blah)|]
let meta = [sx|blah|]
in [sx|(#{meta} (#{meta} #{meta}) #{meta})|]
, testCase "splicing simple" do
equivto [sx|(a b c d e f g)|]
let metas = Symbol <$> ["c","d","e"]
in [sx|(a b ##{metas} f g)|]
, testCase "splicing multiple" do
equivto [sx|(a (b c d) e f g)|]
let
e1 = Symbol "c"
e2 = Symbol <$> ["e","f"]
in [sx|(a (b #{e1} d) ##{e2} g)|]
]