| @ -0,0 +1,14 @@ | |||
| .stack-work | |||
| # tools | |||
| /repl.sh | |||
| /.ghci | |||
| # grammar reference | |||
| /Haskell Grammar.txt | |||
| # test files | |||
| /*.hs | |||
| # except for this one | |||
| !/Setup.hs | |||
| @ -0,0 +1,30 @@ | |||
| Copyright Abigail Magalhães (c) 2021 | |||
| All rights reserved. | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, are permitted provided that the following conditions are met: | |||
| * Redistributions of source code must retain the above copyright | |||
| notice, this list of conditions and the following disclaimer. | |||
| * Redistributions in binary form must reproduce the above | |||
| copyright notice, this list of conditions and the following | |||
| disclaimer in the documentation and/or other materials provided | |||
| with the distribution. | |||
| * Neither the name of Abigail Magalhães nor the names of other | |||
| contributors may be used to endorse or promote products derived | |||
| from this software without specific prior written permission. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| @ -0,0 +1 @@ | |||
| # ahc | |||
| @ -0,0 +1,2 @@ | |||
| import Distribution.Simple | |||
| main = defaultMain | |||
| @ -0,0 +1,35 @@ | |||
| name: ahc | |||
| version: 0.1.0.0 | |||
| homepage: https://github.com/plt-hokusai/ahc#readme | |||
| license: BSD3 | |||
| license-file: LICENSE | |||
| author: Abigail Magalhães | |||
| maintainer: [email protected] | |||
| copyright: 2021 Abigail Magalhães | |||
| category: Web | |||
| build-type: Simple | |||
| cabal-version: >=2.0 | |||
| extra-source-files: README.md | |||
| executable ahc | |||
| hs-source-dirs: src | |||
| main-is: Main.hs | |||
| default-language: Haskell2010 | |||
| build-depends: base ^>= 4.14 | |||
| , mtl ^>= 2.2 | |||
| , syb ^>= 0.7 | |||
| , text ^>= 1.2 | |||
| , array ^>= 0.5 | |||
| , containers ^>= 0.6 | |||
| , bytestring ^>= 0.10 | |||
| , pretty-show ^>= 1.10 | |||
| other-modules: | |||
| Frontend.Autogen.Lexer, | |||
| Frontend.Autogen.Parser, | |||
| Frontend.Lexer.Tokens, | |||
| Frontend.Parser.Posn, | |||
| Frontend.Syntax | |||
| build-tool-depends: alex:alex >= 3.2.4 && < 4.0 | |||
| , happy:happy >= 1.19.12 && < 2.0 | |||
| @ -0,0 +1,10 @@ | |||
| cradle: | |||
| multi: | |||
| - path: "./src/Frontend/Autogen" | |||
| config: | |||
| cradle: | |||
| none: | |||
| - path: "./" | |||
| config: | |||
| cradle: | |||
| stack: | |||
| @ -0,0 +1,373 @@ | |||
| { | |||
| module Frontend.Autogen.Lexer where | |||
| import qualified Data.ByteString.Lazy as Lbs | |||
| import qualified Data.Text.Encoding as T | |||
| import qualified Data.Text as T | |||
| import Frontend.Lexer.Tokens | |||
| import Control.Monad | |||
| import Debug.Trace | |||
| } | |||
| %wrapper "monadUserState-bytestring" | |||
| $alpha = [a-zA-Z] | |||
| $digit = [0-9] | |||
| $white_nol = $white # \n | |||
| tokens :- | |||
| $white_nol+ ; | |||
| <0,module_header> "--" .* \n | |||
| { just $ pushStartCode newline } | |||
| <0,module_header,import_> | |||
| $alpha [$alpha $digit \_ \']* { variableOrKeyword } | |||
| <0> \= { always TokEqual } | |||
| <0> \: \: { always TokDoubleColon } | |||
| <0> \\ { \i l -> setPendingLC *> yield' (const TokLambda) i l } | |||
| <0> "->" { always TokArrow } | |||
| <0> "_" { always TokUnder } | |||
| <0> \{ { always TokOBrace } | |||
| <0> \[ { always TokOSquare } | |||
| <0,module_header,import_> { | |||
| \, { always TokComma } | |||
| \( { always TokOParen } | |||
| \) { always TokCParen } | |||
| } | |||
| <0> \} { closeBrace } | |||
| <0> \] { always TokCSquare } | |||
| <0> \;+ { always TokSemi } | |||
| <0> \n { just $ pushStartCode newline } | |||
| <0> \" { just startString } | |||
| <string> { | |||
| \\ \" { stringSeg (T.singleton '"') } | |||
| \\ \\ { stringSeg (T.singleton '\\') } | |||
| \\ a { stringSeg (T.singleton '\a') } | |||
| \\ b { stringSeg (T.singleton '\b') } | |||
| \\ f { stringSeg (T.singleton '\f') } | |||
| \\ n { stringSeg (T.singleton '\n') } | |||
| \\ \n { stringSeg (T.singleton '\n') } | |||
| \\ r { stringSeg (T.singleton '\r') } | |||
| \\ v { stringSeg (T.singleton '\v') } | |||
| \\ t { stringSeg (T.singleton '\t') } | |||
| \" { endString } | |||
| [^\\\"]+ { stringChar } | |||
| } | |||
| <0,newline,comment,import_,module_header> | |||
| "{-" { just $ pushStartCode comment } | |||
| <comment> { | |||
| "-}" { \i l -> popStartCode *> skip i l } | |||
| . ; | |||
| } | |||
| -- newline: emit a semicolon when de-denting | |||
| <newline> { | |||
| \n ; | |||
| "--" .* \n ; | |||
| () { offsideRule } | |||
| } | |||
| -- layout: indentation of the next token is context for offside rule | |||
| <layout> { | |||
| \n ; | |||
| "--" .* \n ; | |||
| \{ { openBrace } | |||
| () { startLayout } | |||
| } | |||
| <import_> { | |||
| \n { just $ pushStartCode newline } | |||
| "--" .* \n { just $ pushStartCode newline } | |||
| } | |||
| <empty_layout> () { emptyLayout } | |||
| <pending> () { emitPendingToken } | |||
| <module_header> { | |||
| \n ; | |||
| } | |||
| { | |||
| alexEOF :: Alex Token | |||
| alexEOF = do | |||
| (AlexPn _ l c, _, _, _) <- alexGetInput | |||
| maybePopImportSC | |||
| state <- getUserState | |||
| unless (T.null (stringBuffer state)) $ do | |||
| alexError $ "Unterminated string literal at line " ++ show l ++ ", column " ++ show c | |||
| case layoutColumns state of | |||
| -- EOF is allowed to close as many layout contexts as there are | |||
| -- pending (number of pending layout contexts is the length of the | |||
| -- list minus one, since there's the one initial layout context.) | |||
| _:tail -> do | |||
| mapUserState $ \s -> | |||
| s { pendingTokens = (Token TokLEnd l c <$ tail) ++ [Token TokEof l c] | |||
| , layoutColumns = [] | |||
| } | |||
| pushStartCode pending | |||
| pure (Token TokLEnd l c) | |||
| _ -> pure $ Token TokEof l c | |||
| yield k inp i = clearPendingLC *> yield' k inp i | |||
| yield' k (AlexPn _ l c, _, s, _) i = do | |||
| pure (Token (k $! (T.decodeUtf8 (Lbs.toStrict (Lbs.take i s)))) l c) | |||
| setPendingLC = mapUserState $ \s -> s { pendingLambdaCase = True } | |||
| clearPendingLC = mapUserState $ \s -> s { pendingLambdaCase = False } | |||
| always :: TokenClass -> AlexInput -> Int64 -> Alex Token | |||
| always k x i = yield (const k) x i | |||
| startString = do | |||
| mapUserState $ \s -> s { stringBuffer = T.empty } | |||
| pushStartCode string | |||
| endString (AlexPn _ l c, _, _, _) _i = do | |||
| text <- stringBuffer <$> getUserState | |||
| mapUserState $ \s -> s { stringBuffer = T.empty } | |||
| popStartCode | |||
| pure (Token (TokString text) l c) | |||
| stringChar input@(AlexPn _ _ _, _, buf, _) i = do | |||
| mapUserState $ \s -> s { stringBuffer = stringBuffer s <> T.decodeUtf8 (Lbs.toStrict (Lbs.take i buf)) } | |||
| alexMonadScan | |||
| stringSeg text _ _ = do | |||
| mapUserState $ \s -> s { stringBuffer = stringBuffer s <> text } | |||
| alexMonadScan | |||
| data LayoutState | |||
| = LetLayout { layoutCol :: Int } | |||
| | Layout { layoutCol :: Int } | |||
| | ModLayout { layoutCol :: Int } | |||
| deriving (Show) | |||
| data AlexUserState = | |||
| AlexUserState { layoutColumns :: [LayoutState] | |||
| , startCodes :: [Int] | |||
| , leastColumn :: Int | |||
| , pendingLayoutKw :: Maybe (Int -> LayoutState) | |||
| , pendingTokens :: [Token] | |||
| , pendingLambdaCase :: Bool | |||
| , haveModuleHeader :: Bool | |||
| , stringBuffer :: T.Text | |||
| } | |||
| alexInitUserState = AlexUserState [] [] 0 Nothing [] False False T.empty | |||
| emitPendingToken :: AlexAction Token | |||
| emitPendingToken _ _ = do | |||
| t <- getUserState | |||
| case pendingTokens t of | |||
| [] -> do | |||
| popStartCode | |||
| alexMonadScan | |||
| (x:xs) -> do | |||
| mapUserState $ \s -> s { pendingTokens = xs } | |||
| pure x | |||
| delayToken :: Token -> Alex () | |||
| delayToken t = do | |||
| mapUserState $ \s -> s { pendingTokens = t:pendingTokens s } | |||
| pushStartCode pending | |||
| just :: Alex a -> AlexAction Token | |||
| just k _ _ = k *> alexMonadScan | |||
| getUserState :: Alex AlexUserState | |||
| getUserState = Alex $ \s -> Right (s, alex_ust s) | |||
| mapUserState :: (AlexUserState -> AlexUserState) -> Alex () | |||
| mapUserState k = Alex $ \s -> Right (s { alex_ust = k $! alex_ust s }, ()) | |||
| pushStartCode :: Int -> Alex () | |||
| pushStartCode c = do | |||
| sc <- alexGetStartCode | |||
| mapUserState $ \s -> s { startCodes = sc:startCodes s } | |||
| alexSetStartCode c | |||
| popStartCode :: Alex () | |||
| popStartCode = do | |||
| sc <- startCodes <$> getUserState | |||
| case sc of | |||
| [] -> alexSetStartCode 0 | |||
| (x:xs) -> do | |||
| mapUserState $ \s -> s { startCodes = xs } | |||
| alexSetStartCode x | |||
| offsideRule :: AlexInput -> Int64 -> Alex Token | |||
| offsideRule (AlexPn _ line col, _, s, _) _ = do | |||
| ~(col':ctx) <- layoutColumns <$> getUserState | |||
| case col `compare` layoutCol col' of | |||
| EQ -> do | |||
| popStartCode | |||
| maybePopImportSC | |||
| pure (Token TokSemi line col) | |||
| GT -> do | |||
| popStartCode | |||
| alexMonadScan | |||
| LT -> do | |||
| mapUserState $ \s -> s { layoutColumns = ctx } | |||
| pure (Token TokLEnd line col) | |||
| maybePopImportSC :: Alex () | |||
| maybePopImportSC = do | |||
| startcode <- alexGetStartCode | |||
| when (startcode == import_) popStartCode | |||
| emptyLayout :: AlexInput -> Int64 -> Alex Token | |||
| emptyLayout (AlexPn _ line col, _, _, _) _ = do | |||
| popStartCode | |||
| pushStartCode newline | |||
| pure (Token TokLEnd line col) | |||
| startLayout :: AlexInput -> Int64 -> Alex Token | |||
| startLayout (AlexPn _ line col, _, _, _) _ = do | |||
| state <- getUserState | |||
| popStartCode | |||
| let | |||
| col' = | |||
| case layoutColumns state of | |||
| [] -> 0 | |||
| (x:_) -> layoutCol x | |||
| layoutKind = case pendingLayoutKw state of | |||
| Just s -> s | |||
| Nothing -> Layout | |||
| if col < col' | |||
| then pushStartCode empty_layout | |||
| else mapUserState $ \s -> s { layoutColumns = layoutKind col:layoutColumns s } | |||
| pure (Token TokLStart line col) | |||
| getLayout :: Alex LayoutState | |||
| getLayout = do | |||
| t <- getUserState | |||
| case layoutColumns t of | |||
| (x:_) -> pure x | |||
| _ -> error "No layout?" | |||
| openBrace :: AlexInput -> Int64 -> Alex Token | |||
| openBrace (AlexPn _ line col, _, _, _) _ = do | |||
| popStartCode | |||
| mapUserState $ \s -> s { layoutColumns = Layout minBound:layoutColumns s } | |||
| pure (Token TokOBrace line col) | |||
| popLayoutContext :: Alex () | |||
| popLayoutContext = mapUserState $ \s -> s { layoutColumns = tail (layoutColumns s) } | |||
| closeBrace :: AlexInput -> Int64 -> Alex Token | |||
| closeBrace (AlexPn _ line col, _, _, _) _ = do | |||
| ~(col':_) <- layoutColumns <$> getUserState | |||
| if layoutCol col' < 0 | |||
| then popLayoutContext | |||
| else pure () | |||
| pure (Token TokCBrace line col) | |||
| variableOrKeyword :: AlexAction Token | |||
| variableOrKeyword (AlexPn _ l c, _, s, _) size = do | |||
| sc <- alexGetStartCode | |||
| state <- getUserState | |||
| clearPendingLC | |||
| let | |||
| text = T.decodeUtf8 (Lbs.toStrict (Lbs.take size s)) | |||
| col = layoutCol (head (layoutColumns state)) | |||
| case T.unpack text of | |||
| "as" | |||
| | sc == import_, c > col -> pure (Token TokAs l c) | |||
| | sc == import_ -> offsideKeyword (TokVar text) l c | |||
| | otherwise -> pure (Token (TokVar text) l c) | |||
| "qualified" | |||
| | sc == import_, c > col -> pure (Token TokQualified l c) | |||
| | sc == import_ -> offsideKeyword (TokVar text) l c | |||
| | otherwise -> pure (Token (TokVar text) l c) | |||
| "let" -> laidOut' (Just LetLayout) TokLet l c | |||
| "in" -> do | |||
| laidout <- getLayout | |||
| case laidout of | |||
| -- let .. in critical pair: | |||
| -- 'in' is allowed to close a layout context before the offside rule would apply. | |||
| LetLayout _ -> earlyEnd TokIn l c | |||
| _ -> pure (Token TokIn l c) | |||
| "data" -> pure (Token TokData l c) | |||
| "where" -> do | |||
| -- if this is the where in the module_header, then | |||
| -- pop the start code so that the offside rule applies again | |||
| when (sc == module_header) popStartCode | |||
| laidOut' (if sc == module_header then Just ModLayout else Nothing) TokWhere l c | |||
| "case" | |||
| -- "case" is a layout token if it's immediately following a \\ | |||
| | pendingLambdaCase state -> laidOut TokCase l c | |||
| | otherwise -> pure (Token TokCase l c) | |||
| "import" -> do | |||
| pushStartCode import_ | |||
| pure (Token TokImport l c) | |||
| "of" -> laidOut TokOf l c | |||
| "module" -> do | |||
| unless (haveModuleHeader state) $ do | |||
| mapUserState $ \s -> s { haveModuleHeader = True } | |||
| pushStartCode module_header | |||
| pure (Token TokModule l c) | |||
| (x:_) | |||
| | Data.Char.isUpper x -> pure (Token (TokCon text) l c) | |||
| | otherwise -> pure (Token (TokVar text) l c) | |||
| [] -> error "empty keyword/identifier" | |||
| earlyEnd tok l c = do | |||
| popLayoutContext | |||
| delayToken (Token tok l c) | |||
| pure (Token TokLEnd l c) | |||
| offsideKeyword tok l c = do | |||
| popLayoutContext | |||
| delayToken (Token tok l c) | |||
| pure (Token TokSemi l c) | |||
| laidOut' n x l c = do | |||
| pushStartCode layout | |||
| mapUserState $ \s -> s { leastColumn = c, pendingLayoutKw = n } | |||
| pure (Token x l c) | |||
| laidOut = laidOut' Nothing | |||
| } | |||
| @ -0,0 +1,237 @@ | |||
| { | |||
| {-# LANGUAGE FlexibleContexts, FlexibleInstances, ViewPatterns #-} | |||
| module Frontend.Autogen.Parser where | |||
| import qualified Data.Text as T | |||
| import Data.Text (Text) | |||
| import Frontend.Lexer.Tokens | |||
| import Frontend.Parser.Posn | |||
| import Frontend.Syntax | |||
| import Frontend.Autogen.Lexer | |||
| import Prelude hiding (span) | |||
| import Debug.Trace | |||
| } | |||
| %name parseExp Exp | |||
| %name parseMod Module | |||
| %name parseType Type | |||
| %tokentype { Token } | |||
| %monad { Alex } | |||
| %lexer { lexer } { Token TokEof _ _ } | |||
| %errorhandlertype explist | |||
| %error { parseError } | |||
| %token | |||
| VAR { Token (TokVar _) _ _ } | |||
| CON { Token (TokCon _) _ _ } | |||
| STRING { Token (TokString _) _ _ } | |||
| 'eof' { Token TokEof _ _ } | |||
| '(' { Token TokOParen _ _ } | |||
| ')' { Token TokCParen _ _ } | |||
| '{' { Token TokOBrace _ _ } | |||
| '}' { Token TokCBrace _ _ } | |||
| START { Token TokLStart _ _ } | |||
| END { Token TokLEnd _ _ } | |||
| '[' { Token TokOSquare _ _ } | |||
| ']' { Token TokCSquare _ _ } | |||
| '{-#' { Token TokOPragma _ _ } | |||
| '#-}' { Token TokCPragma _ _ } | |||
| '\\' { Token TokLambda _ _ } | |||
| '->' { Token TokArrow _ _ } | |||
| '_' { Token TokUnder _ _ } | |||
| '::' { Token TokDoubleColon _ _ } | |||
| ';' { Token TokSemi _ _ } | |||
| '=' { Token TokEqual _ _ } | |||
| ',' { Token TokComma _ _ } | |||
| 'let' { Token TokLet _ _ } | |||
| 'in' { Token TokIn _ _ } | |||
| 'data' { Token TokData _ _ } | |||
| 'case' { Token TokCase _ _ } | |||
| 'module' { Token TokModule _ _ } | |||
| 'where' { Token TokWhere _ _ } | |||
| 'import' { Token TokImport _ _ } | |||
| 'as' { Token TokAs _ _ } | |||
| 'qualified' { Token TokQualified _ _ } | |||
| %% | |||
| Exp :: { Exp } | |||
| : InfixExp '::' Type { span $1 $3 $ Annot $1 $3 } | |||
| | InfixExp { $1 } | |||
| InfixExp :: { Exp } | |||
| : LeftExp {- ... -} { $1 } | |||
| -- | LeftExp qop InfixExp { Infix $1 (getVar $2) $3 } | |||
| LeftExp :: { Exp } | |||
| : '\\' Apat List(Apat) '->' Exp { span $1 $5 (makeLams ($2:$3) $5) } | |||
| | 'let' LaidOutList(Decl) 'in' Exp { span $1 $4 $ Let (thd $2) $4 } | |||
| | FuncExp { $1 } | |||
| FuncExp :: { Exp } | |||
| : FuncExp Aexp { span $1 $2 $ App $1 $2 } | |||
| | Aexp { $1 } | |||
| Aexp :: { Exp } | |||
| : qvar { span $1 $1 $ Ref (getVar $1) } | |||
| | qcon { span $1 $1 $ Con (getVar $1) } | |||
| | '(' CommaList(Exp) ')' { span $1 $3 $ makeTuple $2 } | |||
| | STRING { span $1 $1 $ Literal (LitString (getString $1)) } | |||
| Type :: { Type } | |||
| : Btype '->' Type { span $1 $3 $ Tyarr $1 $3 } | |||
| | Btype { $1 } | |||
| Btype :: { Type } | |||
| : Btype Atype { span $1 $2 $ Tyapp $1 $2 } | |||
| | Atype { $1 } | |||
| Atype :: { Type } | |||
| : qvar { span $1 $1 $ Tyvar (getVar $1) } | |||
| | qcon { span $1 $1 $ Tycon (getVar $1) } | |||
| | '(' CommaList(Type) ')' { span $1 $3 $ makeTupleType $2 } | |||
| Pat :: { Pat } | |||
| : Lpat { $1 } | |||
| Lpat :: { Pat } | |||
| : Apat { $1 } | |||
| Apat :: { Pat } | |||
| : VAR { span $1 $1 $ Var (getVar $1) } | |||
| | '_' { span $1 $1 $ Wildcard } | |||
| | '(' CommaList(Pat) ')' { span $1 $3 $ makeTuplePattern $2 } | |||
| Decl :: { Decl } | |||
| : VAR '::' Type { TySig (getVar $1) $3 } | |||
| | VAR Apat List(Apat) Rhs { FunDecl (getVar $1) ($2:$3) $4 } | |||
| | Pat Rhs { PatDecl $1 $2 } | |||
| Rhs :: { Rhs } | |||
| : '=' Exp { BareRhs $2 [] } | |||
| | '=' Exp 'where' LaidOutList(Decl) { BareRhs $2 (thd $4) } | |||
| LaidOutList(p) | |||
| : START Opt(Semis) LOLContents(p, END) { (startPosn $1, lolEnd $3, lolList $3) } | |||
| | '{' Opt(Semis) LOLContents(p, '}') { (startPosn $1, lolEnd $3, lolList $3) } | |||
| LOLContents(p, End) | |||
| : p Semis LOLContents(p,End) { lolCons $1 $3 } | |||
| | p Opt(Semis) End { lolCons $1 (emptyLol $3) } | |||
| | Opt(Semis) End { emptyLol $2 } | |||
| Module :: { Module } | |||
| : 'module' CON ImportExportList 'where' LaidOutList(ModItem) | |||
| { Module { moduleName = getVar $2 | |||
| , moduleExports = $3 | |||
| , moduleItems = thd $5 } | |||
| } | |||
| ImportExportList :: { Maybe [NamespacedItem Text] } | |||
| : {-empty-} { Nothing } | |||
| | '(' CommaList(NSItem) ')' { Just $2 } | |||
| NSItem :: { NamespacedItem Text } | |||
| : VAR { IEVar (getVar $1) } | |||
| | CON { IECon (getVar $1) } | |||
| | 'module' CON { IEModule (getVar $2) } | |||
| ModItem :: { Item } | |||
| : Decl { ModDecl $1 } | |||
| | Import { ModImport $1 } | |||
| Import | |||
| : 'import' qcon ImportExportList | |||
| { Import (getVar $2) $3 False Nothing } | |||
| | 'import' qcon ImportExportList 'as' CON | |||
| { Import (getVar $2) $3 False (Just (getVar $5)) } | |||
| | 'import' 'qualified' qcon ImportExportList | |||
| { Import (getVar $3) $4 True Nothing } | |||
| | 'import' 'qualified' qcon ImportExportList 'as' CON | |||
| { Import (getVar $3) $4 True (Just (getVar $6)) } | |||
| Opt(p) | |||
| : { () } | |||
| | p { () } | |||
| Semis | |||
| : ';' Semis { () } | |||
| | ';' { () } | |||
| -- TODO: qualified names | |||
| qvar : VAR { $1 } | |||
| qcon : CON { $1 } | |||
| List(p) | |||
| : {-empty-} { [] } | |||
| | p List(p) { $1:$2 } | |||
| CommaList(p) | |||
| : {-empty-} { [] } | |||
| | p { [$1] } | |||
| | p ',' CommaList(p) { $1:$3 } | |||
| Block(p) | |||
| : START p END { (startPosn $1, endPosn $3, $2) } | |||
| | '{' p '}' { (startPosn $1, endPosn $3, $2) } | |||
| { | |||
| type Exp = FeExpr Text | |||
| type Pat = FePat Text | |||
| type Decl = FeDecl Text | |||
| type Type = FeType Text | |||
| type Rhs = FeRhs Text | |||
| type Module = FeModule Text | |||
| type Item = ModuleItem Text | |||
| lexer cont = alexMonadScan >>= cont | |||
| parseError x = alexError (show x) | |||
| makeLams xs b = foldr Lam b xs | |||
| getVar (Token (TokVar s) _ _) = s | |||
| getVar (Token (TokCon s) _ _) = s | |||
| getVar _ = error "getVar non-var" | |||
| getString (Token (TokString s) _ _) = s | |||
| getString _ = error "getString non-string" | |||
| data LOL a = LOL { lolEnd :: Posn, lolList :: [a] } | |||
| emptyLol :: HasPosn x => x -> LOL a | |||
| emptyLol t = LOL (endPosn t) [] | |||
| lolCons :: a -> LOL a -> LOL a | |||
| lolCons x (LOL p xs) = LOL p (x:xs) | |||
| makeTupleType [x] = ParenType x | |||
| makeTupleType xs = Tytup xs | |||
| makeTuplePattern [x] = ParenPat x | |||
| makeTuplePattern xs = TupPat xs | |||
| makeTuple [x] = ParenExp x | |||
| makeTuple xs = Tuple xs | |||
| } | |||
| @ -0,0 +1,90 @@ | |||
| module Frontend.Lexer.Tokens where | |||
| import qualified Data.Text as T | |||
| import Data.Text (Text) | |||
| data TokenClass | |||
| = TokVar Text | |||
| | TokCon Text | |||
| | TokString Text | |||
| | TokEof | |||
| | TokLambda | |||
| | TokArrow | |||
| | TokUnder | |||
| | TokOParen | |||
| | TokOBrace | |||
| | TokOSquare | |||
| | TokOPragma | |||
| | TokCParen | |||
| | TokCBrace | |||
| | TokCSquare | |||
| | TokCPragma | |||
| | TokDoubleColon | |||
| | TokEqual | |||
| | TokComma | |||
| | TokPipe | |||
| | TokLet | |||
| | TokIn | |||
| | TokLStart | |||
| | TokLEnd | |||
| | TokModule | |||
| | TokImport | |||
| | TokQualified | |||
| | TokAs | |||
| | TokWhere | |||
| | TokLambdaCase | |||
| | TokCase | |||
| | TokOf | |||
| | TokData | |||
| | TokSemi | |||
| deriving (Eq, Show, Ord) | |||
| tokSize :: TokenClass -> Int | |||
| tokSize (TokVar x) = T.length x | |||
| tokSize (TokCon x) = T.length x | |||
| tokSize TokEof = 0 | |||
| tokSize TokLambda = 1 | |||
| tokSize TokOParen = 1 | |||
| tokSize TokOBrace = 1 | |||
| tokSize TokOSquare = 1 | |||
| tokSize TokOPragma = 3 | |||
| tokSize TokCBrace = 1 | |||
| tokSize TokCParen = 1 | |||
| tokSize TokCSquare = 1 | |||
| tokSize TokCPragma = 3 | |||
| tokSize TokDoubleColon = 2 | |||
| tokSize TokEqual = 1 | |||
| tokSize TokComma = 1 | |||
| tokSize TokSemi = 1 | |||
| tokSize TokUnder = 1 | |||
| tokSize TokArrow = 2 | |||
| tokSize TokIn = 2 | |||
| tokSize TokLStart = 0 | |||
| tokSize TokLEnd = 0 | |||
| tokSize TokPipe = 1 | |||
| tokSize (TokString t) = 2 + T.length t | |||
| tokSize TokLambdaCase = length "\\case" | |||
| tokSize TokWhere = length "where" | |||
| tokSize TokData = length "data" | |||
| tokSize TokOf = length "of" | |||
| tokSize TokCase = length "case" | |||
| tokSize TokModule = length "module" | |||
| tokSize TokQualified = length "qualified" | |||
| tokSize TokImport = length "import" | |||
| tokSize TokLet = 3 | |||
| tokSize TokAs = 2 | |||
| data Token | |||
| = Token { tokenClass :: TokenClass | |||
| , tokStartLine :: !Int | |||
| , tokStartCol :: !Int | |||
| } | |||
| deriving (Eq, Show, Ord) | |||
| @ -0,0 +1,33 @@ | |||
| {-# LANGUAGE FlexibleInstances #-} | |||
| {-# LANGUAGE DefaultSignatures #-} | |||
| module Frontend.Parser.Posn where | |||
| import Frontend.Lexer.Tokens | |||
| import Data.Typeable | |||
| data Posn | |||
| = Posn { posnLine :: {-# UNPACK #-} !Int | |||
| , posnColm :: {-# UNPACK #-} !Int | |||
| } | |||
| deriving (Eq, Show, Ord) | |||
| class HasPosn a where | |||
| startPosn :: a -> Posn | |||
| endPosn :: a -> Posn | |||
| span :: (HasPosn b, HasPosn c) => b -> c -> a -> a | |||
| default span :: Typeable a => b -> c -> a -> a | |||
| span _ _ x = error $ "Can't span " ++ show (typeOf x) | |||
| instance HasPosn Token where | |||
| startPosn (Token _ l c) = Posn l c | |||
| endPosn (Token t l c) = Posn l (c + tokSize t) | |||
| instance HasPosn (Posn, Posn, a) where | |||
| startPosn (s, _, _) = s | |||
| endPosn (_, e, _) = e | |||
| span start end (_, _, x) = (startPosn start, endPosn end, x) | |||
| thd :: (a, b, c) -> c | |||
| thd (_, _, z) = z | |||
| @ -0,0 +1,112 @@ | |||
| module Frontend.Syntax where | |||
| import Frontend.Parser.Posn | |||
| import qualified Data.Text as T | |||
| data FeExpr var | |||
| = Ref var | |||
| | Con var | |||
| | App (FeExpr var) (FeExpr var) | |||
| | Lam (FePat var) (FeExpr var) | |||
| | Let [FeDecl var] (FeExpr var) | |||
| | Tuple [FeExpr var] | |||
| | Annot (FeExpr var) (FeType var) | |||
| | Literal Literal | |||
| | ParenExp (FeExpr var) | |||
| | SPExpr (FeExpr var) Posn Posn | |||
| deriving (Eq, Show, Ord) | |||
| instance HasPosn (FeExpr var) where | |||
| startPosn (SPExpr _ s _) = s | |||
| startPosn _ = error "no start posn in parsed expression?" | |||
| endPosn (SPExpr _ _ e) = e | |||
| endPosn _ = error "no end posn in parsed expression?" | |||
| span sp ep (SPExpr x _ _) = SPExpr x (startPosn sp) (endPosn ep) | |||
| span sp ep x = SPExpr x (startPosn sp) (endPosn ep) | |||
| data FePat var | |||
| = Var var | |||
| | Wildcard | |||
| | TupPat [FePat var] | |||
| | LitPat Literal | |||
| | ParenPat (FePat var) -- parsed parentheses | |||
| | SPPat (FePat var) Posn Posn | |||
| deriving (Eq, Show, Ord) | |||
| instance HasPosn (FePat var) where | |||
| startPosn (SPPat _ s _) = s | |||
| startPosn _ = error "no start posn in parsed expression?" | |||
| endPosn (SPPat _ _ e) = e | |||
| endPosn _ = error "no end posn in parsed pattern?" | |||
| span sp ep (SPPat x _ _) = SPPat x (startPosn sp) (endPosn ep) | |||
| span sp ep x = SPPat x (startPosn sp) (endPosn ep) | |||
| data FeType var | |||
| = Tyvar var | |||
| | Tycon var | |||
| | Tyapp (FeType var) (FeType var) | |||
| | Tyarr (FeType var) (FeType var) | |||
| | Tytup [FeType var] | |||
| | ParenType (FeType var) -- parsed parentheses | |||
| | SPType (FeType var) Posn Posn | |||
| deriving (Eq, Show, Ord) | |||
| instance HasPosn (FeType var) where | |||
| startPosn (SPType _ s _) = s | |||
| startPosn _ = error "no start posn in parsed type?" | |||
| endPosn (SPType _ _ e) = e | |||
| endPosn _ = error "no end posn in parsed type?" | |||
| span sp ep (SPType x _ _) = SPType x (startPosn sp) (endPosn ep) | |||
| span sp ep x = SPType x (startPosn sp) (endPosn ep) | |||
| data FeDecl var | |||
| = PatDecl (FePat var) (FeRhs var) | |||
| | FunDecl var [FePat var] (FeRhs var) | |||
| | TySig var (FeType var) | |||
| deriving (Eq, Show, Ord) | |||
| data FeRhs var | |||
| = BareRhs (FeExpr var) [FeDecl var] | |||
| deriving (Eq, Show, Ord) | |||
| data Literal | |||
| = LitString T.Text | |||
| | LitNumber Integer | |||
| deriving (Eq, Show, Ord) | |||
| data FeModule var | |||
| = Module { moduleName :: var | |||
| , moduleExports :: Maybe [NamespacedItem var] | |||
| , moduleItems :: [ModuleItem var] | |||
| } | |||
| deriving (Eq, Show, Ord) | |||
| data ModuleImport var | |||
| = Import { importMod :: var | |||
| , importList :: Maybe [NamespacedItem var] | |||
| , importQualified :: Bool | |||
| , importAlias :: Maybe var | |||
| } | |||
| deriving (Eq, Show, Ord) | |||
| data NamespacedItem var | |||
| = IEVar var | |||
| | IECon var | |||
| | IEModule var | |||
| deriving (Eq, Show, Ord) | |||
| data ModuleItem var | |||
| = ModDecl (FeDecl var) | |||
| | ModImport (ModuleImport var) | |||
| deriving (Eq, Show, Ord) | |||
| @ -0,0 +1,51 @@ | |||
| -- {-# LANGUAGE BlockArguments #-} | |||
| module Main where | |||
| import qualified Data.ByteString.Lazy as Lbs | |||
| import Data.Foldable | |||
| import Frontend.Lexer.Tokens | |||
| import Frontend.Autogen.Lexer | |||
| import Frontend.Autogen.Parser | |||
| import qualified Data.Text.Encoding as T | |||
| import qualified Data.Text as T | |||
| import Control.Monad ( unless ) | |||
| import System.Environment (getArgs) | |||
| import Text.Show.Pretty (pPrint) | |||
| import Debug.Trace | |||
| main :: IO () | |||
| main = do | |||
| args <- getArgs | |||
| for_ args $ \str -> do | |||
| Main.lex pPrint parseMod =<< Lbs.readFile str | |||
| testParse :: String -> IO () | |||
| testParse s = Main.lex print parseMod (Lbs.fromStrict (T.encodeUtf8 (T.pack s))) | |||
| testLex :: String -> IO () | |||
| testLex s = Main.lex (const (pure ())) (scan []) (Lbs.fromStrict (T.encodeUtf8 (T.pack s))) | |||
| lex :: (a -> IO ()) -> Alex a -> Lbs.ByteString -> IO () | |||
| lex show cont arg = do | |||
| let x = runAlex arg cont | |||
| case x of | |||
| Left e -> print e | |||
| Right x -> show x | |||
| scan :: [Token] -> Alex [Token] | |||
| scan acc = do | |||
| tok <- alexMonadScan | |||
| sc <- alexGetStartCode | |||
| state <- getUserState | |||
| traceM . unlines $ | |||
| [ "----------------" | |||
| , "just lexed: " ++ show tok | |||
| , "sc: " ++ show sc | |||
| , "sc stack: " ++ show (startCodes state) | |||
| ] | |||
| case tokenClass tok of | |||
| TokEof -> pure (reverse acc) | |||
| _ -> scan (tok:acc) | |||
| @ -0,0 +1,67 @@ | |||
| # This file was automatically generated by 'stack init' | |||
| # | |||
| # Some commonly used options have been documented as comments in this file. | |||
| # For advanced use and comprehensive documentation of the format, please see: | |||
| # https://docs.haskellstack.org/en/stable/yaml_configuration/ | |||
| # Resolver to choose a 'specific' stackage snapshot or a compiler version. | |||
| # A snapshot resolver dictates the compiler version and the set of packages | |||
| # to be used for project dependencies. For example: | |||
| # | |||
| # resolver: lts-3.5 | |||
| # resolver: nightly-2015-09-21 | |||
| # resolver: ghc-7.10.2 | |||
| # | |||
| # The location of a snapshot can be provided as a file or url. Stack assumes | |||
| # a snapshot provided as a file might change, whereas a url resource does not. | |||
| # | |||
| # resolver: ./custom-snapshot.yaml | |||
| # resolver: https://example.com/snapshots/2018-01-01.yaml | |||
| resolver: | |||
| url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/3.yaml | |||
| # User packages to be built. | |||
| # Various formats can be used as shown in the example below. | |||
| # | |||
| # packages: | |||
| # - some-directory | |||
| # - https://example.com/foo/bar/baz-0.0.2.tar.gz | |||
| # subdirs: | |||
| # - auto-update | |||
| # - wai | |||
| packages: | |||
| - . | |||
| # Dependency packages to be pulled from upstream that are not in the resolver. | |||
| # These entries can reference officially published versions as well as | |||
| # forks / in-progress versions pinned to a git hash. For example: | |||
| # | |||
| # extra-deps: | |||
| # - acme-missiles-0.3 | |||
| # - git: https://github.com/commercialhaskell/stack.git | |||
| # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a | |||
| # | |||
| # extra-deps: [] | |||
| # Override default flag values for local packages and extra-deps | |||
| # flags: {} | |||
| # Extra package databases containing global packages | |||
| # extra-package-dbs: [] | |||
| # Control whether we use the GHC we find on the path | |||
| # system-ghc: true | |||
| # | |||
| # Require a specific version of stack, using version ranges | |||
| # require-stack-version: -any # Default | |||
| # require-stack-version: ">=2.7" | |||
| # | |||
| # Override the architecture used by stack, especially useful on Windows | |||
| # arch: i386 | |||
| # arch: x86_64 | |||
| # | |||
| # Extra directories used by stack for building | |||
| # extra-include-dirs: [/path/to/dir] | |||
| # extra-lib-dirs: [/path/to/dir] | |||
| # | |||
| # Allow a newer minor version of GHC than the snapshot specifies | |||
| # compiler-check: newer-minor | |||
| @ -0,0 +1,13 @@ | |||
| # This file was autogenerated by Stack. | |||
| # You should not edit this file by hand. | |||
| # For more information, please see the documentation at: | |||
| # https://docs.haskellstack.org/en/stable/lock_files | |||
| packages: [] | |||
| snapshots: | |||
| - completed: | |||
| size: 585603 | |||
| url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/3.yaml | |||
| sha256: 694573e96dca34db5636edb1fe6c96bb233ca0f9fb96c1ead1671cdfa9bd73e9 | |||
| original: | |||
| url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/3.yaml | |||