add failing test case
This commit is contained in:
parent
6ea439cf91
commit
361049003f
295
hledger.hs
295
hledger.hs
@ -1,156 +1,20 @@
|
|||||||
-- hledger - ledger-like money management utilities
|
-- hledger - ledger-like money management utilities
|
||||||
-- GPLv3, (c) Simon Michael & contributors,
|
-- GPLv3, (c) Simon Michael & contributors,
|
||||||
-- ledger is at http://newartisans.com/ledger.html
|
-- ledger is at http://newartisans.com/ledger.html
|
||||||
|
|
||||||
import Debug.Trace
|
|
||||||
import System.Directory (getHomeDirectory)
|
|
||||||
import System.Environment (getEnv)
|
|
||||||
import Control.Exception (assert)
|
|
||||||
import Text.ParserCombinators.Parsec
|
|
||||||
import qualified Text.ParserCombinators.Parsec.Token as P
|
|
||||||
import Text.ParserCombinators.Parsec.Language
|
|
||||||
--import TildeExpand -- confuses my ghc 6.7
|
|
||||||
|
|
||||||
-- sample data
|
|
||||||
|
|
||||||
sample_entry = "\
|
|
||||||
\2007/01/27 * joes diner\n\
|
|
||||||
\ expenses:food:dining $10.00\n\
|
|
||||||
\ expenses:gifts $10.00\n\
|
|
||||||
\ assets:checking $-20.00\n\
|
|
||||||
\\n\" --"
|
|
||||||
|
|
||||||
sample_entry2 = "\
|
|
||||||
\2007/01/28 coopportunity\n\
|
|
||||||
\ expenses:food:groceries $47.18\n\
|
|
||||||
\ assets:checking\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_entry3 = "\
|
|
||||||
\2007/01/01 * opening balance\n\
|
|
||||||
\ assets:cash $4.82\n\
|
|
||||||
\ equity:opening balances\n\
|
|
||||||
\\n\
|
|
||||||
\2007/01/01 * opening balance\n\
|
|
||||||
\ assets:cash $4.82\n\
|
|
||||||
\ equity:opening balances\n\
|
|
||||||
\\n\
|
|
||||||
\2007/01/28 coopportunity\n\
|
|
||||||
\ expenses:food:groceries $47.18\n\
|
|
||||||
\ assets:checking\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_periodic_entry = "\
|
|
||||||
\~ monthly from 2007/2/2\n\
|
|
||||||
\ assets:saving $200.00\n\
|
|
||||||
\ assets:checking\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_periodic_entry2 = "\
|
|
||||||
\~ monthly from 2007/2/2\n\
|
|
||||||
\ assets:saving $200.00 ;auto savings\n\
|
|
||||||
\ assets:checking\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_periodic_entry3 = "\
|
|
||||||
\~ monthly from 2007/01/01\n\
|
|
||||||
\ assets:cash $4.82\n\
|
|
||||||
\ equity:opening balances\n\
|
|
||||||
\\n\
|
|
||||||
\~ monthly from 2007/01/01\n\
|
|
||||||
\ assets:cash $4.82\n\
|
|
||||||
\ equity:opening balances\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_transaction = " expenses:food:dining $10.00\n"
|
|
||||||
|
|
||||||
sample_transaction2 = " assets:checking\n"
|
|
||||||
|
|
||||||
sample_ledger = "\
|
|
||||||
\\n\
|
|
||||||
\2007/01/27 * joes diner\n\
|
|
||||||
\ expenses:food:dining $10.00\n\
|
|
||||||
\ expenses:gifts $10.00\n\
|
|
||||||
\ assets:checking $-20.00\n\
|
|
||||||
\\n\
|
|
||||||
\\n\
|
|
||||||
\2007/01/28 coopportunity\n\
|
|
||||||
\ expenses:food:groceries $47.18\n\
|
|
||||||
\ assets:checking $-47.18\n\
|
|
||||||
\\n\
|
|
||||||
\" --"
|
|
||||||
|
|
||||||
sample_ledger2 = "\
|
|
||||||
\;comment\n\
|
|
||||||
\2007/01/27 * joes diner\n\
|
|
||||||
\ expenses:food:dining $10.00\n\
|
|
||||||
\ assets:checking $-47.18\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_ledger3 = "\
|
|
||||||
\2007/01/27 * joes diner\n\
|
|
||||||
\ expenses:food:dining $10.00\n\
|
|
||||||
\;intra-entry comment\n\
|
|
||||||
\ assets:checking $-47.18\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_ledger4 = "\
|
|
||||||
\!include \"somefile\"\n\
|
|
||||||
\2007/01/27 * joes diner\n\
|
|
||||||
\ expenses:food:dining $10.00\n\
|
|
||||||
\ assets:checking $-47.18\n\
|
|
||||||
\\n" --"
|
|
||||||
|
|
||||||
sample_ledger5 = ""
|
|
||||||
|
|
||||||
-- a data model
|
|
||||||
|
|
||||||
data Ledger = Ledger {
|
|
||||||
modifier_entries :: [ModifierEntry],
|
|
||||||
periodic_entries :: [PeriodicEntry],
|
|
||||||
entries :: [Entry]
|
|
||||||
} deriving (Show, Eq)
|
|
||||||
data Entry = Entry {
|
|
||||||
date :: Date,
|
|
||||||
status :: Bool,
|
|
||||||
code :: String,
|
|
||||||
description :: String,
|
|
||||||
transactions :: [Transaction]
|
|
||||||
} deriving (Show, Eq)
|
|
||||||
data ModifierEntry = ModifierEntry {
|
|
||||||
valueexpr :: String,
|
|
||||||
m_transactions :: [Transaction]
|
|
||||||
} deriving (Show, Eq)
|
|
||||||
data PeriodicEntry = PeriodicEntry {
|
|
||||||
periodexpr :: String,
|
|
||||||
p_transactions :: [Transaction]
|
|
||||||
} deriving (Show, Eq)
|
|
||||||
data Transaction = Transaction {
|
|
||||||
account :: Account,
|
|
||||||
amount :: Amount
|
|
||||||
} deriving (Show, Eq)
|
|
||||||
data Amount = Amount {
|
|
||||||
currency :: String,
|
|
||||||
quantity :: Float
|
|
||||||
} deriving (Read, Show, Eq)
|
|
||||||
type Date = String
|
|
||||||
type Account = String
|
|
||||||
|
|
||||||
-- ledger file parsing
|
|
||||||
-- struggling.. easier with a token parser ?
|
|
||||||
-- here's the ledger 2.5 grammar:
|
-- here's the ledger 2.5 grammar:
|
||||||
{-
|
{-
|
||||||
"The ledger file format is quite simple, but also very flexible. It supports many options,
|
"The ledger file format is quite simple, but also very flexible. It supports
|
||||||
though typically the user can ignore most of them. They are summarized below.
|
many options, though typically the user can ignore most of them. They are
|
||||||
|
summarized below. The initial character of each line determines what the
|
||||||
The initial character of each line determines what the line means, and how it should be
|
line means, and how it should be interpreted. Allowable initial characters
|
||||||
interpreted. Allowable initial characters are:
|
are:
|
||||||
|
|
||||||
NUMBER A line beginning with a number denotes an entry. It may be followed by any
|
NUMBER A line beginning with a number denotes an entry. It may be followed by any
|
||||||
number of lines, each beginning with whitespace, to denote the entry’s account
|
number of lines, each beginning with whitespace, to denote the entry’s account
|
||||||
transactions. The format of the first line is:
|
transactions. The format of the first line is:
|
||||||
|
|
||||||
DATE[=EDATE] [*|!] [(CODE)] DESC
|
DATE[=EDATE] [*|!] [(CODE)] DESC
|
||||||
|
|
||||||
If ‘*’ appears after the date (with optional effective date), it indicates the entry
|
If ‘*’ appears after the date (with optional effective date), it indicates the entry
|
||||||
is “cleared”, which can mean whatever the user wants it t omean. If ‘!’ appears
|
is “cleared”, which can mean whatever the user wants it t omean. If ‘!’ appears
|
||||||
after the date, it indicates d the entry is “pending”; i.e., tentatively cleared from
|
after the date, it indicates d the entry is “pending”; i.e., tentatively cleared from
|
||||||
@ -235,7 +99,153 @@ i, o, b, h
|
|||||||
timelog files."
|
timelog files."
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
import Debug.Trace
|
||||||
|
import System.Directory (getHomeDirectory)
|
||||||
|
import System.Environment (getEnv)
|
||||||
|
import Control.Exception (assert)
|
||||||
|
import Text.ParserCombinators.Parsec
|
||||||
|
import qualified Text.ParserCombinators.Parsec.Token as P
|
||||||
|
import Text.ParserCombinators.Parsec.Language
|
||||||
|
--import TildeExpand -- confuses my ghc 6.7
|
||||||
|
|
||||||
|
-- sample data
|
||||||
|
|
||||||
|
sample_entry = "\
|
||||||
|
\2007/01/27 * joes diner\n\
|
||||||
|
\ expenses:food:dining $10.00\n\
|
||||||
|
\ expenses:gifts $10.00\n\
|
||||||
|
\ assets:checking $-20.00\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_entry2 = "\
|
||||||
|
\2007/01/28 coopportunity\n\
|
||||||
|
\ expenses:food:groceries $47.18\n\
|
||||||
|
\ assets:checking\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_entry3 = "\
|
||||||
|
\2007/01/01 * opening balance\n\
|
||||||
|
\ assets:cash $4.82\n\
|
||||||
|
\ equity:opening balances\n\
|
||||||
|
\\n\
|
||||||
|
\2007/01/01 * opening balance\n\
|
||||||
|
\ assets:cash $4.82\n\
|
||||||
|
\ equity:opening balances\n\
|
||||||
|
\\n\
|
||||||
|
\2007/01/28 coopportunity\n\
|
||||||
|
\ expenses:food:groceries $47.18\n\
|
||||||
|
\ assets:checking\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_periodic_entry = "\
|
||||||
|
\~ monthly from 2007/2/2\n\
|
||||||
|
\ assets:saving $200.00\n\
|
||||||
|
\ assets:checking\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_periodic_entry2 = "\
|
||||||
|
\~ monthly from 2007/2/2\n\
|
||||||
|
\ assets:saving $200.00 ;auto savings\n\
|
||||||
|
\ assets:checking\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_periodic_entry3 = "\
|
||||||
|
\~ monthly from 2007/01/01\n\
|
||||||
|
\ assets:cash $4.82\n\
|
||||||
|
\ equity:opening balances\n\
|
||||||
|
\\n\
|
||||||
|
\~ monthly from 2007/01/01\n\
|
||||||
|
\ assets:cash $4.82\n\
|
||||||
|
\ equity:opening balances\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_transaction = " expenses:food:dining $10.00\n"
|
||||||
|
|
||||||
|
sample_transaction2 = " assets:checking\n"
|
||||||
|
|
||||||
|
sample_ledger = "\
|
||||||
|
\\n\
|
||||||
|
\2007/01/27 * joes diner\n\
|
||||||
|
\ expenses:food:dining $10.00\n\
|
||||||
|
\ expenses:gifts $10.00\n\
|
||||||
|
\ assets:checking $-20.00\n\
|
||||||
|
\\n\
|
||||||
|
\\n\
|
||||||
|
\2007/01/28 coopportunity\n\
|
||||||
|
\ expenses:food:groceries $47.18\n\
|
||||||
|
\ assets:checking $-47.18\n\
|
||||||
|
\\n\
|
||||||
|
\" --"
|
||||||
|
|
||||||
|
sample_ledger2 = "\
|
||||||
|
\;comment\n\
|
||||||
|
\2007/01/27 * joes diner\n\
|
||||||
|
\ expenses:food:dining $10.00\n\
|
||||||
|
\ assets:checking $-47.18\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_ledger3 = "\
|
||||||
|
\2007/01/27 * joes diner\n\
|
||||||
|
\ expenses:food:dining $10.00\n\
|
||||||
|
\;intra-entry comment\n\
|
||||||
|
\ assets:checking $-47.18\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_ledger4 = "\
|
||||||
|
\!include \"somefile\"\n\
|
||||||
|
\2007/01/27 * joes diner\n\
|
||||||
|
\ expenses:food:dining $10.00\n\
|
||||||
|
\ assets:checking $-47.18\n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
sample_ledger5 = ""
|
||||||
|
|
||||||
|
sample_ledger6 = "\
|
||||||
|
\~ monthly from 2007/1/21\n\
|
||||||
|
\ expenses:entertainment $16.23 ;netflix\n\
|
||||||
|
\ assets:checking\n\
|
||||||
|
\\n\
|
||||||
|
\; 2007/01/01 * opening balance\n\
|
||||||
|
\; assets:saving $200.04\n\
|
||||||
|
\; equity:opening balances \n\
|
||||||
|
\\n" --"
|
||||||
|
|
||||||
|
-- a data model
|
||||||
|
|
||||||
|
data Ledger = Ledger {
|
||||||
|
modifier_entries :: [ModifierEntry],
|
||||||
|
periodic_entries :: [PeriodicEntry],
|
||||||
|
entries :: [Entry]
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
data ModifierEntry = ModifierEntry {
|
||||||
|
valueexpr :: String,
|
||||||
|
m_transactions :: [Transaction]
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
data PeriodicEntry = PeriodicEntry {
|
||||||
|
periodexpr :: String,
|
||||||
|
p_transactions :: [Transaction]
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
data Entry = Entry {
|
||||||
|
date :: Date,
|
||||||
|
status :: Bool,
|
||||||
|
code :: String,
|
||||||
|
description :: String,
|
||||||
|
transactions :: [Transaction]
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
data Transaction = Transaction {
|
||||||
|
account :: Account,
|
||||||
|
amount :: Amount
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
data Amount = Amount {
|
||||||
|
currency :: String,
|
||||||
|
quantity :: Float
|
||||||
|
} deriving (Read, Show, Eq)
|
||||||
|
type Date = String
|
||||||
|
type Account = String
|
||||||
|
|
||||||
|
-- ledger file parsing
|
||||||
|
|
||||||
|
-- struggling.. easier with a token parser ?
|
||||||
ledgerLanguageDef = LanguageDef {
|
ledgerLanguageDef = LanguageDef {
|
||||||
commentStart = ""
|
commentStart = ""
|
||||||
, commentEnd = ""
|
, commentEnd = ""
|
||||||
@ -369,6 +379,7 @@ main = do
|
|||||||
showParseResult (parse ledger "" sample_ledger3)
|
showParseResult (parse ledger "" sample_ledger3)
|
||||||
showParseResult (parse ledger "" sample_ledger4)
|
showParseResult (parse ledger "" sample_ledger4)
|
||||||
showParseResult (parse ledger "" sample_ledger5)
|
showParseResult (parse ledger "" sample_ledger5)
|
||||||
|
showParseResult (parse ledger "" sample_ledger6)
|
||||||
showParseResult (parse ledger "" sample_periodic_entry)
|
showParseResult (parse ledger "" sample_periodic_entry)
|
||||||
showParseResult (parse ledger "" sample_periodic_entry2)
|
showParseResult (parse ledger "" sample_periodic_entry2)
|
||||||
parseMyLedgerFile >>= showParseResult
|
parseMyLedgerFile >>= showParseResult
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user