diff options
-rw-r--r-- | core/api.ts | 4 | ||||
-rw-r--r-- | core/http/client.ts | 4 | ||||
-rw-r--r-- | core/http/server.ts | 1 | ||||
-rw-r--r-- | core/raw/api.ts | 14 | ||||
-rw-r--r-- | db/db.ts | 28 | ||||
-rw-r--r-- | examples/readme.md | 17 | ||||
-rw-r--r-- | examples/sentence-word-lookup.ts | 23 | ||||
-rw-r--r-- | language/parser.ts | 23 | ||||
-rw-r--r-- | main.ts | 11 | ||||
-rw-r--r-- | test/reading/test.ts | 3 |
10 files changed, 80 insertions, 48 deletions
diff --git a/core/api.ts b/core/api.ts index 0891081..017e131 100644 --- a/core/api.ts +++ b/core/api.ts @@ -7,8 +7,8 @@ import { ParseResult } from "../language/types.ts"; * swapped easily. */ export default abstract class API { - /** @summary prepare API client */ - abstract prepare(): Promise<void>; + /** @summary resolved when ready */ + abstract ready: Promise<void>; /** @summary parse sentence */ abstract parseSentence(input: string): Promise<ParseResult>; diff --git a/core/http/client.ts b/core/http/client.ts index a77b616..365aa76 100644 --- a/core/http/client.ts +++ b/core/http/client.ts @@ -1,6 +1,5 @@ import "../../util/array.ts"; -import { ParseResult } from "../../language/types.ts"; import API from "../api.ts"; import { ConnectionProps, ConnectionPropsDefault } from "./props.ts"; import { APIRequest, APIRequestParseSentence, APIResponseParseSentence } from "./types.ts"; @@ -13,13 +12,12 @@ import { APIRequest, APIRequestParseSentence, APIResponseParseSentence } from ". */ export default class YomikunRemoteAPIClient implements API { private props: ConnectionProps; + ready: Promise<void> = Promise.resolve(); constructor(options?: ConnectionProps) { this.props = { ...ConnectionPropsDefault, ...options }; } - async prepare() { } - private async request(details: APIRequest) { var response = await fetch(`http://${this.props.host}:${this.props.port}`, { method: "POST", diff --git a/core/http/server.ts b/core/http/server.ts index b5d6c13..1af8d25 100644 --- a/core/http/server.ts +++ b/core/http/server.ts @@ -2,7 +2,6 @@ import { serve } from "https://deno.land/std@0.192.0/http/server.ts"; import "../../util/string.ts"; -import { ParseResult } from "../../language/types.ts"; import YomikunRAWAPI from "../raw/api.ts"; import { ConnectionProps, ConnectionPropsDefault } from "./props.ts"; import { APIRequest, APIRequestParseSentence, APIResponseParseSentence } from "./types.ts"; diff --git a/core/raw/api.ts b/core/raw/api.ts index 2d29eed..06db022 100644 --- a/core/raw/api.ts +++ b/core/raw/api.ts @@ -5,6 +5,7 @@ import YomikunError from "../../util/error.ts"; /** @summary internal Yomikun API client (DO NOT USE DIRECTLY) */ export default class YomikunRAWAPI implements API { private _parser: Parser; + ready: Promise<void>; constructor() { if (this.constructor === YomikunRAWAPI) { @@ -12,16 +13,15 @@ export default class YomikunRAWAPI implements API { } this._parser = new Parser(); - } - async prepare() { - await Promise.all([ - this._parser.prepare(), - ]); - } + this.ready = new Promise(async resolve => { + await this._parser.ready; + resolve(); + }) + } async parseSentence(input: string) { - return this._parser.parse(input); + return await this._parser.parse(input); } }; @@ -3,7 +3,6 @@ import * as path from 'https://deno.land/std@0.102.0/path/mod.ts'; import { TokenTags } from "../language/tags.ts"; import "../util/string.ts"; -import YomikunError from "../util/error.ts"; export interface DBDictInfo { id: number; @@ -49,14 +48,15 @@ interface DBFindResult { */ export default class DB { private connection: Database; - public ready: boolean = false; + public ready: Promise<void>; + private here = path.dirname(path.fromFileUrl(import.meta.url)); private paths = { db: { - dict: path.resolve('db', 'dict.db'), - user: path.resolve('db', 'user.db'), + dict: path.resolve(this.here, 'dict.db'), + user: path.resolve(this.here, 'user.db'), }, query: { - find: path.resolve('db', 'find.sql'), + find: path.resolve(this.here, 'find.sql'), }, } as const; private statement: { @@ -68,25 +68,23 @@ export default class DB { this.connection = new Database(":memory:", { create: false }); this.statement = { attach: this.connection.prepare("attach database ? as ?"), - queryTerm: this.connection.prepare(""), // initialized in prepare() + queryTerm: this.connection.prepare(""), }; this.attach(this.paths.db.dict, 'dict'); this.attach(this.paths.db.user, 'user'); + this.ready = new Promise<void>(async resolve => { + const statement = await Deno.readTextFile(this.paths.query.find); + this.statement.queryTerm = this.connection.prepare(statement); + resolve(); + }); } private attach(dbPath: string, alias?: string) { this.statement.attach.run(dbPath, alias); } - async prepare() { - const statement = await Deno.readTextFile(this.paths.query.find); - this.statement.queryTerm = this.connection.prepare(statement); - this.ready = true; - } - - findTerm(term: string): FindResult[] { - if (!this.ready) throw new YomikunError("DB not ready yet, call `async DB::prepare()` first"); - + async findTerm(term: string): Promise<FindResult[]> { + await this.ready; var results = this.statement.queryTerm.all({ term }) as unknown as DBFindResult[]; var terms: FindResult[] = results?.map(term => { if (term.rules == null) term.rules = ""; diff --git a/examples/readme.md b/examples/readme.md new file mode 100644 index 0000000..9eb8760 --- /dev/null +++ b/examples/readme.md @@ -0,0 +1,17 @@ +# API Examples + +## **ALL OF THESE EXAMPLES ARE CURRENTLY NOT WORKING, AND ARE USED TO MODEL THE API UNTIL THEY WORK** + +~This folder contains API examples. These files show some common workflows +using the Yomikun API.~ + +Examples (checked = working): + +- [ ] Lookup a word in a sentence +- [ ] Get furigana in HTML for a sentence +- [ ] Correct the reading of a word (because of ambiguous word boundries) by inserting a break +- [ ] Login as a regular user and ignore an expression +- [ ] Login as root and import a dictionary from a local file +- [ ] Series-specific search with a lot of jargon +- [ ] Lookup kanji details of a word + diff --git a/examples/sentence-word-lookup.ts b/examples/sentence-word-lookup.ts new file mode 100644 index 0000000..7f5331b --- /dev/null +++ b/examples/sentence-word-lookup.ts @@ -0,0 +1,23 @@ +import YomikunDirectAPIClient from "../core/direct/client.ts"; + +// Create a direct (local) API instance +var api = new YomikunDirectAPIClient(); +// Excplicitly wait until everything is ready +// await api.ready; + +// This sentence does not contain all information until it is explicitly +// fetched by the user. Each subclass instantiated from an API instance keeps a +// reference to that API instance for fetching additional data. +var sentence = api.sentence("この紅茶は甘すぎる"); + +// Pick the word 紅茶 from the sentence in some different ways: +// var word = sentence.at("紅茶"); // reference substring (matches first only) +// var word = sentence.terms[1]; // reference word index (depends on correct deconjugations/parsing) +var word = sentence.terms.find(t => t.writing == "紅茶"); // filter terms by writing (matches first only) + +// Fetch definitions for word +var glossary = word.glossary(); + + +// WIP + diff --git a/language/parser.ts b/language/parser.ts index 9bfdc4b..27aa5ee 100644 --- a/language/parser.ts +++ b/language/parser.ts @@ -7,27 +7,28 @@ import "../util/set.ts"; /** @summary main Parser class */ export default class Parser { db: DB; + ready: Promise<void>; constructor() { this.db = new DB(); - } - async prepare() { - await Promise.all([ - this.db.prepare(), - ]); + this.ready = new Promise<void>(async resolve => { + await this.db.ready; + resolve(); + }); } - parse(sentence: string, options?: InputSentenceProps): ParseResult { - let parseResult = this.parseTerms(sentence, options); + async parse(sentence: string, options?: InputSentenceProps): Promise<ParseResult> { + await this.ready; + let parseResult = await this.parseTerms(sentence, options); if ((options?.depth || ParseDepth.Term) <= ParseDepth.Term) return parseResult; - parseResult = this.addGlossary(parseResult, options); + parseResult = await this.addGlossary(parseResult, options); if ((options?.depth || ParseDepth.Term) <= ParseDepth.Term) return parseResult; return parseResult; } /** @summary parse sentence into terms with readings */ - private parseTerms(sentence: string, options?: InputSentenceProps): ParseResult { + private async parseTerms(sentence: string, options?: InputSentenceProps): Promise<ParseResult> { const MAX_LOOKAHEAD = options?.lookahead ?? 15; const PRIORITY_MOD_HIGHER = options?.priorityMod?.high ?? 10; const PRIORITY_MOD_LOWER = options?.priorityMod?.low ?? 0.1; @@ -38,7 +39,7 @@ export default class Parser { }; for (let start = 0; start < sentence.length; start++) { - var results = this.db.findTerm(sentence.substring(start, start + MAX_LOOKAHEAD)); + var results = await this.db.findTerm(sentence.substring(start, start + MAX_LOOKAHEAD)); // current starting point did not yield results, try again at next character or until end of input if (results.length == 0) continue; @@ -112,7 +113,7 @@ export default class Parser { return parseResult; } - private addGlossary(input: ParseResult, options?: InputSentenceProps): ParseResult { + private async addGlossary(input: ParseResult, options?: InputSentenceProps): Promise<ParseResult> { // TODO: annotate input with glossaries from DB options; // prevent unused warning return input; @@ -1,6 +1,3 @@ -import * as path from 'https://deno.land/std@0.102.0/path/mod.ts'; -Deno.chdir(path.dirname(path.fromFileUrl(Deno.mainModule))); - import { ParseResult } from "./language/types.ts"; function prettyprintParseResult(input: ParseResult) { @@ -38,7 +35,7 @@ async function apiTest(api: API) { // test 1 (direct api) await (async () => { var api = new YomikunDirectAPIClient(); - await api.prepare(); + await api.ready; console.log("Prepare direct api done"); await apiTest(api); @@ -49,12 +46,10 @@ console.log("\n".repeat(2)); // test 2 (remote api) await (async () => { // default host = localhost:9400 - var server = new YomikunRemoteAPIServer(); - await server.prepare(); - server.start(); + new YomikunRemoteAPIServer().start(); var api = new YomikunRemoteAPIClient(); - await api.prepare(); + await api.ready; console.log("Prepare remote api done"); await apiTest(api); diff --git a/test/reading/test.ts b/test/reading/test.ts index 9d426d4..9caf890 100644 --- a/test/reading/test.ts +++ b/test/reading/test.ts @@ -17,7 +17,8 @@ interface Test { tags: Array<string>; }; -const tests = JSON.parse(await Deno.readTextFile(path.resolve('test', 'reading', 'cases.json'))) as Test[]; +const here = path.dirname(path.fromFileUrl(import.meta.url)); +const tests = JSON.parse(await Deno.readTextFile(path.resolve(here, 'cases.json'))) as Test[]; console.log(`amount of sentences: ${tests.length}`); console.log(`average sentence length: ${tests.map(t => t.test.input.length).reduce((a, b) => a + b) / tests.length}`); |