parsing: aliases now match by regular expression
alias match patterns (the part left of the =) are now case-insensitive regular expressions matching anywhere in the account name. The replacement string (the part right of the =) can replace multiple matches within the account name. The replacement string does not yet support any of the usual syntax like backreferences.
This commit is contained in:
		
							parent
							
								
									be9b637e0c
								
							
						
					
					
						commit
						e892fdc6d5
					
				@ -385,8 +385,14 @@ filterJournalTransactionsByAccount apats j@Journal{jtxns=ts} = j{jtxns=filter tm
 | 
				
			|||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Apply additional account aliases (eg from the command-line) to all postings in a journal.
 | 
					-- | Apply additional account aliases (eg from the command-line) to all postings in a journal.
 | 
				
			||||||
journalApplyAliases :: [(AccountName,AccountName)] -> Journal -> Journal
 | 
					journalApplyAliases :: [AccountAlias] -> Journal -> Journal
 | 
				
			||||||
journalApplyAliases aliases j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts}
 | 
					journalApplyAliases aliases j@Journal{jtxns=ts} =
 | 
				
			||||||
 | 
					  -- (if null aliases
 | 
				
			||||||
 | 
					  --  then id
 | 
				
			||||||
 | 
					  --  else (dbgtrace $
 | 
				
			||||||
 | 
					  --        "applying additional command-line aliases:\n"
 | 
				
			||||||
 | 
					  --        ++ chomp (unlines $ map (" "++) $ lines $ ppShow aliases))) $
 | 
				
			||||||
 | 
					  j{jtxns=map fixtransaction ts}
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
      fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
 | 
					      fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
 | 
				
			||||||
      fixposting p@Posting{paccount=a} = p{paccount=accountNameApplyAliases aliases a}
 | 
					      fixposting p@Posting{paccount=a} = p{paccount=accountNameApplyAliases aliases a}
 | 
				
			||||||
 | 
				
			|||||||
@ -219,13 +219,13 @@ concatAccountNames as = accountNameWithPostingType t $ intercalate ":" $ map acc
 | 
				
			|||||||
    where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as
 | 
					    where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Rewrite an account name using the first applicable alias from the given list, if any.
 | 
					-- | Rewrite an account name using the first applicable alias from the given list, if any.
 | 
				
			||||||
accountNameApplyAliases :: [(AccountName,AccountName)] -> AccountName -> AccountName
 | 
					accountNameApplyAliases :: [AccountAlias] -> AccountName -> AccountName
 | 
				
			||||||
