well
This commit is contained in:
		
							parent
							
								
									60f5eb274c
								
							
						
					
					
						commit
						f0d6558df9
					
				|  | @ -37,3 +37,7 @@ main = do | ||||||
|           ] |           ] | ||||||
|   ppr interp |   ppr interp | ||||||
|   ppr res |   ppr res | ||||||
|  | 
 | ||||||
|  | {- | ||||||
|  | ppr $ let { Right l = parse lexPrlg "stdin" "c(cc(X)) :- a, b. " ; Right p = parse parsePrlg "stdin" l; clause:_ = map (ast2prlg defaultOps) p; ((StrTable _ strfwd strback),codei) = strtablizePrlg emptystrtable clause } in compileRule (Id (strfwd M.! ":-") 2) (Id (strfwd M.! ",") 2) codei | ||||||
|  |  -} | ||||||
|  |  | ||||||
							
								
								
									
										207
									
								
								app/Parser.hs
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								app/Parser.hs
									
									
									
									
									
								
							|  | @ -3,19 +3,25 @@ module Parser where | ||||||
| import Control.Applicative (liftA2) | import Control.Applicative (liftA2) | ||||||
| import Control.Monad (void) | import Control.Monad (void) | ||||||
| import Data.Char | import Data.Char | ||||||
|  | import Data.List.Split (splitOn) | ||||||
| import Data.Void | import Data.Void | ||||||
| import Text.Megaparsec | import Text.Megaparsec | ||||||
| import Text.Megaparsec.Char | import Text.Megaparsec.Char | ||||||
| 
 | 
 | ||||||
| import Compiler (PrlgStr (..)) | import Compiler (PrlgStr(..)) | ||||||
| 
 | 
 | ||||||
| specialOp = (`elem` ",;|") | singleToks = ",;|()[]" | ||||||
| 
 | 
 | ||||||
| specialChar = (`elem` "()[]") | identParts = "_" | ||||||
| 
 | 
 | ||||||
| specialName = (`elem` "_") | notOpToks = "\'%" ++ identParts | ||||||
| 
 | 
 | ||||||
| specialUnused = (`elem` "\'%") | isOperatorlike x = | ||||||
|  |   (isSymbol x || isPunctuation x) && not (x `elem` singleToks ++ notOpToks) | ||||||
|  | 
 | ||||||
|  | isIdentStart x = (x `elem` identParts) || isAlpha x | ||||||
|  | 
 | ||||||
|  | isIdentOther x = isIdentStart x || isAlphaNum x || isMark x | ||||||
| 
 | 
 | ||||||
| type Lexer = Parsec Void String | type Lexer = Parsec Void String | ||||||
| 
 | 
 | ||||||
