snail 0.1.2.1 → 0.1.2.2
raw patch · 6 files changed
+149/−17 lines, 6 filesdep ~QuickCheckdep ~containersdep ~hspecnew-component:exe:snailPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: QuickCheck, containers, hspec, hspec-discover, megaparsec, mtl, text, text-display
API changes (from Hackage documentation)
- Snail.Ast: instance GHC.Show.Show Snail.Ast.Bracket
- Snail.Ast: instance GHC.Show.Show Snail.Ast.SnailAst
+ Snail.Ast: instance GHC.Internal.Show.Show Snail.Ast.Bracket
+ Snail.Ast: instance GHC.Internal.Show.Show Snail.Ast.SnailAst
Files
- CHANGELOG.md +4/−0
- README.md +30/−0
- example/Main.hs +59/−0
- snail.cabal +43/−16
- src/Snail/Lexer.hs +1/−1
- test/Snail/LexerSpec.hs +12/−0
CHANGELOG.md view
@@ -1,3 +1,7 @@+### Version 0.1.2.2++* Update dependencies upper bounds+ ### Version 0.1.2.1 * Didn't export unwrap utility
README.md view
@@ -101,7 +101,37 @@ 1. The `arith` language from [Types and Programming Languages][tapl]: https://github.com/chiroptical/snail-arith/blob/main/src/Lib.hs 2. Languages from [essentials-of-compilation][essentials-of-compilation]: https://github.com/chiroptical/essentials-of-compilation (each chapter is a module) +## Differences from S-Cargot++I was recently reminded of [s-cargot][s-cargot]. From their GitHub,++> S-Cargot is a library for parsing and emitting S-expressions, designed to be+> flexible, customizable, and extensible.++This is quite different from Snail. Snail is not flexible, customizable, or+extensible. I have chosen a syntax and either you use it or you don't.+With S-Cargot, you can build a custom S-expression parser. Snail provides one.++I don't think you can re-write Snail using S-Cargot? It doesn't appear to+support `'(x)` or `(let [x 10] x)`. Please correct me if I missed something.++I tried to mimic their example+(https://github.com/aisamanra/s-cargot/blob/master/example/example.hs) in+./example/Main.hs.++## Resources for implementing interpreters and compilers++These are resources I am personally interested in exploring, there are **many** others++- [Types and Programming Languages][tapl]+- [Essentials of Compilation][essentials-of-compilation]+- [Compiler Design][cs4410]+- [Compiler Construction][cse131]+ [tapl]: https://www.cis.upenn.edu/~bcpierce/tapl [haskell-parse-issue]: https://twitter.com/chiroptical/status/1471568781906518018 [pl-wikipedia]: https://en.wikipedia.org/wiki/Programming_language [essentials-of-compilation]: https://mitpress.mit.edu/9780262047760/essentials-of-compilation+[s-cargot]: https://github.com/aisamanra/s-cargot+[cs4410]: https://course.ccs.neu.edu/cs4410sp21/+[cse131]: https://ucsd-cse131-f19.github.io/
+ example/Main.hs view
@@ -0,0 +1,59 @@+module Main where++import Snail+import Control.Monad.Except+import Data.Text (Text)+import Data.Text qualified as Text+import Text.Read (readMaybe)++data Operation = Addition | Multiplication + deriving (Eq, Show)++data Ast+ = AstInt Integer+ | AstOperation Operation Ast Ast+ deriving (Eq, Show)++data LangError + = UnknownLexeme Text+ | UnknownOperation Text+ | TextLiteralUnsupported+ | EmptyExpression+ deriving (Eq, Show)++parseLeaf :: (MonadError LangError m) => Text -> m Ast+parseLeaf = \case+ txt ->+ case readMaybe @Integer $ Text.unpack txt of+ Nothing -> throwError $ UnknownLexeme txt+ Just int -> pure $ AstInt int++parseOp :: (MonadError LangError m) => Text -> m Operation+parseOp = \case+ "+" -> pure Addition+ "*" -> pure Multiplication+ txt -> throwError $ UnknownOperation txt++fromSnail :: (MonadError LangError m) => SnailAst -> m Ast+fromSnail = \case+ -- no text literals are supported in this language+ TextLiteral _ -> throwError TextLiteralUnsupported+ -- `X` where `X` is a leaf in 'Ast'+ Lexeme (_, leaf) -> parseLeaf leaf+ SExpression _ _ [Lexeme (_, txt), argumentOne, argumentTwo] -> do+ op <- parseOp txt+ one <- fromSnail argumentOne+ two <- fromSnail argumentTwo+ pure $ AstOperation op one two+ -- empty expressions are invalid+ SExpression _ _ [] -> throwError EmptyExpression+ -- expression of expressions, e.g. `((X))` -> `(X)`+ SExpression leading fence exprs ->+ fromSnail . unwrap . SExpression leading fence $ unwrap <$> exprs++main :: IO ()+main = do+ case parseSnail "(+ 1 2)" of+ Left err -> print $ "Unable to parse snail: " <> show err+ Right [snail] -> runExceptT (fromSnail snail) >>= print+ Right _ -> putStrLn "Excepted single expression"
snail.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack name: snail-version: 0.1.2.1+version: 0.1.2.2 synopsis: A programming language with no semantics description: An s-expression parser and abstract syntax tree for a programming language with no semantics. If you wanted to write an interpreter or compiler you convert the AST into your own. category: Parsing@@ -51,19 +51,44 @@ hs-source-dirs: src default-extensions:+ FlexibleContexts ImportQualifiedPost LambdaCase OverloadedStrings+ TypeApplications build-depends:- QuickCheck >=2.14.3 && <2.15+ QuickCheck >=2.14.3 && <2.20 , base >=4.15 && <5- , containers >=0.6.7 && <0.7- , megaparsec >=9.3.1 && <9.4- , mtl >=2.2.2 && <2.3- , text >=2.0.2 && <2.1- , text-display >=0.0.5 && <0.1+ , containers >=0.6.7 && <0.8+ , megaparsec >=9.5.0 && <9.9+ , mtl >=2.3.1 && <2.4+ , text >=2.0.2 && <2.2+ , text-display >=0.0.5 && <0.1 || >=1.0 && <1.1 default-language: Haskell2010 +executable snail+ main-is: Main.hs+ other-modules:+ Paths_snail+ hs-source-dirs:+ example+ default-extensions:+ FlexibleContexts+ ImportQualifiedPost+ LambdaCase+ OverloadedStrings+ TypeApplications+ build-depends:+ QuickCheck >=2.14.3 && <2.20+ , base >=4.15 && <5+ , containers >=0.6.7 && <0.8+ , megaparsec >=9.5.0 && <9.9+ , mtl >=2.3.1 && <2.4+ , snail+ , text >=2.0.2 && <2.2+ , text-display >=0.0.5 && <0.1 || >=1.0 && <1.1+ default-language: Haskell2010+ test-suite snail-test type: exitcode-stdio-1.0 main-is: Spec.hs@@ -76,21 +101,23 @@ hs-source-dirs: test default-extensions:+ FlexibleContexts ImportQualifiedPost LambdaCase OverloadedStrings+ TypeApplications ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: HUnit >=1.6.2 && <1.7- , QuickCheck >=2.14.3 && <2.15+ , QuickCheck >=2.14.3 && <2.20 , base >=4.15 && <5- , containers >=0.6.7 && <0.7- , hspec >=2.10.10 && <2.11- , hspec-discover >=2.10.10 && <2.11- , megaparsec >=9.3.1 && <9.4- , mtl >=2.2.2 && <2.3+ , containers >=0.6.7 && <0.8+ , hspec >=2.11.8 && <2.12+ , hspec-discover >=2.11.8 && <2.12+ , megaparsec >=9.5.0 && <9.9+ , mtl >=2.3.1 && <2.4 , raw-strings-qq ==1.1.* , snail- , text >=2.0.2 && <2.1- , text-display >=0.0.5 && <0.1+ , text >=2.0.2 && <2.2+ , text-display >=0.0.5 && <0.1 || >=1.0 && <1.1 default-language: Haskell2010
src/Snail/Lexer.hs view
@@ -114,7 +114,7 @@ recursively separated by 'spaces' in 'sExpression'. -} leaves :: Parser SnailAst-leaves = lexeme <|> textLiteral <|> sExpression+leaves = try sExpression <|> lexeme <|> textLiteral -- | Parse an 'SExpression' sExpression :: Parser SnailAst
test/Snail/LexerSpec.hs view
@@ -160,3 +160,15 @@ it "fails to parse nested mismatched brackets" $ do parseMaybe snailAst "([)]" `shouldSatisfy` isNothing++ it "associates inner '#' with the s-expression" $ do+ let Just [result] = parseMaybe snailAst "(different-char/2 #(inside))"+ foldLexemes result `shouldBe` ["different-char/2", "inside"]++ it "fails with two leading characters" $ do+ parseMaybe snailAst "@,(foo)" `shouldSatisfy` isNothing++ -- NOTE: this may or may not be the desired behavior+ it "parses two leading characters as valid lexeme" $ do+ let Just [result] = parseMaybe snailAst "(@,(foo ...))"+ foldLexemes result `shouldBe` ["@,", "foo", "..."]