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  }