|  | @ -32,20 +38,9 @@ tok :: Lexer Lexeme | ||||||
| tok = | tok = | ||||||
|   Tok <$> |   Tok <$> | ||||||
|   choice |   choice | ||||||
|     [ pure <$> satisfy specialOp |     [ pure <$> oneOf singleToks | ||||||
|     , pure <$> satisfy specialChar |     , some $ satisfy isOperatorlike | ||||||
|     , some $ |     , (:) <$> satisfy isIdentStart <*> many (satisfy isIdentOther) | ||||||
|       satisfy $ \x -> |  | ||||||
|         all |  | ||||||
|           ($ x) |  | ||||||
|           [ not . specialOp |  | ||||||
|           , not . specialChar |  | ||||||
|           , not . specialUnused |  | ||||||
|           , not . specialName |  | ||||||
|           , liftA2 (||) isSymbol isPunctuation |  | ||||||
|           ] |  | ||||||
|     , (:) <$> satisfy (\x -> any ($ x) [specialName, isAlpha]) <*> |  | ||||||
|       many (satisfy $ \x -> any ($ x) [specialName, isAlphaNum, isMark]) |  | ||||||
|     , some (satisfy isNumber) |     , some (satisfy isNumber) | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|  | @ -69,11 +64,11 @@ lexPrlg :: Lexer [Lexeme] | ||||||
| lexPrlg = many lexeme <* (many blank >> eof) | lexPrlg = many lexeme <* (many blank >> eof) | ||||||
| 
 | 
 | ||||||
| data AST | data AST | ||||||
|   = Call String [AST] |   = Call String [[AST]] | ||||||
|   | Seq [AST] |   | Seq [AST] | ||||||
|   | List [AST] (Maybe [AST]) |   | List [AST] (Maybe [AST]) | ||||||
|   | Literal String |   | Literal String | ||||||
|   deriving (Show) |   deriving (Show, Eq) | ||||||
| 
 | 
 | ||||||
| type Parser = Parsec Void [Lexeme] | type Parser = Parsec Void [Lexeme] | ||||||
| 
 | 
 | ||||||
|  | @ -82,7 +77,7 @@ isBlank _ = False | ||||||
| 
 | 
 | ||||||
| ws = many $ satisfy isBlank | ws = many $ satisfy isBlank | ||||||
| 
 | 
 | ||||||
| free = (ws >>) | free = (<* ws) -- we eat blanks _after_ the token. | ||||||
| 
 | 
 | ||||||
| isNormalTokStr = (`notElem` [".", "[", "]", "(", "|", ")"]) | isNormalTokStr = (`notElem` [".", "[", "]", "(", "|", ")"]) | ||||||
| 
 | 
 | ||||||
|  | @ -94,17 +89,17 @@ unTok (Tok t) = t | ||||||
| unTok (QTok t _) = t | unTok (QTok t _) = t | ||||||
| 
 | 
 | ||||||
| literal :: Parser AST | literal :: Parser AST | ||||||
| literal = Literal . unTok <$> (satisfy isNormalTok <* notFollowedBy lParen) | literal = Literal . unTok <$> free (satisfy isNormalTok <* notFollowedBy lParen) | ||||||
| 
 | 
 | ||||||
| call = do | call = do | ||||||
|   fn <- unTok <$> satisfy isNormalTok |   fn <- unTok <$> satisfy isNormalTok -- not free | ||||||
|   Seq inner <- parens -- not free! |   Seq inner <- free parens | ||||||
|   return $ Call fn inner |   return $ Call fn $ splitOn [Literal ","] inner | ||||||
| 
 | 
 | ||||||
| parens = Seq <$> (lParen *> some seqItem <* free rParen) | parens = Seq <$> (free lParen *> some seqItem <* free rParen) | ||||||
| 
 | 
 | ||||||
| list = do | list = do | ||||||
|   lBracket |   free lBracket | ||||||
|   choice |   choice | ||||||
|     [ List [] Nothing <$ free rBracket |     [ List [] Nothing <$ free rBracket | ||||||
|     , do items <- some seqItem |     , do items <- some seqItem | ||||||
|  | @ -115,7 +110,7 @@ list = do | ||||||
|            ] |            ] | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
| seqItem = free $ choice [try call, literal, parens, list] | seqItem = choice [try call, literal, parens, list] | ||||||
| 
 | 
 | ||||||
| simpleTok :: String -> Parser () | simpleTok :: String -> Parser () | ||||||
| simpleTok s = void $ single (Tok s) | simpleTok s = void $ single (Tok s) | ||||||
|  | @ -133,10 +128,156 @@ listTail = simpleTok "|" | ||||||
| rBracket = simpleTok "]" | rBracket = simpleTok "]" | ||||||
| 
 | 
 | ||||||
| clause :: Parser AST | clause :: Parser AST | ||||||
| clause = Seq <$> some seqItem <* free comma | clause = Seq <$> some (free seqItem) <* free comma | ||||||
| 
 | 
 | ||||||
| parsePrlg :: Parser [AST] | parsePrlg :: Parser [AST] | ||||||
| parsePrlg = many clause <* free eof | parsePrlg = ws *> many clause <* eof | ||||||
| 
 | 
 | ||||||
| operatorize :: [AST] -> [PrlgStr] | data Op = | ||||||
| operatorize = undefined |   Op Int Fixity | ||||||
|  |   deriving (Show, Eq) | ||||||
|  | 
 | ||||||
|  | data ArgKind | ||||||
|  |   = X | ||||||
|  |   | Y | ||||||
|  |   deriving (Show, Eq) | ||||||
|  | 
 | ||||||
|  | data Fixity | ||||||
|  |   = Infix ArgKind ArgKind | ||||||
|  |   | Prefix ArgKind | ||||||
|  |   | Suffix ArgKind | ||||||
|  |   deriving (Show, Eq) | ||||||
|  | 
 | ||||||
|  | isPrefix (Prefix _) = True | ||||||
|  | isPrefix _ = False | ||||||
|  | 
 | ||||||
|  | numArgs :: Op -> Int | ||||||
|  | numArgs (Op _ f) = go f | ||||||
|  |   where | ||||||
|  |     go (Infix _ _) = 2 | ||||||
|  |     go _ = 1 | ||||||
|  | 
 | ||||||
|  | type Ops = [(String, Op)] | ||||||
|  | 
 | ||||||
|  | defaultOps :: Ops | ||||||
|  | defaultOps = | ||||||
|  |   [ ("", Op 0 $ Infix X Y) | ||||||
|  |   , ("+", Op 100 $ Prefix X) | ||||||
|  |   , ("!", Op 100 $ Suffix Y) | ||||||
|  |   , ("-", Op 100 $ Prefix Y) | ||||||
|  |   , ("*", Op 100 $ Infix Y X) | ||||||
|  |   , ("+", Op 200 $ Infix Y X) | ||||||
|  |   , ("++", Op 200 $ Infix X Y) | ||||||
|  |   , ("-", Op 200 $ Infix Y X) | ||||||
|  |   , ("<", Op 300 $ Infix X X) | ||||||
|  |   , (">", Op 300 $ Infix X X) | ||||||
|  |   , ("=", Op 400 $ Infix X X) | ||||||
|  |   , (",", Op 800 $ Infix X Y) | ||||||
|  |   , (";", Op 900 $ Infix X Y) | ||||||
|  |   , (":-", Op 1000 $ Infix X X) | ||||||
|  |   ] | ||||||
|  | 
 | ||||||
|  | ast2prlg :: Ops -> AST -> PrlgStr | ||||||
|  | ast2prlg ot (List _ _) = error "no lists yet" | ||||||
|  | ast2prlg ot (Seq ss) = shunt ot ss | ||||||
|  | ast2prlg ot (Literal s) = LiteralS s | ||||||
|  | ast2prlg ot (Call fn ss) = CallS fn $ map (shunt ot) ss | ||||||
|  | 
 | ||||||
|  | shunt :: Ops -> [AST] -> PrlgStr | ||||||
|  | shunt optable = start | ||||||
|  |   where | ||||||
|  |     start :: [AST] -> PrlgStr | ||||||
|  |     start [x] = rec x --singleton, possibly either a single operator name or a single value | ||||||
|  |     start [] = error "wat seq" | ||||||
|  |     start xs = wo [] [] xs | ||||||
|  |     {- "want operand" state, incoming literal -} | ||||||
|  |     wo :: Ops -> [PrlgStr] -> [AST] -> PrlgStr | ||||||
|  |     wo ops vs (l@(Literal x):xs) | ||||||
|  |       | Just _ <- getPrefix x | ||||||
|  |       , Just (ops', vs') <- pushPrefix ops vs x = wo ops' vs' xs | ||||||
|  |       | isOperand x = ho ops (rec l : vs) xs | ||||||
|  |       | otherwise = error $ "want valid operand " ++ show (ops,vs,l,xs) | ||||||
|  |     {- incoming non-literal (i.e., surely operand), push it and switch to "have operand" -} | ||||||
|  |     wo ops vs (x:xs) = ho ops (rec x : vs) xs | ||||||
|  |     {- end of stream, but the operand is missing -} | ||||||
|  |     wo ops vs [] = error "missing final operand" | ||||||
|  |     {- "have operand" state, incoming operator -} | ||||||
|  |     ho :: Ops -> [PrlgStr] -> [AST] -> PrlgStr | ||||||
|  |     ho ops vs xs'@(Literal x:xs) | ||||||
|  |       | Just _ <- getSuffix x | ||||||
|  |       , Just (ops', vs') <- pushSuffix ops vs x = ho ops' vs' xs | ||||||
|  |       | Just _ <- getInfix x | ||||||
|  |       , Just (ops', vs') <- pushInfix ops vs x = wo ops' vs' xs | ||||||
|  |       | isOperand x = ho ops vs (Literal "" : xs') -- app (see below) | ||||||
|  |       | Just _ <- getPrefix x = ho ops vs (Literal "" : xs') -- also app! | ||||||
|  |       | otherwise = error "want valid infix" | ||||||
|  |     {- incoming operand; there's an app between -} | ||||||
|  |     ho ops vs xs@(_:_) = ho ops vs (Literal "" : xs) | ||||||
|  |     {- the last operand was last, pop until finished -} | ||||||
|  |     ho [] [res] [] = res | ||||||
|  |     ho ops vs [] = | ||||||
|  |       let (ops', vs') = pop ops vs | ||||||
|  |        in ho ops' vs' [] | ||||||
|  |     {- recurse to delimited subexpression -} | ||||||
|  |     rec :: AST -> PrlgStr | ||||||
|  |     rec = ast2prlg optable | ||||||
|  |     {- pop a level, possibly uncovering a higher prio -} | ||||||
|  |     pop ((x, Op _ (Infix _ _)):ops) (r:l:vs) = (ops, (CallS x [l, r] : vs)) | ||||||
|  |     pop ((x, Op _ (Prefix _)):ops) (p:vs) = (ops, (CallS x [p] : vs)) | ||||||
|  |     pop ((x, Op _ (Suffix _)):ops) (p:vs) = (ops, (CallS x [p] : vs)) | ||||||
|  |     pop _ _ = error "pop borked" | ||||||
|  |     {- Operator checks -} | ||||||
|  |     uniq [x] = Just x | ||||||
|  |     uniq _ = Nothing | ||||||
|  |     getPrefix x = uniq [op | (s, op@(Op _ (Prefix _))) <- optable, s == x] | ||||||
|  |     getSuffix x = uniq [op | (s, op@(Op _ (Suffix _))) <- optable, s == x] | ||||||
|  |     getInfix x = uniq [op | (s, op@(Op _ (Infix _ _))) <- optable, s == x] | ||||||
|  |     isOperand x = null [op | (s, op) <- optable, s == x] | ||||||
|  |     {- actual pushery -} | ||||||
|  |     canPush [] op = Just True | ||||||
|  |     canPush ((_, Op p f):ops) (Op np nf) = go p f np nf | ||||||
|  |       where | ||||||
|  |         {- helper -} | ||||||
|  |         prioLtOp X = (<) | ||||||
|  |         prioLtOp Y = (<=) | ||||||
|  |         {- pushing a prefix -} | ||||||
|  |         go prio (Infix _ l) nprio (Prefix _) = | ||||||
|  |           if prioLtOp l nprio prio | ||||||
|  |             then Just True | ||||||
|  |             else Nothing | ||||||
|  |         go prio (Prefix l) nprio (Prefix r) = | ||||||
|  |           if prioLtOp l nprio prio | ||||||
|  |             then Just True | ||||||
|  |             else Nothing | ||||||
|  |         go prio (Suffix l) nprio (Prefix r) = error "wat sufix" --not just a normal prio clash | ||||||
|  |         {- pushing a suffix -} | ||||||
|  |         go prio (Prefix l) nprio (Suffix r) = clash prio l nprio r | ||||||
|  |         go prio (Suffix _) nprio (Suffix r) = Just $ prioLtOp r nprio prio | ||||||
|  |         go prio (Infix _ l) nprio (Suffix r) = clash prio l nprio r | ||||||
|  |         {- pushing an infix -} | ||||||
|  |         go prio (Prefix l) nprio (Infix r _) = clash prio l nprio r | ||||||
|  |         go prio (Suffix _) nprio (Infix r _) = | ||||||
|  |           if prioLtOp r nprio prio | ||||||
|  |             then Nothing | ||||||
|  |             else Just False | ||||||
|  |         go prio (Infix _ l) nprio (Infix r _) = clash prio l nprio r | ||||||
|  |         {- helper for cases that look like: a `xfy` b `yfx` c -} | ||||||
|  |         clash p l np r | ||||||
|  |           | p < np = Just False | ||||||
|  |           | p > np = Just True | ||||||
|  |           | p == np | ||||||
|  |           , r == Y = Just False | ||||||
|  |           | p == np | ||||||
|  |           , l == Y | ||||||
|  |           , r == X = Just True | ||||||
|  |           | otherwise = Nothing | ||||||
|  |     {- actual shunting -} | ||||||
|  |     pushPrefix ops vs x = getPrefix x >>= shunt1 ops vs x | ||||||
|  |     pushSuffix ops vs x = getSuffix x >>= shunt1 ops vs x | ||||||
|  |     pushInfix ops vs x = getInfix x >>= shunt1 ops vs x | ||||||
|  |     shunt1 ops vs x op = do | ||||||
|  |       cp <- canPush ops op | ||||||
|  |       if cp | ||||||
|  |         then return ((x, op) : ops, vs) | ||||||
|  |         else let (ops', vs') = pop ops vs | ||||||
|  |               in shunt1 ops' vs' x op --prefix would behave differently here but that's impossible by canPush | ||||||
|  |  | ||||||
|  | @ -29,6 +29,6 @@ executable prlg | ||||||
| 
 | 
 | ||||||
|     -- LANGUAGE extensions used by modules in this package. |     -- LANGUAGE extensions used by modules in this package. | ||||||
|     -- other-extensions: |     -- other-extensions: | ||||||
|     build-depends:    base >=4.16, containers, megaparsec, haskeline, pretty-simple |     build-depends:    base >=4.16, containers, megaparsec, haskeline, pretty-simple, split | ||||||
|     hs-source-dirs:   app |     hs-source-dirs:   app | ||||||
|     default-language: Haskell2010 |     default-language: Haskell2010 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue