Skip to content

Commit 3d83677

Browse files
authored
Merge pull request #73 from ilo-token/master
0.5.1
2 parents 95b0d65 + a340f76 commit 3d83677

21 files changed

+335
-341
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ On this on-development version, things can be broken.
1515
</details>
1616
-->
1717

18+
## 0.5.1
19+
20+
- Fix adverb placement within verbs: "sina o lape pona" can now translate into
21+
"You should nicely sleep" instead of "You nicely should sleep".
22+
- Fix "token is not defined" bug when telo misikeke error messages is turned on.
23+
1824
## 0.5.0
1925

2026
ilo Token can now translate "o" imperative sentences such as "o toki" and "mi o

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ its wiki. You may open an issue for:
2222
- Fix suggestion to the wiki
2323

2424
Please remember to search first before opening an issue, it might already exist!
25-
Duplicate issues are unnecessary.
25+
Also check the [changelog](./CHANGELOG.md) and open the on-development
26+
changelog, it might be already fixed but hasn't published yet! Duplicate issues
27+
are unnecessary.
2628

2729
## [Discussion](https://github.com/ilo-token/ilo-token.github.io/discussions)
2830

bundle.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import { denoPlugins } from "@luca/esbuild-deno-loader";
22
import { debounce } from "@std/async/debounce";
33
import * as ESBuild from "esbuild";
4+
import * as Dictionary from "./dictionary/build.ts";
45

56
const WATCH = [
6-
"./dictionary/build.ts",
7-
"./dictionary/dictionary",
8-
"./dictionary/misc.ts",
9-
"./dictionary/parser.ts",
10-
"./dictionary/type.ts",
11-
"./telo_misikeke/linku_data.json",
12-
"./telo_misikeke/Parser.js",
13-
"./telo_misikeke/rules.js",
14-
"./telo_misikeke/telo_misikeke.js",
7+
"./dictionary/",
8+
"./telo_misikeke/",
159
"./src/",
1610
"./project_data.json",
1711
];
18-
const DICTIONARY = /dictionary[/\\][^/\\]+$/;
12+
const DICTIONARY_DIST_CODE = /[/\\]dictionary[/\\]dictionary\.ts$/;
13+
const DICTIONARY = /[/\\]dictionary[/\\]dictionary$/;
1914

