type signatures

This commit is contained in:
Simon Michael 2007-02-09 00:18:20 +00:00
parent a316e901e7
commit b95709270b

View File

@ -1,8 +1,9 @@
#!/usr/bin/runhaskell #!/usr/bin/runhaskell
-- hledger - ledger-compatible money management utilities -- hledger - ledger-compatible money management utilities (& haskell study)
-- GPLv3, (c) Simon Michael & contributors, -- GPLv3, (c) Simon Michael & contributors,
-- ledger is at http://newartisans.com/ledger.html --
-- here's the v2.5 grammar: -- John Wiegley's ledger is at http://newartisans.com/ledger.html .
-- Here's the v2.5 grammar:
{- {-
"The ledger file format is quite simple, but also very flexible. It supports "The ledger file format is quite simple, but also very flexible. It supports
many options, though typically the user can ignore most of them. They are many options, though typically the user can ignore most of them. They are
@ -99,6 +100,7 @@ i, o, b, h
files. See the timeclocks documentation for more info on the syntax of its files. See the timeclocks documentation for more info on the syntax of its
timelog files." timelog files."
-} -}
-- parsec example: http://pandoc.googlecode.com/svn/trunk/src/Text/Pandoc/Readers/RST.hs
import Debug.Trace import Debug.Trace
import Test.QuickCheck import Test.QuickCheck
@ -279,6 +281,8 @@ reserved = P.reserved lexer
reservedOp = P.reservedOp lexer reservedOp = P.reservedOp lexer
-- parsers -- parsers
ledger :: Parser Ledger
ledger = do ledger = do
ledgernondatalines ledgernondatalines
-- for now these must come first, unlike ledger -- for now these must come first, unlike ledger
@ -289,19 +293,16 @@ ledger = do
eof eof
return (Ledger modifier_entries periodic_entries entries) return (Ledger modifier_entries periodic_entries entries)
ledgernondatalines :: Parser [String]
ledgernondatalines = many (ledgerdirective <|> ledgercomment <|> do {whiteSpace1; return []}) ledgernondatalines = many (ledgerdirective <|> ledgercomment <|> do {whiteSpace1; return []})
whiteSpace1 = do space; whiteSpace ledgercomment :: Parser String
restofline = anyChar `manyTill` newline
ledgercomment = char ';' >> restofline <?> "comment" ledgercomment = char ';' >> restofline <?> "comment"
ledgerdirective :: Parser String
ledgerdirective = char '!' >> restofline <?> "directive" ledgerdirective = char '!' >> restofline <?> "directive"
ledgertransactions = (ledgertransaction <?> "transaction") `manyTill` (newline <?> "blank line") ledgermodifierentry :: Parser ModifierEntry
-- => unlike ledger, we need to end the file with a blank line
ledgermodifierentry = do ledgermodifierentry = do
char '=' <?> "entry" char '=' <?> "entry"
many spacenonewline many spacenonewline
@ -310,6 +311,7 @@ ledgermodifierentry = do
ledgernondatalines ledgernondatalines
return (ModifierEntry valueexpr transactions) return (ModifierEntry valueexpr transactions)
ledgerperiodicentry :: Parser PeriodicEntry
ledgerperiodicentry = do ledgerperiodicentry = do
char '~' <?> "entry" char '~' <?> "entry"
many spacenonewline many spacenonewline
@ -318,6 +320,7 @@ ledgerperiodicentry = do
ledgernondatalines ledgernondatalines
return (PeriodicEntry periodexpr transactions) return (PeriodicEntry periodexpr transactions)
ledgerentry :: Parser Entry
ledgerentry = do ledgerentry = do
date <- ledgerdate date <- ledgerdate
status <- ledgerstatus status <- ledgerstatus
@ -327,12 +330,20 @@ ledgerentry = do
ledgernondatalines ledgernondatalines
return (Entry date status code description transactions) return (Entry date status code description transactions)
ledgerdate :: Parser String
ledgerdate = do date <- many1 (digit <|> char '/'); many1 spacenonewline; return date ledgerdate = do date <- many1 (digit <|> char '/'); many1 spacenonewline; return date
ledgerstatus :: Parser Bool
ledgerstatus = try (do { char '*'; many1 spacenonewline; return True } ) <|> return False ledgerstatus = try (do { char '*'; many1 spacenonewline; return True } ) <|> return False
ledgercode :: Parser String
ledgercode = try (do { char '('; code <- anyChar `manyTill` char ')'; many1 spacenonewline; return code } ) <|> return "" ledgercode = try (do { char '('; code <- anyChar `manyTill` char ')'; many1 spacenonewline; return code } ) <|> return ""
ledgertransactions :: Parser [Transaction]
ledgertransactions = (ledgertransaction <?> "transaction") `manyTill` (newline <?> "blank line")
-- => unlike ledger, we need to end the file with a blank line
ledgertransaction :: Parser Transaction
ledgertransaction = do ledgertransaction = do
many1 spacenonewline many1 spacenonewline
account <- ledgeraccount <?> "account" account <- ledgeraccount <?> "account"
@ -343,8 +354,10 @@ ledgertransaction = do
return (Transaction account amount) return (Transaction account amount)
-- account names may have single spaces in them, and are terminated by two or more spaces -- account names may have single spaces in them, and are terminated by two or more spaces
ledgeraccount :: Parser String
ledgeraccount = many1 (alphaNum <|> char ':' <|> try (do {spacenonewline; do {notFollowedBy spacenonewline; return ' '}})) ledgeraccount = many1 (alphaNum <|> char ':' <|> try (do {spacenonewline; do {notFollowedBy spacenonewline; return ' '}}))
ledgeramount :: Parser Amount
ledgeramount = try (do ledgeramount = try (do
many1 spacenonewline many1 spacenonewline
currency <- many (noneOf "-.0123456789\n") <?> "currency" currency <- many (noneOf "-.0123456789\n") <?> "currency"
@ -353,10 +366,18 @@ ledgeramount = try (do
) <|> ) <|>
return (Amount "" 0) return (Amount "" 0)
ledgereol :: Parser String
ledgereol = ledgercomment <|> do {newline; return []} ledgereol = ledgercomment <|> do {newline; return []}
spacenonewline :: Parser Char
spacenonewline = satisfy (\c -> c `elem` " \v\f\t") spacenonewline = satisfy (\c -> c `elem` " \v\f\t")
restofline :: Parser String
restofline = anyChar `manyTill` newline
whiteSpace1 :: Parser ()
whiteSpace1 = do space; whiteSpace
-- tests -- tests
test1 = TestCase (assertEqual "1==1" 1 1) test1 = TestCase (assertEqual "1==1" 1 1)
@ -370,6 +391,7 @@ tests = Test.HUnit.test [
prop_test1 = 1 == 1 prop_test1 = 1 == 1
prop2 = 1 == 1 prop2 = 1 == 1
test :: IO ()
test = do test = do
parseTest ledgertransaction sample_transaction parseTest ledgertransaction sample_transaction
parseTest ledgertransaction sample_transaction2 parseTest ledgertransaction sample_transaction2
@ -400,31 +422,25 @@ printParseResult r =
-- ok, what can we do with it ? -- ok, what can we do with it ?
parseMyLedgerFile = do showLedger :: Ledger -> String
ledgerFile >>= parseFromFile ledger >>= return
where
ledgerFile = do
filepath <- getEnv "LEDGER" `catch` \_ -> return "ledger.dat"
-- don't know how to accomplish this great feat
--ledger_file <- tildeExpand filepath
let ledger_file = filepath
return ledger_file
showLedger l = "Ledger has\n" showLedger l = "Ledger has\n"
++ (showModifierEntries $ modifier_entries l) ++ (showModifierEntries $ modifier_entries l)
++ (showPeriodicEntries $ periodic_entries l) ++ (showPeriodicEntries $ periodic_entries l)
++ (showEntries $ entries l) ++ (showEntries $ entries l)
showModifierEntries :: [ModifierEntry] -> String
showModifierEntries [] = "" showModifierEntries [] = ""
showModifierEntries es = showModifierEntries es =
(show n) ++ " modifier " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es) (show n) ++ " modifier " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es)
where n = length es where n = length es
showPeriodicEntries :: [PeriodicEntry] -> String
showPeriodicEntries [] = "" showPeriodicEntries [] = ""
showPeriodicEntries es = showPeriodicEntries es =
(show n) ++ " periodic " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es) (show n) ++ " periodic " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es)
where n = length es where n = length es
showEntries :: [Entry] -> String
showEntries [] = "" showEntries [] = ""
showEntries es = showEntries es =
(show n) ++ " " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es) (show n) ++ " " ++ (inflectEntries n) ++ ":\n" ++ unlines (map show es)
@ -451,13 +467,27 @@ instance Show Transaction where
instance Show Amount where show a = (currency a) ++ (show $ quantity a) instance Show Amount where show a = (currency a) ++ (show $ quantity a)
r = register parseMyLedgerFile :: IO (Either ParseError Ledger)
parseMyLedgerFile = do
ledgerFile >>= parseFromFile ledger >>= return
where
ledgerFile = do
filepath <- getEnv "LEDGER" `catch` \_ -> return "ledger.dat"
-- don't know how to accomplish this great feat
--ledger_file <- tildeExpand filepath
let ledger_file = filepath
return ledger_file
-- commands
register :: IO ()
register = do register = do
p <- parseMyLedgerFile p <- parseMyLedgerFile
case p of case p of
Left err -> do putStr "ledger parse error at "; print err Left err -> do putStr "ledger parse error at "; print err
Right l -> putStr $ showLedger l Right l -> putStr $ showLedger l
main :: IO ()
main = do main = do
(opts, args) <- getArgs >>= getOptions (opts, args) <- getArgs >>= getOptions
putStr "options: "; print opts putStr "options: "; print opts