Compare commits

...

4 Commits

9 changed files with 35 additions and 27 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ replacement, and each removed path, column or flag.
and built from SCB, GeoNames, Trafikverket NVDB and the US Census Bureau, and an and built from SCB, GeoNames, Trafikverket NVDB and the US Census Bureau, and an
`address` record over one consistent draw of them. `sv_SE.address` and `address` record over one consistent draw of them. `sv_SE.address` and
`en_US.address` read those records, so `en_US.address.street` no longer carries `en_US.address` read those records, so `en_US.address.street` no longer carries
`name` and `suffix`, and a locale folder loads only beside `geo`. `name` and `suffix`, and a locale folder loads only beside `geo`.
- `{date(from,to,'layout')}` and `{time('layout')}`: a second between two days, or - `{date(from,to,'layout')}` and `{time('layout')}`: a second between two days, or
within one, in a quoted Go layout. `sv_SE.date`, `en_US.date`, `sv_SE.time` and within one, in a quoted Go layout. `sv_SE.date`, `en_US.date`, `sv_SE.time` and
`en_US.time` render through them, so `date.year`, `date.month`, `date.day` and `en_US.time` render through them, so `date.year`, `date.month`, `date.day` and
+7 -4
View File
@@ -540,7 +540,8 @@ between the two days is reachable, so a layout with a clock draws the time too.
The quotes delimit a layout outside a selector only, so `[O'Fallon]` in an The quotes delimit a layout outside a selector only, so `[O'Fallon]` in an
argument stays a name. Rejected at `New`: a bound that is no calendar date, or not argument stays a name. Rejected at `New`: a bound that is no calendar date, or not
before the other; an unquoted layout, naming the quoted one; a layout naming no field, which is text; before the other; an unquoted layout, naming the quoted one; a layout naming no field, which is text;
and for `time` a layout naming a date field, naming `date`. `{seq()}` spans `Fake` for `date` a layout naming no date field, naming `time`; and for `time` a layout
naming a date field, naming `date`. `{seq()}` spans `Fake`
calls and `repeat`, resets with a new generator, and is the natural primary key for calls and `repeat`, resets with a new generator, and is the natural primary key for
the SQL example above. the SQL example above.
@@ -617,7 +618,7 @@ its groups by name; the unnamed group spans them all.
```json ```json
{ "format": "{payer} pays {payee}; signed {signature}", { "format": "{payer} pays {payee}; signed {signature}",
"payer": { "format": "{/sv_SE.person.first} {/sv_SE.person.last}", "drawGroup": "payer" }, "payer": { "format": "{/sv_SE.person.first} {/sv_SE.person.last}", "drawGroup": "payer" },
"payee": "{/sv_SE.person.first} {/sv_SE.person.last}", "payee": "{/sv_SE.person.first} {/sv_SE.person.last}",
"signature": { "format": "{/sv_SE.person.last}", "drawGroup": "payer" } } "signature": { "format": "{/sv_SE.person.last}", "drawGroup": "payer" } }
``` ```
@@ -1079,11 +1080,13 @@ renamed or retyped line is a major.
load, since nothing could then select it. load, since nothing could then select it.
- **The Swedish ids draw Skatteverket's test series.** A Luhn-valid personnummer - **The Swedish ids draw Skatteverket's test series.** A Luhn-valid personnummer
over a random birth number may be a living person's; 238 and 239 after any date over a random birth number may be a living person's; 238 and 239 after any date
are blocked from assignment, so the shipped `personnummer` and are blocked from assignment, so the shipped `personnummer` and
`samordningsnummer` use those. They sit in a `birth-number` table under `sex` `samordningsnummer` use those. They sit in a `birth-number` table under `sex`
rather than as a column of it: the render's shared draw of the family is what rather than as a column of it: the render's shared draw of the family is what
makes the number and the name agree on sex, and `sex` stays one shape across makes the number and the name agree on sex, and `sex` stays one shape across
locales instead of collecting every sex-keyed id fact. locales instead of collecting every sex-keyed id fact. A samordningsnummer's
day, the birthday plus 60, is drawn from 61 to 88, valid in every month, rather
than computed from the date drawn.
- **The US given names come from a mirror of the SSA file.** ssa.gov refuses a - **The US given names come from a mirror of the SSA file.** ssa.gov refuses a
client outside the US, so `names-us.py` reads a GitHub copy that ends at 2020, client outside the US, so `names-us.py` reads a GitHub copy that ends at 2020,
which a count over the births since 1930 barely feels; `--names` takes the which a count over the births since 1930 barely feels; `--names` takes the
+6 -6
View File
@@ -26,12 +26,12 @@ func benchPath(b *testing.B, dir, path string) {
} }
} }
func BenchmarkPerson(b *testing.B) { benchPath(b, "data", "sv_SE.person") } func BenchmarkPerson(b *testing.B) { benchPath(b, "data", "sv_SE.person") }
func BenchmarkAddress(b *testing.B) { benchPath(b, "data", "sv_SE.address") } func BenchmarkAddress(b *testing.B) { benchPath(b, "data", "sv_SE.address") }
func BenchmarkWord(b *testing.B) { benchPath(b, "data", "sv_SE.word") } func BenchmarkWord(b *testing.B) { benchPath(b, "data", "sv_SE.word") }
func BenchmarkCreditcard(b *testing.B) { benchPath(b, "data", "misc.creditcard") } func BenchmarkCreditcard(b *testing.B) { benchPath(b, "data", "misc.creditcard") }
func BenchmarkSSN(b *testing.B) { benchPath(b, "data", "sv_SE.personnummer") } func BenchmarkPersonnummer(b *testing.B) { benchPath(b, "data", "sv_SE.personnummer") }
func BenchmarkUUIDv7(b *testing.B) { benchPath(b, "data", "misc.uuid") } func BenchmarkUUIDv7(b *testing.B) { benchPath(b, "data", "misc.uuid") }
func tmpData(b *testing.B, name, body string) string { func tmpData(b *testing.B, name, body string) string {
b.Helper() b.Helper()
+12 -9
View File
@@ -472,17 +472,14 @@ func iban(r rng, cc string) string {
const dayLayout = "2006-01-02" const dayLayout = "2006-01-02"
// The two instants a layout is proved against: alike in nothing, so a layout that // Alike in no field; layoutDay differs from layoutProbe in its date fields only.
// formats them alike names no field, and one that tells their days apart at the same
// clock names a date field.
var ( var (
layoutProbe = time.Date(2001, 2, 3, 4, 5, 6, 0, time.UTC) layoutProbe = time.Date(2001, 2, 3, 4, 5, 6, 0, time.UTC)
layoutProbe2 = time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC) layoutProbe2 = time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)
layoutDay = time.Date(2010, 11, 12, 4, 5, 6, 0, time.UTC) layoutDay = time.Date(2010, 11, 12, 4, 5, 6, 0, time.UTC)
) )
// layoutArg is the Go layout a quoted arg holds; unquoted, it is refused naming the // layoutArg is the Go layout a quoted arg holds, refused when unquoted or constant.
// quoted spelling, since a layout may carry the comma that splits args.
func layoutArg(a string) (string, error) { func layoutArg(a string) (string, error) {
if len(a) < 2 || a[0] != '\'' || a[len(a)-1] != '\'' { if len(a) < 2 || a[0] != '\'' || a[len(a)-1] != '\'' {
return "", fmt.Errorf("layout %s is not quoted; write '%s'", a, strings.Trim(a, "'")) return "", fmt.Errorf("layout %s is not quoted; write '%s'", a, strings.Trim(a, "'"))
@@ -503,8 +500,8 @@ func layoutOf(a string) string {
return layout return layout
} }
// layoutArity checks a call that ends in a layout takes n args; an unquoted layout // layoutArity checks a call ending in a layout takes n args, naming the quoted
// carrying a comma splits into more, so the error names its quoted spelling. // layout when an unquoted one split into more.
func layoutArity(name string, n int, a []string) error { func layoutArity(name string, n int, a []string) error {
if len(a) == n { if len(a) == n {
return nil return nil
@@ -531,8 +528,14 @@ func dateArgs(_ map[string]node, a []string) error {
if !from.Before(to) { if !from.Before(to) {
return fmt.Errorf("date(from,to,layout): from %s is not before to %s", a[0], a[1]) return fmt.Errorf("date(from,to,layout): from %s is not before to %s", a[0], a[1])
} }
_, err = layoutArg(a[2]) layout, err := layoutArg(a[2])
return err if err != nil {
return err
}
if layoutProbe.Format(layout) == layoutDay.Format(layout) {
return fmt.Errorf("date(from,to,layout): '%s' names no date field; write time('%s')", layout, layout)
}
return nil
} }
func timeArg(_ map[string]node, a []string) error { func timeArg(_ map[string]node, a []string) error {
+1
View File
@@ -310,6 +310,7 @@ func TestBuiltinDateArgs(t *testing.T) {
`"{time(15:04)}"`: "'15:04'", `"{time(15:04)}"`: "'15:04'",
`"{time('2006-01-02 15:04')}"`: "date(", `"{time('2006-01-02 15:04')}"`: "date(",
`"{time('x')}"`: "text", `"{time('x')}"`: "text",
`"{date(1990-01-01,1990-12-31,'15:04')}"`: "time('15:04')",
} { } {
_, err := compile(parse(t, tmpl)) _, err := compile(parse(t, tmpl))
if err == nil || !strings.Contains(err.Error(), want) { if err == nil || !strings.Contains(err.Error(), want) {
+1 -1
View File
@@ -19,7 +19,7 @@ CACHE = Path(__file__).resolve().parent / "cache"
SHEETS = {"f": "Tilltalsnamn kvinnor", "m": "Tilltalsnamn män"} SHEETS = {"f": "Tilltalsnamn kvinnor", "m": "Tilltalsnamn män"}
SURNAMES = "Efternamn" SURNAMES = "Efternamn"
NAME = re.compile(r"^[^\W\d_]{2,}([ '-][^\W\d_]{2,})*$") NAME = re.compile(r"^[^\W\d_]{2,}([ '-][^\W\d_]{2,})*$")
PARTICLES = {"af", "av", "de", "den", "der", "di", "du", "la", "le", "van", "von"} PARTICLES = {"af", "av", "da", "de", "del", "den", "der", "di", "dos", "du", "la", "le", "van", "von"}
def cased(name): def cased(name):
+2 -1
View File
@@ -23,6 +23,7 @@ SURNAMES = "https://www2.census.gov/topics/genealogy/2010surnames/names.zip"
OUT = Path(__file__).resolve().parent.parent / "data" / "en_US" OUT = Path(__file__).resolve().parent.parent / "data" / "en_US"
CACHE = Path(__file__).resolve().parent / "cache" CACHE = Path(__file__).resolve().parent / "cache"
MC = re.compile(r"^Mc([a-z])") MC = re.compile(r"^Mc([a-z])")
PLACEHOLDERS = {"Baby", "Female", "Infant", "Male", "Notnamed", "Unknown", "Unnamed"}
def csv_or_zip_rows(data, member): def csv_or_zip_rows(data, member):
@@ -41,7 +42,7 @@ def csv_or_zip_rows(data, member):
def given(data, from_year): def given(data, from_year):
counts = collections.Counter() counts = collections.Counter()
for r in csv_or_zip_rows(data, r"yob\d{4}\.txt"): for r in csv_or_zip_rows(data, r"yob\d{4}\.txt"):
if len(r) >= 4 and r[3].isdigit() and int(r[3]) >= from_year: if len(r) >= 4 and r[3].isdigit() and int(r[3]) >= from_year and r[0] not in PLACEHOLDERS:
counts[(r[0], r[1].lower())] += int(r[2]) counts[(r[0], r[1].lower())] += int(r[2])
return counts return counts
+4 -4
View File
@@ -379,7 +379,6 @@ Ayla f 23690
Aylin f 12970 Aylin f 12970
Azalea f 7052 Azalea f 7052
Azariah m 3910 Azariah m 3910
Baby m 6059
Bailee f 14798 Bailee f 14798
Bailey f 102509 Bailey f 102509
Bailey m 20062 Bailey m 20062
@@ -468,6 +467,7 @@ Bobbie f 78660
Bobbie m 14570 Bobbie m 14570
Bobby f 8180 Bobby f 8180
Bobby m 299694 Bobby m 299694
Bode m 3348
Bodhi m 8375 Bodhi m 8375
Bodie m 4314 Bodie m 4314
Bonita f 35102 Bonita f 35102
@@ -1674,7 +1674,6 @@ Imran m 3415
Ina f 15116 Ina f 15116
India f 21903 India f 21903
Inez f 22303 Inez f 22303
Infant m 4133
Ingrid f 25835 Ingrid f 25835
Ira m 33974 Ira m 33974
Irene f 150081 Irene f 150081
@@ -2615,6 +2614,7 @@ Lynn f 180561
Lynn m 44787 Lynn m 44787
Lynne f 61433 Lynne f 61433
Lynnette f 15029 Lynnette f 15029
Lynwood m 3349
Lyric f 17861 Lyric f 17861
Lyric m 3810 Lyric m 3810
Mabel f 24359 Mabel f 24359
@@ -3274,6 +3274,7 @@ Ricky m 221617
Rico m 9313 Rico m 9313
Ridge m 5086 Ridge m 5086
Rigoberto m 11018 Rigoberto m 11018
Rihanna f 5970
Rikki f 6846 Rikki f 6846
Riley f 117947 Riley f 117947
Riley m 92931 Riley m 92931
@@ -3804,6 +3805,7 @@ Tylor m 7980
Tyquan m 3547 Tyquan m 3547
Tyra f 14416 Tyra f 14416
Tyree m 13959 Tyree m 13959
Tyrel m 3354
Tyrell m 15575 Tyrell m 15575
Tyrese m 8673 Tyrese m 8673
Tyron m 7456 Tyron m 7456
@@ -3814,8 +3816,6 @@ Tyson m 44397
Ulises m 10786 Ulises m 10786
Ulysses m 13923 Ulysses m 13923
Unique f 7103 Unique f 7103
Unknown f 9225
Unknown m 8912
Uriah m 9786 Uriah m 9786
Uriel m 17743 Uriel m 17743
Urijah m 3365 Urijah m 3365
1 name sex count
379 Aylin f 12970
380 Azalea f 7052
381 Azariah m 3910
Baby m 6059
382 Bailee f 14798
383 Bailey f 102509
384 Bailey m 20062
467 Bobbie m 14570
468 Bobby f 8180
469 Bobby m 299694
470 Bode m 3348
471 Bodhi m 8375
472 Bodie m 4314
473 Bonita f 35102
1674 Ina f 15116
1675 India f 21903
1676 Inez f 22303
Infant m 4133
1677 Ingrid f 25835
1678 Ira m 33974
1679 Irene f 150081
2614 Lynn m 44787
2615 Lynne f 61433
2616 Lynnette f 15029
2617 Lynwood m 3349
2618 Lyric f 17861
2619 Lyric m 3810
2620 Mabel f 24359
3274 Rico m 9313
3275 Ridge m 5086
3276 Rigoberto m 11018
3277 Rihanna f 5970
3278 Rikki f 6846
3279 Riley f 117947
3280 Riley m 92931
3805 Tyquan m 3547
3806 Tyra f 14416
3807 Tyree m 13959
3808 Tyrel m 3354
3809 Tyrell m 15575
3810 Tyrese m 8673
3811 Tyron m 7456
3816 Ulises m 10786
3817 Ulysses m 13923
3818 Unique f 7103
Unknown f 9225
Unknown m 8912
3819 Uriah m 9786
3820 Uriel m 17743
3821 Urijah m 3365
+1 -1
View File
@@ -243,7 +243,7 @@ func (t *table) proveNamesInsideParent() error {
for r := 0; r < t.rows(); r++ { for r := 0; r < t.rows(); r++ {
k := t.cell(r, t.parent) + "\t" + t.cell(r, t.name) k := t.cell(r, t.parent) + "\t" + t.cell(r, t.name)
if first, dup := inside[k]; dup { if first, dup := inside[k]; dup {
return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent)) return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent))
} }
inside[k] = r inside[k] = r
} }