2015
function buildOptions(minify: boolean): ESBuild.BuildOptions {
2116
return {
@@ -33,14 +28,12 @@ async function buildAll(
3328
options: Readonly<{
3429
minify: boolean;
3530
buildDictionary: boolean;
36-
checkDictionary?: boolean;
3731
}>,
3832
): Promise<void> {
39-
const { minify, buildDictionary, checkDictionary } = options;
33+
const { minify, buildDictionary } = options;
4034
try {
4135
if (buildDictionary) {
42-
const Dictionary = await import("./dictionary/build.ts");
43-
await Dictionary.build(checkDictionary ?? true);
36+
await Dictionary.build();
4437
}
4538
// deno-lint-ignore no-console
4639
console.log("Building main.js...");
@@ -67,19 +60,19 @@ if (import.meta.main) {
6760
let dictionaryChanged = false;
6861
const buildDebounced = debounce((buildDictionary: boolean) => {
6962
task = task.then(async () => {
70-
await buildAll({
71-
minify: false,
72-
buildDictionary,
73-
checkDictionary: false,
74-
});
63+
await buildAll({ minify: false, buildDictionary });
7564
dictionaryChanged = false;
7665
});
7766
}, 500);
7867
for await (const event of watcher) {
79-
if (event.paths.some((path) => DICTIONARY.test(path))) {
80-
dictionaryChanged = true;
68+
if (
69+
!event.paths.every((path) => DICTIONARY_DIST_CODE.test(path))
70+
) {
71+
if (event.paths.some((path) => DICTIONARY.test(path))) {
72+
dictionaryChanged = true;
73+
}
74+
buildDebounced(dictionaryChanged);
8175
}
82-
buildDebounced(dictionaryChanged);
8376
}
8477
throw new Error("unreachable");
8578
}

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"dependencies": ["build-dictionary"]
1010
},
1111
"start": "deno run --allow-net --allow-read --no-prompt jsr:@std/http/file-server ./dist/",
12-
"watch": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run --no-prompt ./bundle.ts watch",
12+
"watch": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run --no-prompt --watch ./bundle.ts watch",
1313
"update": "deno outdated --update && deno run --allow-write --allow-net --no-prompt ./telo_misikeke/update.ts",
1414
"build-dictionary": "deno run --allow-read --allow-write --no-prompt ./dictionary/build.ts"
1515
},

dictionary/build.ts

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,26 @@
1-
import { extractErrorMessage } from "../src/misc.ts";
21
import { parseDictionary } from "./parser.ts";
32

43
const SOURCE = new URL("./dictionary", import.meta.url);
54
const DESTINATION = new URL("./dictionary.ts", import.meta.url);
65

7-
class ImportError extends Error {
8-
constructor(message?: string, options?: { cause: unknown }) {
9-
super(message, options);
10-
this.name = "ImportError";
11-
}
12-
}
13-
async function importPrevious(): Promise<string> {
14-
try {
15-
const { original } = await import("./dictionary.ts");
16-
return original;
17-
} catch (error) {
18-
throw new ImportError(extractErrorMessage(error), { cause: error });
19-
}
20-
}
21-
export async function build(checkFile: boolean): Promise<void> {
22-
const currentPromise = Deno.readTextFile(SOURCE);
23-
if (checkFile) {
24-
try {
25-
const [current, previous] = await Promise.all([
26-
currentPromise,
27-
importPrevious(),
28-
]);
29-
if (current === previous) {
30-
return;
31-
}
32-
} catch (error) {
33-
if (!(error instanceof ImportError)) {
34-
throw error;
35-
}
36-
}
37-
}
6+
export async function build(): Promise<void> {
387
// deno-lint-ignore no-console
398
console.log("Building dictionary...");
40-
const text = await currentPromise;
9+
const text = await Deno.readTextFile(SOURCE);
4110
const json = JSON.stringify(
4211
Object.fromEntries(parseDictionary(text)),
4312
undefined,
4413
2,
4514
);
46-
const original = JSON.stringify(text);
4715
const code = `\
4816
// This code is autogenerated
4917
5018
import { Dictionary } from "./type.ts";
5119
5220
export const dictionary: Dictionary = new Map(Object.entries(${json}));
53-
54-
export const original = ${original};
5521
`;
5622
await Deno.writeTextFile(DESTINATION, code);
5723
}
5824
if (import.meta.main) {
59-
const checkFile = true;
60-
await build(checkFile);
25+
await build();
6126
}

dictionary/parser.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memoize } from "@std/cache/memoize";
12
import { escape as escapeHtml } from "@std/html/entities";
23
import { escape as escapeRegex } from "@std/regexp/escape";
34
import nlp from "compromise/three";
@@ -35,12 +36,30 @@ import {
3536
const RESERVED_SYMBOLS = "#()*+/:;<=>@[\\]^`{|}~";
3637
const WORDS = new RegExp(`[^${escapeRegex(RESERVED_SYMBOLS)}]`);
3738

38-
const comment = match(/#[^\n\r]*/, "comment");
39-
const spaces = sourceOnly(all(choiceOnlyOne(match(/\s/, "space"), comment)));
4039
function lex<T>(parser: Parser<T>): Parser<T> {
4140
return parser.skip(spaces);
4241
}
42+
const comment = match(/#[^\n\r]*/, "comment");
43+
const spaces = sourceOnly(all(choiceOnlyOne(match(/\s/, "space"), comment)));
4344
const backtick = matchString("`", "backtick");
45+
46+
const tokiPonaWord = lex(match(/[a-z][a-zA-Z]*/, "word"));
47+
const openParenthesis = lex(matchString("(", "open parenthesis"));
48+
const closeParenthesis = lex(matchString(")", "close parenthesis"));
49+
const openBracket = lex(matchString("[", "open bracket"));
50+
const closeBracket = lex(matchString("]", "close bracket"));
51+
const comma = lex(matchString(",", "comma"));
52+
const colon = lex(matchString(":", "colon"));
53+
const semicolon = lex(matchString(";", "semicolon"));
54+
const slash = lex(matchString("/", "slash"));
55+
56+
const keyword = memoize(<T extends string>(keyword: T): Parser<T> =>
57+
lex(match(/[a-z\-]+/, keyword))
58+
.filter((that) =>
59+
keyword === that ||
60+
throwError(new UnexpectedError(`"${that}"`, `"${keyword}"`))
61+
) as Parser<T>
62+
);
4463
const unescapedWord = allAtLeastOnce(
4564
choiceOnlyOne(
4665
match(WORDS, "word"),
@@ -55,16 +74,8 @@ const unescapedWord = allAtLeastOnce(
5574
word !== "" || throwError(new ArrayResultError("missing word"))
5675
);
5776
const word = unescapedWord.map(escapeHtml);
58-
const slash = lex(matchString("/", "slash"));
5977
const forms = sequence(word, all(slash.with(word)))
6078
.map(([first, rest]) => [first, ...rest]);
61-
function keyword<T extends string>(keyword: T): Parser<T> {
62-
return lex(match(/[a-z\-]+/, keyword))
63-
.filter((that) =>
64-
keyword === that ||
65-
throwError(new UnexpectedError(`"${that}"`, `"${keyword}"`))
66-
) as Parser<T>;
67-
}
6879
const number = choiceOnlyOne(keyword("singular"), keyword("plural"));
6980
const optionalNumber = optionalAll(number);
7081
const perspective = choiceOnlyOne(
@@ -73,18 +84,12 @@ const perspective = choiceOnlyOne(
7384
keyword("third"),
7485
);
7586
function tag<T>(parser: Parser<T>): Parser<T> {
76-
return lex(matchString("(", "open parenthesis"))
77-
.with(parser)
78-
.skip(lex(matchString(")", "close parenthesis")));
87+
return openParenthesis.with(parser).skip(closeParenthesis);
7988
}
8089
function template<T>(parser: Parser<T>): Parser<T> {
81-
return lex(matchString("[", "open square bracket"))
82-
.with(parser)
83-
.skip(lex(matchString("]", "close square bracket")));
84-
}
85-
function simpleUnit(kind: string): Parser<string> {
86-
return word.skip(tag(keyword(kind)));
90+
return openBracket.with(parser).skip(closeBracket);
8791
}
92+
const simpleUnit = memoize((kind: string) => word.skip(tag(keyword(kind))));
8893
function detectRepetition(
8994
source: ReadonlyArray<string>,
9095
): { before: string; repeat: string; after: string } {
@@ -324,7 +329,6 @@ function verbOnly(tagInside: Parser<unknown>): Parser<VerbForms> {
324329
}),
325330
);
326331
}
327-
const semicolon = lex(matchString(";", "semicolon"));
328332
const definition = choiceOnlyOne<Definition>(
329333
adjective
330334
.skip(semicolon)
@@ -492,12 +496,8 @@ const definition = choiceOnlyOne<Definition>(
492496
type: "filler",
493497
})),
494498
);
495-
const singleWord = lex(match(/[a-z][a-zA-Z]*/, "word"));
496-
const head = sequence(
497-
all(singleWord.skip(lex(matchString(",", "comma")))),
498-
singleWord,
499-
)
500-
.skip(matchString(":", "colon"))
499+
const head = sequence(all(tokiPonaWord.skip(comma)), tokiPonaWord)
500+
.skip(colon)
501501
.map(([init, last]) => [...init, last]);
502502
const entry = withSource(spaces.with(all(definition)))
503503
.map(([definitions, src]) => ({ definitions, src: src.trimEnd() }));
@@ -512,6 +512,7 @@ const dictionaryParser = spaces
512512
)
513513
)
514514
.parser();
515+
515516
const definitionExtractor = spaces
516517
.with(all(optionalAll(lex(head)).with(lex(match(/[^;]*;/, "definition")))))
517518
.skip(end)

project_data.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.5.0",
2+
"version": "0.5.1",
33
"onDevelopment": false,
44
"releaseDate": "2025-3-9"
55
}

src/cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export interface Clearable {
22
clear(): void;
33
}
4-
export class Cache {
4+
export class ClearableCacheSet {
55
readonly #caches: Set<WeakRef<Clearable>> = new Set();
66
add(cache: Clearable): void {
77
this.#caches.add(new WeakRef(cache));

src/dictionary.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { dictionary as globalDictionary } from "../dictionary/dictionary.ts";
22
import { parseDictionary } from "../dictionary/parser.ts";
33
import { Definition, Dictionary } from "../dictionary/type.ts";
44

5+
// All of these global constants are mutable
6+
57
const customDictionary: Dictionary = new Map();
68
export const dictionary: Dictionary = new Map();
79

0 commit comments

Comments
 (0)