From 4538550638712c32087407750475e93c88fa9816 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Fri, 9 Aug 2024 16:04:26 +0200 Subject: Add support for mintmarks --- mintages/parser.go | 61 +++++++++++++++++++++++++++++++++++++++++-------- mintages/parser_test.go | 47 +++++++++++++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 14 deletions(-) (limited to 'mintages') diff --git a/mintages/parser.go b/mintages/parser.go index 18e7f33..87b9d19 100644 --- a/mintages/parser.go +++ b/mintages/parser.go @@ -23,15 +23,19 @@ func (e SyntaxError) Error() string { e.file, e.linenr, e.expected, e.got) } -type coinset [8]int +type Row struct { + Label string + Cols [8]int +} type Data struct { StartYear int - Circ, BU, Proof []coinset + Circ, BU, Proof []Row } func ForCountry(code string) (Data, error) { path := filepath.Join("data", "mintages", code) + f, err := os.Open(path) if err != nil { return Data{}, err @@ -42,12 +46,15 @@ func ForCountry(code string) (Data, error) { func parse(reader io.Reader, file string) (Data, error) { var ( - data Data // Our data struct - slice *[]coinset // Where to append mintages + data Data // Our data struct + slice *[]Row // Where to append mintages + year int // The current year we are at ) scanner := bufio.NewScanner(reader) for linenr := 1; scanner.Scan(); linenr++ { + var mintmark string + line := scanner.Text() tokens := strings.FieldsFunc(strings.TrimSpace(line), unicode.IsSpace) @@ -84,6 +91,20 @@ func parse(reader io.Reader, file string) (Data, error) { } data.StartYear, _ = strconv.Atoi(arg) } + + year = data.StartYear - 1 + case isLabel(tokens[0]): + mintmark = tokens[0][:len(tokens[0])-1] + tokens = tokens[1:] + if !isNumeric(tokens[0], true) && tokens[0] != "?" { + return Data{}, SyntaxError{ + expected: "mintage row after label", + got: tokens[0], + file: file, + linenr: linenr, + } + } + fallthrough case isNumeric(tokens[0], true), tokens[0] == "?": switch { case slice == nil: @@ -102,7 +123,7 @@ func parse(reader io.Reader, file string) (Data, error) { } } - numcoins := len(coinset{}) + numcoins := len(Row{}.Cols) tokcnt := len(tokens) if tokcnt != numcoins { @@ -118,12 +139,24 @@ func parse(reader io.Reader, file string) (Data, error) { } } - var row coinset + var row Row + switch { + case mintmark == "": + year += 1 + row.Label = strconv.Itoa(year) + case mintmark[len(mintmark)-1] == '*': + year += 1 + mintmark = mintmark[:len(mintmark)-1] + fallthrough + default: + row.Label = fmt.Sprintf("%d %s", year, mintmark) + } + for i, tok := range tokens { if tok == "?" { - row[i] = -1 + row.Cols[i] = -1 } else { - row[i] = atoiWithDots(tok) + row.Cols[i] = atoiWithDots(tok) } } *slice = append(*slice, row) @@ -141,11 +174,15 @@ func parse(reader io.Reader, file string) (Data, error) { for each year that we haven’t filled in info for. This avoids things accidentally breaking if the new year comes and we forget to add extra rows. */ - for _, ms := range [...]*[]coinset{&data.Circ, &data.BU, &data.Proof} { + for _, ms := range [...]*[]Row{&data.Circ, &data.BU, &data.Proof} { finalYear := len(*ms) + data.StartYear - 1 missing := time.Now().Year() - finalYear for i := 0; i < missing; i++ { - *ms = append(*ms, coinset{-1, -1, -1, -1, -1, -1, -1, -1}) + label := strconv.Itoa(finalYear + i + 1) + *ms = append(*ms, Row{ + Label: label, + Cols: [8]int{-1, -1, -1, -1, -1, -1, -1, -1}, + }) } } @@ -167,6 +204,10 @@ func isNumeric(s string, dot bool) bool { return true } +func isLabel(s string) bool { + return s[len(s)-1] == ':' && len(s) > 1 +} + func atoiWithDots(s string) int { n := 0 for _, ch := range s { diff --git a/mintages/parser_test.go b/mintages/parser_test.go index 9ff4bc5..b2e4ef9 100644 --- a/mintages/parser_test.go +++ b/mintages/parser_test.go @@ -34,7 +34,7 @@ func TestParserComplete(t *testing.T) { number of padding mintages is actually correct. */ for i, row := range data.Circ { - for j, col := range row { + for j, col := range row.Cols { var n int switch { case i == 1 && j == 1, i >= 2: @@ -49,7 +49,7 @@ func TestParserComplete(t *testing.T) { } for i, row := range data.BU { - for j, col := range row { + for j, col := range row.Cols { var n int switch { case i == 1 && j == 1, i >= 2: @@ -64,7 +64,7 @@ func TestParserComplete(t *testing.T) { } for i, row := range data.Proof { - for j, col := range row { + for j, col := range row.Cols { var n int switch { case i == 1 && j == 1, i >= 2: @@ -106,7 +106,7 @@ func TestParserNoProof(t *testing.T) { } for _, row := range data.Proof { - for _, col := range row { + for _, col := range row.Cols { if col != -1 { t.Fatalf("Expected data.Proof[i][j]=-1; got %d", col) } @@ -119,6 +119,45 @@ func TestParserNoProof(t *testing.T) { } } +func TestParserMintmarks(t *testing.T) { + data, err := parse(bytes.NewBuffer([]byte(` + BEGIN 2020 + BEGIN CIRC + 1.000 1001 1002 1003 1004 1005 1006 1007 + KNM*: 2000 ? 2002 2003 2004 2005 2006 2007 + MdP: 3000 ? 3002 3003 3004 3005 3006 3007 + `)), "-") + + if err != nil { + t.Fatalf(`Expected err=nil; got "%s"`, err) + } + + for i, row := range data.Circ { + for j, col := range row.Cols { + var n int + switch { + case i > 0 && j == 1, i >= 3: + n = -1 + default: + n = 1000*i + j + 1000 + } + if col != n { + t.Fatalf("Expected data.Circ[i][j]=%d; got %d", n, col) + } + } + } + + if data.Circ[0].Label != "2020" { + t.Fatalf(`Expected data.Circ[0].Label="2020"; got %s`, data.Circ[0].Label) + } + if data.Circ[1].Label != "2021\u00A0KNM" { + t.Fatalf(`Expected data.Circ[1].Label="2021 KNM"; got %s`, data.Circ[1].Label) + } + if data.Circ[2].Label != "2021\u00A0MdP" { + t.Fatalf(`Expected data.Circ[2].Label="2021 MdP"; got %s`, data.Circ[2].Label) + } +} + func TestParserNoYear(t *testing.T) { _, err := parse(bytes.NewBuffer([]byte(` BEGIN CIRC -- cgit v1.2.3