{- Copyright 2008 Debris This file is part of rcc. rcc is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. rcc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with rcc; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA $Id: rcc.hs,v 0.1 2008/07/04 08:16 debris $ -} -- Import main libraries. -- Environment, and the parsec monad. module Main where import Monad import Prelude import System import System.Console.Readline import System.Environment import Text.ParserCombinators.Parsec hiding (spaces) -- main application entry point. -- parse input expression on args. main :: IO () main = getArgs >>= putStrLn . show . rccEvaluate . rccReadExpression . (!! 0) -- Read input expression. -- checks for errors using symbols found. rccReadExpression :: String -> Type rccReadExpression input = case parse rccParseExpression "" input of Left err -> StringType $ "Syntax error at: " ++ show err Right val -> val -- "Syntax error: Symbol expected at" ++ show err -- Symbols definition. -- oneOf should be working by defining symbols to be any -- of the instances we mention in the string attached. rccSymbols :: Parser Char rccSymbols = oneOf "!$%&|*+-/:<=>?@^_~#" -- Spaces definition. -- spaces should not be taken in consideration, so we use -- the skipMany1 function to skip them out of the parser. rccSpaces :: Parser () rccSpaces = skipMany1 space -- Type definitions. -- types are currently somekind limited but they should -- be enough to perform some computation. data Type = AtomicType String | BooleanType Bool | IntegerType Integer | ListType [Type] | PairType [Type] Type | StringType String -- Parse atomic type. -- Parses an atomic type of the language. -- This can be either a primitive or a boolean result. rccParseAtomic :: Parser Type rccParseAtomic = do x <- letter <|> rccSymbols y <- many (letter <|> digit <|> rccSymbols) let item = [x] ++ y return $ case item of "#true" -> BooleanType True "#false" -> BooleanType False otherwise -> AtomicType item -- Parse integer type. -- Parses an integer number by explicity considering -- every character as a digit. rccParseInteger :: Parser Type rccParseInteger = liftM (IntegerType . read) $ many1 digit -- Parse list type. -- Parses a list of types, not delimited -- by any characters, but just by spaces. rccParseList :: Parser Type rccParseList = liftM ListType $ sepBy rccParseExpression rccSpaces -- Parses a pair type. -- As in scheme a pair is composed by two -- lists separated by punctuation mark. rccParsePair :: Parser Type rccParsePair = do head <- endBy rccParseExpression rccSpaces tail <- char '.' >> rccSpaces >> rccParseExpression return $ PairType head tail -- Parses a quoted type. -- Quoted types evade computation on lists, -- wich might be used then as data. rccParseQuote :: Parser Type rccParseQuote = do char '\'' item <- rccParseExpression return $ ListType [AtomicType "quote", item] -- Parses a string type. -- A string is just deilimited by double quotes. -- The drawback is we have to avoid escape characters. rccParseString :: Parser Type rccParseString = do char '"' item <- many (noneOf "\"") char '"' return $ StringType item -- Parses a language construction. -- The language is made of list or pair lexeme -- evaluation nodes, we just traverse them. rccParseConstruct :: Parser Type rccParseConstruct = do char '(' item <- (try rccParseList) <|> rccParsePair char ')' return item -- Parse a full expression. -- Expressions are made of lists, that get parsed -- by trying to get a list or a pair from them. rccParseExpression :: Parser Type rccParseExpression = rccParseAtomic <|> rccParseInteger <|> rccParseQuote <|> rccParseString <|> rccParseConstruct -- Show values for constructions. -- This is needed for the parser to display -- to the user what he actually typed and what -- was the expected expression. rccValue :: Type -> String rccValue (AtomicType item) = item rccValue (BooleanType True) = "#true" rccValue (BooleanType False) = "#false" rccValue (IntegerType item) = show item rccValue (ListType item) = "(" ++ rccGetValues item ++ ")" rccValue (PairType x y) = "(" ++ rccGetValues x ++ " . " ++ rccValue y ++ ")" rccValue (StringType item) = "\"" ++ item ++ "\"" -- Be able to get the items of a list of values -- This is needed for displaying values on -- lists and pairs. rccGetValues :: [Type] -> String rccGetValues = unwords . map rccValue -- Let's declare now the instance that will -- actually show the types to the user. instance Show Type where show = rccValue -- Finally we evaluate expressions on the -- values we get to be able to display them. rccEvaluate :: Type -> Type rccEvaluate item @ (BooleanType _) = item rccEvaluate item @ (IntegerType _) = item rccEvaluate item @ (StringType _) = item rccEvaluate (ListType [AtomicType "quote", item]) = item