Allow multiline expressions
This commit is contained in:
		
							
								
								
									
										78
									
								
								lib/base.tri
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								lib/base.tri
									
									
									
									
									
								
							| @ -17,25 +17,77 @@ yD = yi iE | ||||
| id = (\a : a) | ||||
| triage = (\a b c : t (t a b) c) | ||||
| pair = t | ||||
| matchBool = (\ot of : triage of (\_ : ot) (\_ _ : ot)) | ||||
| matchList = (\oe oc : triage oe _ oc) | ||||
| matchPair = (\op : triage _ _ op) | ||||
|  | ||||
| matchBool = (\ot of : triage  | ||||
|   of  | ||||
|   (\_ : ot)  | ||||
|   (\_ _ : ot) | ||||
| ) | ||||
|  | ||||
| matchList = (\oe oc : triage  | ||||
|   oe  | ||||
|   _  | ||||
|   oc | ||||
| ) | ||||
|  | ||||
| matchPair = (\op : triage  | ||||
|   _  | ||||
|   _  | ||||
|   op | ||||
| ) | ||||
|  | ||||
| not = matchBool false true | ||||
| and = matchBool id (\z : false) | ||||
| if = (\cond then else : t (t else (t t then)) t cond) | ||||
| test = triage "Leaf" (\z : "Stem") (\a b : "Fork") | ||||
|  | ||||
| emptyList = matchList true (\y z : false) | ||||
| head = matchList t (\hd tl : hd) | ||||
| tail = matchList t (\hd tl : tl) | ||||
| lconcat = y (\self : matchList (\k : k) (\h r k : pair h (self r k))) | ||||
| lAnd = triage (\x : false) (\_ x : x) (\_ _ x : x) | ||||
| lOr = triage (\x : x) (\_ _ : true) (\_ _ x : true) | ||||
| hmap = y (\self : matchList (\f : t) (\hd tl f : pair (f hd) (self tl f))) | ||||
|  | ||||
| lconcat = y (\self : matchList  | ||||
|   (\k : k)  | ||||
|   (\h r k : pair h (self r k))) | ||||
|  | ||||
| lAnd = (triage  | ||||
|   (\x : false)  | ||||
|   (\_ x : x)  | ||||
|   (\_ _ x : x) | ||||
| ) | ||||
|  | ||||
| lOr = (triage  | ||||
|   (\x : x)  | ||||
|   (\_ _ : true)  | ||||
|   (\_ _ x : true) | ||||
| ) | ||||
|  | ||||
| hmap = y (\self :  | ||||
|   matchList  | ||||
|     (\f : t)  | ||||
|     (\hd tl f : pair  | ||||
|                   (f hd)  | ||||
|                   (self tl f))) | ||||
| map = (\f l : hmap l f) | ||||
| equal = y (\self : triage (triage true (\z : false) (\y z : false)) (\ax : triage false (self ax) (\y z : false)) (\ax ay : triage false (\z : false) (\bx by : lAnd (self ax bx) (self ay by)))) | ||||
|  | ||||
| equal = y (\self : triage  | ||||
|   (triage  | ||||
|     true  | ||||
|     (\z : false)  | ||||
|     (\y z : false))  | ||||
|   (\ax : triage  | ||||
|           false  | ||||
|           (self ax)  | ||||
|           (\y z : false))  | ||||
|   (\ax ay : triage  | ||||
|               false  | ||||
|               (\z : false)  | ||||
|               (\bx by : lAnd (self ax bx) (self ay by)))) | ||||
|  | ||||
| hfilter = y (\self : matchList (\f : t) (\hd tl f : matchBool (t hd) i (f hd) (self tl f))) | ||||
| filter = (\f l : hfilter l f) | ||||
| hfoldl = y (\self f l x : matchList (\acc : acc) (\hd tl acc : self f tl (f acc hd)) l x) | ||||
| foldl  = (\f x l : hfoldl f l x) | ||||
| hfoldr = y (\self x f l : matchList x (\hd tl : f (self x f tl) hd) l) | ||||
| foldr  = (\f x l : hfoldr x f l) | ||||
| filter  = (\f l : hfilter l f) | ||||
|  | ||||
| hfoldl  = y (\self f l x : matchList (\acc : acc) (\hd tl acc : self f tl (f acc hd)) l x) | ||||
| foldl   = (\f x l : hfoldl f l x) | ||||
|  | ||||
| hfoldr  = y (\self x f l : matchList x (\hd tl : f (self x f tl) hd) l) | ||||
| foldr   = (\f x l : hfoldr x f l) | ||||
|  | ||||
							
								
								
									
										29
									
								
								src/Eval.hs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/Eval.hs
									
									
									
									
									
								
							| @ -13,22 +13,23 @@ evalSingle env term | ||||
