{-# LANGUAGE CPP #-} module Language.Go.Parser.Parser where import Language.Go.Parser.Tokens import Language.Go.Parser.Tokens (GoTokenPos(..)) import Language.Go.Parser.Lexer (alexScanTokens) import Language.Go.Syntax.AST import Control.Monad import Data.Maybe (isJust) import Text.Parsec.Prim import Text.Parsec.Error (ParseError) import Text.Parsec.Combinator {- | Tokenize Go Language source code TODO: * [ ] Semicolon insertion (currently ';' is required) * [ ] Unicode identifiers (currently '#[...]' can be used) -} goTokenize :: String -> [GoTokenPos] goTokenize s = filter whitespace (alexScanTokens s) where whitespace (GoTokenPos _ x) = nonnull x && nonsemi x && comment x comment x = (not (goTokenEq x (GoTokComment False ""))) nonsemi = (/= GoTokSemicolonAuto) nonnull = (/= GoTokNone) --goBrace --goParen --goBracket -- | Parse Go Language source code into AST goParse :: String -> String -> Either ParseError GoSource goParse filename s = goParseTokens filename $ goTokenize s -- | Parse Go Language token list into AST goParseTokens :: String -> [GoTokenPos] -> Either ParseError GoSource goParseTokens filename toks = parse goSource filename toks -- | Go @Source@ production goSource :: GoParser GoSource goSource = do pkg <- goSemi goPackageClause --imp <- many $ goSemi goTopLevelPrel top <- many $ goSemi goTopLevelDecl goSourceRest return $ GoSource pkg [] top goSourceRest :: GoParser () goSourceRest = do rest <- many anyToken case length rest of 0 -> return () _ -> fail "language-go: Source not consumed" -- | Go @PackageClase@ production goPackageClause :: GoParser GoId goPackageClause = do goTokPackage goIdentifier {- | Nonstandard @TopLevelPrel@ production This is not part of the standard, but is here to abstract away some of the details of possible extensions to the language. -} goTopLevelPrel :: GoParser GoPrel goTopLevelPrel = fail "" -- | Go @TopLevelDecl@ production goTopLevelDecl :: GoParser GoDecl goTopLevelDecl = goConstDecl <|> goTypeDecl <|> goVarDecl <|> goFunctionDecl <|> goMethodDecl {- goImportDecl :: GoParser GoImportDecl goImportDecl = do goTokImport goImportSpec goImportSpec -} -- tokens goTokenEq :: GoToken -> GoToken -> Bool goTokenEq (GoTokComment _ _) (GoTokComment _ _) = True goTokenEq (GoTokInteger _ _) (GoTokInteger _ _) = True goTokenEq (GoTokFloat _ _) (GoTokFloat _ _) = True goTokenEq (GoTokFloatI _ _) (GoTokFloatI _ _) = True goTokenEq (GoTokId _) (GoTokId _) = True goTokenEq (GoTokOp _) (GoTokOp _) = True goTokenEq a b = a == b goToken :: GoToken -> GoParser GoToken goToken tok = token showTok posnTok testTok where showTok (GoTokenPos pos t) = show t posnTok (GoTokenPos pos t) = pos testTok (GoTokenPos pos t) = if goTokenEq tok t then Just t else Nothing goIdentifier :: GoParser GoId goIdentifier = do GoTokId name <- goToken $ GoTokId "" return $ GoId name goOperator :: GoParser GoOp goOperator = do GoTokOp name <- goToken $ GoTokOp "" return $ GoOp name goTokLParen = try $ goToken $ GoTokLParen goTokRParen = try $ goToken $ GoTokRParen goTokLBrace = try $ goToken $ GoTokLBrace goTokRBrace = try $ goToken $ GoTokRBrace goTokLBracket = try $ goToken $ GoTokLBracket goTokRBracket = try $ goToken $ GoTokRBracket goTokSemicolon= try $ goToken $ GoTokSemicolon goTokColon = try $ goToken $ GoTokColon goTokComma = try $ goToken $ GoTokComma goTokFullStop = try $ goToken $ GoTokFullStop goTokAsterisk = try $ goToken $ GoTokAsterisk goTokElipsis = try $ goToken $ GoTokElipsis goTokDec = try $ goToken $ GoTokDec goTokInc = try $ goToken $ GoTokInc goTokBreak = try $ goToken $ GoTokBreak goTokCase = try $ goToken $ GoTokCase goTokChan = try $ goToken $ GoTokChan goTokConst = try $ goToken $ GoTokConst goTokContinue = try $ goToken $ GoTokContinue goTokDefault = try $ goToken $ GoTokDefault goTokDefer = try $ goToken $ GoTokDefer goTokElse = try $ goToken $ GoTokElse goTokFallthrough = try $ goToken $ GoTokFallthrough goTokFor = try $ goToken $ GoTokFor goTokFunc = try $ goToken $ GoTokFunc goTokGo = try $ goToken $ GoTokGo goTokGoto = try $ goToken $ GoTokGoto goTokIf = try $ goToken $ GoTokIf goTokImport = try $ goToken $ GoTokImport goTokInterface= try $ goToken $ GoTokInterface goTokMap = try $ goToken $ GoTokMap goTokPackage = try $ goToken $ GoTokPackage goTokRange = try $ goToken $ GoTokRange goTokReturn = try $ goToken $ GoTokReturn goTokSelect = try $ goToken $ GoTokSelect goTokStruct = try $ goToken $ GoTokStruct goTokSwitch = try $ goToken $ GoTokSwitch goTokType = try $ goToken $ GoTokType goTokVar = try $ goToken $ GoTokVar goTokBinaryOps = goOperator goTokAssignOps = goOperator goUnaryOp :: GoParser () goUnaryOp = return () goBinaryOp :: GoParser () goBinaryOp = return () --oneOf goTokBinaryOps goAssignOp :: GoParser () goAssignOp = return () --oneOf goTokAssignOps goTokArrowOp :: GoParser () goTokArrowOp = do op <- goOperator case op of GoOp "<-" -> return () _ -> fail "language-go: Expected '<-' token" goTokColonEq :: GoParser () goTokColonEq = do op <- goOperator case op of GoOp ":=" -> return () _ -> fail "language-go: Expected ':=' token" goTokEq :: GoParser () goTokEq = do op <- goOperator case op of GoOp "=" -> return () _ -> fail "language-go: Expected '=' token" -- combinators goAfter :: GoParser b -> GoParser a -> GoParser a goAfter y x = do z <- x ; y ; return z goSemi :: GoParser a -> GoParser a goSemi = goAfter goTokSemicolon -- literals goLitInteger :: GoParser GoLit goLitInteger = try $ do GoTokInteger s i <- goToken $ GoTokInteger Nothing 0 return $ GoLitInt i goLitFloat :: GoParser GoLit goLitFloat = try $ do GoTokFloat s f <- goToken $ GoTokFloat Nothing 0.0 return $ GoLitFloat f goLitFloatI :: GoParser GoLit goLitFloatI = try $ do GoTokFloatI s f <- goToken $ GoTokFloatI Nothing 0.0 return $ GoLitFloatI f goLitChar :: GoParser GoLit goLitChar = try $ do GoTokChar s c <- goToken $ GoTokChar Nothing ' ' return $ GoLitChar c goLitString :: GoParser GoLit goLitString = try $ do GoTokString s c <- goToken $ GoTokString Nothing "" return $ GoLitString c -- parsers goBuiltinCall = () goType = goTypeName <|> goTypeLit <|> goTypeParen goTypeParen = do goTokLParen t <- goType goTokRParen return t goTypeName :: GoParser GoType goTypeName = do (GoQual q n) <- goQualifiedIdent return $ GoTypeName q n goTypeLit :: GoParser GoType goTypeLit = goArrayType <|> goStructType <|> goPointerType <|> goFunctionType <|> goInterfaceType <|> goSliceType <|> goMapType <|> goChannelType goArrayLength = goExpression goArrayType :: GoParser GoType goArrayType = do goTokLBracket l <- goArrayLength goTokRBracket t <- goType return $ GoArrayType l t goSliceType :: GoParser GoType goSliceType = do goTokLBracket goTokRBracket t <- goType return $ GoSliceType t goStructType :: GoParser GoType goStructType = do goTokStruct goTokLBrace d <- many1 goFieldDecl goTokRBrace return $ GoStructType [] goFieldTag = goLitString goFieldDecl = return () -- do -- ids <- goIdentifierList -- t <- goType -- tag <- optional goFieldTag -- return (ids, t, tag) goPointerType :: GoParser GoType goPointerType = do goTokAsterisk t <- goType return $ GoPointerType t goFunctionType :: GoParser GoType goFunctionType = do goTokFunc s <- goSignature return $ GoFunctionType s goSignature :: GoParser GoSig goSignature = do par <- goParameters res <- option [] goResult return $ GoSig par res goResult :: GoParser [GoParam] goResult = goParameters <|> do ty <- goType ; return [GoParam Nothing ty] goParameters :: GoParser [GoParam] goParameters = do goTokLParen params <- option [] $ do ps <- goParameterList optional goTokComma return ps goTokRParen return params goParameterList :: GoParser [GoParam] goParameterList = sepBy1 goParameterDecl goTokComma >>= (return . concat) {- x <- goParameterDecl xs <- many1 (do goTokComma ; goParameterDecl) return (x ++ concat xs) -} goParameterDecl :: GoParser [GoParam] goParameterDecl = do is <- option [] goIdentifierList optional goTokElipsis t <- goType return $ flip map is (\i -> GoParam (Just i) t) goInterfaceType :: GoParser GoType goInterfaceType = do goTokInterface goTokLBrace many1 $ do -- goMethodSpec goTokSemicolon goTokRBrace return $ GoInterfaceType $ GoInterface $ GoId "" {- goMethodSpec = do goMethodName goSignature goInterfaceTypeName -- TODO ^FIXME^ -} --goInterfaceTypeName = goTypeName goMapType :: GoParser GoType goMapType = do goTokMap goTokLBracket ktype <- goType goTokRBracket etype <- goType return $ GoMapType ktype etype {- -- TODO: get this working goChannelQuip = do goTokArrowOp goTokChan <|> do goTokChan goTokArrowOp <|> do goTokChan -} goChannelQuip = goTokChan goChannelType = do goChannelQuip etype <- goType return etype goDeclaration :: GoParser GoDecl goDeclaration = goConstDecl <|> goTypeDecl <|> goVarDecl goStatement :: GoParser GoStmt goStatement = liftM GoDeclStmt goDeclaration -- 'Statement/Declaration' -- <|> liftM GoSimpleStmt goSimple -- 'Statement/SimpleStmt' -- <|> goLabeledStmt -- <|> goGoStmt <|> goReturnStmt <|> goBreakStmt <|> goContinueStmt -- <|> goGotoStmt -- <|> goFallthroughStmt -- <|> goBlockStmt -- <|> goIfStmt -- <|> goSwitchStmt -- <|> goSelectStmt -- <|> goForStmt -- <|> goDeferStmt goLabeledStmt :: GoParser GoStmt goLabeledStmt = do id <- goIdentifier goTokColon st <- goStatement return $ GoLabeledStmt id st goSimple :: GoParser GoSimp goSimple = liftM GoSimpExpr goExpression -- 'SimpleStmt/ExpressionStmt' <|> goIncDecStmt -- 'SimpleStmt/IncDecStmt' <|> goAssignment -- 'SimpleStmt/Assignment' <|> goShortVarDecl -- 'SimpleStmt/ShortVarDecl' goIncDecStmt :: GoParser GoSimp goIncDecStmt = try $ do ex <- goExpression op <- goOperator case op of GoOp "++" -> return $ GoSimpInc ex GoOp "--" -> return $ GoSimpDec ex _ -> fail "IncDecStmt What?" -- | Go @Assignment@ production goAssignment :: GoParser GoSimp goAssignment = try $ do lv <- goExpressionList op <- goOperator rv <- goExpressionList let failure = fail "Assignment What?" case op of GoOp opname -> if last opname == '=' then return $ GoSimpAsn lv op rv else failure -- _ -> failure -- | Go @ShortVarDecl@ production goShortVarDecl :: GoParser GoSimp goShortVarDecl = do ids <- goIdentifierList goTokColonEq exs <- goExpressionList return $ GoSimpVar ids exs goBlock :: GoParser GoBlock goBlock = liftM GoBlock $ between goTokLBrace goTokRBrace $ many $ goSemi goStatement -- <|> (try $ do goTokLBrace ; goTokRBrace ; return $ GoBlock []) goIdentifierList :: GoParser [GoId] goIdentifierList = sepBy1 goIdentifier goTokComma goExpressionList :: GoParser [GoExpr] goExpressionList = sepBy1 goExpression goTokComma -- goCVDecl' :: GoParser GoDecl goCVSpecs :: GoParser [GoCVSpec] goCVSpecs = goCVSpecs' <|> goCVSpecs'' where goCVSpecs' = do spec <- goCVSpec return $ [spec] goCVSpecs'' = do goTokLParen specs <- many $ goSemi goCVSpec goTokRParen return $ specs goCVSpec :: GoParser GoCVSpec goCVSpec = do ids <- goIdentifierList goCVSpec'' ids <|> goCVSpec' ids where goCVSpec' :: [GoId] -> GoParser GoCVSpec goCVSpec' ids = do goTokEq exs <- goExpressionList return $ GoCVSpec ids Nothing exs goCVSpec'' :: [GoId] -> GoParser GoCVSpec goCVSpec'' ids = do typ <- goType goTokEq exs <- goExpressionList return $ GoCVSpec ids (Just typ) exs goConstDecl :: GoParser GoDecl goConstDecl = goTokConst >> goCVSpecs >>= (return . GoConst) goVarDecl :: GoParser GoDecl goVarDecl = goTokVar >> goCVSpecs >>= (return . GoVar) goTypeDecl :: GoParser GoDecl goTypeDecl = goTokType >> goTypeSpecs >>= (return . GoType) goTypeSpecs :: GoParser [GoTypeSpec] goTypeSpecs = goTypeDecl' <|> goTypeDecl'' where goTypeDecl' = do spec <- goTypeSpec return $ [spec] goTypeDecl'' = do goTokLParen specs <- many $ goSemi goTypeSpec goTokRParen return $ specs goTypeSpec :: GoParser GoTypeSpec goTypeSpec = do id <- goIdentifier ty <- goType return $ GoTypeSpec id ty goFunctionDecl :: GoParser GoDecl goFunctionDecl = try $ do goTokFunc id <- goIdentifier sg <- goSignature bk <- option GoNoBlock goBlock return $ GoFunc $ GoFuncDecl id sg bk goMethodDecl :: GoParser GoDecl goMethodDecl = try $ do goTokFunc rc <- goReceiver id <- goIdentifier sg <- goSignature bk <- option GoNoBlock goBlock return $ GoMeth $ GoMethDecl rc id sg bk goReceiver :: GoParser GoRec goReceiver = between goTokLParen goTokRParen recspec where recspec = do id <- optionMaybe goIdentifier pt <- optionMaybe goOperator -- Asterisk ty <- goTypeName return $ GoRec (isJust pt) id ty goQualifiedIdent :: GoParser GoPrim goQualifiedIdent = do qual <- option [] $ try $ do id <- goIdentifier fs <- goOperator case fs of GoOp "." -> return [id] _ -> fail "QualifiedIdent What?" name <- goIdentifier return $ GoQual qual name goOperand :: GoParser GoPrim goOperand = goLiteralPrim <|> goQualifiedIdent -- <|> goMethodExpr -- <|> (Expression) -- not encoded -- TODO: consider changing Prim/Lit relation goLiteralPrim :: GoParser GoPrim goLiteralPrim = do (GoTokenPos _ tok) <- lookAhead anyToken liftM (flip GoLiteral (from tok)) goLiteral where from (GoTokInteger (Just s) _) = s from (GoTokFloat (Just s) _) = s from (GoTokFloatI (Just s) _) = s from (GoTokString (Just s) _) = s from (GoTokChar (Just s) _) = s from _ = "" goLiteral :: GoParser GoLit goLiteral = goBasicLit -- <|> goCompositeLit -- <|> goFunctionLit goBasicLit :: GoParser GoLit goBasicLit = goLitInteger <|> goLitFloat <|> goLitFloatI <|> goLitChar <|> goLitString goCompositeLit = do goLiteralType goLiteralValue goArrayElipsisType = do goTokLBracket goTokElipsis goTokRBracket goType -- only in TypeLit: -- Pointer -- InterfaceType -- ChannelType -- FunctionType (available as FunctionLit) -- only in LiteralType: -- goArrayElipsisType goLiteralType :: GoParser GoType goLiteralType = goStructType -- <|> goArrayType -- <|> goArrayElipsisType -- <|> goSliceType -- <|> goMapType <|> goTypeName goLiteralValue :: GoParser GoComp goLiteralValue = do goTokLBrace optional $ do goElementList optional goTokComma goTokRBrace return $ GoComp [] goElementList = do goElement many1 $ do goTokComma goElement goElement :: GoParser GoElement goElement = do key <- goElementKey val <- goValue return $ GoElement key val goElementKey :: GoParser GoKey goElementKey = option GoKeyNone keyed where keyed = do k <- goKey goTokColon return k goKey :: GoParser GoKey goKey = liftM GoKeyField goIdentifier -- FieldName <|> liftM GoKeyIndex goExpression -- ElementIndex goValue :: GoParser GoValue goValue = liftM GoValueExpr goExpression <|> liftM GoValueComp goLiteralValue goFunctionLit :: GoParser GoFuncExpr goFunctionLit = do sg <- goSignature bk <- goBlock return $ GoFuncExpr sg bk goExpression :: GoParser GoExpr goExpression = goPrimaryExpr <|> goUnaryExpr <|> goBinaryExpr goPrimaryExpr :: GoParser GoExpr goPrimaryExpr = liftM GoPrim goPrimaryPrim goPrimaryPrim :: GoParser GoPrim goPrimaryPrim = goOperand -- <|> goConversion -- <|> goBuiltinCall -- <|> do goPrimaryExpr ; goSelector -- <|> do goPrimaryExpr ; goIndex -- <|> do goPrimaryExpr ; goSlice -- <|> do goPrimaryExpr ; goTypeAssertion -- <|> do goPrimaryExpr ; goCall goUnaryExpr :: GoParser GoExpr goUnaryExpr = do goUnaryOp ; goUnaryExpr goBinaryExpr :: GoParser GoExpr goBinaryExpr = do goExpression ; goBinaryOp ; goUnaryExpr goSelector :: GoParser GoId goSelector = do goTokFullStop ; goIdentifier goIndex = do goTokLBracket ex <- goExpression goTokRBracket return ex goSlice :: GoParser GoPrim goSlice = fail "" goTypeAssertion :: GoParser GoPrim goTypeAssertion = fail "" goCall :: GoParser GoPrim goCall = fail "" -- p <- goPrimaryPrim goArgumentList :: GoParser [GoExpr] goArgumentList = return [] goMethodExpr = () goReceiverType = goTypeName -- <|> goReceiverParen goConversion :: GoParser () goConversion = do goType goTokLParen goExpression goTokRParen return () {- goIfStmt = () goSwitchStmt = goExprSwitchStmt <|> goTypeSwitchStmt goExprSwitchStmt = () goTypeSwitchStmt = () goTypeSwitchGuard = () goTypeCaseClause = () goTypeSwitchCase = () goTypeList = () -- goForStmt -- goCondition -- goForClause -- goInitStmt -- goPostStmt -- goRangeClause goGoStmt = do goTokGo goExpression -} goReturnStmt = do goTokReturn rv <- option [] goExpressionList return $ GoReturnStmt rv goBreakStmt = do goTokBreak id <- optionMaybe goIdentifier return $ GoBreakStmt id goContinueStmt = do goTokContinue id <- optionMaybe goIdentifier return $ GoContinueStmt id