accountNameApplyAliases aliases a = withorigtype
 | 
					accountNameApplyAliases aliases a = accountNameWithPostingType atype aname'
 | 
				
			||||||
    where
 | 
					  where
 | 
				
			||||||
      (a',t) = (accountNameWithoutPostingType a, accountNamePostingType a)
 | 
					    (aname,atype) = (accountNameWithoutPostingType a, accountNamePostingType a)
 | 
				
			||||||
      firstmatchingalias = headDef Nothing $ map Just $ filter (\(orig,_) -> orig == a' || orig `isAccountNamePrefixOf` a') aliases
 | 
					    firstmatchingalias = headDef Nothing $ map Just $ filter (\(re,_) -> regexMatchesCI re aname) aliases
 | 
				
			||||||
      rewritten = maybe a' (\(orig,alias) -> alias++drop (length orig) a') firstmatchingalias
 | 
					    applyAlias = uncurry regexReplaceCI
 | 
				
			||||||
      withorigtype = accountNameWithPostingType t rewritten
 | 
					    aname' = maybe id applyAlias firstmatchingalias $ aname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tests_Hledger_Data_Posting = TestList [
 | 
					tests_Hledger_Data_Posting = TestList [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -31,6 +31,8 @@ import Data.Time.LocalTime
 | 
				
			|||||||
import System.Time (ClockTime(..))
 | 
					import System.Time (ClockTime(..))
 | 
				
			||||||
import Text.Parsec.Pos
 | 
					import Text.Parsec.Pos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Hledger.Utils.Regex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SmartDate = (String,String,String)
 | 
					type SmartDate = (String,String,String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,6 +48,8 @@ data Interval = NoInterval
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type AccountName = String
 | 
					type AccountName = String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AccountAlias = (Regexp,Replacement)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data Side = L | R deriving (Eq,Show,Read,Ord,Typeable,Data)
 | 
					data Side = L | R deriving (Eq,Show,Read,Ord,Typeable,Data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Commodity = String
 | 
					type Commodity = String
 | 
				
			||||||
@ -172,7 +176,7 @@ data JournalContext = Ctx {
 | 
				
			|||||||
    , ctxAccount   :: ![AccountName]     -- ^ the current stack of parent accounts/account name components
 | 
					    , ctxAccount   :: ![AccountName]     -- ^ the current stack of parent accounts/account name components
 | 
				
			||||||
                                        --   specified with "account" directive(s). Concatenated, these
 | 
					                                        --   specified with "account" directive(s). Concatenated, these
 | 
				
			||||||
                                        --   are the account prefix prepended to parsed account names.
 | 
					                                        --   are the account prefix prepended to parsed account names.
 | 
				
			||||||
    , ctxAliases   :: ![(AccountName,AccountName)] -- ^ the current list of account name aliases in effect
 | 
					    , ctxAliases   :: ![AccountAlias]   -- ^ the current list of account name aliases in effect
 | 
				
			||||||
    } deriving (Read, Show, Eq, Data, Typeable)
 | 
					    } deriving (Read, Show, Eq, Data, Typeable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
deriving instance Data (ClockTime)
 | 
					deriving instance Data (ClockTime)
 | 
				
			||||||
 | 
				
			|||||||
@ -134,10 +134,10 @@ popParentAccount = do ctx0 <- getState
 | 
				
			|||||||
getParentAccount :: GenParser tok JournalContext String
 | 
					getParentAccount :: GenParser tok JournalContext String
 | 
				
			||||||
getParentAccount = liftM (concatAccountNames . reverse . ctxAccount) getState
 | 
					getParentAccount = liftM (concatAccountNames . reverse . ctxAccount) getState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
addAccountAlias :: (AccountName,AccountName) -> GenParser tok JournalContext ()
 | 
					addAccountAlias :: AccountAlias -> GenParser tok JournalContext ()
 | 
				
			||||||
addAccountAlias a = updateState (\(ctx@Ctx{..}) -> ctx{ctxAliases=a:ctxAliases})
 | 
					addAccountAlias a = updateState (\(ctx@Ctx{..}) -> ctx{ctxAliases=a:ctxAliases})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
getAccountAliases :: GenParser tok JournalContext [(AccountName,AccountName)]
 | 
					getAccountAliases :: GenParser tok JournalContext [AccountAlias]
 | 
				
			||||||
getAccountAliases = liftM ctxAliases getState
 | 
					getAccountAliases = liftM ctxAliases getState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clearAccountAliases :: GenParser tok JournalContext ()
 | 
					clearAccountAliases :: GenParser tok JournalContext ()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,40 +1,66 @@
 | 
				
			|||||||
# alias-related tests
 | 
					# alias-related tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 1. command-line --alias option.  Note multiple applicable aliases, but
 | 
					# 1. alias directive. The pattern is a case-insensitive regular
 | 
				
			||||||
# only one is applied per account name.  Spaces are allowed if quoted.
 | 
					# expression matching anywhere in the account name. Only the most
 | 
				
			||||||
hledgerdev -f- print --alias 'a a=A' --alias b=B
 | 
					# recently declared matching alias is applied to an account name. The
 | 
				
			||||||
 | 
					# replacement can replace multiple matches within the account name.
 | 
				
			||||||
 | 
					# The replacement pattern does not yet support match references.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					hledgerdev -f- print
 | 
				
			||||||
<<<
 | 
					<<<
 | 
				
			||||||
 | 
					alias a=b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    a a  1
 | 
					    A a  1
 | 
				
			||||||
 | 
					    a a  2
 | 
				
			||||||
 | 
					    c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					alias A (.)=\1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2011/01/01
 | 
				
			||||||
 | 
					    A a  1
 | 
				
			||||||
 | 
					    a a  2
 | 
				
			||||||
    c
 | 
					    c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>>>
 | 
					>>>
 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    A             1
 | 
					    b b             1
 | 
				
			||||||
    c            -1
 | 
					    b b             2
 | 
				
			||||||
 | 
					    c              -3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2011/01/01
 | 
				
			||||||
 | 
					    \1             1
 | 
				
			||||||
 | 
					    \1             2
 | 
				
			||||||
 | 
					    c             -3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>>>=0
 | 
					>>>=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 2. alias directive, and an account with unbalanced posting indicators.
 | 
					# 2. command-line --alias option. Only the first matching alias is
 | 
				
			||||||
hledgerdev -f- print
 | 
					# applied per account name. Spaces are allowed if quoted.
 | 
				
			||||||
 | 
					# 
 | 
				
			||||||
 | 
					hledgerdev -f- print --alias 'A (.)=a' --alias a=b
 | 
				
			||||||
<<<
 | 
					<<<
 | 
				
			||||||
alias b=B
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    (b)  1
 | 
					    a a  1
 | 
				
			||||||
 | 
					    A a  2
 | 
				
			||||||
 | 
					    c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>>>
 | 
					>>>
 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    (B)             1
 | 
					    a             1
 | 
				
			||||||
 | 
					    a             2
 | 
				
			||||||
 | 
					    c            -3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>>>=0
 | 
					>>>=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 3. --alias options run after alias directives. Subaccounts are also
 | 
					# 3. Alias options run after alias directives. At most one of each is
 | 
				
			||||||
# matched and rewritten. Accounts with an internal part matching the alias
 | 
					# applied.
 | 
				
			||||||
# are ignored.
 | 
					#
 | 
				
			||||||
hledgerdev -f- print --alias a=A --alias B=C
 | 
					hledgerdev -f- print --alias a=A --alias B=C --alias B=D --alias C=D
 | 
				
			||||||
<<<
 | 
					<<<
 | 
				
			||||||
alias a=B
 | 
					alias ^a=B
 | 
				
			||||||
 | 
					alias ^a=E
 | 
				
			||||||
 | 
					alias E=F
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    [a:x]    1
 | 
					    [a:x]    1
 | 
				
			||||||
@ -42,8 +68,8 @@ alias a=B
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
>>>
 | 
					>>>
 | 
				
			||||||
2011/01/01
 | 
					2011/01/01
 | 
				
			||||||
    [C:x]             1
 | 
					    [E:x]             1
 | 
				
			||||||
    [x:a:x]            -1
 | 
					    [x:A:x]            -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>>>2
 | 
					>>>2
 | 
				
			||||||
>>>=0
 | 
					>>>=0
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user