diff options
author | Thomas Voss <mail@thomasvoss.com> | 2024-08-12 14:56:37 +0200 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2024-08-12 14:56:37 +0200 |
commit | 792b2bd299b611e378679e5a269cc249e079c60e (patch) | |
tree | 1c601d00398b79460a0f5694aad02536fec39897 | |
parent | 0003a3d51b2a7be791bf60b2cddaebb14dc65183 (diff) |
Use a new mintage format
-rw-r--r-- | cmd/mfmt/main.go | 168 | ||||
-rw-r--r-- | cmd/mfmt/main_test.go | 214 | ||||
-rw-r--r-- | cmd/mfmt/util_test.go | 28 | ||||
-rw-r--r-- | contrib/syntax/mintage.vim | 13 | ||||
-rw-r--r-- | data/mintages/ad | 97 | ||||
-rw-r--r-- | data/mintages/at | 190 | ||||
-rw-r--r-- | lib/mintage/parser.go | 318 | ||||
-rw-r--r-- | lib/mintage/parser_test.go | 315 | ||||
-rw-r--r-- | main.go | 26 | ||||
-rw-r--r-- | template/coins_mintages.templ | 44 |
10 files changed, 758 insertions, 655 deletions
diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go index 263b258..7f7ec8b 100644 --- a/cmd/mfmt/main.go +++ b/cmd/mfmt/main.go @@ -1,25 +1,35 @@ +/* Simple formatter for mintage files. This is not perfect and doesn’t + check for syntactic correctness, it’s just to get stuff aligned + nicely. Maybe in the future I will construct a military-grade + formatter. */ + package main import ( + "bufio" "fmt" "io" "os" "path/filepath" - "unsafe" + "regexp" + "strings" +) - "git.thomasvoss.com/euro-cash.eu/lib/mintage" +const ( + ws = " \t" + longestNum = len("1.000.000.000") ) -const cols = unsafe.Sizeof(mintage.Row{}.Cols) / - unsafe.Sizeof(mintage.Row{}.Cols[0]) +var ( + rv int -var rv int + reMintageYear = regexp.MustCompile(`^\d{4}(-[^ \t]+)?`) + reMintageRow = regexp.MustCompile(`^(([0-9.]+|\?)[ \t]+)*([0-9.]+|\?)$`) +) func main() { if len(os.Args) == 1 { - if err := mfmt("-", os.Stdin, os.Stdout); err != nil { - warn(err) - } + mfmt("-", os.Stdin, os.Stdout) } for _, arg := range os.Args[1:] { f, err := os.OpenFile(arg, os.O_RDWR, 0) @@ -28,117 +38,70 @@ func main() { continue } defer f.Close() - if err := mfmt(arg, f, f); err != nil { - warn(err) - } + mfmt(arg, f, f) } os.Exit(rv) } -func mfmt(path string, in io.Reader, out io.Writer) error { - data, err := mintage.Parse(in, path) - if err != nil { - return err - } - - f, outfile := out.(*os.File) - if outfile { - _, err := f.Seek(0, io.SeekStart) - if err != nil { - return err +func mfmt(file string, r io.Reader, w io.Writer) { + scanner := bufio.NewScanner(r) + for linenr := 1; scanner.Scan(); linenr++ { + line := strings.Trim(scanner.Text(), ws) + + switch { + case len(line) == 0, line[0] == '#': + fmt.Fprintln(w, line) + case reMintageYear.MatchString(line): + fmtMintageYear(line, w) + case reMintageRow.MatchString(line): + fmtMintageRow(line, w) + default: + warn(fmt.Sprintf("%s:%d: potential syntax error", file, linenr)) + fmt.Fprintln(w, line) } } +} - fmt.Fprintf(out, `BEGIN %d`, data.StartYear) - for i, tbl := range data.Tables { - if len(tbl) != 0 { - var section string - switch mintage.CoinType(i) { - case mintage.TypeCirculated: - section = "CIRC" - case mintage.TypeNIFC: - section = "BU" - case mintage.TypeProof: - section = "PROOF" - } - fmt.Fprintf(out, "\n\nBEGIN %s\n", section) - formatSection(out, tbl) - } - } - fmt.Fprintln(out, "") - - if outfile { - if off, err := f.Seek(0, io.SeekCurrent); err != nil { - return err - } else if err = f.Truncate(off); err != nil { - return err - } +func fmtMintageYear(line string, w io.Writer) { + switch i := strings.IndexAny(line, ws); i { + case -1: + fmt.Fprintln(w, line) + default: + fmt.Fprintf(w, "%s %s\n", line[:i], strings.TrimLeft(line[i:], ws)) } - return nil } -func formatSection(out io.Writer, rows []mintage.Row) { - var ( - longestMM int - longestNum [cols]int - ) +func fmtMintageRow(line string, w io.Writer) { + tokens := strings.FieldsFunc(line, func(r rune) bool { + return strings.ContainsRune(ws, r) + }) - for i, row := range rows { - n := len(row.Mintmark) - if n != 0 && i != 0 && row.Year != rows[i-1].Year { - n++ - } - longestMM = max(longestMM, n) + for i, tok := range tokens { + s := formatMintage(tok) - for j, col := range row.Cols { - n := intlen(col) - longestNum[j] = max(longestNum[j], n+(n-1)/3) + if i == 0 { + fmt.Fprintf(w, "\t%*s", longestNum, s) + } else { + fmt.Fprintf(w, "%*s", longestNum+1, s) } } - extraSpace := 0 - if longestMM != 0 { - extraSpace = 2 - } - - for i, row := range rows { - var label string - if row.Mintmark != "" { - if i != 0 && row.Year != rows[i-1].Year { - label = row.Mintmark + "*: " - } else { - label = row.Mintmark + ": " - } - } - fmt.Fprintf(out, "%-*s", longestMM+extraSpace, label) - for j, n := range row.Cols { - if j != 0 { - fmt.Fprintf(out, " ") - } - fmt.Fprintf(out, "%*s", longestNum[j], formatInt(n)) - } - - if i != len(rows)-1 { - fmt.Fprintln(out, "") - } - } + fmt.Fprintln(w) } -func formatInt(n int) string { - if n <= mintage.Invalid { - panic(fmt.Sprintf("invalid input %d", n)) - } else if n == mintage.Unknown { - return "?" +func formatMintage(s string) string { + if s == "?" { + return s } + n := atoiWithDots(s) digits := intlen(n) dots := (digits - 1) / 3 out := make([]byte, digits+dots) for i, j := len(out)-1, 0; ; i-- { out[i] = byte(n%10) + 48 - n /= 10 - if n == 0 { + if n /= 10; n == 0 { return string(out) } if j++; j == 3 { @@ -150,9 +113,7 @@ func formatInt(n int) string { func intlen(v int) int { switch { - case v <= mintage.Invalid: - panic("mintage count is negative and not -1") - case v == 0, v == mintage.Unknown: + case v == 0: return 1 default: n := 0 @@ -163,7 +124,18 @@ func intlen(v int) int { } } -func warn(e error) { +func atoiWithDots(s string) int { + n := 0 + for _, ch := range s { + if ch == '.' { + continue + } + n = n*10 + int(ch) - '0' + } + return n +} + +func warn(e any) { fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), e) rv = 1 } diff --git a/cmd/mfmt/main_test.go b/cmd/mfmt/main_test.go index 72e3665..7366ae0 100644 --- a/cmd/mfmt/main_test.go +++ b/cmd/mfmt/main_test.go @@ -6,103 +6,135 @@ import ( ) func runTest(t *testing.T, in, out string, nilErr bool) { - r := bytes.NewReader([]byte(in)) - w := new(bytes.Buffer) - - err := mfmt("-", r, w) - if nilErr && err != nil { - t.Fatalf(`Expected err=nil; got "%s"`, err) - } else if !nilErr && err == nil { - t.Fatalf(`Expected err=Error; got nil`) - } - - if nilErr && w.String() != out { - t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String()) - } -} - -func TestSpacing(t *testing.T) { - in := ` - BEGIN 2002 - BEGIN CIRC - - -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - - - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - runTest(t, in, out, true) -} - -func TestAddDots(t *testing.T) { - in := `BEGIN 2002 - -BEGIN CIRC -378383000 326439000 217031000 441621000 203388000 169088000 223544000 196438000 - 10817950 118526000 ? 43859072 50913200 9074200 150000 4679500 -` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - runTest(t, in, out, true) } func TestComplete(t *testing.T) { in := ` - BEGIN 2002 - BEGIN CIRC - - -378383000 326439000 21703...1000 441621000 203388000 .1.6.9.0.8.8.0.0.0 223544000 196438000 - - - 10817950 118526000 ? 43859072 50913200 9074..200 150000 4679500 - - BEGIN PROOF - - -378383000 123456789 ..5 441621000 203388000 .1.6.9.0.8.8.0.0.0 223544000 196438000 - - - 10817950 118526000 ? 43859072 50913200 9074..200 150000 4679500 +2014 +60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 +70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 +3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 + +# Ref: https://example.com +2015 + 0 0 0 0 0 0 0 1.072.400 +40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 + ? ? ? ? ? ? ? ? + +2016 + 0 0 0 0 0 0 2.339.200 0 +35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 + ? ? ? ? ? ? ? ? + +2017 +2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 +22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 + ? ? ? ? ? ? ? ? + +2018 +2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 +20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 + ? ? ? ? ? ? ? ? + +2019 +2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 +15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + ? ? ? ? ? ? ? ? + +2020 + 0 0 0 860.000 175.000 740.000 0 1.500.000 +12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 + ? ? ? ? ? ? ? ? + +2021 + 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2022 + 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2023 + 0 0 0 0 0 0 0 2.075.250 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + + 2014 "Council of Europe" + ? ? ? ` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 - -BEGIN PROOF -378.383.000 123.456.789 5 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 + out := ` +2014 + 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 + 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 + 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 + +# Ref: https://example.com +2015 + 0 0 0 0 0 0 0 1.072.400 + 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 + ? ? ? ? ? ? ? ? + +2016 + 0 0 0 0 0 0 2.339.200 0 + 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 + ? ? ? ? ? ? ? ? + +2017 + 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 + 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 + ? ? ? ? ? ? ? ? + +2018 + 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 + 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 + ? ? ? ? ? ? ? ? + +2019 + 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + ? ? ? ? ? ? ? ? + +2020 + 0 0 0 860.000 175.000 740.000 0 1.500.000 + 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 + ? ? ? ? ? ? ? ? + +2021 + 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2022 + 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2023 + 0 0 0 0 0 0 0 2.075.250 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2014 "Council of Europe" + ? ? ? ` - runTest(t, in, out, true) -} -func TestLabelAlignment(t *testing.T) { - in := `BEGIN 2002 - -BEGIN CIRC -378383000 326439000 217031000 441621000 203388000 169088000 223544000 196438000 -KNM*: 10817950 118526000 ? 43859072 50913200 9074200 150000 4679500 -MdP: 115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 -` - out := `BEGIN 2002 + r := bytes.NewReader([]byte(in)) + w := new(bytes.Buffer) -BEGIN CIRC - 378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 -KNM*: 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -MdP: 115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 -` - runTest(t, in, out, true) + mfmt("-", r, w) + if w.String() != out { + t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String()) + } } diff --git a/cmd/mfmt/util_test.go b/cmd/mfmt/util_test.go index 1041e4f..8366a50 100644 --- a/cmd/mfmt/util_test.go +++ b/cmd/mfmt/util_test.go @@ -3,26 +3,19 @@ package main -import ( - "testing" - - "git.thomasvoss.com/euro-cash.eu/lib/mintage" -) +import "testing" func TestFormatInt(t *testing.T) { - for _, x := range [...]struct { - n int - s string - }{ - {0, "0"}, - {123, "123"}, - {81758, "81.758"}, - {752759237528, "752.759.237.528"}, - {mintage.Unknown, "?"}, + for _, x := range [...]struct{ x, y string }{ + {"?", "?"}, + {"0", "0"}, + {"123", "123"}, + {"81758", "81.758"}, + {"752759237528", "752.759.237.528"}, } { - s := formatInt(x.n) - if s != x.s { - t.Fatalf(`Expected s="%s"; got "%s"`, x.s, s) + s := formatMintage(x.x) + if s != x.y { + t.Fatalf(`Expected s="%s"; got "%s"`, x.y, s) } } } @@ -33,7 +26,6 @@ func TestIntLen(t *testing.T) { {123, len("123")}, {81758, len("81758")}, {752759237528, len("752759237528")}, - {mintage.Unknown, len("?")}, } { n := intlen(x.x) if n != x.y { diff --git a/contrib/syntax/mintage.vim b/contrib/syntax/mintage.vim index 8096f16..be2ea14 100644 --- a/contrib/syntax/mintage.vim +++ b/contrib/syntax/mintage.vim @@ -1,9 +1,12 @@ if exists('b:current_syntax') finish endif -let b:current_syntax = 'mintage' +let b:current_syntax = 'mintage2' -syntax keyword Keyword BEGIN CIRC BU PROOF -syntax match Label /[^\s]\+\*\?:/ -syntax match Number /[0-9\.]\+/ -syntax match Todo /?/ +syntax match Comment /^\s*#.*/ +syntax match Number /[0-9.]\+/ +syntax match Identifier /\v\d{4}(-\S+)?/ +syntax match String /"[^"]\{-}"/ + +" ‘Todo’ is semantically a better syntax group, but it looks bad +syntax match Error /?/ diff --git a/data/mintages/ad b/data/mintages/ad index cd888d3..ca348ce 100644 --- a/data/mintages/ad +++ b/data/mintages/ad @@ -1,40 +1,57 @@ -BEGIN 2014 - -BEGIN CIRC - 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 - 0 0 0 0 0 0 0 1.072.400 - 0 0 0 0 0 0 2.339.200 0 -2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 -2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 -2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 - 0 0 0 860.000 175.000 740.000 0 1.500.000 - 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 - 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 - 0 0 0 0 0 0 0 2.075.250 - ? ? ? ? ? ? ? ? - -BEGIN BU -70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 -40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 -35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 -22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 -20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 -15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 -12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -BEGIN PROOF -3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? +2014 + 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 + 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 + 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 + +2015 + 0 0 0 0 0 0 0 1.072.400 + 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 + ? ? ? ? ? ? ? ? + +2016 + 0 0 0 0 0 0 2.339.200 0 + 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 + ? ? ? ? ? ? ? ? + +2017 + 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 + 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 + ? ? ? ? ? ? ? ? + +2018 + 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 + 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 + ? ? ? ? ? ? ? ? + +2019 + 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + ? ? ? ? ? ? ? ? + +2020 + 0 0 0 860.000 175.000 740.000 0 1.500.000 + 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 + ? ? ? ? ? ? ? ? + +2021 + 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2022 + 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2023 + 0 0 0 0 0 0 0 2.075.250 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2014 "Council of Europe" + ? ? ? diff --git a/data/mintages/at b/data/mintages/at index 923e965..cae2455 100644 --- a/data/mintages/at +++ b/data/mintages/at @@ -1,76 +1,114 @@ -BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 108.523.000 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 -174.700.000 163.200.000 66.100.000 5.200.000 4.100.000 3.100.000 2.600.000 0 - 48.300.000 39.800.000 5.600.000 40.000.000 8.200.000 3.200.000 7.700.000 2.300.000 -111.900.000 72.200.000 52.700.000 81.300.000 45.000.000 3.000.000 41.100.000 0 - 50.900.000 125.100.000 96.700.000 70.200.000 45.300.000 3.000.000 65.500.000 2.600.000 -158.900.000 120.400.000 5.800.000 15.900.000 49.800.000 14.700.000 40.300.000 0 -168.500.000 104.200.000 63.700.000 42.800.000 4.200.000 30.000.000 11.200.000 17.000.000 -189.600.000 148.600.000 66.600.000 27.600.000 21.300.000 6.000.000 8.000.000 27.700.000 -169.300.000 78.100.000 35.300.000 25.000.000 10.800.000 0 0 21.200.000 -179.200.000 121.560.000 36.100.000 30.160.000 25.200.000 0 0 10.100.000 -185.500.000 116.100.000 48.000.000 27.600.000 10.500.000 0 0 20.100.000 -118.000.000 45.400.000 61.000.000 63.100.000 9.000.000 0 0 12.300.000 - 0 0 0 12.300.000 30.000.000 5.000.000 5.200.000 0 - 37.700.000 57.200.000 35.200.000 39.500.000 30.000.000 15.000.000 8.000.000 17.700.000 -138.500.000 85.650.000 22.600.000 30.200.000 20.400.000 17.100.000 5.100.000 0 -130.900.000 91.200.000 15.000.000 15.100.000 25.600.000 2.800.000 2.700.000 15.800.000 - 85.500.000 57.300.000 5.600.000 12.100.000 19.800.000 14.900.000 4.000.000 12.700.000 - 73.400.000 64.600.000 20.200.000 12.100.000 21.000.000 7.400.000 5.400.000 9.900.000 - 38.000.000 58.400.000 16.700.000 15.700.000 25.300.000 8.400.000 7.000.000 9.100.000 - 28.400.000 36.100.000 13.700.000 26.500.000 38.000.000 8.400.000 9.000.000 38.300.000 - ? ? ? ? ? ? ? ? - -BEGIN BU -? ? ? ? ? ? ? ? -? ? ? 15.000 ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? -? ? ? ? ? ? ? ? - -BEGIN PROOF - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? -15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 - ? ? ? ? ? ? ? ? -15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 -15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 - ? ? ? ? ? ? ? ? -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 - ? ? ? ? ? ? ? ? -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 -10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 - 0 0 0 0 0 0 0 0 +2002 + 378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2003 + 10.817.950 118.526.000 108.523.000 43.859.072 50.913.200 9.074.200 150.000 4.679.500 + ? ? ? 15.000 ? ? ? ? + ? ? ? ? ? ? ? ? + +2004 + 115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2005 + 174.700.000 163.200.000 66.100.000 5.200.000 4.100.000 3.100.000 2.600.000 0 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2006 + 48.300.000 39.800.000 5.600.000 40.000.000 8.200.000 3.200.000 7.700.000 2.300.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2007 + 111.900.000 72.200.000 52.700.000 81.300.000 45.000.000 3.000.000 41.100.000 0 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2008 + 50.900.000 125.100.000 96.700.000 70.200.000 45.300.000 3.000.000 65.500.000 2.600.000 + ? ? ? ? ? ? ? ? + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + +2009 + 158.900.000 120.400.000 5.800.000 15.900.000 49.800.000 14.700.000 40.300.000 0 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2010 + 168.500.000 104.200.000 63.700.000 42.800.000 4.200.000 30.000.000 11.200.000 17.000.000 + ? ? ? ? ? ? ? ? + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + +2011 + 189.600.000 148.600.000 66.600.000 27.600.000 21.300.000 6.000.000 8.000.000 27.700.000 + ? ? ? ? ? ? ? ? + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + +2012 + 169.300.000 78.100.000 35.300.000 25.000.000 10.800.000 0 0 21.200.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2013 + 179.200.000 121.560.000 36.100.000 30.160.000 25.200.000 0 0 10.100.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2014 + 185.500.000 116.100.000 48.000.000 27.600.000 10.500.000 0 0 20.100.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2015 + 118.000.000 45.400.000 61.000.000 63.100.000 9.000.000 0 0 12.300.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2016 + 0 0 0 12.300.000 30.000.000 5.000.000 5.200.000 0 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2017 + 37.700.000 57.200.000 35.200.000 39.500.000 30.000.000 15.000.000 8.000.000 17.700.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2018 + 138.500.000 85.650.000 22.600.000 30.200.000 20.400.000 17.100.000 5.100.000 0 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2019 + 130.900.000 91.200.000 15.000.000 15.100.000 25.600.000 2.800.000 2.700.000 15.800.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2020 + 85.500.000 57.300.000 5.600.000 12.100.000 19.800.000 14.900.000 4.000.000 12.700.000 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2021 + 73.400.000 64.600.000 20.200.000 12.100.000 21.000.000 7.400.000 5.400.000 9.900.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2022 + 38.000.000 58.400.000 16.700.000 15.700.000 25.300.000 8.400.000 7.000.000 9.100.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2023 + 28.400.000 36.100.000 13.700.000 26.500.000 38.000.000 8.400.000 9.000.000 38.300.000 + ? ? ? ? ? ? ? ? + 10.000 10.000 10.000 10.000 10.000 10.000 10.000 10.000 + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + 0 0 0 0 0 0 0 0 diff --git a/lib/mintage/parser.go b/lib/mintage/parser.go index fb92e64..76f13e7 100644 --- a/lib/mintage/parser.go +++ b/lib/mintage/parser.go @@ -6,22 +6,7 @@ import ( "io" "strconv" "strings" - "unicode" -) - -type CoinType int - -const ( - TypeCirculated CoinType = iota - TypeNIFC - TypeProof - coinTypes -) - -const ( - _ = -iota - Unknown // Unknown mintage - Invalid // All mintages <= than this are invalid + "time" ) type SyntaxError struct { @@ -35,151 +20,248 @@ func (e SyntaxError) Error() string { e.file, e.linenr, e.expected, e.got) } -type Row struct { +type Data struct { + Standard []SRow + Commemorative []CRow +} + +type SRow struct { Year int Mintmark string - Cols [8]int + Mintages [typeCount][denoms]int } -type Set struct { - StartYear int - Tables [coinTypes][]Row +type CRow struct { + Year int + Mintmark string + Name string + Mintage [typeCount]int } -func (r Row) Label() string { - if r.Mintmark != "" { - return fmt.Sprintf("%d %s", r.Year, r.Mintmark) - } - return strconv.Itoa(r.Year) -} +const ( + TypeCirc = iota + TypeNIFC + TypeProof + typeCount +) -func Parse(reader io.Reader, file string) (Set, error) { - var ( - data Set - ctype CoinType = -1 - ) +const ( + Unknown = -iota - 1 + Invalid +) - scanner := bufio.NewScanner(reader) +const ( + denoms = 8 + ws = " \t" +) + +func Parse(r io.Reader, file string) (Data, error) { + yearsSince := time.Now().Year() - 1999 + 1 + data := Data{ + Standard: make([]SRow, 0, yearsSince), + Commemorative: make([]CRow, 0, yearsSince), + } + + scanner := bufio.NewScanner(r) for linenr := 1; scanner.Scan(); linenr++ { - var mintmark struct { - s string - star bool + line := strings.Trim(scanner.Text(), ws) + if isBlankOrComment(line) { + continue } - line := scanner.Text() - tokens := strings.FieldsFunc(strings.TrimSpace(line), unicode.IsSpace) + if len(line) < 4 || !isNumeric(line[:4], false) { + return Data{}, SyntaxError{ + expected: "4-digit year", + got: line, + linenr: linenr, + file: file, + } + } - switch { - case len(tokens) == 0: - continue - case tokens[0] == "BEGIN": - if len(tokens)-1 != 1 { - return Set{}, SyntaxError{ - expected: "single argument to ‘BEGIN’", - got: fmt.Sprintf("%d arguments", len(tokens)-1), - file: file, + var ( + commem bool + mintmark string + ) + year, _ := strconv.Atoi(line[:4]) + line = line[4:] + + if len(line) != 0 { + if strings.ContainsRune(ws, rune(line[0])) { + commem = true + goto out + } + if line[0] != '-' { + return Data{}, SyntaxError{ + expected: "end-of-line or mintmark", + got: line, linenr: linenr, + file: file, } } - arg := tokens[1] + if line = line[1:]; len(line) == 0 { + return Data{}, SyntaxError{ + expected: "mintmark", + got: "end-of-line", + linenr: linenr, + file: file, + } + } - switch arg { - case "CIRC": - ctype = TypeCirculated - case "BU": - ctype = TypeNIFC - case "PROOF": - ctype = TypeProof + switch i := strings.IndexAny(line, ws); i { + case 0: + return Data{}, SyntaxError{ + expected: "mintmark", + got: "whitespace", + linenr: linenr, + file: file, + } + case -1: + mintmark = line default: - if !isNumeric(arg, false) { - return Set{}, SyntaxError{ - expected: "‘CIRC’, ‘BU’, ‘PROOF’, or a year", - got: arg, - file: file, + mintmark, line = line[:i], line[i:] + commem = true + } + } + out: + + if !commem { + row := SRow{ + Year: year, + Mintmark: mintmark, + } + for i := range row.Mintages { + line = "" + for isBlankOrComment(line) { + if !scanner.Scan() { + return Data{}, SyntaxError{ + expected: "mintage row", + got: "end-of-file", + linenr: linenr, + file: file, + } + } + line = strings.Trim(scanner.Text(), ws) + linenr++ + } + + tokens := strings.FieldsFunc(line, func(r rune) bool { + return strings.ContainsRune(ws, r) + }) + if tokcnt := len(tokens); tokcnt != denoms { + word := "entries" + if tokcnt == 1 { + word = "entry" + } + return Data{}, SyntaxError{ + expected: fmt.Sprintf("%d mintage entries", denoms), + got: fmt.Sprintf("%d %s", tokcnt, word), linenr: linenr, + file: file, + } + } + + for j, tok := range tokens { + if tok != "?" && !isNumeric(tok, true) { + return Data{}, SyntaxError{ + expected: "numeric mintage figure or ‘?’", + got: tok, + linenr: linenr, + file: file, + } + } + + if tok == "?" { + row.Mintages[i][j] = Unknown + } else { + row.Mintages[i][j] = atoiWithDots(tok) } } - data.StartYear, _ = strconv.Atoi(arg) } - case isLabel(tokens[0]): - n := len(tokens[0]) - if n > 2 && tokens[0][n-2] == '*' { - mintmark.star = true - mintmark.s = tokens[0][:n-2] - } else { - mintmark.s = tokens[0][:n-1] + + data.Standard = append(data.Standard, row) + } else { + row := CRow{ + Year: year, + Mintmark: mintmark, } - tokens = tokens[1:] - if !isNumeric(tokens[0], true) && tokens[0] != "?" { - return Set{}, SyntaxError{ - expected: "mintage row after label", - got: tokens[0], - file: file, + line = strings.TrimLeft(line, ws) + if line[0] != '"' { + return Data{}, SyntaxError{ + expected: "string", + got: line, linenr: linenr, + file: file, } } - fallthrough - case isNumeric(tokens[0], true), tokens[0] == "?": - switch { - case ctype == -1: - return Set{}, SyntaxError{ - expected: "coin type declaration", - got: tokens[0], - file: file, + + line = line[1:] + switch i := strings.IndexByte(line, '"'); i { + case -1: + return Data{}, SyntaxError{ + expected: "closing quote", + got: "end-of-line", linenr: linenr, + file: file, } - case data.StartYear == 0: - return Set{}, SyntaxError{ - expected: "start year declaration", - got: tokens[0], + case 0: + return Data{}, SyntaxError{ + expected: "commemorated event", + got: "empty string", + linenr: linenr, file: file, + } + default: + row.Name, line = line[:i], line[i+1:] + } + + if len(line) != 0 { + return Data{}, SyntaxError{ + expected: "end-of-line", + got: line, linenr: linenr, + file: file, } } - numcoins := len(Row{}.Cols) - tokcnt := len(tokens) + for isBlankOrComment(line) { + if !scanner.Scan() { + return Data{}, SyntaxError{ + expected: "mintage row", + got: "end-of-file", + linenr: linenr, + file: file, + } + } + line = strings.Trim(scanner.Text(), ws) + linenr++ + } - if tokcnt != numcoins { + tokens := strings.FieldsFunc(line, func(r rune) bool { + return strings.ContainsRune(ws, r) + }) + if tokcnt := len(tokens); tokcnt != typeCount { word := "entries" if tokcnt == 1 { word = "entry" } - return Set{}, SyntaxError{ - expected: fmt.Sprintf("%d mintage entries", numcoins), + return Data{}, SyntaxError{ + expected: fmt.Sprintf("%d mintage entries", typeCount), got: fmt.Sprintf("%d %s", tokcnt, word), - file: file, linenr: linenr, - } - } - - row := Row{Mintmark: mintmark.s} - if len(data.Tables[ctype]) == 0 { - row.Year = data.StartYear - } else { - rows := data.Tables[ctype] - row.Year = rows[len(rows)-1].Year - if row.Mintmark == "" || mintmark.star { - row.Year++ + file: file, } } for i, tok := range tokens { if tok == "?" { - row.Cols[i] = Unknown + row.Mintage[i] = Unknown } else { - row.Cols[i] = atoiWithDots(tok) + row.Mintage[i] = atoiWithDots(tok) } } - data.Tables[ctype] = append(data.Tables[ctype], row) - default: - return Set{}, SyntaxError{ - expected: "‘BEGIN’ directive or mintage row", - got: fmt.Sprintf("invalid token ‘%s’", tokens[0]), - file: file, - linenr: linenr, - } + + data.Commemorative = append(data.Commemorative, row) } } @@ -199,12 +281,6 @@ func isNumeric(s string, dot bool) bool { return true } -func isLabel(s string) bool { - n := len(s) - return (n > 2 && s[n-1] == ':' && s[n-2] == '*') || - (n > 1 && s[n-1] == ':') -} - func atoiWithDots(s string) int { n := 0 for _, ch := range s { @@ -215,3 +291,7 @@ func atoiWithDots(s string) int { } return n } + +func isBlankOrComment(s string) bool { + return len(s) == 0 || s[0] == '#' +} diff --git a/lib/mintage/parser_test.go b/lib/mintage/parser_test.go index 3cc8ea3..76e0f01 100644 --- a/lib/mintage/parser_test.go +++ b/lib/mintage/parser_test.go @@ -8,188 +8,151 @@ import ( func TestParserComplete(t *testing.T) { data, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 2020 + 1000 1001 1002 1003 1004 1005 1006 1007 + 1100 1101 1102 1103 1104 1105 1106 1107 + 1200 1201 1202 1203 1204 1205 1206 1207 + 2021-KNM + 2.000 ? 2002 2003 2004 2005 2006 2007 + 2.100 ? 2102 2103 2104 2105 2106 2107 + 2.200 ? 2202 2203 2204 2205 2206 2207 + 2021-MdP + 3000 3001 3002 3003 3004 3005 3006 3007 + 3100 3101 3102 3103 3104 3105 3106 3107 + 3200 3201 3202 3203 3204 3205 3206 3207 + 2022 + 4000 4001 4.002 4003 4004 4005 4006 4007 + 4100 4101 4.102 4103 4104 4105 4106 4107 + 4200 4201 4.202 4203 4204 4205 4206 4207 + + 2009 "10th Anniversary of Economic and Monetary Union" + 1000 2000 3000 + 2022-⋆ "35th Anniversary of the Erasmus Programme" + 1001 ? 3001 `)), "-") if err != nil { t.Fatalf(`Expected err=nil; got "%s"`, err) } - if data.StartYear != 2020 { - t.Fatalf("Expected data.StartYear=2020; got %d", - data.StartYear) - } - for i, row := range data.Tables[TypeCirculated] { - for j, col := range row.Cols { - var n int - if i == 1 && j == 1 { - n = -1 - } else { - n = 1000*i + j + 1000 - } - if col != n { - t.Fatalf("Expected data.Tables[TypeCirculated][i][j]=%d; got %d", n, col) + for i, row := range data.Standard { + for k := TypeCirc; k <= TypeProof; k++ { + for j, col := range row.Mintages[k] { + n := 1000*(i+1) + 100*k + j + if i == 1 && j == 1 { + n = Unknown + } + if col != n { + t.Fatalf("Expected data.Standard[%d].Mintages[%d][%d]=%d; got %d", + i, k, j, col, n) + } } } } - for i, row := range data.Tables[TypeNIFC] { - for j, col := range row.Cols { - var n int - if i == 1 && j == 1 { - n = -1 - } else { - n = 1000*i + j + 1100 + for i, row := range data.Commemorative { + for k := TypeCirc; k <= TypeProof; k++ { + n := 1000*(k+1) + i + if i == 1 && k == 1 { + n = Unknown } - if col != n { - t.Fatalf("Expected data.Tables[TypeNIFC][i][j]=%d; got %d", n, col) + if row.Mintage[k] != n { + t.Fatalf("Expected row.Mintage[%d]=%d; got %d", + k, n, row.Mintage[k]) } } } - for i, row := range data.Tables[TypeProof] { - for j, col := range row.Cols { - var n int - if i == 1 && j == 1 { - n = -1 - } else { - n = 1000*i + j + 1200 - } - if col != n { - t.Fatalf("Expected data.Tables[TypeProof][i][j]=%d; got %d", n, col) - } - } + if len(data.Standard) != 4 { + t.Fatalf("Expected len(data.Standard)=2; got %d", len(data.Standard)) } - - if len(data.Tables[TypeCirculated]) != 2 { - t.Fatalf("Expected len(data.Tables[TypeCirculated])=2; got %d", len(data.Tables[TypeCirculated])) + if len(data.Commemorative) != 2 { + t.Fatalf("Expected len(data.Commemorative)=2; got %d", len(data.Commemorative)) } - if len(data.Tables[TypeNIFC]) != 2 { - t.Fatalf("Expected len(data.Tables[TypeNIFC])=2; got %d", len(data.Tables[TypeNIFC])) - } - if len(data.Tables[TypeProof]) != 2 { - t.Fatalf("Expected len(data.Tables[TypeProof])=2; got %d", len(data.Tables[TypeProof])) - } -} - -func TestParserNoProof(t *testing.T) { - data, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - `)), "-") - if err != nil { - t.Fatalf(`Expected err=nil; got "%s"`, err) - } - - if len(data.Tables[TypeProof]) != 0 { - t.Fatalf("Expected len(data.Tables[TypeProof])=0; got %d", len(data.Tables[TypeProof])) + for i, x := range [...]struct { + year int + mintmark, name string + }{ + {2009, "", "10th Anniversary of Economic and Monetary Union"}, + {2022, "⋆", "35th Anniversary of the Erasmus Programme"}, + } { + if data.Commemorative[i].Year != x.year { + t.Fatalf("Expected data.Commemorative[%d].Year=%d; got %d", + i, x.year, data.Commemorative[i].Year) + } + if data.Commemorative[i].Mintmark != x.mintmark { + t.Fatalf(`Expected data.Commemorative[%d].Mintmark="%s"; got "%s"`, + i, x.mintmark, data.Commemorative[i].Mintmark) + } + if data.Commemorative[i].Name != x.name { + t.Fatalf(`Expected data.Commemorative[%d].Name="%s"; got "%s"`, + i, x.name, data.Commemorative[i].Name) + } } } 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 + 2020 + 1000 1001 1002 1003 1004 1005 1006 1007 + 1100 1101 1102 1103 1104 1105 1106 1107 + 1200 1201 1202 1203 1204 1205 1206 1207 + 2021-KNM + 2.000 ? 2002 2003 2004 2005 2006 2007 + 2.100 ? 2102 2103 2104 2105 2106 2107 + 2.200 ? 2202 2203 2204 2205 2206 2207 + 2021-MdP + 3000 3001 3002 3003 3004 3005 3006 3007 + 3100 3101 3102 3103 3104 3105 3106 3107 + 3200 3201 3202 3203 3204 3205 3206 3207 + 2022 + 4000 4001 4.002 4003 4004 4005 4006 4007 + 4100 4101 4.102 4103 4104 4105 4106 4107 + 4200 4201 4.202 4203 4204 4205 4206 4207 `)), "-") if err != nil { t.Fatalf(`Expected err=nil; got "%s"`, err) } - for i, row := range data.Tables[TypeCirculated] { - for j, col := range row.Cols { - var n int - if i > 0 && j == 1 { - n = -1 - } else { - n = 1000*i + j + 1000 + for i, row := range data.Standard { + for j, col := range row.Mintages[TypeCirc] { + n := 1000*(i+1) + j + if i == 1 && j == 1 { + n = Unknown } if col != n { - t.Fatalf("Expected data.Tables[TypeCirculated][i][j]=%d; got %d", n, col) + t.Fatalf("Expected data.Standard[%d].Mintages[TypeCirc][%d]=%d; got %d", + i, j, col, n) } } } - for i, y := range [...]int{2020, 2021, 2021} { - if data.Tables[TypeCirculated][i].Year != y { - t.Fatalf("Expected data.Tables[TypeCirculated][%d].Year=%d; got %d", - i, y, data.Tables[TypeCirculated][i].Year) + for i, y := range [...]int{2020, 2021, 2021, 2022} { + if data.Standard[i].Year != y { + t.Fatalf("Expected data.Standard[%d].Year=%d; got %d", + i, y, data.Standard[i].Year) } } - for i, s := range [...]string{"", "KNM", "MdP"} { - if data.Tables[TypeCirculated][i].Mintmark != s { - t.Fatalf(`Expected data.Tables[TypeCirculated][%d].Mintmark="%s"; got "%s"`, - i, s, data.Tables[TypeCirculated][i].Mintmark) + + for i, m := range [...]string{"", "KNM", "MdP", ""} { + if data.Standard[i].Mintmark != m { + t.Fatalf(`Expected data.Standard[%d].Mintmark="%s"; got "%s"`, + i, m, data.Standard[i].Mintmark) } } } func TestParserNoYear(t *testing.T) { _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserNoType(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserNoYearOrType(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 1.000 1001 1002 1003 1004 1005 1006 1007 + 1.100 1101 1102 1103 1104 1105 1106 1107 + 1.200 1201 1202 1203 1204 1205 1206 1207 + 2021 + 2000 ? 2002 2003 2004 2005 2006 2007 + 2100 ? 2102 2103 2104 2105 2106 2107 + 2200 ? 2202 2203 2204 2205 2206 2207 `)), "-") var sErr SyntaxError @@ -200,17 +163,14 @@ func TestParserNoYearOrType(t *testing.T) { func TestParserBadToken(t *testing.T) { _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - I’m bad - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 2020 + 1.000 1001 1002 1003 1004 1005 1006 1007 + 1.100 1101 1102 1103 1104 1105 1106 1107 + 1.200 1201 1202 1203 1204 1205 1206 1207 + 2021 Naughty! + 2000 ? 2002 2003 2004 2005 2006 2007 + 2100 ? 2102 2103 2104 2105 2106 2107 + 2200 ? 2202 2203 2204 2205 2206 2207 `)), "-") var sErr SyntaxError @@ -221,16 +181,14 @@ func TestParserBadToken(t *testing.T) { func TestParserShortRow(t *testing.T) { _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 2020 + 1.000 1001 1002 1003 1004 1005 1006 1007 + 1.100 1101 1102 1103 1104 1105 1106 1107 + 1.200 1201 1202 1203 1204 1205 1206 1207 + 2021 + 2000 ? 2002 2003 2004 2005 2006 2007 + 2100 ? 2102 2103 2104 2105 2106 + 2200 ? 2202 2203 2204 2205 2206 2207 `)), "-") var sErr SyntaxError @@ -241,16 +199,14 @@ func TestParserShortRow(t *testing.T) { func TestParserLongRow(t *testing.T) { _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRC - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 2108 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 2020 + 1.000 1001 1002 1003 1004 1005 1006 1007 + 1.100 1101 1102 1103 1104 1105 1106 1107 + 1.200 1201 1202 1203 1204 1205 1206 1207 + 2021 + 2000 ? 2002 2003 2004 2005 2006 2007 + 2100 ? 2102 2103 2104 2105 2106 2107 2108 + 2200 ? 2202 2203 2204 2205 2206 2207 `)), "-") var sErr SyntaxError @@ -259,18 +215,15 @@ func TestParserLongRow(t *testing.T) { } } -func TestParserBadCoinType(t *testing.T) { +func TestParserMissingRow(t *testing.T) { _, err := Parse(bytes.NewBuffer([]byte(` - BEGIN 2020 - BEGIN CIRCULATED - 1.000 1001 1002 1003 1004 1005 1006 1007 - 2000 ? 2002 2003 2004 2005 2006 2007 - BEGIN BU - 1.100 1101 1102 1103 1104 1105 1106 1107 - 2100 ? 2102 2103 2104 2105 2106 2107 - BEGIN PROOF - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2200 ? 2202 2203 2204 2205 2206 2207 + 2020 + 1.000 1001 1002 1003 1004 1005 1006 1007 + 1.100 1101 1102 1103 1104 1105 1106 1107 + 1.200 1201 1202 1203 1204 1205 1206 1207 + 2021 + 2000 ? 2002 2003 2004 2005 2006 2007 + 2200 ? 2202 2203 2204 2205 2206 2207 `)), "-") var sErr SyntaxError @@ -82,14 +82,8 @@ func i18nHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var p, pZero lib.Printer - if c, err := r.Cookie("locale"); errors.Is(err, http.ErrNoCookie) { - log.Println("Language cookie not set") - } else { - var ok bool - p, ok = lib.Printers[strings.ToLower(c.Value)] - if !ok { - log.Printf("Language ‘%s’ is unsupported\n", c.Value) - } + if c, err := r.Cookie("locale"); err == nil { + p = lib.Printers[strings.ToLower(c.Value)] } ctx := context.WithValue( @@ -112,26 +106,22 @@ func mintageHandler(next http.Handler) http.Handler { countries := lib.SortedCountries( r.Context().Value("printer").(lib.Printer)) code := strings.ToLower(cmp.Or(r.FormValue("code"), countries[0].Code)) - ctype := strings.ToLower(cmp.Or(r.FormValue("type"), "circ")) + ctype := strings.ToLower(r.FormValue("type")) path := filepath.Join("data", "mintages", code) f, _ := os.Open(path) // TODO: Handle error defer f.Close() - set, _ := mintage.Parse(f, path) // TODO: Handle error + data, _ := mintage.Parse(f, path) // TODO: Handle error - var idx mintage.CoinType switch ctype { - case "circ": - idx = mintage.TypeCirculated - case "nifc": - idx = mintage.TypeNIFC - case "proof": - idx = mintage.TypeProof + case "circ", "nifc", "proof": + default: + ctype = "circ" } ctx := context.WithValue(r.Context(), "code", code) ctx = context.WithValue(ctx, "type", ctype) - ctx = context.WithValue(ctx, "table", set.Tables[idx]) + ctx = context.WithValue(ctx, "mintages", data) ctx = context.WithValue(ctx, "countries", countries) next.ServeHTTP(w, r.WithContext(ctx)) }) diff --git a/template/coins_mintages.templ b/template/coins_mintages.templ index 47940e1..3446644 100644 --- a/template/coins_mintages.templ +++ b/template/coins_mintages.templ @@ -17,7 +17,8 @@ var denoms = [...]float64{ templ CoinsMintages() { {{ code := ctx.Value("code").(string) - table := ctx.Value("table").([]mintage.Row) + data := ctx.Value("mintages").(mintage.Data) + ctype := ctx.Value("type").(string) p := ctx.Value("printer").(lib.Printer) }} <header> @@ -58,14 +59,15 @@ templ CoinsMintages() { </select> </label> <fieldset> - @coinTypeRadio("circ", p.T("Circulation Coins")) - @coinTypeRadio("nifc", p.T("NIFC / BU Sets")) - @coinTypeRadio("proof", p.T("Proof Coins")) + @coinTypeRadio(ctype, "circ", p.T("Circulation Coins")) + @coinTypeRadio(ctype, "nifc", p.T("NIFC / BU Sets")) + @coinTypeRadio(ctype, "proof", p.T("Proof Coins")) </fieldset> </div> <button type="submit">{ p.T("Filter") }</button> </form> <figure> + <figcaption>{ p.T("Standard Issue Coins") }</figcaption> <table class="mintage-table" role="grid"> <thead> <th>{ p.T("Year") }</th> @@ -74,16 +76,16 @@ templ CoinsMintages() { } </thead> <tbody> - for _, row := range table { + for _, row := range data.Standard { <tr> <th scope="col"> - if row.Mintmark != "" { + if len(row.Mintmark) != 0 { { strconv.Itoa(row.Year) } <sub><small>{ row.Mintmark }</small></sub> } else { { strconv.Itoa(row.Year) } } </th> - for _, col := range row.Cols { + for _, col := range row.Mintages[strToCtype(ctype)] { switch col { case mintage.Unknown: <td>{ p.T("Unknown") }</td> @@ -98,19 +100,43 @@ templ CoinsMintages() { </tbody> </table> </figure> + <figure> + <figcaption>{ p.T("Commemorative Coins") }</figcaption> + <table class="mintage-table" role="grid"> + <thead> + <th>{ p.T("Year") }</th> + <th>{ p.T("Commemorated Issue") }</th> + <th>{ p.T("Mintage") }</th> + </thead> + </table> + </figure> </section> </main> } -templ coinTypeRadio(short, long string) { +templ coinTypeRadio(ctype, short, long string) { <label for={ "compact-" + short }> <input id={ "compact-" + short } type="radio" name="type" value={ short } - checked?={ ctx.Value("type").(string) == short } + checked?={ ctype == short } /> { long } </label> } + +func strToCtype(s string) int { + switch s { + case "circ": + return mintage.TypeCirc + case "nifc": + return mintage.TypeNIFC + case "proof": + return mintage.TypeProof + } + + /* Should never happen, but just incase */ + return mintage.TypeCirc +} |