3 files changed,
41 insertions(+),
41 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-08-04 14:36:36 +0300
Authored at:
2026-08-04 13:07:16 +0300
Change ID:
uonzsmkvkxquwtvtzsmnovluzpkyoolr
Parent:
9ee2ed8
M
journal/lexer/lexer.go
··· 8 8 "olexsmir.xyz/clerk/journal/token" 9 9 ) 10 10 11 -type Mode uint 11 +type mode uint 12 12 13 13 const ( 14 14 // start of a line, nothing consumed 15 - ModeDefault Mode = iota 15 + modeDefault mode = iota 16 16 17 17 // after ; # * % ; at start of line, or anywhere inline 18 18 // everything until \n is comment text 19 - ModeComment 19 + modeComment 20 20 21 21 // after lexing a date at column 0 22 22 // expects: optional status, optional code, description, comment 23 - ModeTransaction 23 + modeTransaction 24 24 25 25 // after lexing an indent at start of line 26 26 // expects: account name, then two spaces, then amount 27 - ModePosting 27 + modePosting 28 28 29 29 // after ~, period expression 30 30 // expects: period, optional description (after 2+ spaces), optional comment 31 - ModePeriodic 31 + modePeriodic 32 32 33 33 // after =, automates transaction 34 34 // expects: expression 35 - ModeAutomated 35 + modeAutomated 36 36 37 37 // after a directive keyword like account, commodity, include 38 38 // expects: rest of directive content 39 - ModeDirective 39 + modeDirective 40 40 ) 41 41 42 42 type Lexer struct { 43 43 file string 44 44 input []byte 45 - mode Mode 45 + mode mode 46 46 47 47 ch rune // current rune (0 = EOF/sentinel) 48 48 chSize int // byte size of current rune ··· 78 78 // Next returns next token in the input 79 79 func (l *Lexer) Next() token.Token { 80 80 switch l.mode { 81 - case ModeDefault: 81 + case modeDefault: 82 82 return l.lexDefault() 83 - case ModeComment: 83 + case modeComment: 84 84 return l.lexComment() 85 - case ModeTransaction: 85 + case modeTransaction: 86 86 return l.lexTransaction() 87 - case ModePosting: 87 + case modePosting: 88 88 return l.lexPosting() 89 - case ModePeriodic: 89 + case modePeriodic: 90 90 return l.lexPeriodic() 91 - case ModeAutomated: 91 + case modeAutomated: 92 92 return l.lexAutomated() 93 - case ModeDirective: 93 + case modeDirective: 94 94 return l.lexDirective() 95 95 } 96 96 panic("unreachable") ··· 115 115 case l.ch == ' ' || l.ch == '\t': 116 116 tok := l.lexIndent() 117 117 if l.subdirective { 118 - l.mode = ModeDirective 118 + l.mode = modeDirective 119 119 l.subdirective = false 120 120 } else { 121 - l.mode = ModePosting 121 + l.mode = modePosting 122 122 } 123 123 l.postingExpectAccount = true 124 124 return tok 125 125 case l.ch == ';' || l.ch == '#' || l.ch == '%': 126 - l.mode = ModeComment 126 + l.mode = modeComment 127 127 return l.lexSingle(token.SEMICOLON) // todo: ?? 128 128 case l.ch == '*': // * at col 0 == comment 129 - l.mode = ModeComment 129 + l.mode = modeComment 130 130 return l.lexSingle(token.STAR) 131 131 case l.ch == '~': 132 - l.mode = ModePeriodic 132 + l.mode = modePeriodic 133 133 return l.lexSingle(token.TILDE) 134 134 case l.ch == '=': 135 - l.mode = ModeAutomated 135 + l.mode = modeAutomated 136 136 return l.lexSingle(token.EQ) 137 137 case l.ch == '+': 138 138 return l.lexSingle(token.PLUS) ··· 155 155 return token.Token{Type: token.ILLEGAL, Literal: string(l.input[s.offset:l.pos]), Span: l.span(s)} 156 156 } 157 157 tok := l.lexDate() 158 - l.mode = ModeTransaction 158 + l.mode = modeTransaction 159 159 l.transactionPastStatus = false 160 160 return tok 161 161 default: ··· 167 167 168 168 func (l *Lexer) lexComment() token.Token { 169 169 if l.ch == '\n' || l.ch == '\r' || l.ch == 0 { 170 - l.mode = ModeDefault 170 + l.mode = modeDefault 171 171 if l.ch == '\r' { 172 172 l.col = 0 173 173 l.advance() ··· 180 180 } 181 181 182 182 if l.ch == '\n' || l.ch == '\r' || l.ch == 0 { 183 - l.mode = ModeDefault 183 + l.mode = modeDefault 184 184 if l.ch == '\r' { 185 185 l.col = 0 186 186 l.advance() ··· 205 205 case 0: 206 206 return l.token(token.EOF, "") 207 207 case '\n': 208 - l.mode = ModeDefault 208 + l.mode = modeDefault 209 209 return l.lexNewline() 210 210 case '\r': 211 211 l.col = 0 ··· 214 214 case ' ', '\t': 215 215 return l.lexWhitespace() 216 216 case ';': 217 - l.mode = ModeComment 217 + l.mode = modeComment 218 218 return l.lexSingle(token.SEMICOLON) 219 219 case '*': 220 220 if !l.transactionPastStatus { ··· 271 271 case 0: 272 272 return l.token(token.EOF, "") 273 273 case '\n': 274 - l.mode = ModeDefault 274 + l.mode = modeDefault 275 275 return l.lexNewline() 276 276 case '\r': 277 277 l.col = 0 278 278 l.advance() 279 279 return l.lexNewline() 280 280 case ';': 281 - l.mode = ModeComment 281 + l.mode = modeComment 282 282 return l.lexSingle(token.SEMICOLON) 283 283 case ' ', '\t': 284 284 return l.lexWhitespace() ··· 292 292 case 0: 293 293 return l.token(token.EOF, "") 294 294 case '\n': 295 - l.mode = ModeDefault 295 + l.mode = modeDefault 296 296 return l.lexNewline() 297 297 case '\r': 298 298 l.col = 0 ··· 301 301 case ' ', '\t': 302 302 return l.lexWhitespace() 303 303 case ';': 304 - l.mode = ModeComment 304 + l.mode = modeComment 305 305 return l.lexSingle(token.SEMICOLON) 306 306 default: 307 307 return l.lexText() ··· 315 315 return l.token(token.EOF, "") 316 316 case l.ch == '\n': 317 317 l.postingExpectAccount = false 318 - l.mode = ModeDefault 318 + l.mode = modeDefault 319 319 return l.lexNewline() 320 320 case l.ch == '\r': 321 321 l.postingExpectAccount = false 322 322 l.col = 0 323 323 l.advance() 324 - l.mode = ModeDefault 324 + l.mode = modeDefault 325 325 return l.lexNewline() 326 326 case l.ch == ';': 327 327 l.postingExpectAccount = false 328 - l.mode = ModeComment 328 + l.mode = modeComment 329 329 return l.lexSingle(token.SEMICOLON) 330 330 case l.ch == ' ' || l.ch == '\t': 331 331 return l.lexWhitespace() ··· 378 378 func (l *Lexer) lexDirective() token.Token { 379 379 switch { 380 380 case l.ch == '\n', l.ch == 0: 381 - l.mode = ModeDefault 381 + l.mode = modeDefault 382 382 return l.lexNewline() 383 383 case l.ch == '\r': 384 - l.mode = ModeDefault 384 + l.mode = modeDefault 385 385 l.col = 0 386 386 l.advance() 387 387 return l.lexNewline() 388 388 case l.ch == ';': 389 - l.mode = ModeComment 389 + l.mode = modeComment 390 390 return l.lexSingle(token.SEMICOLON) 391 391 case l.ch == ' ', l.ch == '\t': 392 392 return l.lexWhitespace() ··· 444 444 func (l *Lexer) lexNewline() token.Token { 445 445 s := l.save() 446 446 l.advance() 447 - l.mode = ModeDefault 447 + l.mode = modeDefault 448 448 return token.Token{Type: token.NEWLINE, Literal: "\n", Span: l.span(s)} 449 449 } 450 450 ··· 601 601 if kind == token.ILLEGAL { // todo: report an error ?? 602 602 kind = token.TEXT 603 603 } else { 604 - l.mode = ModeDirective 604 + l.mode = modeDirective 605 605 l.subdirective = kind == token.ACCOUNT || kind == token.COMMODITY 606 606 l.includePath = kind == token.INCLUDE 607 607 }