aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/http/client.ts33
-rw-r--r--core/http/server.ts39
-rw-r--r--core/http/types.ts25
-rw-r--r--language/parser.ts16
-rw-r--r--language/tags.ts6
-rw-r--r--main.ts8
-rw-r--r--util/array.ts10
-rw-r--r--util/set.ts4
-rw-r--r--util/string.ts41
9 files changed, 138 insertions, 44 deletions
diff --git a/core/http/client.ts b/core/http/client.ts
index 42d75f0..a77b616 100644
--- a/core/http/client.ts
+++ b/core/http/client.ts
@@ -1,7 +1,9 @@
-import { ParseDepth, ParseResult } from "../../language/types.ts";
-import YomikunError from "../../util/error.ts";
+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";
/**
* @summary Yomikun HTTP API
@@ -18,14 +20,27 @@ export default class YomikunRemoteAPIClient implements API {
async prepare() { }
- async parseSentence(input: string) {
- var response = await fetch(`http://${this.props.host}:${this.props.port}/parseSentence`);
- console.log(response.body);
+ private async request(details: APIRequest) {
+ var response = await fetch(`http://${this.props.host}:${this.props.port}`, {
+ method: "POST",
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(details),
+ });
+ return response.json();
+ }
- return {
- depth: ParseDepth.Term,
- tokens: [],
- } as ParseResult;
+ async parseSentence(input: string) {
+ var request: APIRequestParseSentence = {
+ command: "parseSentence",
+ options: {
+ input: input,
+ },
+ };
+ var { response } = await this.request(request) as APIResponseParseSentence;
+ return response;
}
}
diff --git a/core/http/server.ts b/core/http/server.ts
index 8a6786e..b5d6c13 100644
--- a/core/http/server.ts
+++ b/core/http/server.ts
@@ -1,33 +1,44 @@
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";
-interface Endpoint {
- endpoint: string;
-};
export default class YomikunRemoteAPIServer extends YomikunRAWAPI {
private props: ConnectionProps;
+ private handlers: Record<string, (req: APIRequest) => Promise<Response>> = {
+ parseSentence: async _req => {
+ var req = _req as APIRequestParseSentence;
+ var input = req.options?.input
+ if (!input) return new Response("", { status: 404 });
+ return new Response(JSON.stringify({
+ command: "parseSentence",
+ response: await this.parseSentence(input),
+ } as APIResponseParseSentence));
+ },
+ };
constructor(options?: ConnectionProps) {
super();
this.props = { ...ConnectionPropsDefault, ...options };
}
- async parseSentence(input: string) {
- return await super.parseSentence(input);
- }
-
async start() {
- serve((req) => {
- return new Response("Hello world!");
- }, { port: this.props.port });
- }
-
- async prepare() {
- await super.prepare();
+ serve(async (req) => {
+ if (req.method != "POST") return new Response("", { status: 400 }); // wrong request (not post)
+ var request = (await req.text()).json({}) as APIRequest;
+ if (!request.command) return new Response("", { status: 400 }); // wrong request (no command)
+ var handler = this.handlers[request.command];
+ if (!handler) return new Response("", { status: 404 }); // not found (unknown command)
+ return await handler(request);
+ }, {
+ port: this.props.port,
+ onListen: () => { }
+ });
}
}
diff --git a/core/http/types.ts b/core/http/types.ts
new file mode 100644
index 0000000..bbba1b5
--- /dev/null
+++ b/core/http/types.ts
@@ -0,0 +1,25 @@
+import { ParseResult } from "../../language/types.ts";
+
+export interface APIRequest {
+ command: string;
+ options: any;
+};
+
+export interface APIRequestParseSentence extends APIRequest {
+ command: "parseSentence";
+ options: {
+ input: string;
+ };
+};
+
+export interface APIResponse {
+ command: string;
+ response: any;
+ // final: boolean;
+};
+
+export interface APIResponseParseSentence extends APIResponse {
+ command: "parseSentence";
+ response: ParseResult;
+};
+
diff --git a/language/parser.ts b/language/parser.ts
index 9c6bef2..9bfdc4b 100644
--- a/language/parser.ts
+++ b/language/parser.ts
@@ -52,12 +52,12 @@ export default class Parser {
if (!result.tags.anyOf(Object.values(Tag.Class.Verb))) return false;
// ignore other wrong deconjugations
- if (result.tags.has(Tag.Class.Verb.U) &&
- !result.tags.has(Tag.Inflection.Reason.U)) return false;
- if (result.tags.has(Tag.Class.Verb.Ru) &&
- !result.tags.has(Tag.Inflection.Reason.Ru)) return false;
- if (result.tags.has(Tag.Class.Verb.Suru) &&
- !result.tags.has(Tag.Inflection.Reason.Suru)) return false;
+ if (result.tags.includes(Tag.Class.Verb.U) &&
+ !result.tags.includes(Tag.Inflection.Reason.U)) return false;
+ if (result.tags.includes(Tag.Class.Verb.Ru) &&
+ !result.tags.includes(Tag.Inflection.Reason.Ru)) return false;
+ if (result.tags.includes(Tag.Class.Verb.Suru) &&
+ !result.tags.includes(Tag.Inflection.Reason.Suru)) return false;
}
// all other results should be valid grammatically
@@ -73,12 +73,12 @@ export default class Parser {
const lastTokenName = parseResult.tokens.peek()?.tags.anyOf(Object.values(Tag.Name));
// give higher priority to suffixes when last token was a name, else lower priority
- if (result.tags.has(Tag.Class.Suffix))
+ if (result.tags.includes(Tag.Class.Suffix))
result.sort *= lastTokenName ? PRIORITY_MOD_HIGHER : PRIORITY_MOD_LOWER;
// give lower priority to terms matched only by their readings, and are
// usually written in kanji
- if (!result.tags.has(Tag.Auxiliary.UsuallyKana) && !result.match.kanji)
+ if (!result.tags.includes(Tag.Auxiliary.UsuallyKana) && !result.match.kanji)
result.sort *= PRIORITY_MOD_LOWER;
return result;
diff --git a/language/tags.ts b/language/tags.ts
index d56ce98..d40904f 100644
--- a/language/tags.ts
+++ b/language/tags.ts
@@ -1,3 +1,5 @@
+import "../util/array.ts";
+
/** @constant Tags that have significant meaning to the parser */
export const Tag = {
/** @constant grammatical classes */
@@ -98,7 +100,7 @@ export const Tag = {
export type TokenTag = string; // no way around it
-export type TokenTags = Set<TokenTag>;
+export type TokenTags = Array<TokenTag>;
/** @summary parse concatenated tag string to TokenTags */
export function parseTags(input: string) {
@@ -111,6 +113,6 @@ export function parseTags(input: string) {
filteredTags.push(tag);
}
- return new Set(filteredTags) as TokenTags;
+ return filteredTags.set().arr() as TokenTags; // make sure array doesn't contain duplicates
}
diff --git a/main.ts b/main.ts
index 00be856..5c59ea9 100644
--- a/main.ts
+++ b/main.ts
@@ -13,7 +13,7 @@ function prettyprintParseResult(input: ParseResult) {
out += " (";
out += token.reading.map(r => r.ruby ? r.ruby : r.text).reduce((a, b) => a + b);
out += ") ";
- out += token.tags.arr().map(a => `[${a}]`).join(" ");
+ out += token.tags.map(a => `[${a}]`).join(" ");
console.log(out);
}
@@ -36,7 +36,7 @@ async function apiTest(api: API) {
}
// test 1 (direct api)
-(async () => {
+await (async () => {
var api = new YomikunDirectAPIClient();
await api.prepare();
@@ -44,8 +44,10 @@ async function apiTest(api: API) {
await apiTest(api);
})();
+console.log("\n".repeat(2));
+
// test 2 (remote api)
-(async () => {
+await (async () => {
// default host = localhost:9400
var server = new YomikunRemoteAPIServer();
await server.prepare();
diff --git a/util/array.ts b/util/array.ts
index 76e2a9e..5b8c512 100644
--- a/util/array.ts
+++ b/util/array.ts
@@ -1,17 +1,23 @@
declare global {
interface Array<T> {
+ /** @summary check if any of the elements of `arr2` are included in `this` */
anyOf(arr2: Array<T>): boolean;
+ /** @summary return last element of array without removing it */
peek(): T;
+ /** @summary create Set from this array */
+ set(): Set<T>;
}
}
-/** @summary check if any of the elements of `arr2` are included in `this` */
Array.prototype.anyOf = function(arr2) {
return !!this.filter(e => arr2.includes(e)).length;
};
-/** @summary return last element of array without removing it */
Array.prototype.peek = function() {
return this[this.length - 1];
};
+Array.prototype.set = function() {
+ return new Set(this);
+}
+
diff --git a/util/set.ts b/util/set.ts
index 9790682..1b4eb19 100644
--- a/util/set.ts
+++ b/util/set.ts
@@ -1,16 +1,16 @@
declare global {
interface Set<T> {
+ /** @summary check if any of the elements of `arr2` are included in `this` */
anyOf(arr2: Array<T>): boolean;
+ /** @summary return set items as array */
arr(): Array<T>;
}
}
-/** @summary return set items as array */
Set.prototype.arr = function() {
return Array.from(this);
}
-/** @summary check if any of the elements of `arr2` are included in `this` */
Set.prototype.anyOf = function(arr2) {
return !!this.arr().filter(e => arr2.includes(e)).length;
};
diff --git a/util/string.ts b/util/string.ts
index e0cc5eb..16d8f0a 100644
--- a/util/string.ts
+++ b/util/string.ts
@@ -4,12 +4,33 @@ import JapaneseString from "../language/japanese.ts";
declare global {
/** @summary extended String prototype functions */
interface String {
+ /** @summary get UnicodeRange for character at index 0 */
range(): UnicodeRange;
+ /** @summary create a RangeTally object for counting used unicode ranges in string */
rangeTally(): RangeTally;
+ /** @summary get JapaneseString from this string */
jp(): JapaneseString;
+ /** @summary parse concatenated tag string to TokenTags */
parseTags(): TokenTags;
+
+ /**
+ * @summary Remove all instances of a substring in a string, using a regular expression or search string
+ * @param searchValue A string to search for
+ */
+ removeAll(searchValue: string | RegExp): string;
+
+ /**
+ * @summary parse string as JSON, with optional fallback value
+ *
+ * fallback is undefined by default. if fallback is specified, it will be
+ * returned if JSON.parse throws any error. if fallback is not specified,
+ * no errors will be caught.
+ *
+ * @argument fallback return this value if parsing fails
+ */
+ json(fallback?: any): any;
}
}
@@ -27,7 +48,6 @@ export enum UnicodeRange {
type RangeTally = Record<UnicodeRange, number>;
-/** @summary get UnicodeRange for character at index 0 */
String.prototype.range = function() {
var code = this.charCodeAt(0);
@@ -46,20 +66,33 @@ String.prototype.range = function() {
return UnicodeRange.Unknown;
}
-/** @summary create a RangeTally object for counting used unicode ranges in string */
String.prototype.rangeTally = function() {
var tally = Object.keys(UnicodeRange).reduce((a: any,c) => (a[c] = 0, a), {}) as RangeTally;
for (var char of this) tally[char.range()]++;
return tally;
};
-/** @summary get JapaneseString from this string */
String.prototype.jp = function() {
return new JapaneseString(this);
}
-/** @summary parse concatenated tag string to TokenTags */
String.prototype.parseTags = function() {
return parseTags(this as string);
}
+String.prototype.removeAll = function(searchValue: string | RegExp) {
+ return this.replaceAll(searchValue, "");
+}
+
+String.prototype.json = function(fallback?: any) {
+ if (fallback) {
+ try {
+ return JSON.parse(this as string);
+ } catch {
+ return fallback;
+ }
+ } else {
+ return JSON.parse(this as string);
+ }
+}
+