8 files changed,
84 insertions(+),
6 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-05-23 17:52:23 +0300
Authored at:
2026-05-21 14:49:17 +0300
Change ID:
tvzyzpzxlzorwpmwwluvotnnlvlnmkvk
Parent:
fc9cae1
M
journal/ast/directives.go
··· 12 12 13 13 type CommodityDirective struct { 14 14 Commodity string 15 - Format Amount // optional format hint: "1,000.00 UAH" 15 + Format Amount // optional format hint: "1,000.00 UAH" 16 16 Comment *Comment // optional inline comment 17 17 Span token.Span 18 18 } ··· 84 84 } 85 85 86 86 func (MarketPriceDirective) entryNode() {} 87 + 88 +type ConversionDirective struct { 89 + From Amount 90 + To Amount 91 + Comment *Comment // optional inline comment 92 + Span token.Span 93 +} 94 + 95 +func (ConversionDirective) entryNode() {} 87 96 88 97 type ApplyDirective struct { 89 98 Expr string // text after apply e.g "tag foo"
M
journal/ast/dump.go
··· 78 78 fmt.Fprintf(b, "Mark: %q\n", string(e.Mark)) 79 79 case *MarketPriceDirective: 80 80 dumpMarketPriceDirective(b, e, depth) 81 + case *ConversionDirective: 82 + dumpConversionDirective(b, e, depth) 81 83 case *DefaultCommodityDirective: 82 84 indent(b, depth) 83 85 fmt.Fprintf(b, "DefaultCommodityDirective %s\n", e.Span) ··· 325 327 indent(b, depth+1) 326 328 fmt.Fprintf(b, "Commodity: %q\n", m.Commodity) 327 329 dumpAmount(b, &m.Amount, depth+1) 330 +} 331 + 332 +func dumpConversionDirective(b *strings.Builder, c *ConversionDirective, depth int) { 333 + indent(b, depth) 334 + fmt.Fprintf(b, "ConversionDirective %s\n", c.Span) 335 + indent(b, depth+1) 336 + fmt.Fprintf(b, "From:\n") 337 + dumpAmount(b, &c.From, depth+2) 338 + indent(b, depth+1) 339 + fmt.Fprintf(b, "To:\n") 340 + dumpAmount(b, &c.To, depth+2) 341 + dumpOptComment(b, c.Comment, depth+1) 328 342 } 329 343 330 344 func dumpComment(b *strings.Builder, c *Comment, depth int) {
M
journal/parser/parser.go
··· 40 40 switch t { 41 41 case token.COMMENTKW, token.ACCOUNT, token.COMMODITY, token.INCLUDE, 42 42 token.ALIAS, token.PAYEE, token.TAG, token.APPLY, token.END, 43 - token.YEAR, token.DECIMALMARK, token.D, token.P, token.N: 43 + token.YEAR, token.DECIMALMARK, token.D, token.P, token.N, token.C: 44 44 return true 45 45 } 46 46 return false ··· 93 93 return p.parseMarketPriceDirective() 94 94 case token.N: 95 95 return p.parseIgnoredDirective() 96 + case token.C: 97 + return p.parseConversionDirective() 96 98 case token.APPLY: 97 99 return p.parseApplyDirective() 98 100 case token.END: ··· 561 563 return com 562 564 } 563 565 566 +func (p *Parser) parseConversionDirective() *ast.ConversionDirective { 567 + s := p.cur.Span 568 + cd := &ast.ConversionDirective{} 569 + p.expect(token.C) 570 + p.skipWhitespace() 571 + 572 + if p.isAmountStart() { 573 + cd.From = *p.parseAmount() 574 + } else { 575 + p.errorf("expected amount, got %s", p.cur.Type) 576 + } 577 + 578 + p.skipWhitespace() 579 + if p.got(token.EQ) { 580 + p.advance() 581 + p.skipWhitespace() 582 + if p.isAmountStart() { 583 + cd.To = *p.parseAmount() 584 + } else { 585 + p.errorf("expected amount, got %s", p.cur.Type) 586 + } 587 + } 588 + 589 + p.skipWhitespace() 590 + cd.Comment = p.parseOptInlineComment() 591 + p.expectNewline() 592 + cd.Span = p.span(s) 593 + return cd 594 +} 595 + 564 596 func (p *Parser) parseIgnoredDirective() *ast.IgnoredDirective { 565 597 s := p.cur.Span 566 598 p.expect(token.N) ··· 797 829 switch p.cur.Type { 798 830 case token.WHITESPACE: 799 831 p.skipWhitespace() 800 - if p.got(token.COMMODITYMARK) { 832 + if p.got(token.COMMODITYMARK) || p.got(token.TEXT) { 801 833 amt.HasSpace = true 802 834 amt.Commodity = p.cur.Literal 803 835 amt.CommodityPos = ast.CommodityAfter 804 836 p.advance() 805 837 } 806 - case token.COMMODITYMARK: 838 + case token.COMMODITYMARK, token.TEXT: 807 839 amt.Commodity = p.cur.Literal 808 840 amt.CommodityPos = ast.CommodityAfter 809 841 p.advance()
A
journal/parser/testdata/golden/Parser_ParseFile__C_directive.golden
··· 1 +Journal 2 + ConversionDirective j:1:1-2:1 3 + From: 4 + Amount j:1:3-1:3 5 + Quantity: 1 6 + Commodity: "s" 7 + CommodityPos: After 8 + HasSpace: false 9 + Precision: 2 10 + Decimal: "." 11 + To: 12 + Amount j:1:11-1:11 13 + Quantity: 100 14 + Commodity: "c" 15 + CommodityPos: After 16 + HasSpace: false 17 + Precision: 0 18 + Decimal: "."
M
journal/token/type_string.go
··· 58 58 _ = x[D-47] 59 59 _ = x[P-48] 60 60 _ = x[N-49] 61 + _ = x[C-50] 61 62 } 62 63 63 -const _Type_name = "ILLEGALEOFWHITESPACEINDENTNEWLINEINTDECIMALSTRINGTEXTBANGSTARPERCENTHASHSEMICOLONCOLONEQEQEQEQEQEQATATATPIPEPLUSMINUSTILDELPARENRPARENLBRACELBRACELBRACERBRACERBRACERBRACELBRACKETRBRACKETCOMMODITYMARKDATETIMEPARENEXPRCOMMENTKWACCOUNTCOMMODITYINCLUDEALIASPAYEETAGAPPLYENDYEARDECIMALMARKDPN" 64 +const _Type_name = "ILLEGALEOFWHITESPACEINDENTNEWLINEINTDECIMALSTRINGTEXTBANGSTARPERCENTHASHSEMICOLONCOLONEQEQEQEQEQEQATATATPIPEPLUSMINUSTILDELPARENRPARENLBRACELBRACELBRACERBRACERBRACERBRACELBRACKETRBRACKETCOMMODITYMARKDATETIMEPARENEXPRCOMMENTKWACCOUNTCOMMODITYINCLUDEALIASPAYEETAGAPPLYENDYEARDECIMALMARKDPNC" 64 65 65 -var _Type_index = [...]uint16{0, 7, 10, 20, 26, 33, 36, 43, 49, 53, 57, 61, 68, 72, 81, 86, 88, 92, 98, 100, 104, 108, 112, 117, 122, 128, 134, 140, 152, 164, 170, 178, 186, 199, 203, 207, 216, 225, 232, 241, 248, 253, 258, 261, 266, 269, 273, 284, 285, 286, 287} 66 +var _Type_index = [...]uint16{0, 7, 10, 20, 26, 33, 36, 43, 49, 53, 57, 61, 68, 72, 81, 86, 88, 92, 98, 100, 104, 108, 112, 117, 122, 128, 134, 140, 152, 164, 170, 178, 186, 199, 203, 207, 216, 225, 232, 241, 248, 253, 258, 261, 266, 269, 273, 284, 285, 286, 287, 288} 66 67 67 68 func (i Type) String() string { 68 69 idx := int(i) - 0