|
@@ -0,0 +1,88 @@
|
|
|
+{-# LANGUAGE OverloadedStrings #-}
|
|
|
+
|
|
|
+module Main where
|
|
|
+
|
|
|
+import Control.Applicative ((<|>))
|
|
|
+import Data.Char (isDigit)
|
|
|
+import Data.SCargot
|
|
|
+import Data.SCargot.Repr.Basic
|
|
|
+import Data.Text (Text, pack)
|
|
|
+import Numeric (readHex)
|
|
|
+import System.Environment (getArgs)
|
|
|
+import Text.Parsec (anyChar, char, digit, many1, manyTill, newline, satisfy, string)
|
|
|
+import Text.Parsec.Text (Parser)
|
|
|
+
|
|
|
+-- Our operators are going to represent addition, subtraction, or
|
|
|
+-- multiplication
|
|
|
+data Op = Add | Sub | Mul deriving (Eq, Show)
|
|
|
+
|
|
|
+-- The atoms of our language are either one of the aforementioned
|
|
|
+-- operators, or positive integers
|
|
|
+data Atom = AOp Op | ANum Int deriving (Eq, Show)
|
|
|
+
|
|
|
+-- Once parsed, our language will consist of the applications of
|
|
|
+-- binary operators with literal integers at the leaves
|
|
|
+data Expr = EOp Op Expr Expr | ENum Int deriving (Eq, Show)
|
|
|
+
|
|
|
+-- Conversions to and from our Expr type
|
|
|
+toExpr :: SExpr Atom -> Either String Expr
|
|
|
+toExpr (A (AOp op) ::: l ::: r ::: Nil) = EOp op <$> toExpr l <*> toExpr r
|
|
|
+toExpr (A (ANum n)) = pure (ENum n)
|
|
|
+toExpr sexpr = Left ("Unable to parse expression: " ++ show sexpr)
|
|
|
+
|
|
|
+fromExpr :: Expr -> SExpr Atom
|
|
|
+fromExpr (EOp op l r) = A (AOp op) ::: fromExpr l ::: fromExpr r ::: Nil
|
|
|
+fromExpr (ENum n) = A (ANum n) ::: Nil
|
|
|
+
|
|
|
+-- Parser and serializer for our Atom type
|
|
|
+pAtom :: Parser Atom
|
|
|
+pAtom = ((ANum . read) <$> many1 digit)
|
|
|
+ <|> (char '+' *> pure (AOp Add))
|
|
|
+ <|> (char '-' *> pure (AOp Sub))
|
|
|
+ <|> (char '*' *> pure (AOp Mul))
|
|
|
+
|
|
|
+sAtom :: Atom -> Text
|
|
|
+sAtom (AOp Add) = "+"
|
|
|
+sAtom (AOp Sub) = "-"
|
|
|
+sAtom (AOp Mul) = "*"
|
|
|
+sAtom (ANum n) = pack (show n)
|
|
|
+
|
|
|
+-- Our comment syntax is going to be Haskell-like:
|
|
|
+hsComment :: Parser ()
|
|
|
+hsComment = string "--" >> manyTill anyChar newline >> return ()
|
|
|
+
|
|
|
+-- Our custom reader macro: grab the parse stream and read a
|
|
|
+-- hexadecimal number from it:
|
|
|
+hexReader :: Reader Atom
|
|
|
+hexReader _ = (A . ANum . rd) <$> many1 (satisfy isHexDigit)
|
|
|
+ where isHexDigit c = isDigit c || c `elem` hexChars
|
|
|
+ rd = fst . head . readHex
|
|
|
+ hexChars :: String
|
|
|
+ hexChars = "AaBbCcDdEeFf"
|
|
|
+
|
|
|
+-- Our final s-expression parser and printer:
|
|
|
+myLangParser :: SExprParser Atom Expr
|
|
|
+myLangParser
|
|
|
+ = setComment hsComment -- set comment syntax to be Haskell-style
|
|
|
+ $ addReader '#' hexReader -- add hex reader
|
|
|
+ $ setCarrier toExpr -- convert final repr to Expr
|
|
|
+ $ mkParser pAtom -- create spec with Atom type
|
|
|
+
|
|
|
+mkLangPrinter :: SExprPrinter Atom Expr
|
|
|
+mkLangPrinter
|
|
|
+ = setFromCarrier fromExpr
|
|
|
+ $ setIndentStrategy (const Align)
|
|
|
+ $ basicPrint sAtom
|
|
|
+
|
|
|
+
|
|
|
+main :: IO ()
|
|
|
+main = do
|
|
|
+ sExprText <- pack . head <$> getArgs
|
|
|
+ either putStrLn print (decode myLangParser sExprText)
|
|
|
+
|
|
|
+{-
|
|
|
+Exmaple usage:
|
|
|
+
|
|
|
+$ dist/build/example/example "$(echo -e '(+ (* 2 20) 10) (* 10 10)')"
|
|
|
+[EOp Add (EOp Mul (ENum 2) (ENum 20)) (ENum 10),EOp Mul (ENum 10) (ENum 10)]
|
|
|
+-}
|