Current section

Files

Jump to
ragex priv parsers haskell Parser.hs
Raw

priv/parsers/haskell/Parser.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import GHC.Generics
import Language.Haskell.Exts
import System.Exit (exitFailure)
import System.IO (hPutStrLn, stderr)
-- | Response wrapper for JSON output
data Response = Response
{ status :: String,
ast :: Maybe Aeson.Value,
errorMessage :: Maybe String
}
deriving (Generic, Show)
instance Aeson.ToJSON Response where
toJSON = Aeson.genericToJSON Aeson.defaultOptions
-- | Parse Haskell source and output JSON AST
main :: IO ()
main = do
source <- TIO.getContents
let sourceStr = T.unpack source
-- Try parsing as module first, then as declaration, then as expression
case parseModule sourceStr of
ParseOk parsedModule -> do
let astJson = moduleToJson parsedModule
BL.putStrLn $
Aeson.encode $
Response
{ status = "ok",
ast = Just astJson,
errorMessage = Nothing
}
ParseFailed _ _ -> case parseDecl sourceStr of
ParseOk parsedDecl -> do
let astJson = declToJsonFull parsedDecl
BL.putStrLn $
Aeson.encode $
Response
{ status = "ok",
ast = Just astJson,
errorMessage = Nothing
}
ParseFailed _ _ -> case parseExp sourceStr of
ParseOk parsedExpr -> do
let astJson = exprToJson parsedExpr
BL.putStrLn $
Aeson.encode $
Response
{ status = "ok",
ast = Just astJson,
errorMessage = Nothing
}
ParseFailed loc err -> do
hPutStrLn stderr $ "Parse error at " ++ show loc ++ ": " ++ err
BL.putStrLn $
Aeson.encode $
Response
{ status = "error",
ast = Nothing,
errorMessage = Just $ "Parse error: " ++ err
}
exitFailure
-- | Convert Haskell expression to JSON
exprToJson :: Exp SrcSpanInfo -> Aeson.Value
exprToJson expr = case expr of
-- Literals
Lit _ lit -> Aeson.object
[ "type" Aeson..= ("literal" :: String)
, "value" Aeson..= literalToJson lit
]
-- Variables
Var _ qname -> Aeson.object
[ "type" Aeson..= ("var" :: String)
, "name" Aeson..= qnameToString qname
]
-- Constructor
Con _ qname -> Aeson.object
[ "type" Aeson..= ("con" :: String)
, "name" Aeson..= qnameToString qname
]
-- Application (function call)
App _ func arg -> Aeson.object
[ "type" Aeson..= ("app" :: String)
, "function" Aeson..= exprToJson func
, "argument" Aeson..= exprToJson arg
]
-- Infix application (operators)
InfixApp _ left op right -> Aeson.object
[ "type" Aeson..= ("infix" :: String)
, "left" Aeson..= exprToJson left
, "operator" Aeson..= qopToString op
, "right" Aeson..= exprToJson right
]
-- Lambda
Lambda _ pats body -> Aeson.object
[ "type" Aeson..= ("lambda" :: String)
, "patterns" Aeson..= map patToJson pats
, "body" Aeson..= exprToJson body
]
-- Let binding
Let _ binds body -> Aeson.object
[ "type" Aeson..= ("let" :: String)
, "bindings" Aeson..= bindsToJson binds
, "body" Aeson..= exprToJson body
]
-- If-then-else
If _ cond thenExp elseExp -> Aeson.object
[ "type" Aeson..= ("if" :: String)
, "condition" Aeson..= exprToJson cond
, "then" Aeson..= exprToJson thenExp
, "else" Aeson..= exprToJson elseExp
]
-- Case expression
Case _ scrut alts -> Aeson.object
[ "type" Aeson..= ("case" :: String)
, "scrutinee" Aeson..= exprToJson scrut
, "alternatives" Aeson..= map altToJson alts
]
-- List
List _ exprs -> Aeson.object
[ "type" Aeson..= ("list" :: String)
, "elements" Aeson..= map exprToJson exprs
]
-- Tuple
Tuple _ Boxed exprs -> Aeson.object
[ "type" Aeson..= ("tuple" :: String)
, "elements" Aeson..= map exprToJson exprs
]
-- Parenthesized expression
Paren _ expr' -> exprToJson expr'
-- List comprehension
ListComp _ expr quals -> Aeson.object
[ "type" Aeson..= ("list_comp" :: String)
, "expression" Aeson..= exprToJson expr
, "qualifiers" Aeson..= map qualStmtToJson quals
]
-- Do notation
Do _ stmts -> Aeson.object
[ "type" Aeson..= ("do" :: String)
, "statements" Aeson..= map stmtToJson stmts
]
-- Fallback for unsupported constructs
_ -> Aeson.object
[ "type" Aeson..= ("unsupported" :: String)
, "original" Aeson..= show expr
]
-- | Convert literal to JSON
literalToJson :: Literal SrcSpanInfo -> Aeson.Value
literalToJson lit = case lit of
Int _ i _ -> Aeson.object
[ "literalType" Aeson..= ("int" :: String)
, "value" Aeson..= i
]
Frac _ r _ -> Aeson.object
[ "literalType" Aeson..= ("float" :: String)
, "value" Aeson..= (fromRational r :: Double)
]
Char _ c _ -> Aeson.object
[ "literalType" Aeson..= ("char" :: String)
, "value" Aeson..= [c]
]
String _ s _ -> Aeson.object
[ "literalType" Aeson..= ("string" :: String)
, "value" Aeson..= s
]
_ -> Aeson.object
[ "literalType" Aeson..= ("other" :: String)
, "value" Aeson..= show lit
]
-- | Convert qualified name to string
qnameToString :: QName l -> String
qnameToString (Qual _ (ModuleName _ m) (Ident _ n)) = m ++ "." ++ n
qnameToString (Qual _ (ModuleName _ m) (Symbol _ s)) = m ++ "." ++ s
qnameToString (UnQual _ (Ident _ n)) = n
qnameToString (UnQual _ (Symbol _ s)) = s
qnameToString (Special _ (UnitCon _)) = "()"
qnameToString (Special _ (ListCon _)) = "[]"
qnameToString (Special _ (TupleCon _ Boxed n)) = replicate (n - 1) ','
qnameToString _ = "unknown"
-- | Convert qualified operator to string
qopToString :: QOp l -> String
qopToString (QVarOp _ qname) = qnameToString qname
qopToString (QConOp _ qname) = qnameToString qname
-- | Convert pattern to JSON
patToJson :: Pat SrcSpanInfo -> Aeson.Value
patToJson pat = case pat of
PVar _ (Ident _ n) -> Aeson.object
[ "type" Aeson..= ("var_pat" :: String)
, "name" Aeson..= n
]
PLit _ _ lit -> Aeson.object
[ "type" Aeson..= ("lit_pat" :: String)
, "literal" Aeson..= literalToJson lit
]
PWildCard _ -> Aeson.object
[ "type" Aeson..= ("wildcard" :: String)
]
_ -> Aeson.object
[ "type" Aeson..= ("unsupported_pat" :: String)
, "original" Aeson..= show pat
]
-- | Convert bindings to JSON
bindsToJson :: Binds SrcSpanInfo -> Aeson.Value
bindsToJson (BDecls _ decls) = Aeson.toJSON $ map declToJson decls
bindsToJson _ = Aeson.Null
-- | Convert module to JSON
moduleToJson :: Module SrcSpanInfo -> Aeson.Value
moduleToJson (Module _ _ _ _ decls) = Aeson.object
[ "type" Aeson..= ("module" :: String)
, "declarations" Aeson..= map declToJsonFull decls
]
moduleToJson _ = Aeson.object
[ "type" Aeson..= ("unsupported_module" :: String)
]
-- | Convert declaration to JSON (full version with type signatures)
declToJsonFull :: Decl SrcSpanInfo -> Aeson.Value
declToJsonFull decl = case decl of
-- Type signature
TypeSig _ names ty -> Aeson.object
[ "type" Aeson..= ("type_sig" :: String)
, "names" Aeson..= map nameToString names
, "signature" Aeson..= typeToJson ty
]
-- Data type declaration
DataDecl _ dataOrNew _ declHead qualConDecls _ -> Aeson.object
[ "type" Aeson..= ("data_decl" :: String)
, "data_or_new" Aeson..= dataOrNewToString dataOrNew
, "name" Aeson..= declHeadToString declHead
, "constructors" Aeson..= map qualConDeclToJson qualConDecls
]
-- Type class declaration
ClassDecl _ _ declHead _ classDecls -> Aeson.object
[ "type" Aeson..= ("class_decl" :: String)
, "name" Aeson..= declHeadToString declHead
, "methods" Aeson..= maybe [] (map classDeclToJson) classDecls
]
-- Instance declaration
InstDecl _ _ instRule instDecls -> Aeson.object
[ "type" Aeson..= ("instance_decl" :: String)
, "rule" Aeson..= instRuleToJson instRule
, "methods" Aeson..= maybe [] (map instDeclToJson) instDecls
]
-- Function binding
FunBind _ matches -> Aeson.object
[ "type" Aeson..= ("fun_bind" :: String)
, "matches" Aeson..= map matchToJson matches
]
-- Pattern binding
PatBind _ pat rhs _ -> Aeson.object
[ "type" Aeson..= ("pat_bind" :: String)
, "pattern" Aeson..= patToJson pat
, "rhs" Aeson..= rhsToJson rhs
]
-- Type alias
TypeDecl _ declHead ty -> Aeson.object
[ "type" Aeson..= ("type_alias" :: String)
, "name" Aeson..= declHeadToString declHead
, "definition" Aeson..= typeToJson ty
]
_ -> Aeson.object
[ "type" Aeson..= ("unsupported_decl" :: String)
, "original" Aeson..= show decl
]
-- | Convert declaration to JSON (simple version for bindings)
declToJson :: Decl SrcSpanInfo -> Aeson.Value
declToJson decl = case decl of
PatBind _ pat rhs _ -> Aeson.object
[ "type" Aeson..= ("pat_bind" :: String)
, "pattern" Aeson..= patToJson pat
, "rhs" Aeson..= rhsToJson rhs
]
_ -> Aeson.object
[ "type" Aeson..= ("unsupported_decl" :: String)
]
-- | Convert right-hand side to JSON
rhsToJson :: Rhs SrcSpanInfo -> Aeson.Value
rhsToJson (UnGuardedRhs _ expr) = exprToJson expr
rhsToJson _ = Aeson.Null
-- | Convert case alternative to JSON
altToJson :: Alt SrcSpanInfo -> Aeson.Value
altToJson (Alt _ pat rhs _) = Aeson.object
[ "pattern" Aeson..= patToJson pat
, "rhs" Aeson..= rhsToJson rhs
]
-- | Convert qualifier statement to JSON
qualStmtToJson :: QualStmt SrcSpanInfo -> Aeson.Value
qualStmtToJson (QualStmt _ stmt) = stmtToJson stmt
qualStmtToJson _ = Aeson.Null
-- | Convert statement to JSON
stmtToJson :: Stmt SrcSpanInfo -> Aeson.Value
stmtToJson stmt = case stmt of
Generator _ pat expr -> Aeson.object
[ "type" Aeson..= ("generator" :: String)
, "pattern" Aeson..= patToJson pat
, "expression" Aeson..= exprToJson expr
]
Qualifier _ expr -> Aeson.object
[ "type" Aeson..= ("qualifier" :: String)
, "expression" Aeson..= exprToJson expr
]
LetStmt _ binds -> Aeson.object
[ "type" Aeson..= ("let_stmt" :: String)
, "bindings" Aeson..= bindsToJson binds
]
_ -> Aeson.object
[ "type" Aeson..= ("unsupported_stmt" :: String)
]
-- | Convert type to JSON
typeToJson :: Type SrcSpanInfo -> Aeson.Value
typeToJson ty = case ty of
TyFun _ arg res -> Aeson.object
[ "type" Aeson..= ("type_fun" :: String)
, "argument" Aeson..= typeToJson arg
, "result" Aeson..= typeToJson res
]
TyTuple _ Boxed types -> Aeson.object
[ "type" Aeson..= ("type_tuple" :: String)
, "elements" Aeson..= map typeToJson types
]
TyList _ ty' -> Aeson.object
[ "type" Aeson..= ("type_list" :: String)
, "element" Aeson..= typeToJson ty'
]
TyApp _ t1 t2 -> Aeson.object
[ "type" Aeson..= ("type_app" :: String)
, "constructor" Aeson..= typeToJson t1
, "argument" Aeson..= typeToJson t2
]
TyVar _ name -> Aeson.object
[ "type" Aeson..= ("type_var" :: String)
, "name" Aeson..= nameToString name
]
TyCon _ qname -> Aeson.object
[ "type" Aeson..= ("type_con" :: String)
, "name" Aeson..= qnameToString qname
]
_ -> Aeson.object
[ "type" Aeson..= ("unsupported_type" :: String)
, "original" Aeson..= show ty
]
-- | Convert name to string
nameToString :: Name l -> String
nameToString (Ident _ n) = n
nameToString (Symbol _ s) = s
-- | Convert data or newtype to string
dataOrNewToString :: DataOrNew l -> String
dataOrNewToString (DataType _) = "data"
dataOrNewToString (NewType _) = "newtype"
-- | Convert declaration head to string
declHeadToString :: DeclHead l -> String
declHeadToString (DHead _ name) = nameToString name
declHeadToString (DHInfix _ _ name) = nameToString name
declHeadToString (DHParen _ dh) = declHeadToString dh
declHeadToString (DHApp _ dh _) = declHeadToString dh
-- | Convert qualified constructor declaration to JSON
qualConDeclToJson :: QualConDecl SrcSpanInfo -> Aeson.Value
qualConDeclToJson (QualConDecl _ _ _ conDecl) = conDeclToJson conDecl
-- | Convert constructor declaration to JSON
conDeclToJson :: ConDecl SrcSpanInfo -> Aeson.Value
conDeclToJson (ConDecl _ name types) = Aeson.object
[ "name" Aeson..= nameToString name
, "types" Aeson..= map typeToJson types
]
conDeclToJson (RecDecl _ name fields) = Aeson.object
[ "name" Aeson..= nameToString name
, "fields" Aeson..= map fieldDeclToJson fields
]
conDeclToJson _ = Aeson.object
[ "unsupported" Aeson..= True
]
-- | Convert field declaration to JSON
fieldDeclToJson :: FieldDecl SrcSpanInfo -> Aeson.Value
fieldDeclToJson (FieldDecl _ names ty) = Aeson.object
[ "names" Aeson..= map nameToString names
, "type" Aeson..= typeToJson ty
]
-- | Convert instance rule to JSON
instRuleToJson :: InstRule SrcSpanInfo -> Aeson.Value
instRuleToJson (IRule _ _ _ instHead) = instHeadToJson instHead
instRuleToJson _ = Aeson.Null
-- | Convert instance head to JSON
instHeadToJson :: InstHead SrcSpanInfo -> Aeson.Value
instHeadToJson (IHCon _ qname) = Aeson.object
[ "class" Aeson..= qnameToString qname
]
instHeadToJson (IHApp _ ih ty) = Aeson.object
[ "head" Aeson..= instHeadToJson ih
, "type" Aeson..= typeToJson ty
]
instHeadToJson _ = Aeson.Null
-- | Convert match to JSON
matchToJson :: Match SrcSpanInfo -> Aeson.Value
matchToJson (Match _ name pats rhs _) = Aeson.object
[ "name" Aeson..= nameToString name
, "patterns" Aeson..= map patToJson pats
, "rhs" Aeson..= rhsToJson rhs
]
matchToJson _ = Aeson.Null
-- | Convert class declaration to JSON
classDeclToJson :: ClassDecl SrcSpanInfo -> Aeson.Value
classDeclToJson (ClsDecl _ decl) = declToJsonFull decl
classDeclToJson _ = Aeson.Null
-- | Convert instance declaration to JSON
instDeclToJson :: InstDecl SrcSpanInfo -> Aeson.Value
instDeclToJson (InsDecl _ decl) = declToJsonFull decl
instDeclToJson _ = Aeson.Null