all repos

clerk @ cea9a8c

missing tooling for ledger/hledger

clerk/journal/semantic/build.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
journal/semantic: collect payees, 1 month ago
1
package semantic
2
3
import (
4
	"olexsmir.xyz/clerk/journal"
5
	"olexsmir.xyz/clerk/journal/ast"
6
)
7
8
// Build constructs [Context] from a list of parsed files.
9
// Files should be in dependency order(includes before includers).
10
func Build(files []*journal.ParsedFile) *Context {
11
	c := &Context{
12
		Files:       files,
13
		Accounts:    make(map[string]*AccountInfo),
14
		Commodities: make(map[string]*CommodityInfo),
15
		Payees:      make(map[string]*PayeeInfo),
16
	}
17
	for i, pf := range files {
18
		for _, entry := range pf.Ast.Entries {
19
			switch e := entry.(type) {
20
			case *ast.AccountDirective:
21
				c.addAccountDirective(e)
22
			case *ast.CommodityDirective:
23
				c.addCommodityDirective(e)
24
			case *ast.PayeeDirective:
25
				c.addPayeeDirective(e)
26
			case *ast.Transaction:
27
				c.addPostings(i, e.Postings)
28
				c.addPayee(i, e.Payee)
29
			case *ast.PeriodicTransaction:
30
				c.addPostings(i, e.Postings)
31
			case *ast.AutomatedTransaction:
32
				c.addPostings(i, e.Postings)
33
			}
34
		}
35
	}
36
	return c
37
}
38
39
func (c *Context) addAccountDirective(ad *ast.AccountDirective) {
40
	aname := ad.Account.String()
41
	info, ok := c.Accounts[aname]
42
	if !ok {
43
		info = &AccountInfo{}
44
		c.Accounts[aname] = info
45
	}
46
	info.Directives = append(info.Directives, ad)
47
}
48
49
func (c *Context) addPayeeDirective(pd *ast.PayeeDirective) {
50
	info, ok := c.Payees[pd.Name]
51
	if !ok {
52
		info = &PayeeInfo{}
53
		c.Payees[pd.Name] = info
54
	}
55
	info.Directives = append(info.Directives, &ast.Payee{
56
		Name: pd.Name,
57
		Span: pd.Span,
58
	})
59
}
60
61
func (c *Context) addCommodityDirective(cd *ast.CommodityDirective) {
62
	info, ok := c.Commodities[cd.Commodity]
63
	if !ok {
64
		info = &CommodityInfo{}
65
		c.Commodities[cd.Commodity] = info
66
	}
67
	info.Directives = append(info.Directives, cd)
68
}
69
70
func (c *Context) addPayee(fileIndex int, payee *ast.Payee) {
71
	if payee == nil {
72
		return
73
	}
74
	info, ok := c.Payees[payee.Name]
75
	if !ok {
76
		info = &PayeeInfo{}
77
		c.Payees[payee.Name] = info
78
	}
79
	info.Usage = append(info.Usage, PayeeUsage{
80
		FileIndex: fileIndex,
81
		Payee:     payee,
82
	})
83
}
84
85
func (c *Context) addPostings(fileIndex int, postings []*ast.Posting) {
86
	for _, posting := range postings {
87
		// Account tracking
88
		aname := posting.Account.String()
89
		info, ok := c.Accounts[aname]
90
		if !ok {
91
			info = &AccountInfo{}
92
			c.Accounts[aname] = info
93
		}
94
		info.Usages = append(info.Usages, AccountUsage{
95
			FileIndex: fileIndex,
96
			Posting:   posting,
97
		})
98
99
		// Commodity tracking
100
		c.addCommodityUsage(fileIndex, posting.Amount)
101
		if posting.Cost != nil {
102
			c.addCommodityUsage(fileIndex, &posting.Cost.Amount)
103
		}
104
		if posting.Balance != nil {
105
			c.addCommodityUsage(fileIndex, &posting.Balance.Amount)
106
			if posting.Balance.Cost != nil {
107
				c.addCommodityUsage(fileIndex, &posting.Balance.Cost.Amount)
108
			}
109
		}
110
	}
111
}
112
113
func (c *Context) addCommodityUsage(fileIndex int, am *ast.Amount) {
114
	if am == nil || am.Commodity == "" {
115
		return
116
	}
117
	info, ok := c.Commodities[am.Commodity]
118
	if !ok {
119
		info = &CommodityInfo{}
120
		c.Commodities[am.Commodity] = info
121
	}
122
	info.Usages = append(info.Usages, CommodityUsage{
123
		FileIndex: fileIndex,
124
		Amount:    am,
125
	})
126
}