summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/mfmt/main.go168
-rw-r--r--cmd/mfmt/main_test.go214
-rw-r--r--cmd/mfmt/util_test.go28
-rw-r--r--contrib/syntax/mintage.vim13
-rw-r--r--data/mintages/ad97
-rw-r--r--data/mintages/at190
-rw-r--r--lib/mintage/parser.go318
-rw-r--r--lib/mintage/parser_test.go315
-rw-r--r--main.go26
-rw-r--r--template/coins_mintages.templ44
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
diff --git a/main.go b/main.go
index 270eabb..25049dd 100644
--- a/main.go
+++ b/main.go
@@ -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) }&nbsp;<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
+}