github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/identifiers/parser.go (about) 1 // Copyright 2021 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 5 package identifiers 6 7 // This file provides a parser for block range strings. 8 // 9 // Supported formats are (including permutations): 10 // [blockNumber] 11 // [blockNumber]:[stepOrPeriod] 12 // [blockNumber]-[blockNumber] 13 // [blockNumber]-[blockNumber]:[stepOrPeriod] 14 // [special] 15 // [special]:[stepOrPeriod] 16 // [special]-[blockNumber] 17 // [special]-[blockNumber]:[stepOrPeriod] 18 // [special]-[special] 19 // [special]-[special]:[stepOrPeriod] 20 // [special]-[date] 21 // [special]-[date]:[stepOrPeriod] 22 // [date] 23 // [date]:[stepOrPeriod] 24 // [date]-[blockNumber] 25 // [date]-[blockNumber]:[stepOrPeriod] 26 // [date]-[special] 27 // [date]-[special]:[stepOrPeriod] 28 // [date]-[date] 29 // [date]-[date]:[stepOrPeriod] 30 // 31 // 10 32 // 10:100 33 // 10:weekly 34 35 // 10-1000 36 // 10-1000:100 37 // 10-1000:weekly 38 39 // 10-london 40 // 10-london:100 41 // 10-london:weekly 42 43 // 10-2021-10-03 44 // 10-2021-10-03T10 45 // 10-2021-10-03T10:30:59 46 // 10-2021-10-03T10:30:59:100 47 48 // 2021-10-03 49 // 2021-10-03T10:30:59 50 // 2021-10-03T10:30:59:100 51 // 2021-10-03T10:30:59-1000:100 52 53 import ( 54 "encoding/json" 55 56 "github.com/alecthomas/participle/v2" 57 "github.com/alecthomas/participle/v2/lexer" 58 ) 59 60 // Define "tokens" for our lexer 61 var rangeLexer = lexer.MustSimple([]lexer.SimpleRule{ 62 {Name: `Date`, Pattern: `\d{4}-\d{2}-\d{2}(T[\d]{2}(:[\d]{2})?(:[\d]{2})?(UTC)?)?`}, 63 {Name: `Special`, Pattern: `[a-z_]+[0-9]*`}, 64 {Name: `Hash`, Pattern: `0x[a-f0-9]{64}`}, 65 {Name: `Hex`, Pattern: `0x[a-f0-9]+`}, 66 {Name: `Unsigned`, Pattern: `^[0-9]+`}, 67 {Name: `PointSeparator`, Pattern: `-`}, 68 {Name: `ModifierSeparator`, Pattern: `:`}, 69 }) 70 71 // A Point carries information about when a range starts or ends. It can be 72 // a block number, a date or special name (e.g. "london" is translated to 73 // block 12965000) 74 type Point struct { 75 Number uint `parser:"@Hex|@Unsigned" json:"number,omitempty"` 76 Hash string `parser:"| @Hash" json:"hash,omitempty"` 77 Date string `parser:"| @Date" json:"date,omitempty"` 78 Special string `parser:"| @Special" json:"special,omitempty"` 79 } 80 81 func (p Point) String() string { 82 str, _ := json.MarshalIndent(p, "", " ") 83 return string(str) 84 } 85 86 // Modifier changes the meaning of the given range. For example, if step of 87 // 10 is provided, the range does not mean "each block from start to end" 88 // anymore, but instead "every 10th block from start to end". Similarly 89 // a period can be provided to get only blocks based on frequency (e.g. weekly) 90 type Modifier struct { 91 Step uint `parser:"@Unsigned" json:"step,omitempty"` 92 Period string `parser:"| @('hourly'|'daily'|'weekly'|'monthly'|'quarterly'|'annually'|'next'|'prev'|'all')" json:"period,omitempty"` 93 } 94 95 // Range is uses after having defined both Point and Modifier, we can construct 96 // our Range, which consist of a starting point, optionally followed by an ending 97 // point and optionally finished by a modifier. Separators for points and modifier 98 // are defined above as PointSeparator and ModifierSeparator 99 type Range struct { 100 Points []*Point `parser:"@@('-'@@)?"` 101 Modifier *Modifier `parser:"(':'@@)?"` 102 } 103 104 // Build parser 105 var parser = participle.MustBuild[Range]( 106 participle.Lexer(rangeLexer), 107 ) 108 109 // Parse takes a string and tries to parse it into Range struct, which always have 110 // at least one Point (but no more than two: start and end) and may have 111 // Modifier. 112 func Parse(source string) (*Range, error) { 113 blockRange, err := parser.ParseString("", source) 114 115 return blockRange, err 116 }