|   | SFunc name [] body <- term = | ||||
|       let res = evalAST env body | ||||
|       in Map.insert "__result" res (Map.insert name res env) | ||||
|   | SApp func arg <- term      = Map.insert "__result" | ||||
|       (apply (evalAST env func) (evalAST env arg)) env | ||||
|   | SVar name <- term          = case Map.lookup name env of | ||||
|       Just v  -> Map.insert "__result" v env | ||||
|       Nothing -> errorWithoutStackTrace $ "Variable " ++ name ++ " not defined" | ||||
|   | otherwise                  = Map.insert "__result" (evalAST env term) env | ||||
|   | SApp func arg <- term = | ||||
|       let res = apply (evalAST env func) (evalAST env arg) | ||||
|       in Map.insert "__result" res env | ||||
|   | SVar name <- term = | ||||
|       case Map.lookup name env of | ||||
|         Just v  -> Map.insert "__result" v env | ||||
|         Nothing -> errorWithoutStackTrace $ "Variable " ++ name ++ " not defined" | ||||
|   | otherwise = | ||||
|       Map.insert "__result" (evalAST env term) env | ||||
|  | ||||
| evalTricu :: Env -> [TricuAST] -> Env | ||||
| evalTricu env list = evalTricu' env (filter (/= SEmpty) list) | ||||
|   where | ||||
|     evalTricu' :: Env -> [TricuAST] -> Env | ||||
|     evalTricu' env [] = env | ||||
|     evalTricu' env [s] = | ||||
|       let updatedEnv = evalSingle env s | ||||
|       in Map.insert "__result" (result updatedEnv) updatedEnv | ||||
|     evalTricu' env (x:xs) = evalTricu (evalSingle env x) xs | ||||
| evalTricu env []     = env | ||||
| evalTricu env [x]    = | ||||
|   let updatedEnv = evalSingle env x | ||||
|   in Map.insert "__result" (result updatedEnv) updatedEnv | ||||
| evalTricu env (x:xs) = | ||||
|   evalTricu (evalSingle env x) xs | ||||
|  | ||||
| evalAST :: Env -> TricuAST -> T | ||||
| evalAST env term | ||||
|  | ||||
| @ -61,7 +61,10 @@ lnewline :: Lexer LToken | ||||
| lnewline = char '\n' *> pure LNewline | ||||
|  | ||||
| sc :: Lexer () | ||||
| sc = space space1 (skipLineComment "--") (skipBlockComment "|-" "-|") | ||||
| sc = space | ||||
|   (void $ takeWhile1P (Just "space") (\c -> c == ' ' || c == '\t')) | ||||
|   (skipLineComment "--") | ||||
|   (skipBlockComment "|-" "-|") | ||||
|  | ||||
| tricuLexer :: Lexer [LToken] | ||||
| tricuLexer = do | ||||
| @ -75,7 +78,8 @@ tricuLexer = do | ||||
|   pure tokens | ||||
|     where | ||||
|       tricuLexer' =  | ||||
|         [ try identifier | ||||
|         [ try lnewline | ||||
|         , try identifier | ||||
|         , try keywordT | ||||
|         , try integerLiteral | ||||
|         , try stringLiteral | ||||
|  | ||||
| @ -81,4 +81,7 @@ main = do | ||||
|       putStrLn $ decodeResult $ result $ evalTricu library $ parseTricu value | ||||
|  | ||||
| runTricu :: String -> T | ||||
| runTricu = result . evalTricu Map.empty . parseTricu | ||||
| runTricu input = | ||||
|   let asts     = parseTricu input | ||||
|       finalEnv = evalTricu Map.empty asts | ||||
|    in result finalEnv | ||||
|  | ||||
							
								
								
									
										466
									
								
								src/Parser.hs
									
									
									
									
									
								
							
							
						
						
									
										466
									
								
								src/Parser.hs
									
									
									
									
									
								
							| @ -1,281 +1,299 @@ | ||||
| module Parser where | ||||
|  | ||||
| import Lexer | ||||
| import Research hiding       (toList) | ||||
| import Research | ||||
|  | ||||
| import Data.List.NonEmpty    (toList) | ||||
| import Control.Monad (void) | ||||
| import Control.Monad.State | ||||
| import Data.List.NonEmpty (toList) | ||||
| import Data.Void (Void) | ||||
| import Text.Megaparsec | ||||
| import Text.Megaparsec.Char | ||||
| import Text.Megaparsec.Error (ParseErrorBundle, errorBundlePretty) | ||||
|  | ||||
| import qualified Data.Set as Set | ||||
|  | ||||
| type Parser    = Parsec Void [LToken] | ||||
| type AltParser = Parsec Void String | ||||
| data PState = PState | ||||
|   { depth :: Int | ||||
|   } deriving (Show) | ||||
|  | ||||
| type ParserM = StateT PState (Parsec Void [LToken]) | ||||
|  | ||||
| satisfyM :: (LToken -> Bool) -> ParserM LToken | ||||
| satisfyM f = do | ||||
|   token <- lift (satisfy f) | ||||
|   modify' (updateDepth token) | ||||
|   return token | ||||
|  | ||||
| updateDepth :: LToken -> PState -> PState | ||||
| updateDepth LOpenParen  st = st { depth = depth st + 1 } | ||||
| updateDepth LCloseParen st = st { depth = max 0 (depth st - 1) } | ||||
| updateDepth _           st = st | ||||
|  | ||||
| topLevelNewline :: ParserM () | ||||
| topLevelNewline = do | ||||
|   st <- get | ||||
|   if depth st == 0 | ||||
|     then void (satisfyM (== LNewline)) | ||||
|     else fail "Top-level exit in paren context" | ||||
|  | ||||
| parseProgram :: [LToken] -> Either (ParseErrorBundle [LToken] Void) [TricuAST] | ||||
| parseProgram tokens = | ||||
|   runParser (evalStateT parseProgramM (PState 0)) "" tokens | ||||
|  | ||||
| parseSingleExpr :: [LToken] -> Either (ParseErrorBundle [LToken] Void) TricuAST | ||||
| parseSingleExpr tokens = | ||||
|   runParser (evalStateT (scnParserM *> parseExpressionM <* eofM) (PState 0)) "" tokens | ||||
|  | ||||
| parseTricu :: String -> [TricuAST] | ||||
| parseTricu input | ||||
|   | null tokens = [] | ||||
|   | otherwise   = map parseSingle tokens | ||||
|   where | ||||
|     tokens | ||||
|       | null (lexTricu input) = [] | ||||
|       | otherwise             = lines input | ||||
| parseTricu input = | ||||
|   case lexTricu input of | ||||
|     [] -> [] | ||||
|     toks -> | ||||
|       case parseProgram toks of | ||||
|         Left err   -> error (handleParseError err) | ||||
|         Right asts -> asts | ||||
|  | ||||
| parseSingle :: String -> TricuAST | ||||
| parseSingle input | ||||
|   | null tokens          = SEmpty | ||||
|   | Left  err  <- parsed = error $ handleParseError err | ||||
|   | Right ast  <- parsed = ast | ||||
|   where | ||||
|     tokens = lexTricu input | ||||
|     parsed = runParser parseExpression "" tokens | ||||
| parseSingle input = | ||||
|   case lexTricu input of | ||||
|     [] -> SEmpty | ||||
|     toks -> | ||||
|       case parseSingleExpr toks of | ||||
|         Left err -> error (handleParseError err) | ||||
|         Right ast -> ast | ||||
|  | ||||
| parseExpression :: Parser TricuAST | ||||
| parseExpression = choice | ||||
|   [ try parseFunction | ||||
|   , try parseLambda | ||||
|   , try parseLambdaExpression | ||||
|   , try parseListLiteral | ||||
|   , try parseApplication | ||||
|   , try parseTreeTerm | ||||
|   , parseLiteral | ||||
| parseProgramM :: ParserM [TricuAST] | ||||
| parseProgramM = do | ||||
|   skipMany topLevelNewline | ||||
|   exprs <- sepEndBy parseOneExpression (some topLevelNewline) | ||||
|   skipMany topLevelNewline | ||||
|   return exprs | ||||
|  | ||||
| parseOneExpression :: ParserM TricuAST | ||||
| parseOneExpression = scnParserM *> parseExpressionM | ||||
|  | ||||
| scnParserM :: ParserM () | ||||
| scnParserM = skipMany $ do | ||||
|   t <- lookAhead anySingle | ||||
|   st <- get | ||||
|   if depth st > 0 && isNewline t | ||||
|     then void (satisfyM isNewline) | ||||
|     else | ||||
|       fail "In paren context or no space token" <|> empty | ||||
|  | ||||
| eofM :: ParserM () | ||||
| eofM = lift eof | ||||
|  | ||||
| parseExpressionM :: ParserM TricuAST | ||||
| parseExpressionM = choice | ||||
|   [ try parseFunctionM | ||||
|   , try parseLambdaM | ||||
|   , try parseLambdaExpressionM | ||||
|   , try parseListLiteralM | ||||
|   , try parseApplicationM | ||||
|   , try parseTreeTermM | ||||
|   , parseLiteralM | ||||
|   ] | ||||
|  | ||||
| scnParser :: Parser () | ||||
| scnParser = skipMany (satisfy isNewline) | ||||
| parseFunctionM :: ParserM TricuAST | ||||
| parseFunctionM = do | ||||
|   LIdentifier name <- satisfyM isIdentifier | ||||
|   args <- many (satisfyM isIdentifier) | ||||
|   _    <- satisfyM (== LAssign) | ||||
|   scnParserM | ||||
|   body <- parseExpressionM | ||||
|   pure (SFunc name (map getIdentifier args) body) | ||||
|  | ||||
| parseFunction :: Parser TricuAST | ||||
| parseFunction = do | ||||
|   LIdentifier name <- satisfy isIdentifier | ||||
|   args <- many (satisfy isIdentifier) | ||||
|   satisfy (== LAssign) | ||||
|   body <- parseExpression | ||||
|   return (SFunc name (map getIdentifier args) body) | ||||
| parseLambdaM :: ParserM TricuAST | ||||
| parseLambdaM = between (satisfyM (== LOpenParen)) (satisfyM (== LCloseParen)) $ do | ||||
|   _       <- satisfyM (== LBackslash) | ||||
|   param   <- satisfyM isIdentifier | ||||
|   rest    <- many (satisfyM isIdentifier) | ||||
|   _       <- satisfyM (== LColon) | ||||
|   scnParserM | ||||
|   body    <- parseLambdaExpressionM | ||||
|   let nested = foldr (\v acc -> SLambda [getIdentifier v] acc) body rest | ||||
|   pure (SLambda [getIdentifier param] nested) | ||||
|  | ||||
| parseAtomicBase :: Parser TricuAST | ||||
| parseAtomicBase = choice | ||||
|     [ parseTreeLeaf | ||||
|     , parseGrouped | ||||
|     ] | ||||
|  | ||||
| parseLambda :: Parser TricuAST | ||||
| parseLambda = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) $ do | ||||
|   satisfy (== LBackslash) | ||||
|   param    <- satisfy isIdentifier | ||||
|   rest     <- many (satisfy isIdentifier) | ||||
|   satisfy (== LColon) | ||||
|   body     <- parseLambdaExpression | ||||
|   let nestedLambda = foldr (\v acc -> SLambda [v] acc) body (map getIdentifier rest) | ||||
|   return (SLambda [getIdentifier param] nestedLambda) | ||||
|  | ||||
| parseLambdaExpression :: Parser TricuAST | ||||
| parseLambdaExpression = choice | ||||
|   [ try parseLambdaApplication | ||||
|   , parseAtomicLambda | ||||
| parseLambdaExpressionM :: ParserM TricuAST | ||||
| parseLambdaExpressionM = choice | ||||
|   [ try parseLambdaApplicationM | ||||
|   , parseAtomicLambdaM | ||||
|   ] | ||||
|  | ||||
| parseAtomicLambda :: Parser TricuAST | ||||
| parseAtomicLambda = choice | ||||
|   [ parseVar | ||||
|   , parseTreeLeaf | ||||
|   , parseLiteral | ||||
|   , parseListLiteral | ||||
|   , try parseLambda | ||||
|   , between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseLambdaExpression | ||||
| parseAtomicLambdaM :: ParserM TricuAST | ||||
| parseAtomicLambdaM = choice | ||||
|   [ parseVarM | ||||
|   , parseTreeLeafM | ||||
|   , parseLiteralM | ||||
|   , parseListLiteralM | ||||
|   , try parseLambdaM | ||||
|   , between (satisfyM (== LOpenParen)) (satisfyM (== LCloseParen)) parseLambdaExpressionM | ||||
|   ] | ||||
|  | ||||
| parseApplication :: Parser TricuAST | ||||
| parseApplication = do | ||||
|   func <- parseAtomicBase | ||||
|   args <- many parseAtomic | ||||
|   return $ foldl (\acc arg -> SApp acc arg) func args | ||||
| parseApplicationM :: ParserM TricuAST | ||||
| parseApplicationM = do | ||||
|   func <- parseAtomicBaseM | ||||
|   scnParserM | ||||
|   args <- many $ do | ||||
|     scnParserM | ||||
|     arg <- parseAtomicM | ||||
|     return arg | ||||
|   return $ foldl SApp func args | ||||
|  | ||||
| parseLambdaApplication :: Parser TricuAST | ||||
| parseLambdaApplication = do | ||||
|   func <- parseAtomicLambda | ||||
|   args <- many parseAtomicLambda | ||||
|   return $ foldl (\acc arg -> SApp acc arg) func args | ||||
| parseLambdaApplicationM :: ParserM TricuAST | ||||
| parseLambdaApplicationM = do | ||||
|   func <- parseAtomicLambdaM | ||||
|   scnParserM | ||||
|   args <- many $ do | ||||
|     arg <- parseAtomicLambdaM | ||||
|     scnParserM | ||||
|     pure arg | ||||
|   pure $ foldl SApp func args | ||||
|  | ||||
| isTreeTerm :: TricuAST -> Bool | ||||
| isTreeTerm TLeaf = True | ||||
| isTreeTerm (TStem _) = True | ||||
| isTreeTerm (TFork _ _) = True | ||||
| isTreeTerm _ = False | ||||
| parseAtomicBaseM :: ParserM TricuAST | ||||
| parseAtomicBaseM = choice | ||||
|   [ parseTreeLeafM | ||||
|   , parseGroupedM | ||||
|   ] | ||||
|  | ||||
| parseTreeLeaf :: Parser TricuAST | ||||
| parseTreeLeaf = satisfy isKeywordT *> notFollowedBy (satisfy (== LAssign)) *> pure TLeaf | ||||
| parseTreeLeafM :: ParserM TricuAST | ||||
| parseTreeLeafM = do | ||||
|   _ <- satisfyM isKeywordT | ||||
|   notFollowedBy (lift (satisfy (== LAssign))) | ||||
|   pure TLeaf | ||||
|  | ||||
| getIdentifier :: LToken -> String | ||||
| getIdentifier (LIdentifier name) = name | ||||
| getIdentifier _ = error "Expected identifier" | ||||
|  | ||||
| parseTreeTerm :: Parser TricuAST | ||||
| parseTreeTerm = do | ||||
|   base <- parseTreeLeafOrParenthesized | ||||
|   rest <- many parseTreeLeafOrParenthesized | ||||
|   pure $ foldl combine base rest | ||||
| parseTreeTermM :: ParserM TricuAST | ||||
| parseTreeTermM = do | ||||
|   base <- parseTreeLeafOrParenthesizedM | ||||
|   rest <- many parseTreeLeafOrParenthesizedM | ||||
|   pure (foldl combine base rest) | ||||
|   where | ||||
|     combine acc next | ||||
|       | TLeaf     <- acc = TStem next | ||||
|       | TStem t   <- acc = TFork t next | ||||
|       | TFork _ _ <- acc = TFork acc next | ||||
|  | ||||
| parseTreeLeafOrParenthesized :: Parser TricuAST | ||||
| parseTreeLeafOrParenthesized = choice | ||||
|   [ between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseTreeTerm | ||||
|   , parseTreeLeaf | ||||
| parseTreeLeafOrParenthesizedM :: ParserM TricuAST | ||||
| parseTreeLeafOrParenthesizedM = choice | ||||
|   [ between (satisfyM (== LOpenParen)) (satisfyM (== LCloseParen)) parseTreeTermM | ||||
|   , parseTreeLeafM | ||||
|   ] | ||||
|  | ||||
| foldTree :: [TricuAST] -> TricuAST | ||||
| foldTree [] = TLeaf | ||||
| foldTree [x] = x | ||||
| foldTree (x:y:rest) = TFork x (foldTree (y:rest)) | ||||
|  | ||||
| parseAtomic :: Parser TricuAST | ||||
| parseAtomic = choice | ||||
|   [ parseVar | ||||
|   , parseTreeLeaf | ||||
|   , parseListLiteral | ||||
|   , parseGrouped | ||||
|   , parseLiteral | ||||
| parseAtomicM :: ParserM TricuAST | ||||
| parseAtomicM = choice | ||||
|   [ parseVarM | ||||
|   , parseTreeLeafM | ||||
|   , parseListLiteralM | ||||
|   , parseGroupedM | ||||
|   , parseLiteralM | ||||
|   ] | ||||
|  | ||||
| parseGrouped :: Parser TricuAST | ||||
| parseGrouped = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseExpression | ||||
| parseGroupedM :: ParserM TricuAST | ||||
| parseGroupedM = between (satisfyM (== LOpenParen)) (satisfyM (== LCloseParen)) $ | ||||
|   scnParserM *> parseExpressionM <* scnParserM | ||||
|  | ||||
| parseLiteral :: Parser TricuAST | ||||
| parseLiteral = choice | ||||
|   [ parseIntLiteral | ||||
|   , parseStrLiteral | ||||
| parseLiteralM :: ParserM TricuAST | ||||
| parseLiteralM = choice | ||||
|   [ parseIntLiteralM | ||||
|   , parseStrLiteralM | ||||
|   ] | ||||
|  | ||||
| parens :: Parser TricuAST -> Parser TricuAST | ||||
| parens p = do | ||||
|   satisfy (== LOpenParen) | ||||
|   result <- p | ||||
|   satisfy (== LCloseParen) | ||||
|   return result | ||||
| parseListLiteralM :: ParserM TricuAST | ||||
| parseListLiteralM = do | ||||
|   _        <- satisfyM (== LOpenBracket) | ||||
|   elements <- many parseListItemM | ||||
|   _        <- satisfyM (== LCloseBracket) | ||||
|   pure (SList elements) | ||||
|  | ||||
| parseListLiteral :: Parser TricuAST | ||||
| parseListLiteral = do | ||||
|   satisfy (== LOpenBracket) | ||||
|   elements <- many parseListItem | ||||
|   satisfy (== LCloseBracket) | ||||
|   return (SList elements) | ||||
|  | ||||
| parseListItem :: Parser TricuAST | ||||
| parseListItem = choice | ||||
|   [ parseGroupedItem | ||||
|   , parseListLiteral | ||||
|   , parseSingleItem | ||||
| parseListItemM :: ParserM TricuAST | ||||
| parseListItemM = choice | ||||
|   [ parseGroupedItemM | ||||
|   , parseListLiteralM | ||||
|   , parseSingleItemM | ||||
|   ] | ||||
|  | ||||
| parseGroupedItem :: Parser TricuAST | ||||
| parseGroupedItem = do | ||||
|   satisfy (== LOpenParen) | ||||
|   inner <- parseExpression | ||||
|   satisfy (== LCloseParen) | ||||
|   return inner | ||||
| parseGroupedItemM :: ParserM TricuAST | ||||
| parseGroupedItemM = do | ||||
|   _     <- satisfyM (== LOpenParen) | ||||
|   inner <- parseExpressionM | ||||
|   _     <- satisfyM (== LCloseParen) | ||||
|   pure inner | ||||
|  | ||||
| parseSingleItem :: Parser TricuAST | ||||
| parseSingleItem = do | ||||
|   token <- satisfy isListItem | ||||
| parseSingleItemM :: ParserM TricuAST | ||||
| parseSingleItemM = do | ||||
|   token <- satisfyM isListItem | ||||
|   case token of | ||||
|     _ | LIdentifier name <- token -> return (SVar name) | ||||
|       | LKeywordT <- token        -> return TLeaf | ||||
|       | otherwise                 -> fail "Unexpected token in list item" | ||||
|     LIdentifier name -> pure (SVar name) | ||||
|     LKeywordT        -> pure TLeaf | ||||
|     _                -> fail "Unexpected token in list item" | ||||
|  | ||||
| parseVarM :: ParserM TricuAST | ||||
| parseVarM = do | ||||
|   LIdentifier name <- satisfyM isIdentifier | ||||
|   if name == "t" || name == "__result" | ||||
|     then fail ("Reserved keyword: " ++ name ++ " cannot be assigned.") | ||||
|     else pure (SVar name) | ||||
|  | ||||
| parseIntLiteralM :: ParserM TricuAST | ||||
| parseIntLiteralM = do | ||||
|   LIntegerLiteral value <- satisfyM isIntegerLiteral | ||||
|   pure (SInt value) | ||||
|  | ||||
| parseStrLiteralM :: ParserM TricuAST | ||||
| parseStrLiteralM = do | ||||
|   LStringLiteral value <- satisfyM isStringLiteral | ||||
|   pure (SStr value) | ||||
|  | ||||
| isKeywordT :: LToken -> Bool | ||||
| isKeywordT LKeywordT = True | ||||
| isKeywordT _         = False | ||||
|  | ||||
| isIdentifier :: LToken -> Bool | ||||
| isIdentifier (LIdentifier _) = True | ||||
| isIdentifier _               = False | ||||
|  | ||||
| isIntegerLiteral :: LToken -> Bool | ||||
| isIntegerLiteral (LIntegerLiteral _) = True | ||||
| isIntegerLiteral _                   = False | ||||
|  | ||||
| isStringLiteral :: LToken -> Bool | ||||
| isStringLiteral (LStringLiteral _)   = True | ||||
| isStringLiteral _                    = False | ||||
|  | ||||
| isLiteral :: LToken -> Bool | ||||
| isLiteral (LIntegerLiteral _) = True | ||||
| isLiteral (LStringLiteral _)  = True | ||||
| isLiteral _                   = False | ||||
|  | ||||
| isListItem :: LToken -> Bool | ||||
| isListItem (LIdentifier _) = True | ||||
| isListItem LKeywordT = True | ||||
| isListItem _ = False | ||||
| isListItem LKeywordT       = True | ||||
| isListItem _               = False | ||||
|  | ||||
| parseVar :: Parser TricuAST | ||||
| parseVar = do | ||||
|   LIdentifier name <- satisfy isIdentifier | ||||
|   if (name == "t" || name == "__result") | ||||
|     then fail $ "Reserved keyword: " ++ name ++ " cannot be assigned." | ||||
|     else return (SVar name) | ||||
| isNewline :: LToken -> Bool | ||||
| isNewline LNewline = True | ||||
| isNewline _        = False | ||||
|  | ||||
| parseIntLiteral :: Parser TricuAST | ||||
| parseIntLiteral = do | ||||
|   LIntegerLiteral value <- satisfy isIntegerLiteral | ||||
|   return (SInt value) | ||||
| getIdentifier :: LToken -> String | ||||
| getIdentifier (LIdentifier name) = name | ||||
| getIdentifier _                  = error "Expected identifier" | ||||
|  | ||||
| parseStrLiteral :: Parser TricuAST | ||||
| parseStrLiteral = do | ||||
|   LStringLiteral value <- satisfy isStringLiteral | ||||
|   return (SStr value) | ||||
|  | ||||
| -- Boolean Helpers | ||||
| isKeywordT (LKeywordT) = True | ||||
| isKeywordT _ = False | ||||
| isIdentifier (LIdentifier _) = True | ||||
| isIdentifier _ = False | ||||
| isIntegerLiteral (LIntegerLiteral _) = True | ||||
| isIntegerLiteral _ = False | ||||
| isStringLiteral (LStringLiteral _) = True | ||||
| isStringLiteral _ = False | ||||
| isLiteral (LIntegerLiteral _) = True | ||||
| isLiteral (LStringLiteral _) = True | ||||
| isLiteral _ = False | ||||
| isNewline (LNewline) = True | ||||
| isNewline _ = False | ||||
|  | ||||
| -- Alternative parsers | ||||
| altSC :: AltParser () | ||||
| altSC = skipMany (char ' ' <|> char '\t' <|> char '\n') | ||||
|  | ||||
| parseTernaryTerm :: AltParser TricuAST | ||||
| parseTernaryTerm = do | ||||
|   altSC | ||||
|   term <- choice parseTernaryTerm' | ||||
|   altSC | ||||
|   pure term | ||||
|   where | ||||
|     parseTernaryTerm' = | ||||
|       [ try (between (char '(') (char ')') parseTernaryTerm) | ||||
|       , try parseTernaryLeaf | ||||
|       , try parseTernaryStem | ||||
|       , try parseTernaryFork | ||||
|       ] | ||||
|  | ||||
| parseTernaryLeaf :: AltParser TricuAST | ||||
| parseTernaryLeaf = char '0' *> pure TLeaf | ||||
|  | ||||
| parseTernaryStem :: AltParser TricuAST | ||||
| parseTernaryStem = char '1' *> (TStem <$> parseTernaryTerm) | ||||
|  | ||||
| parseTernaryFork :: AltParser TricuAST | ||||
| parseTernaryFork = do | ||||
|   char '2' | ||||
|   term1 <- parseTernaryTerm | ||||
|   term2 <- parseTernaryTerm | ||||
|   pure $ TFork term1 term2 | ||||
|  | ||||
| parseTernary :: String -> Either String TricuAST | ||||
| parseTernary input | ||||
|   | Left err <- result  = Left (errorBundlePretty err) | ||||
|   | Right ast <- result = Right ast | ||||
|   where | ||||
|     result = runParser (parseTernaryTerm <* eof) "" input | ||||
|  | ||||
| -- Error Handling | ||||
| handleParseError :: ParseErrorBundle [LToken] Void -> String | ||||
| handleParseError bundle = | ||||
|   let errors = bundleErrors bundle | ||||
|       errorList = toList errors | ||||
|       formattedErrors = map showError errorList | ||||
|   in unlines ("Parse error(s) encountered:" : formattedErrors) | ||||
|   let errors        = bundleErrors bundle | ||||
|       errorList     = Data.List.NonEmpty.toList errors | ||||
|       formattedErrs = map showError errorList | ||||
|   in unlines ("Parse error(s) encountered:" : formattedErrs) | ||||
|  | ||||
| showError :: ParseError [LToken] Void -> String | ||||
| showError (TrivialError offset (Just (Tokens tokenStream)) expected) = | ||||
|   "Parse error at offset " ++ show offset ++ ": unexpected token " | ||||
|   ++ show tokenStream ++ ", expected one of " ++ show (Set.toList expected) | ||||
|   "Parse error at offset " ++ show offset | ||||
|     ++ ": unexpected token " ++ show tokenStream | ||||
|     ++ ", expected one of " ++ show (Set.toList expected) | ||||
| showError (FancyError offset fancy) = | ||||
|   "Parse error at offset " ++ show offset ++ ":\n " ++ unlines (map show (Set.toList fancy)) | ||||
|   "Parse error at offset " ++ show offset ++ ":\n " | ||||
|     ++ unlines (map show (Set.toList fancy)) | ||||
| showError (TrivialError offset Nothing expected) = | ||||
|   "Parse error at offset " ++ show offset ++ ": expected one of " | ||||
|   ++ show (Set.toList expected) | ||||
|   "Parse error at offset " ++ show offset | ||||
|     ++ ": expected one of " ++ show (Set.toList expected) | ||||
|  | ||||
|  | ||||
							
								
								
									
										10
									
								
								test/Spec.hs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								test/Spec.hs
									
									
									
									
									
								
							| @ -71,9 +71,9 @@ lexerTests = testGroup "Lexer Tests" | ||||
| parserTests :: TestTree | ||||
| parserTests = testGroup "Parser Tests" | ||||
|   [ testCase "Error when assigning a value to T" $ do | ||||
|       let input = lexTricu "t = x" | ||||
|       case (runParser parseExpression "" input) of | ||||
|         Left _ -> return () | ||||
|       let tokens = lexTricu "t = x"  | ||||
|       case parseSingleExpr tokens of | ||||
|         Left  _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure when trying to assign the value of T" | ||||
|   , testCase "Parse function definitions" $ do | ||||
|       let input = "x = (\\a b c : a)" | ||||
| @ -149,10 +149,6 @@ parserTests = testGroup "Parser Tests" | ||||
|       let input = "(t) -- (t) -- (t)" | ||||
|           expect = [TLeaf] | ||||
|       parseTricu input @?= expect | ||||
|   , testCase "Comments with no terms" $ do | ||||
|       let input = unlines ["-- (t)", "(t t)"] | ||||
|           expect = [SEmpty,SApp TLeaf TLeaf] | ||||
|       parseTricu input @?= expect  | ||||
|   ] | ||||
|  | ||||
| evaluationTests :: TestTree | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| cabal-version: 1.12 | ||||
|  | ||||
| name:           tricu | ||||
| version:        0.5.0 | ||||
| version:        0.6.0 | ||||
| description:    A micro-language for exploring Tree Calculus | ||||
| author:         James Eversole | ||||
| maintainer:     james@eversole.co | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 James Eversole
					James Eversole