{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings #-} module Data.VRML.Parser ( Parser (..) , parseVRML ) where import Data.VRML.Types import GHC.Generics import Data.Int import Data.Void import Control.Monad (void) import Data.Char (isSpace) import Data.Text hiding (empty) import Text.Megaparsec import Text.Megaparsec.Char as C import Text.Megaparsec.Char.Lexer as L import Data.Text.Prettyprint.Doc import Data.Text.Prettyprint.Doc.Render.Text (putDoc) type Parser = Parsec Void String sc :: Parser () sc = L.space space1 (L.skipLineComment "#") empty lexm :: Parser a -> Parser a lexm = L.lexeme sc --space' = void $ takeWhileP (Just "white space") $ \t -> do space' :: Parser String space' = some $ oneOf [' ', '\t'] space'' :: Parser String space'' = many $ oneOf [' ', '\t'] -- | parser of VRML -- -- >>> parseTest parseVRML "#VRML_SIM R2020a utf8\nUSE hoge1" -- VRML {version = "VRML_SIM R2020a utf8", statements = [StNode (USE (NodeNameId "hoge1"))]} parseVRML :: Parser VRML parseVRML = do version <- string "#" >> manyTill anySingle eol values <- some parseStatement return $ VRML version values parseStatement :: Parser Statement parseStatement = (StRoute <$> parseRoute) <|> (StNode <$> parseNodeStatement) <|> (StProto <$> parseProtoStatement) -- | parser of Node -- -- >>> parseTest parseNodeStatement "hoge {}" -- NodeStatement (Node "hoge" []) -- >>> parseTest parseNodeStatement "DEF hoge1 hoge {}" -- DEF (NodeNameId "hoge1") (Node "hoge" []) -- >>> parseTest parseNodeStatement "USE hoge1" -- USE (NodeNameId "hoge1") parseNodeStatement :: Parser NodeStatement parseNodeStatement = (DEF <$> (lstring "DEF" >> parseNodeNameId) <*> parseNode) <|> (USE <$> (lstring "USE" >> parseNodeNameId)) <|> (NodeStatement <$> parseNode) -- | parser of Proto -- -- >>> parseTest parseProtoStatement "PROTO Cube [] { Box {} }" -- Proto "Cube" [] [] (Node "Box" []) [] parseProtoStatement :: Parser ProtoStatement parseProtoStatement = (id (Proto <$> (lstring "PROTO" >> parseNodeTypeId) <*> (lstring "[" >> many parseInterface <* lstring "]") <*> (lstring "{" >> many parseProtoStatement) <*> parseNode <*> (many parseStatement <* lstring "}"))) <|> (id (ExternProto <$> (lstring "EXTERNPROTO" >> parseNodeTypeId) <*> (lstring "[" >> many parseExternInterface <* lstring "]") <*> parseURLList)) parseRestrictedInterface :: Parser RestrictedInterface parseRestrictedInterface = ( RestrictedInterfaceEventIn <$> (lstring "eventIn" >> parseFieldType) <*> parseEventInId) <|> ( RestrictedInterfaceEventOut <$> (lstring "eventOut" >> parseFieldType) <*> parseEventOutId) <|> ( RestrictedInterfaceField <$> (lstring "field" >> parseFieldType) <*> parseFieldId <*> parseFieldValue) parseInterface :: Parser Interface parseInterface = ( InterfaceEventIn <$> (lstring "eventIn" >> parseFieldType) <*> parseEventInId) <|> ( InterfaceEventOut <$> (lstring "eventOut" >> parseFieldType) <*> parseEventOutId) <|> ( InterfaceField <$> (lstring "field" >> parseFieldType) <*> parseFieldId <*> parseFieldValue) <|> ( InterfaceExposedField <$> (lstring "exposedField" >> parseFieldType) <*> parseFieldId <*> parseFieldValue) parseExternInterface :: Parser ExternInterface parseExternInterface = ( ExternInterfaceEventIn <$> (lstring "eventIn" >> parseFieldType) <*> parseEventInId) <|> ( ExternInterfaceEventOut <$> (lstring "eventOut" >> parseFieldType) <*> parseEventOutId) <|> ( ExternInterfaceField <$> (lstring "field" >> parseFieldType) <*> parseFieldId) <|> ( ExternInterfaceExposedField <$> (lstring "exposedField" >> parseFieldType) <*> parseFieldId) -- | parser of Route -- -- >>> parseTest parseRoute "ROUTE hoge.hoge TO hoge.hoge" -- Route (NodeNameId "hoge") (EventOutId "hoge") (NodeNameId "hoge") (EventInId "hoge") parseRoute :: Parser Route parseRoute = Route <$> (lstring "ROUTE" >> parseNodeNameId) <*> (lstring "." >> parseEventOutId) <*> (lstring "TO" >> parseNodeNameId) <*> (lstring "." >> parseEventInId) parseURLList :: Parser URLList parseURLList = ((\v -> URLList [v]) <$> stringLiteral) <|> (URLList <$> (lstring "[" >> many stringLiteral <* lstring "]")) -- | parser of Node -- -- >>> parseTest parseNode "hoge {hoge 1 hoge 2}" -- Node "hoge" [FV "hoge" (Sfloat 1.0),FV "hoge" (Sfloat 2.0)] -- >>> parseTest parseNode "BmwX5 { translation -78.7 0.4 7.53 }" -- Node "BmwX5" [FV "translation" (Svec3f (-78.7,0.4,7.53))] -- >>> parseTest parseNode "BmwX5 { rotation 0 1 0 1.5708}" -- Node "BmwX5" [FV "rotation" (Srotation (0.0,1.0,0.0,1.5708))] -- >>> parseTest parseNode "BmwX5 { controller \"autonomous_vehicle\" }" -- Node "BmwX5" [FV "controller" (Sstring "autonomous_vehicle")] -- >>> parseTest parseNode "BmwX5 { translation -78.7 0.4 7.53 rotation 0 1 0 1.5708 controller \"autonomous_vehicle\" }" -- Node "BmwX5" [FV "translation" (Svec3f (-78.7,0.4,7.53)),FV "rotation" (Srotation (0.0,1.0,0.0,1.5708)),FV "controller" (Sstring "autonomous_vehicle")] -- >>> parseTest parseNode "Script {}" -- Script [] -- >>> parseTest parseNode "Script { }" -- Script [] parseNode :: Parser Node parseNode = do nid <- parseNodeTypeId case nid of (NodeTypeId "Script") -> do _ <- lstring "{" nbody <- many parseScriptBodyElement _ <- lstring "}" return $ Script nbody _ -> do _ <- lstring "{" nbody <- many parseNodeBodyElement _ <- lstring "}" return $ Node nid nbody parseScriptBodyElement :: Parser ScriptBodyElement parseScriptBodyElement = (SBEventIn <$> (lstring "eventIn" >> parseFieldType) <*> parseEventInId <*> (lstring "IS" >> parseEventInId) ) <|> (SBEventOut <$> (lstring "eventOut" >> parseFieldType) <*> parseEventOutId <*> (lstring "IS" >> parseEventOutId) ) <|> (SBFieldId <$> (lstring "field" >> parseFieldType) <*> parseFieldId <*> (lstring "IS" >> parseFieldId)) <|> (SBRestrictedInterface <$> parseRestrictedInterface) <|> (SBNode <$> parseNodeBodyElement) -- >>> parseTest parseBodyElement "maxPosition 1e4 1e4" -- FV "maxPosition" (Svec2f (1e4,1e4)) -- >>> parseTest parseBodyElement "width IS width" -- NBFieldId "width" "width" parseNodeBodyElement :: Parser NodeBodyElement parseNodeBodyElement = (NBRoute <$> parseRoute) <|> (NBProto <$> parseProtoStatement) <|> (try $ (NBFieldId <$> parseFieldId <*> (lstring "IS" >> parseFieldId) )) <|> (try $ (NBEventIn <$> parseEventInId <*> (lstring "IS" >> parseEventInId) )) <|> (try $ (NBEventOut <$> parseEventOutId <*> (lstring "IS" >> parseEventOutId) )) <|> (FV <$> parseFieldId <*> parseFieldValue) parseNodeNameId :: Parser NodeNameId parseNodeNameId = NodeNameId <$> identifier parseNodeTypeId :: Parser NodeTypeId parseNodeTypeId = NodeTypeId <$> identifier parseFieldId :: Parser FieldId parseFieldId = FieldId <$> identifier parseEventInId :: Parser EventInId parseEventInId = EventInId <$> identifier parseEventOutId :: Parser EventOutId parseEventOutId = EventOutId <$> identifier rws :: [String] rws = ["PROTO","DEF","USE"] -- | parser of Id -- -- >>> parseTest identifier "hogehoge" -- "hogehoge" identifier :: Parser String identifier = (lexm . try) (p >>= check) where p = (:) <$> (oneOf identStart) <*> many (oneOf identLetter) check x = if x `elem` rws then fail $ "keyword " ++ show x ++ " cannot be an identifier" else return x identStart :: [Char] identStart = ['a'..'z'] ++ ['A'..'Z'] ++ ['_'] identLetter :: [Char] identLetter = ['a'..'z'] ++ ['A'..'Z'] ++ ['_'] ++ ['0'..'9'] ++ [':', '<', '>'] lstring = lexm.string -- | parser of FieldType -- -- >>> parseTest parseFieldType "MFColor" -- MFColor -- >>> parseTest parseFieldType "MFString" -- MFString -- >>> parseTest parseFieldType "SFColor" -- SFColor -- >>> parseTest parseFieldType "MFColor " -- MFColor parseFieldType :: Parser FieldType parseFieldType = (lstring "MFBool" >> pure MFBool) <|> (lstring "MFColor" >> pure MFColor) <|> (lstring "MFFloat" >> pure MFFloat) <|> (lstring "MFString" >> pure MFString) <|> (lstring "MFTime" >> pure MFTime) <|> (lstring "MFVec2f" >> pure MFVec2f) <|> (lstring "MFVec3f" >> pure MFVec3f) <|> (lstring "MFNode" >> pure MFNode) <|> (lstring "MFRotation" >> pure MFRotation) <|> (lstring "MFInt32" >> pure MFInt32) <|> (lstring "SFBool" >> pure SFBool) <|> (lstring "SFColor" >> pure SFColor) <|> (lstring "SFFloat" >> pure SFFloat) <|> (lstring "SFImage" >> pure SFImage) <|> (lstring "SFInt32" >> pure SFInt32) <|> (lstring "SFNode" >> pure SFNode) <|> (lstring "SFRotation" >> pure SFRotation) <|> (lstring "SFString" >> pure SFString) <|> (lstring "SFTime" >> pure SFTime) <|> (lstring "SFVec2f" >> pure SFVec2f) <|> (lstring "SFVec3f" >> pure SFVec3f) parseFloat :: Parser Float parseFloat =realToFrac <$> lexm pfloat parseFloat' :: Parser Float parseFloat' =realToFrac <$> pfloat parseInt :: Parser Int32 parseInt = fromIntegral <$> lexm pinteger -- | parser of FieldType -- -- >>> parseTest tupleParser "1e4 1e4" -- (10000.0,10000.0) tupleParser :: Parser (Float,Float) tupleParser = (,) <$> parseFloat <*> parseFloat -- | parser of FieldType -- -- >>> parseTest parseFieldValue "TRUE" -- Sbool True -- >>> parseTest parseFieldValue "FALSE" -- Sbool False -- >>> parseTest parseFieldValue "NULL" -- Snode Nothing -- >>> parseTest parseFieldValue "\"hoge\\\"hoge\"" -- Sstring "hoge\"hoge" -- >>> parseTest parseFieldValue "\"autonomous_vehicle\"" -- Sstring "autonomous_vehicle" -- >>> parseTest parseFieldValue "1e4 1e4" -- Svec2f (10000.0,10000.0) -- >>> parseTest parseFieldValue "[1e4 1e4 1e4,1e4 1e4 1e4]" -- Mvec3f [(10000.0,10000.0,10000.0),(10000.0,10000.0,10000.0)] -- >>> parseTest parseFieldValue "[\n1e4 1e4\n1e4 1e4\n]" -- Mvec2f [(10000.0,10000.0),(10000.0,10000.0)] -- >>> parseTest parseFieldValue "[\n1e4 1e4 1e4\n1e4 1e4 1e4\n]" -- Mvec3f [(10000.0,10000.0,10000.0),(10000.0,10000.0,10000.0)] parseFieldValue :: Parser FieldValue parseFieldValue = (Sbool <$> parseBool) <|> (lstring "NULL" >> pure (Snode Nothing)) <|> (try $ Mrotation <$> parseArrayN ((,,,) <$> parseFloat' <*> (space'' >> parseFloat') <*> (space'' >> parseFloat') <*> (space'' >> parseFloat'))) <|> (try $ Mvec3f <$> parseArrayN ((,,) <$> parseFloat' <*> (space'' >> parseFloat') <*> (space'' >> parseFloat'))) <|> (try $ Mvec2f <$> parseArrayN ((,) <$> parseFloat' <*> (space'' >> parseFloat'))) <|> (try $ Mfloat <$> parseArrayN parseFloat') <|> (try $ Mbool <$> parseArray' parseBool) <|> (try $ Mnode <$> parseArray' parseNodeStatement) <|> (try $ Mstring <$> parseArray' stringLiteral) <|> (try $ Mrotation <$> parseArray ((,,,) <$> parseFloat <*> parseFloat <*> parseFloat <*> parseFloat)) <|> (try $ Mvec3f <$> parseArray ((,,) <$> parseFloat <*> parseFloat <*> parseFloat)) <|> (try $ Mvec2f <$> parseArray ((,) <$> parseFloat <*> parseFloat)) <|> (try $ Mfloat <$> parseArray parseFloat) <|> (try $ Mstring <$> parseArray stringLiteral) <|> (try $ Mbool <$> parseArray parseBool) <|> (try $ (\a b c d -> Srotation (a,b,c,d)) <$> parseFloat <*> parseFloat <*> parseFloat <*> parseFloat) <|> (try $ (\a b c -> Svec3f (a,b,c)) <$> parseFloat <*> parseFloat <*> parseFloat) <|> (try $ (\a b -> Svec2f (a,b)) <$> parseFloat <*> parseFloat) <|> (try $ (Sfloat <$> parseFloat)) <|> (try $ (Sstring <$> stringLiteral)) <|> (try $ (Snode . Just <$> parseNodeStatement)) parseArray :: Parser a -> Parser [a] parseArray parser = do _ <- lstring "[" values <- parser `sepBy` lstring "," _ <- lstring "]" return values parseArrayN :: Parser a -> Parser [a] parseArrayN parser = do _ <- lstring "[" values <- some (try (space'' >> parser >>= \v -> space'' >> eol >> pure v)) _ <- space'' >> lstring "]" return values parseArray' :: Parser a -> Parser [a] parseArray' parser = do _ <- lstring "[" values <- many parser _ <- lstring "]" return values parseBool :: Parser Bool parseBool = (lstring "TRUE" >> pure True) <|> (lstring "FALSE" >> pure False) pinteger :: Parser Integer pinteger = (try L.hexadecimal) <|> (L.decimal) <|> ((string "-") >> (try L.hexadecimal <|> L.decimal) >>= \v -> pure (-v)) pfloat :: Parser Float pfloat = (realToFrac <$> L.scientific) <|> ((string "-") >> L.scientific >>= \v -> realToFrac <$> (pure (-v))) -- | parser of FieldType -- -- >>> parseTest stringLiteral "\"hoge\\\"hoge\"" -- "hoge\"hoge" -- >>> parseTest stringLiteral "\"autonomous_vehicle\"" -- "autonomous_vehicle" stringLiteral :: Parser String stringLiteral = lexm $ char '\"' *> manyTill charLiteral (char '\"')