{- Copyright 2008 Debris <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