| @ -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 | |||||