all repos

clerk @ f68a2c6

missing tooling for ledger/hledger

clerk/journal/ast/dump.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
ast: add enum for subdirective kinds, 1 month ago
1
package ast
2
3
import (
4
	"fmt"
5
	"strings"
6
)
7
8
func Dump(f *Journal) string {
9
	var b strings.Builder
10
	dumpJournal(&b, f)
11
	return b.String()
12
}
13
14
func dumpJournal(b *strings.Builder, f *Journal) {
15
	fmt.Fprintf(b, "Journal\n")
16
	for _, e := range f.Entries {
17
		dumpEntry(b, e, 1)
18
	}
19
	if len(f.Errors) > 0 {
20
		fmt.Fprintf(b, "  Errors\n")
21
		for _, err := range f.Errors {
22
			fmt.Fprintf(b, "    %s: %s\n", err.Span, err.Message)
23
		}
24
	}
25
}
26
27
func dumpEntry(b *strings.Builder, e Entry, depth int) {
28
	switch e := e.(type) {
29
	case *Transaction:
30
		dumpTransaction(b, e, depth)
31
	case *PeriodicTransaction:
32
		dumpPeriodicTransaction(b, e, depth)
33
	case *AutomatedTransaction:
34
		dumpAutomatedTransaction(b, e, depth)
35
	case *BlankLine:
36
		indent(b, depth)
37
		fmt.Fprintf(b, "BlankLine %s\n", e.Span)
38
	case *Comment:
39
		dumpComment(b, e, depth)
40
	case *AccountDirective:
41
		dumpAccountDirective(b, e, depth)
42
	case *CommodityDirective:
43
		dumpCommodityDirective(b, e, depth)
44
	case *IncludeDirective:
45
		indent(b, depth)
46
		fmt.Fprintf(b, "IncludeDirective %s\n", e.Span)
47
		indent(b, depth+1)
48
		fmt.Fprintf(b, "Path: %q\n", e.Path)
49
		dumpOptComment(b, e.Comment, depth+1)
50
	case *AliasDirective:
51
		indent(b, depth)
52
		fmt.Fprintf(b, "AliasDirective %s\n", e.Span)
53
		indent(b, depth+1)
54
		fmt.Fprintf(b, "From: %q\n", e.From)
55
		indent(b, depth+1)
56
		fmt.Fprintf(b, "To: %q\n", e.To)
57
	case *PayeeDirective:
58
		indent(b, depth)
59
		fmt.Fprintf(b, "PayeeDirective %s\n", e.Span)
60
		if e.Name != nil {
61
			indent(b, depth+1)
62
			fmt.Fprintf(b, "Name: %q %s\n", e.Name.Name, e.Name.Span)
63
		}
64
		dumpOptComment(b, e.Comment, depth+1)
65
	case *TagDirective:
66
		indent(b, depth)
67
		fmt.Fprintf(b, "TagDirective %s\n", e.Span)
68
		indent(b, depth+1)
69
		fmt.Fprintf(b, "Name: %q\n", e.Name)
70
		dumpOptComment(b, e.Comment, depth+1)
71
	case *YearDirective:
72
		indent(b, depth)
73
		fmt.Fprintf(b, "YearDirective %s\n", e.Span)
74
		indent(b, depth+1)
75
		fmt.Fprintf(b, "Year: %d\n", e.Year)
76
	case *DecimalMarkDirective:
77
		indent(b, depth)
78
		fmt.Fprintf(b, "DecimalMarkDirective %s\n", e.Span)
79
		indent(b, depth+1)
80
		fmt.Fprintf(b, "Mark: %q\n", string(e.Mark))
81
	case *MarketPriceDirective:
82
		dumpMarketPriceDirective(b, e, depth)
83
	case *ConversionDirective:
84
		dumpConversionDirective(b, e, depth)
85
	case *DefaultCommodityDirective:
86
		indent(b, depth)
87
		fmt.Fprintf(b, "DefaultCommodityDirective %s\n", e.Span)
88
		dumpAmount(b, &e.Amount, depth+1)
89
	case *ApplyDirective:
90
		indent(b, depth)
91
		fmt.Fprintf(b, "ApplyDirective %s\n", e.Span)
92
		indent(b, depth+1)
93
		fmt.Fprintf(b, "Expr: %q\n", e.Expr)
94
		dumpOptComment(b, e.Comment, depth+1)
95
	case *EndDirective:
96
		indent(b, depth)
97
		fmt.Fprintf(b, "EndDirective %s\n", e.Span)
98
		indent(b, depth+1)
99
		fmt.Fprintf(b, "Expr: %q\n", e.Expr)
100
		dumpOptComment(b, e.Comment, depth+1)
101
	case *CommentBlockDirective:
102
		indent(b, depth)
103
		fmt.Fprintf(b, "CommentBlockDirective %s\n", e.Span)
104
		indent(b, depth+1)
105
		fmt.Fprintf(b, "Header: %q\n", e.Header)
106
		indent(b, depth+1)
107
		fmt.Fprintf(b, "Content: %q\n", e.Content)
108
		dumpOptComment(b, e.Comment, depth+1)
109
	case *IgnoredDirective:
110
		indent(b, depth)
111
		fmt.Fprintf(b, "IgnoredDirective %s\n", e.Span)
112
		indent(b, depth+1)
113
		fmt.Fprintf(b, "Text: %s\n", e.Text)
114
	default:
115
		indent(b, depth)
116
		fmt.Fprintf(b, "Unknown %T\n", e)
117
	}
118
}
119
120
func indent(b *strings.Builder, depth int) {
121
	b.WriteString(strings.Repeat("  ", depth))
122
}
123
124
func dumpTransaction(b *strings.Builder, t *Transaction, depth int) {
125
	indent(b, depth)
126
	fmt.Fprintf(b, "Transaction %s\n", t.Span)
127
	indent(b, depth+1)
128
	fmt.Fprintf(b, "Date: %s\n", dumpDate(t.Date))
129
	if t.SecondDate != nil {
130
		indent(b, depth+1)
131
		fmt.Fprintf(b, "SecondDate: %s\n", dumpDate(*t.SecondDate))
132
	}
133
	if t.Status.Value != StatusNone {
134
		indent(b, depth+1)
135
		fmt.Fprintf(b, "State: %q\n", t.Status.Value)
136
	}
137
	if t.Code != nil {
138
		indent(b, depth+1)
139
		fmt.Fprintf(b, "Code: %q %s\n", t.Code.Value, t.Code.Span)
140
	}
141
	if t.Payee != nil {
142
		indent(b, depth+1)
143
		fmt.Fprintf(b, "Payee: %q %s\n", t.Payee.Name, t.Payee.Span)
144
	}
145
	if t.Note != nil {
146
		indent(b, depth+1)
147
		fmt.Fprintf(b, "Note: %q %s\n", t.Note.Value, t.Note.Span)
148
	}
149
	dumpOptComment(b, t.Comment, depth+1)
150
	if len(t.HeaderComments) > 0 {
151
		indent(b, depth+1)
152
		fmt.Fprintf(b, "HeaderComments %s\n", t.Span)
153
		for _, c := range t.HeaderComments {
154
			dumpComment(b, c, depth+2)
155
		}
156
	}
157
	for _, p := range t.Postings {
158
		dumpPosting(b, p, depth+1)
159
	}
160
}
161
162
func dumpAutomatedTransaction(b *strings.Builder, t *AutomatedTransaction, depth int) {
163
	indent(b, depth)
164
	fmt.Fprintf(b, "AutomatedTransaction %s\n", t.Span)
165
	indent(b, depth+1)
166
	fmt.Fprintf(b, "Expr: %q %s\n", t.Expr.Value, t.Expr.Span)
167
	dumpOptComment(b, t.Comment, depth+1)
168
	if len(t.HeaderComments) > 0 {
169
		indent(b, depth+1)
170
		fmt.Fprintf(b, "HeaderComments %s\n", t.Span)
171
		for _, c := range t.HeaderComments {
172
			dumpComment(b, c, depth+2)
173
		}
174
	}
175
	for _, p := range t.Postings {
176
		dumpPosting(b, p, depth+1)
177
	}
178
}
179
180
func dumpPeriodicTransaction(b *strings.Builder, t *PeriodicTransaction, depth int) {
181
	indent(b, depth)
182
	fmt.Fprintf(b, "PeriodicTransaction %s\n", t.Span)
183
	indent(b, depth+1)
184
	fmt.Fprintf(b, "Period: %q\n", t.Period.Raw)
185
	if t.Period.From != nil {
186
		indent(b, depth+1)
187
		fmt.Fprintf(b, "From: %s\n", dumpDate(*t.Period.From))
188
	}
189
	if t.Period.To != nil {
190
		indent(b, depth+1)
191
		fmt.Fprintf(b, "To: %s\n", dumpDate(*t.Period.To))
192
	}
193
	if t.Status.Value != StatusNone {
194
		indent(b, depth+1)
195
		fmt.Fprintf(b, "Status: %q\n", t.Status.Value)
196
	}
197
	if t.Code != nil {
198
		indent(b, depth+1)
199
		fmt.Fprintf(b, "Code: %q %s\n", t.Code.Value, t.Code.Span)
200
	}
201
	if t.Description != nil {
202
		indent(b, depth+1)
203
		fmt.Fprintf(b, "Description: %q %s\n", t.Description.Value, t.Description.Span)
204
	}
205
	dumpOptComment(b, t.Comment, depth+1)
206
	if len(t.HeaderComments) > 0 {
207
		indent(b, depth+1)
208
		fmt.Fprintf(b, "HeaderComments\n")
209
		for _, c := range t.HeaderComments {
210
			dumpComment(b, c, depth+2)
211
		}
212
	}
213
	for _, p := range t.Postings {
214
		dumpPosting(b, p, depth+1)
215
	}
216
}
217
218
func dumpPosting(b *strings.Builder, p *Posting, depth int) {
219
	indent(b, depth)
220
	fmt.Fprintf(b, "Posting %s\n", p.Span)
221
	if p.Type != PostingReal {
222
		indent(b, depth+1)
223
		fmt.Fprintf(b, "Type: %s\n", p.Type)
224
	}
225
	if p.Status.Value != StatusNone {
226
		indent(b, depth+1)
227
		fmt.Fprintf(b, "Status: %q\n", p.Status.Value)
228
	}
229
	dumpAccount(b, p.Account, depth+1)
230
	if p.Amount != nil {
231
		dumpAmount(b, p.Amount, depth+1)
232
	} else {
233
		indent(b, depth+1)
234
		fmt.Fprintf(b, "Amount: <elided>\n")
235
	}
236
	if p.Cost != nil {
237
		dumpCost(b, p.Cost, depth+1)
238
	}
239
	if p.Balance != nil {
240
		dumpBalanceAssertion(b, p.Balance, depth+1)
241
	}
242
	dumpOptComment(b, p.Comment, depth+1)
243
	if len(p.Comments) > 0 {
244
		for _, c := range p.Comments {
245
			dumpComment(b, &c, depth+1)
246
		}
247
	}
248
}
249
250
func dumpAmount(b *strings.Builder, a *Amount, depth int) {
251
	indent(b, depth)
252
	fmt.Fprintf(b, "Amount %s\n", a.Span)
253
	indent(b, depth+1)
254
	fmt.Fprintf(b, "Quantity: %s\n", a.Quantity.String())
255
	indent(b, depth+1)
256
	fmt.Fprintf(b, "Commodity: %q", a.Commodity)
257
	if a.Commodity != "" {
258
		fmt.Fprintf(b, " %s", a.CommoditySpan)
259
	}
260
	fmt.Fprintf(b, "\n")
261
	indent(b, depth+1)
262
	fmt.Fprintf(b, "CommodityPos: %s\n", a.CommodityPos)
263
	indent(b, depth+1)
264
	fmt.Fprintf(b, "HasSpace: %v\n", a.HasSpace)
265
	if a.IsExpr {
266
		indent(b, depth+1)
267
		fmt.Fprintf(b, "IsExpr: true\n")
268
	}
269
	if a.Expr != "" {
270
		indent(b, depth+1)
271
		fmt.Fprintf(b, "Expr: %q\n", a.Expr)
272
	}
273
	indent(b, depth+1)
274
	fmt.Fprintf(b, "Precision: %d\n", a.QuantityFmt.Precision)
275
	if a.QuantityFmt.Decimal != 0 {
276
		indent(b, depth+1)
277
		fmt.Fprintf(b, "Decimal: %q\n", string(a.QuantityFmt.Decimal))
278
	}
279
	if a.QuantityFmt.Thousands != 0 {
280
		indent(b, depth+1)
281
		fmt.Fprintf(b, "Thousands: %q\n", string(a.QuantityFmt.Thousands))
282
	}
283
}
284
285
func dumpCost(b *strings.Builder, c *Cost, depth int) {
286
	indent(b, depth)
287
	if c.IsTotal {
288
		fmt.Fprintf(b, "Cost(total) %s\n", c.Span)
289
	} else {
290
		fmt.Fprintf(b, "Cost(unit) %s\n", c.Span)
291
	}
292
	dumpAmount(b, &c.Amount, depth+1)
293
}
294
295
func dumpBalanceAssertion(b *strings.Builder, ba *BalanceAssertion, depth int) {
296
	indent(b, depth)
297
	fmt.Fprintf(b, "BalanceAssertion %s\n", ba.Span)
298
	indent(b, depth+1)
299
	fmt.Fprintf(b, "IsStrict: %v\n", ba.IsStrict)
300
	indent(b, depth+1)
301
	fmt.Fprintf(b, "IsInclusive: %v\n", ba.IsInclusive)
302
	indent(b, depth+1)
303
	fmt.Fprintf(b, "IsAssignment: %v\n", ba.IsAssignment)
304
	dumpAmount(b, &ba.Amount, depth+1)
305
	if ba.Cost != nil {
306
		dumpCost(b, ba.Cost, depth+1)
307
	}
308
}
309
310
func dumpAccount(b *strings.Builder, a Account, depth int) {
311
	indent(b, depth)
312
	fmt.Fprintf(b, "Account %s\n", a.Span)
313
	for _, sub := range a.Name {
314
		indent(b, depth+1)
315
		fmt.Fprintf(b, "SubAccount: %q %s\n", sub.Name, sub.Span)
316
	}
317
}
318
319
func dumpAccountDirective(b *strings.Builder, a *AccountDirective, depth int) {
320
	indent(b, depth)
321
	fmt.Fprintf(b, "AccountDirective %s\n", a.Span)
322
	dumpAccount(b, a.Account, depth+1)
323
	for _, sd := range a.Subdirectives {
324
		if sd.Kind == SubdirectiveComment {
325
			dumpComment(b, sd.Comment, depth+1)
326
			continue
327
		}
328
		indent(b, depth+1)
329
		fmt.Fprintf(b, "Subdirective\n")
330
		indent(b, depth+2)
331
		fmt.Fprintf(b, "Name: %q %s\n", sd.Kind.String(), sd.NameSpan)
332
		indent(b, depth+2)
333
		fmt.Fprintf(b, "Value: %q %s\n", sd.Value, sd.ValueSpan)
334
		dumpOptComment(b, sd.Comment, depth+2)
335
	}
336
	dumpOptComment(b, a.Comment, depth+1)
337
}
338
339
func dumpCommodityDirective(b *strings.Builder, c *CommodityDirective, depth int) {
340
	indent(b, depth)
341
	fmt.Fprintf(b, "CommodityDirective %s\n", c.Span)
342
	indent(b, depth+1)
343
	fmt.Fprintf(b, "Commodity: %q %s\n", c.Commodity, c.CommoditySpan)
344
	if c.FormatSub != nil {
345
		indent(b, depth+1)
346
		if c.FormatSub.KeywordSpan.End.Offset > 0 {
347
			fmt.Fprintf(b, "Format %s\n", c.FormatSub.KeywordSpan)
348
		} else {
349
			fmt.Fprintf(b, "Format inline\n")
350
		}
351
		dumpAmount(b, &c.FormatSub.Amount, depth+2)
352
		dumpOptComment(b, c.FormatSub.Comment, depth+2)
353
	}
354
	for _, cm := range c.BlockComments {
355
		dumpComment(b, cm, depth+1)
356
	}
357
	dumpOptComment(b, c.Comment, depth+1)
358
}
359
360
func dumpMarketPriceDirective(b *strings.Builder, m *MarketPriceDirective, depth int) {
361
	indent(b, depth)
362
	fmt.Fprintf(b, "MarketPriceDirective %s\n", m.Span)
363
	indent(b, depth+1)
364
	fmt.Fprintf(b, "Date: %s\n", dumpDate(m.DateTime.Date))
365
	if m.DateTime.Time != nil {
366
		indent(b, depth+1)
367
		fmt.Fprintf(b, "Time: %02d:%02d:%02d\n", m.DateTime.Time.Hour, m.DateTime.Time.Minute, m.DateTime.Time.Second)
368
	}
369
	indent(b, depth+1)
370
	fmt.Fprintf(b, "Commodity: %q\n", m.Commodity)
371
	dumpAmount(b, &m.Amount, depth+1)
372
}
373
374
func dumpConversionDirective(b *strings.Builder, c *ConversionDirective, depth int) {
375
	indent(b, depth)
376
	fmt.Fprintf(b, "ConversionDirective %s\n", c.Span)
377
	indent(b, depth+1)
378
	fmt.Fprintf(b, "From:\n")
379
	dumpAmount(b, &c.From, depth+2)
380
	indent(b, depth+1)
381
	fmt.Fprintf(b, "To:\n")
382
	dumpAmount(b, &c.To, depth+2)
383
	dumpOptComment(b, c.Comment, depth+1)
384
}
385
386
func dumpComment(b *strings.Builder, c *Comment, depth int) {
387
	indent(b, depth)
388
	fmt.Fprintf(b, "Comment %s\n", c.Span)
389
	indent(b, depth+1)
390
	fmt.Fprintf(b, "Marker: %q\n", string(c.Marker))
391
	if len(c.Tags) > 0 {
392
		indent(b, depth+1)
393
		fmt.Fprintf(b, "Tags\n")
394
		for _, t := range c.Tags {
395
			indent(b, depth+2)
396
			fmt.Fprintf(b, "%q=%q  %s\n", t.Key, t.Value, t.Span)
397
		}
398
	}
399
	indent(b, depth+1)
400
	fmt.Fprintf(b, "Text: %q\n", c.Text)
401
}
402
403
func dumpOptComment(b *strings.Builder, c *Comment, depth int) {
404
	if c == nil {
405
		return
406
	}
407
	dumpComment(b, c, depth)
408
}
409
410
func dumpDate(d Date) string {
411
	return fmt.Sprintf("%04d%s%02d%s%02d", d.Year, string(d.Sep), d.Month, string(d.Sep), d.Day)
412
}