all repos

clerk @ 549106949384b5856034829d8d10ac55e42c116c

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
journal: add semantic model, 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
	}
16
	for i, pf := range files {
17
		for _, entry := range pf.Ast.Entries {
18
			switch e := entry.(type) {
19
			case *ast.AccountDirective:
20
				c.addAccountDirective(e)
21
			case *ast.CommodityDirective:
22
				c.addCommodityDirective(e)
23
			case *ast.Transaction:
24
				c.addPostings(i, e.Postings)
25
			case *ast.PeriodicTransaction:
26
				c.addPostings(i, e.Postings)
27
			case *ast.AutomatedTransaction:
28
				c.addPostings(i, e.Postings)
29
			}
30
		}
31
	}
32
	return c
33
}
34
35
func (c *Context) addAccountDirective(ad *ast.AccountDirective) {
36
	aname := ad.Account.String()
37
	info, ok := c.Accounts[aname]
38
	if !ok {
39
		info = &AccountInfo{}
40
		c.Accounts[aname] = info
41
	}
42
	info.Directives = append(info.Directives, ad)
43
}
44
45
func (c *Context) addCommodityDirective(cd *ast.CommodityDirective) {
46
	info, ok := c.Commodities[cd.Commodity]
47
	if !ok {
48
		info = &CommodityInfo{}
49
		c.Commodities[cd.Commodity] = info
50
	}
51
	info.Directives = append(info.Directives, cd)
52
}
53
54
func (c *Context) addPostings(fileIndex int, postings []*ast.Posting) {
55
	for _, posting := range postings {
56
		// Account tracking
57
		aname := posting.Account.String()
58
		info, ok := c.Accounts[aname]
59
		if !ok {
60
			info = &AccountInfo{}
61
			c.Accounts[aname] = info
62
		}
63
		info.Usages = append(info.Usages, AccountUsage{
64
			FileIndex: fileIndex,
65
			Posting:   posting,
66
		})
67
68
		// Commodity tracking
69
		c.addCommodityUsage(fileIndex, posting.Amount)
70
		if posting.Cost != nil {
71
			c.addCommodityUsage(fileIndex, &posting.Cost.Amount)
72
		}
73
		if posting.Balance != nil {
74
			c.addCommodityUsage(fileIndex, &posting.Balance.Amount)
75
			if posting.Balance.Cost != nil {
76
				c.addCommodityUsage(fileIndex, &posting.Balance.Cost.Amount)
77
			}
78
		}
79
	}
80
}
81
82
func (c *Context) addCommodityUsage(fileIndex int, am *ast.Amount) {
83
	if am == nil || am.Commodity == "" {
84
		return
85
	}
86
	info, ok := c.Commodities[am.Commodity]
87
	if !ok {
88
		info = &CommodityInfo{}
89
		c.Commodities[am.Commodity] = info
90
	}
91
	info.Usages = append(info.Usages, CommodityUsage{
92
		FileIndex: fileIndex,
93
		Amount:    am,
94
	})
95
}