проектные команды и ссылки на источник
- заведён internal/source: уровни ссылаются друг на друга путём на диске или git-репозиторием, ревизия закрепляется хвостом #ref; клон делается заново и удаляется, кэша нет - добавлены init, add, pull, list, check в проекте — манифест .conventions.toml, сборка копий по разу на компонент, маркер локальной части, READING.md рядом - проверки формы развязаны с набором: принимают lang.Vocabulary, а язык копии узнаётся по строке о версии — манифеста рядом с ней нет
This commit is contained in:
@@ -292,6 +292,81 @@ func Lookup(version int, code string) (Vocabulary, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Recognize picks the vocabulary a text is written in by the words it names.
|
||||
//
|
||||
// A copy in a consuming repository declares its language by the version line
|
||||
// and by nothing else: no manifest travels with it, and no path back to the
|
||||
// suite is written anywhere in it. So a check run outside a suite learns which
|
||||
// words are normative the same way a reader does — off the line the document
|
||||
// carries. The version number decides between vocabularies naming the same
|
||||
// words, which is what two versions of one natural language would do.
|
||||
func Recognize(text string) (Vocabulary, bool) {
|
||||
var best Vocabulary
|
||||
for _, version := range sortedVersions() {
|
||||
for _, code := range sortedCodes(version) {
|
||||
v := registry[version][code]
|
||||
if !namesAll(text, v.Words()) {
|
||||
continue
|
||||
}
|
||||
if best.Version == 0 || matchesVersion(text, v.Version) {
|
||||
best = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return best, best.Version != 0
|
||||
}
|
||||
|
||||
func namesAll(text string, words []string) bool {
|
||||
for _, w := range words {
|
||||
if !strings.Contains(text, w) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// matchesVersion looks for the number as a whole word, so that version 1 is not
|
||||
// read out of "version 12".
|
||||
func matchesVersion(text string, version int) bool {
|
||||
number := fmt.Sprint(version)
|
||||
for i := 0; ; {
|
||||
j := strings.Index(text[i:], number)
|
||||
if j < 0 {
|
||||
return false
|
||||
}
|
||||
start, end := i+j, i+j+len(number)
|
||||
before := start == 0 || !isDigit(rune(text[start-1]))
|
||||
after := end == len(text) || !isDigit(rune(text[end]))
|
||||
if before && after {
|
||||
return true
|
||||
}
|
||||
i = end
|
||||
if i >= len(text) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isDigit(r rune) bool { return r >= '0' && r <= '9' }
|
||||
|
||||
func sortedVersions() []int {
|
||||
out := make([]int, 0, len(registry))
|
||||
for v := range registry {
|
||||
out = append(out, v)
|
||||
}
|
||||
sort.Ints(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func sortedCodes(version int) []string {
|
||||
out := make([]string, 0, len(registry[version]))
|
||||
for c := range registry[version] {
|
||||
out = append(out, c)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// Foreign lists the words of the other vocabularies of the same version — the
|
||||
// ones that give away a mixture of vocabularies. Words that coincide with the
|
||||
// suite's own are dropped.
|
||||
|
||||
@@ -72,3 +72,31 @@ func TestUnknownVersionAndCode(t *testing.T) {
|
||||
t.Error("an unknown vocabulary was accepted without an error")
|
||||
}
|
||||
}
|
||||
|
||||
// A copy in a consuming repository carries no manifest, so the only thing that
|
||||
// says which words of it are normative is the line it names the language by.
|
||||
func TestRecognizeReadsTheLanguageOffTheVersionLine(t *testing.T) {
|
||||
ru, err := lang.Lookup(1, "ru")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
en, err := lang.Lookup(1, "en")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, want := range []lang.Vocabulary{ru, en} {
|
||||
got, ok := lang.Recognize(want.VersionLine())
|
||||
if !ok {
|
||||
t.Fatalf("the version line of %q was not recognized", want.Code)
|
||||
}
|
||||
if got.Code != want.Code || got.Version != want.Version {
|
||||
t.Errorf("read as %q version %d, expected %q version %d",
|
||||
got.Code, got.Version, want.Code, want.Version)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := lang.Recognize("Обычная проза, ничего не объявляющая."); ok {
|
||||
t.Errorf("prose naming no words passed for a version line")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user