From 2bd40b45e921b3e80b65c372ad96a281a88a0948 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 2 Feb 2024 15:48:40 +0800 Subject: [PATCH 001/738] add a bunch of icons --- icon/16.png | Bin 0 -> 487 bytes icon/180.png | Bin 0 -> 5115 bytes icon/192.png | Bin 0 -> 5625 bytes icon/32.png | Bin 0 -> 895 bytes icon/48.png | Bin 0 -> 1152 bytes icon/ico.ico | Bin 0 -> 15086 bytes index.html | 11 ++++++++--- style.css | 6 ++++++ 8 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 icon/16.png create mode 100644 icon/180.png create mode 100644 icon/192.png create mode 100644 icon/32.png create mode 100644 icon/48.png create mode 100644 icon/ico.ico diff --git a/icon/16.png b/icon/16.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf882dbbb1bf34d2285aed16342165d3ea2371b GIT binary patch literal 487 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 F{6FbFO;rE@ literal 0 HcmV?d00001 diff --git a/icon/32.png b/icon/32.png new file mode 100644 index 0000000000000000000000000000000000000000..c92fbc1d6e9353e9a2c71c57f37b1e390bda544f GIT binary patch literal 895 zcmV-_1AzRAP)5J`HwT=%|a6Er{e>nQRj2IfKZVj+YwV9DrhgYBBs9 z*xeqtBC}(qM*jR=n>tNkY)_P2D+m5V9e`=!a)od;QM78}jY{1=Gl*&JD4DC*oBM^E z1YQJo^{X!jz5ud)YBDlDnK>BJ3nFPMTwl{P^#h0@KAtVyT#mP6dYo)-IUGke37QaY z$GR?JK4lyF0yKfqxW=S2)nfQC*kd^~f!G0B-!!tXWU94B{ENAjaCu+=GkZ_Y9mVYN zuDa9fn!*C`ouS%&E$_nd-{_cDi|GSW0VH->6dghORdDZmu7H_%_IAeqY3gTI2B`1I zr8nz|N+W;CnO=sJ3B`P}sZ#fU7}mTG0DAyR4Q~!v46y)wX6S`n-hXgdqh9KD2=9*{ z%Juvgr;5$rm$IW*J8vjdE&RU#cj%p9^vW=kez=!=7PmDYEjLClZwkPmrVCzpNX8@{ zM=&=wTMS<-1^&5q$s#b89M|g%3@a^q;dL1ofH&KrlQH;o_CWYFfI;eN47~!=<7BrL z!>444j`XCKKxo^TCBJ^!V!SLs>_gTLbE&KWS%3CZ3!7?Iic@jV{z z>D{Olv-pXq{mq+F;7_+&@eQK0HLc$7mSpTqb;->w@UW6 zbECl|yrHcCq3h9#0JJD^SfRbNDgZtboe1qEhyd(KTwO@+%}(c!VwIxBq#`LxnWXmY z(JVH?qM$idx#Cy38G+w{$7-B^*1csfWk>jj2$QI^7aEoj&ACn@pIN~H$PU}S4SfX^ zc^&jif%|G)eiE&5wmDqjXNBBm)VZ3uK2QwOiT@K&N+72Y{Q-ysxrv85p@Tm>Ujab} z(>Kj0^CR|qqRa=r6YRRKd7wNz+Ivm*>GQ9lg8=W`Fj>&g+qv0(`0(%-P#G*ATK$Px zgnXTvHgH+cnUJzIg}(q)1ZMMWSqXF8BjJGL5KXzRc`#itC*QuA@SXa?q?u{-A#Y(e z&(=cH9CtUIad$KR(_svYRtWSV+gn=4zYjZ15Q2V*Lt}1;cr5OWZARkDK^BQ!5jZ!F zQKy(x`cjsq6DV=3WG^3FUny+V`G8jFsKETO;*3aN3N#54r}{HKZk6nBJ{vO6y}H6} zfIeYOl_whY*~}~y7JVXe33M8W*QxW&x@CV_Debc6+i6JQDx7<|Zn&lpc;)P{{pMal zd<8-oLg$ZSZAYA^JX^%JQ26NVu=6YIu7NbH8(5%fUmIac!riz;6#WX7*L?D~CgA<; zVb%Zb(JXs=05}4S)wte|koD`NDS_-raT=ABWiiqA#^ug#%W)FdGAlR>N$z!T*;jzc z8ucIb>#!Mt2r9Xjqu$N#Jv(OL^e?#HlXsRT(+wmV(#dI*gW=1}=Gk)B%?U(XUY>+0 zU?W^YG!>+AW|eI#k(dD%TWQy>fD0@tyzEAuhtoIAsdORnbSWQIJC*v8_QwQ z=*1TJ!dtEquoCgNoDD}s^d(>i;m1yeKFDmIt+~g|3G0ac0q~-N_Q6FbYR-7;RsCKM z1wpdM?A;nycGCsZ?r)j9=;$*YU}QF_{MGgN zzwiI+uSeJPGL~UkHa5l}$Jx_M8Qa7d8y~ly525^hlx^7HJm1RL*42!yLmO0~hg}Y# z{4-6Hg{5XeH<3DF^-<;5O8-_tuQm$$FR*vtrM!LzJencjsO)bPXI7)_*Qh&Y`44TK zcI0cGenWnr^X&Sc`l9S4ZzuKdH1pbi)KOoqhs%A%A)crW66bXa`5!J$>|X_Wp6WOr<&kVVC-$#U`j33Nv3x!CaoWrM=j)qh zZbBXTN9jKcFSNh*4f-ADx+SR^R~g^(Df*psd`;sgkA=uf?hhXrKemc{Zs9yhHuBnf z#7h}5_Oryv?oVy3crw0kY~Fjb*w>eb|0p)0gLus%9i{rt6bq!?&tj=~3Mqp%1ZgeO zs%?0F5ov{8K7z7WPT1?rIEp5%V#=xid|ZGV>p3_#+_?~|e_Wifc0>G(6mvuWhsDYu z%7^jyzl*Eh~Fp}+oAiir(c`%;j{7*uTJ9NaiX)q!B#=L&s<+#CbbV;e;P&OA+-HR zY7cl}uZf56`38QG^eOR!c6+u(;s-H2e9tGmtRu$@&IgZxqu_H7o47~dFH-E0PHX(* za^dk()SgpdH$|UC;)~<6{YbW6&p$x2P*3f1y4x zvkv$li0yjDCoaMHXU$bnzmvag#Rd8RpU$y7UI5=U{uh1qVp}zS$K3eVb&g-&rF8>X zIQJTlaXomxFi?DvBe>M_`t2N>q=-#HwK(X1<@^^h65^lbeH%sn zVf3YW{5O)l{%t!jBI!#Tze4!5PdM0a+*r85^&nMzytlkf?>C6qi;|sw9$q|mDE53~ zB*sI+@dx&vR#E>cCbr1Q8zaOZA48I|F0oD#e|4!njsNrH7sx-QIuYN?^R?JHN6395 z@I5_L^JlwcnlVzPU8+{rl%~t9lr6I@NGp+8 z76sYq8d96xiBz3#PgJMhELEpjsk$4f%tnziNGx04fbLWO=2fw_pB<8QhC}+zmHH{^D3Q?HIG}1;uinkA8pb?su*S z^cC^boBv&%-TdX$b>~lhFY2Gf&~x?phpl5V{myMm=>G-huJ|ze`+jTT`VvSd#;_(k z-`1E3xf51W{%(6ZpJnHOPg z>$s17d|>=V+Y#3(+p^Fq8238AlLqWl`ZKBjMgC%b>)snwK1)_+JVP8|!!A|7f$|UE zzXE<#al>Bt9_QE08y~9L@Oqz2%m3m07wNZaoIgGSrThG|cBZTau-`_dk;NiQK;8`$XS@3@&^Xy%Y4Y3NsoK~wz%H_%65eO z508Rx(aG!o< zx?iqJ5tLnkYu%78?wU2ay3h`+^i!jwj3?nGQ&vjz!yy + + + ilo Token -

ilo Token

+

+ ilo Token + Icon of ilo Token +

⚠ WARNING: Work in progress; Some things may not work properly.

A rule-based Toki Pona to English translator. - Limitations.

diff --git a/style.css b/style.css index ccfc037..1319f8c 100644 --- a/style.css +++ b/style.css @@ -2,6 +2,12 @@ body { margin: 10px; font-family: sans-serif; } +h1 { + vertical-align: middle; +} +h1 > img { + height: 1em; +} a { color: #0057af; } From 135664d7901ec95d9a6799f460827600ae280d6a Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 2 Feb 2024 15:49:38 +0800 Subject: [PATCH 002/738] set to development mode --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index eed60e7..554cd25 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ import { translate } from "./translator.ts"; // Set to false when releasing, set to true when developing -const DEVELOPMENT = false; +const DEVELOPMENT = true; // Don't forget these two when releasing const DATE_RELEASED = new Date("2024-2-1"); const VERSION = "v0.2.2"; From d91159411ae2a8529f74215530420208f9618e82 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 2 Feb 2024 15:50:26 +0800 Subject: [PATCH 003/738] add development version --- CHANGELOG.md | 4 ++++ src/main.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5f3e60..03fc786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ You may need to force restart the page in order to use the latest version: shift + click the restart button; or ctrl + shift + R. +## 0.2.3 + +- Add icons. + ## 0.2.2 Update missed links. diff --git a/src/main.ts b/src/main.ts index 554cd25..2733fa9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { translate } from "./translator.ts"; const DEVELOPMENT = true; // Don't forget these two when releasing const DATE_RELEASED = new Date("2024-2-1"); -const VERSION = "v0.2.2"; +const VERSION = "v0.2.3"; // TODO: maybe use worker document.addEventListener("DOMContentLoaded", () => { From 16afe7f1c9b467a549160b62de2f2e0886ffb86b Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 2 Feb 2024 15:50:48 +0800 Subject: [PATCH 004/738] remove notes --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index bc6a685..1fd22a5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # ilo Token -> The repository and the website uses to old name, it will be renamed on the next update. - A rule-based Toki Pona to English translator that translates into multiple sentences. This emphasizes how broad Toki Pona can be. Everything is hardcoded, no machine learning involved. [Try it](https://neverrare.github.io/ilo-token/) From efe6607ce73394a14e4c76efffaa8a914ac7feb2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 3 Feb 2024 08:18:30 +0800 Subject: [PATCH 005/738] update link name --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 7236e34..ffd77c2 100644 --- a/index.html +++ b/index.html @@ -57,7 +57,7 @@

and ping me (never_rare) at dedicated forum channel: Toki Pona to multiple English sentence translatorilo Token - Rule-based Toki Pona to English translator.
  • From 8aadc86ffb6ea6b7b6ba3795c2d5e65bee562fd8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 3 Feb 2024 14:35:11 +0800 Subject: [PATCH 006/738] prevent adding words to nimi ku suli dictionary --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 34343fc..271932f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,7 @@ "name": "ku-suli", "path": "${workspaceRoot}/nimi-ku-suli.txt", "description": "nimi ku sili", - "addWords": true + "addWords": false } }, "deno.enable": true From bf85fc92d2fd288458dd69dc5cf9362c3a75c3da Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 3 Feb 2024 14:35:27 +0800 Subject: [PATCH 007/738] fix typo --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 271932f..6a60a50 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,7 @@ "nimi-ku-suli": { "name": "ku-suli", "path": "${workspaceRoot}/nimi-ku-suli.txt", - "description": "nimi ku sili", + "description": "nimi ku suli", "addWords": false } }, From 91b9964f1cf8e87632917a912bc5789a9eda78e6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 3 Feb 2024 19:04:58 +0800 Subject: [PATCH 008/738] small wording edit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fd22a5..d0659b5 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ To stop this command, simply press Ctrl + C. ## About the source codes -With exception to `./src/main.ts`, every source codes in `./src/` are environment agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run`. +With exception to `./src/main.ts`, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run`. From a5c56c25cce0c04c13a1e094091174323bc7eaa4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 4 Feb 2024 14:06:48 +0800 Subject: [PATCH 009/738] add note --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03fc786..6b02ae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ You may need to force restart the page in order to use the latest version: shift + click the restart button; or ctrl + shift + R. -## 0.2.3 +## 0.2.3 (On development) - Add icons. From d4b2bb0cec8b5780f40902353725b762fa0453a4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 11 Feb 2024 18:37:47 +0800 Subject: [PATCH 010/738] update icon and remove icon from header --- icon/16.png | Bin 487 -> 504 bytes icon/180.png | Bin 5115 -> 5241 bytes icon/192.png | Bin 5625 -> 5523 bytes icon/32.png | Bin 895 -> 882 bytes icon/48.png | Bin 1152 -> 1328 bytes icon/ico.ico | Bin 15086 -> 15086 bytes index.html | 5 +---- 7 files changed, 1 insertion(+), 4 deletions(-) diff --git a/icon/16.png b/icon/16.png index 7bf882dbbb1bf34d2285aed16342165d3ea2371b..fa6959a9b3e734498f6d4265de087652f3e4c272 100644 GIT binary patch delta 420 zcmV;V0bBm(1NZ}wUVnv2L_t(Ijg^v5YZFlv#eer@q@BEh;6ms!t1b$D05ubvjrw8K zRrBgXs0)S6L?}|MTm2Z~BG5_q1)=M%T_}-xc1n}EE}X)EV;B!Sc$~{Qocl*ql{(Vc zUjewxZqNcj1Lpy4;2U88CZnQ1js25~%PSL-p?^FX75#Mv@_%agTNGY*zangwEO!jS zDL~2cfgv~tE=||o-TVB<^sfX5vpYGWhrpI0m;zpey!vk;eE63bKyHSZ4*eJ<0e)1q z+p4(zxqZ8~h58nff!`BhT9sb9tovr27(kLieg!NLS%~UFVFy6qTt#}Q_JE$t?8anN z^gFB7?)b!htbZG{TG?e_!*J=(fNe#e?Pt+@sNI3ftTYMJ*q4AD%qWt$^GH~|7UXC; z%+2^DI%+zkMx?R-1$?jGPgQxB4stbZ4S;$=t(da4fy-=o%b0t>8PG%Zxyz#C)-4>~ zCtP;QFTghFQ#X%};Xk(EGAol&(Kpwthi+~^-SDjw03E!TZC(Hub?!IgsgUQ20LY>M O0000B)DSX#zcY(!`MWfxh|#yrJW|Oy{q@mefK->ohLIR ztlgXlm6h6?cfGl96qQi;YOsvb2CxBK1Rd|S3{&XGX?BLszkj9r`=i;f`P`m=vnbI3 z=PlUNQD)HbR2tbQ2G2@ntRx0u0UsKY=wwYwQ6L<}`!e zOdrF-T0995JV}0~&)o;kCtzX3$%2lzQg5P;Brb?`D#+K@6uVk>X|aoi?wSqK<9Z2%SB z$3_^3h{!}8NQyGZohoGC6%BO@I^Isu_1cN74x9t-9l$V97wj@~a#jjM1a^<+t}=+7 x0&M`9*+dL^g|*m*H*lt%ncDc*`VRnq0rdEPFKNFzqznK6002ovPDHLkV1h36yh#86 diff --git a/icon/180.png b/icon/180.png index e42e4c0df022b9eb776dac1d39bb450c5964e491..5e75cba2d04f8b9f713c8c7c22ce1f981b8af487 100644 GIT binary patch delta 5232 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 q7z_r3!C){L3UrvmC5 z3}};>Aa}*b?$G0*gQ1lrG`AEw3{X!rpiN@<#|L_lw-ao79xZ)r_rrkhI3V}H$Dz<4 zLN^U`nZH6Ggnu?bpM^dQz-JrKCXyg`$Hx)SQ`#}nla@E37Xa!B2DC}3AXj^fd^&Wq z$hUI=^h|&q{6d{0cKDXwhwJCE5YPwEm!U7=!IvA7MfWQwcl$y|3j4^V$QjW0pyP9A zq0!;=-$q0CfYvnm-vs+f=q1p*0QO2lw4lCa`0Y#|y?@Xq^rg4Qn7*1DpwZ!9#mDW? zwc7FPcSA>@170yy+w9kkyhWyq(N*!Sj>mYOm2xg~gKkv7*XV{EgHD)Y*f!gz zz439f*FFi{@;lJ2FrM>5U7(_j4|8mRFgtI_@4&zvZuou%(XB0WSsu+0=&mED)!hc2i=V3m z?=zSW9r|_Ms|*9cYg`OC_D07qO9W=7cm7J*q>uXoCqc)dA0KZ4!rt1s2n}@iG7ZLV zF54q_sdA9}yDa2*tSfIGO>45;gZyiN{C{K{ZbA?3K!@(s{$(7Pvo*UOe1qWQVej*6 zftgv1y949{+JJeLg{~I5?i#5A-Ls5?)7!fqRmwo_4*jjSf?AS^o^e?|$Q}Us{sJJB zS|IC}D$q4$9w@pVrznD%Oa5H89_~udWswku-%f>o10df~;Eo{4$V5+H;3hdAI)6rt zn6AfT7_8|LL8c{Rxq#6jXY>WgV(8VJh@xLUP%t0rkp$-PMGF<{R{r)PRd! zhTtN=_FPDR8tpS-{W3oxjV=5GWizXDi!sN7dA`f)nhc2qv-5SGr7d)F3oDgQ@AiA< zBXcF~f>vOrYcaw90mKq+&H535nxUZ2mZrd7MPQ&cmv%h|(0;E4n16XH)Rc%YH@3<% zTs|ep{Hmk*&`qEHa=<$e&`fFs^Y>krShQNC`{JtRX}G5+l=YXig|0{q=nCaPQ&_== z_40VA4p-Q?J&I?N#7L@QB!OGVegT;4750_%nnG1-KvyLIG=&qtn_PBc(-x8L;ce(} zfIL}oKP?W>?@0kIxqk;OwA4m~`8k)Z_PwXPkLC-vm)uHWcjc2pEwYwt-xf{2+(^^bUSE zj7E`;LszE;wDi*P=U#a}ZhJ(4*>_A~HPV4f?~SXMD}{kh%H@!*U-o~mJYQh&xBwHD zSi-lO*26Sup?@XI74uD0mlDv@JFT0w#4N~-kJFr<{fni_7K(wEEZs%x+$$xZrOy&< zu_y_k9`CYEt1*S!v*rr4v#H`q;X+~dx+ej&k1tBUPFn3uKz**uwn~*-m?g*D#`H2W(6K#mfhlJ+nfBq*B2aQtw2l1qWh!-wB(?0!uM%= ze;JV7=(1CsrUe>Up1(b$Nr9IQ1mA9T+GzGwr{;823*Ac?=p}`|?)(nv-MkC(ki)OP+~D7Mv+bQkECUHcA&@9eeKyGi^$ z!+(x1qs>3!^Ljk@+bw)oy$>A<=+%YW1|lji*G?Eg_1I-jaPnbSua48s4>XvK~Gub8bE&r z-6oJhqA}vC>jSvvc7a`vTInSJR9ud^PJg$oE!~uW)>UfO|{j&N{7I7jh-bai^ql7BIv1w=ZJgQ<>-6%Rd{)wjM-? z=dCd=OPX`3pA<^UOsPZXC1uQ@1wElW)s-oiRmNrUE~?)kAtE8-(7DdzK<6_=cbLl# z7uv{pELj+bu605QXd!@>PNexn3CMIS4u%egZe<*b1`)bx23mU6?4!sa^M7^7?6;q| zETu9<;pT`%-V$nak7y{wcDZ*J9{do?-g6_u%u)zHby;3=O2Yi&ze68$*|{{Y`+j60 z$eW$6UF8gWGBWm>MhjgVz(UWB0P@#8$V}txln}(MLvjc7AxxxSZ{~O2_qokCy0CQ( z3z1HeZffTMe{Irv4Ih^gWPe()kuEEDkV%yD~E;lB>=c*kF}*u~0eB?GwcZ*Ry70t3gDiQlr;_Ixs1@kdg@I;0 z;>7_$<{#iuE(^g8OaSC}y)~W)h>PB>j_)qPv$OEdRF~E8h%IYt1%JASu+Pb5@A?2B z(|_FG`^noR05ajdnlJ-KkuWCO`4IhZIDVYsy`v(BJK?r2}jEp58lE8$I<+(2LAO}s% zLi>}^H{4ovZp7!U(|-!I^sDRBQu8=4SnGNp`DiDf+8RPGho0t-t}B-@B2gOV1yr!dfb^?vF% zqn?AGA9Yz;XI^1mWuaA+Z15*0ObK|2`=Fzx8+{tCDkPBpMR+K6TU{hw$r|7rTu}Wql#>Tr}!R+cFJ-JgCx*BatbnCfE#G3n%Q_NT>@;O z(mr5L3=j4!^z0WdrJqtRn6@1g%CqvH#$j=1oJ87Wn4M{yf zYZC4vJ`W(@8}J$x0Vaj!Ln{KhLBaF9>whPF*RsN9;A>S01HICGXk9?ZT(%{=%$*)n z#@vC=OTnu3-=$Fl`Rgf2o+fH?*T)xs{v zGV`Hz0X?Hk_sr4;48vU#a_2)kq>GbXOlmL-tqbU%1iDY|{N5ASI7|u5&bu4Z7J8{6sRU>*tb!x8h(U|R+6~`< zehVPqm*cmaxf~h?g;sjZpasX^$3H>WEV3EqT-*)4vBXTc)PP>@)kKkn{?9=)F5ABJ zdVyahyeurU4cZv49Oh6KdZi($41Z`3HJ6@u_C9r6yD+$2=X~g3eD5yri>Dyc$^Ep> zD-20pR;Dr3tiFFNT##|z59V=d13d_YoN?>>U6z;pmi3)kXw^VB0{eM%s{ z|2_3Z1nFGpWoDrh0J;g>TprI$&aZT)U4sFw)Q9dMBJD#jbr$6?pmS!S&3}hBpreWO z9?B)9Y}z$3pfy@(69YOo6KY%bHlT9_v{`5aI;vC}&<3N|@;8_>D( zp$%vQI#)ilxf}*`t}L`o3JvI7S!i=P4CtsLot$I~D+Y9~fVLgq26R-hV&?{Qu0%RH z$t<)19aW@P3Kv?J8qhJtqJJC)v`H&s3o8b6ROzI=Jpq2eaJ#t!|?fN#L zxTY1|m=WzcStkWstujuX0d4t$g!a z#3uLT(~?pG1#a4g4u6~4@2KM$LTJr}U!NP`47??I1In74?|xG>+ych+7F(XF+607L znDsBp;N133OsffEH0^o*mrBaPy$c#QZSfNB_$g>46*Wy1H?aVkC!tk8;EbE{(I7qU zu@a&EJe&++kaaA)MVNavB0>abF>73+1JmmBEGvTy8djgkCOT1b@=fs>7lSsCB3>?R)W2{bN|i@19D<{imXhGY$Q{lYFD1@*m3cO%UV%167nQ)*C$q=;g*n@H^&A;7iQE zDcT^KOXnFD?_hC=RMvm{!Ed?)fI3+=k^+GCU3J$CYi4x_Rdo2@PT$zbB3@zd@?#*ftGr7}=MzL_8?vM0K>g~N(1s&t;&y+hg7(fS*k$O1EIBG2(mkSjxJot?uC~29%ZTXe#4R3Y@U1Pw7Gg*^uK(bD8_a1{;WL z@0H;AA+vegp_sz!>U4*%bsS6k7h6&}Re zJbzNw_$DU(dnwaX6HCefcyd7Te4<+%ckY0CMgr@5&Xteqv?X1a$>+1u2=7>>(sx=2tiuc7#T@}UI zy<<}gcQG3R2@NR&p7Xb77C8hb$C_>KZ|LGY_L}AMjieCm$%6>zgB-~~#(tPEv{Ur8 z@BqkB>nf@t;1pP7@$Nl7e{U&>qD70ar#tyy<|rhZhhIL1hAJ3?IH{`#fw^nlpZ zbdA#6NvqQ(@H;`MV(y!hXI&@5ZO{1m4yTIrN`9y0+aOrmpcLBm2`<14b)xyifwkdr z9?f3XTCQHE1ZGMeg>)YidJJx(?eLbK>nu`eTa~eWOsdPy9T-Zm+go}tG<+7Gp<0

    L%a9 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 E0G=pc#sB~S delta 5618 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 Cja!`n diff --git a/icon/32.png b/icon/32.png index c92fbc1d6e9353e9a2c71c57f37b1e390bda544f..ccfb9802fb0a20ffa5fb8724aa1803fcde49178e 100644 GIT binary patch delta 802 zcmV+-1Ks@p2J!}wU4H`qNkl+O-EC1(QS3=j5yd~igBP1cJa`hkdhu)|xKAlI84uYcYtqIV zvpNvSn>U;J?0nzMi>NC7EO5;dFd8p^coloJU;Lk%wT75zm4EO548Z(vfOb;(6(${k zxkI&`$+buaK)Wi?9sAb+fg4>_Yvk?qVzj00)XrN=0^s_Aubb2wc@wy1b|?UW8(jsN z1~ywN^PT)1fMf-%QR@b<6)67=gat5PG z?HL$ga?yUTs(&YddSY*3{s^`vBtVH~_LD>8ev=b`h6)qqF#lm-0Dy9se=fpVpaFoe zyG^d8MOTn1hNN8|06z+qqnP*pi! z4s#C%O}-)kz+5r9pencfJ*Y~l9NP2!V%B0S4x-Xx{(lb2r9RJH9!Bz20RX(vz6DJ7 z?#Pr^vHf8r2N2(hmVg=}HBqfK;0r1>L~AHD5sm?4ar`G$ULX`uDIi*~V55o@P%Quj zFSIwHv~~5JXivWm5#NdW-^YJwUT9k_fWS3n;F<~(t98m+E~9}gPCg4{M0w~J&B=cT z5MNRk6MwE4B0An3+arNjv86kxp9QXI(3y&D31;?_%Vy1m=7`sPyj|~>BQ*)8-|@|> z*#5e@>;_$+IbnO`7#Virz%danRiG+R)r?e8sz8WPY(dPqre3PpPlpZw_Nw^<*i`KN~uL7uwn6B)oa> z`?epM`V~Z+*i}cFD24fFkisEsS-TB0G6m9ZLzFXKXg_Jir2;VPF73!>mtO;8bc)d~ zdonhaLwLEe{v&WV;rgZQN$aUV;F_UaPF}_K1J?jt>n?lJQf))8V*9GPZ?)v90|5N) g-y8CO9Yg>>0es~)#PgR>2mk;807*qoM6N<$f|6QsDF6Tf delta 815 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 Token -

    - ilo Token - Icon of ilo Token -

    +

    ilo Token

    ⚠ WARNING: Work in progress; Some things may not work properly. From 5a017010fd9ac8f7c0c8760e146b665c8936cb2e Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 12 Feb 2024 13:26:31 +0800 Subject: [PATCH 011/738] update favicon --- icon/16.png | Bin 504 -> 512 bytes icon/32.png | Bin 882 -> 887 bytes icon/48.png | Bin 1328 -> 1343 bytes icon/ico.ico | Bin 15086 -> 15086 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/icon/16.png b/icon/16.png index fa6959a9b3e734498f6d4265de087652f3e4c272..e43df2ad587cab0dd419c0b21ffd72caa9c0f7df 100644 GIT binary patch delta 428 zcmV;d0aO0?1AqjOUVn{AL_t(Ijg^x@OB+!Xg}?hUXfjg>LIduag-{S&xzmi83hEC^ zCS4S?&_ZTnap|i559uO{vA>{@wQDyLokvGZ=DNrXgmxOHXZ7B_?;g&*=RQ$YmJ#{( z86b$gbt=FqiCI8P;-xSE!*1Fh`SvuAB{>P^)AlL?Ie$OAf75tze<9Q}X4?km zCqTw*-{4Gu-^TTJdN&KwzZ9H}TM43RB1`wF2NE6j2&@62Iwx7Ps zOm@_d-D_C5uzv=$P~B441sXs@VHedcpq0PjbC>$u%x@I^9(HUGmi8csJt>nI`8Hb! zg0nGc=+};g`4_K00008PG%Zxyz#C)-4>~ zCtP;QFTghFQ#X%};Xk(EGAol&(Kpwthi+~^-SDjw03E!TZC(Hub?!IgsgUQ20LY>M O0000H0jfpiZF`u#5w%^)Fm5_LC2&6WJEPfj zaA3m#qj~dLRe#R`%dyzg+-Z!)BtQwKj*vj)ahVf_Jt=GQP<*%UO#x97JRJ(@XSpI zS2_kzxqqy##$1BP{kg$lr0S@Z!10W*4s-3WL^KuaB$zrvqQ5^JDUW(h#z=0Ad5PO? zmtf)(3yx>9Tb5-X=u;|_R?%Co9nN;Qx}cj57qFQJDGK^ip(v_FESU$Z2v$I$Ui)+*`YvAlUv l8zAf09BA}kprZh**&noSCV#43&$<8r002ovPDHLkV1fa#czFN- delta 802 zcmV+-1Ks@h2J!}wU4H`qNkl+O-EC1(QS3=j5yd~igBP1cJa`hkdhu)|xKAlI84uYcYtqIV zvpNvSn>U;J?0nzMi>NC7EO5;dFd8p^coloJU;Lk%wT75zm4EO548Z(vfOb;(6(${k zxkI&`$+buaK)Wi?9sAb+fg4>_Yvk?qVzj00)XrN=0^s_Aubb2wc@wy1b|?UW8(jsN z1~ywN^PT)1fMf-%QR@b<6)67=gat5PG z?HL$ga?yUTs(&YddSY*3{s^`vBtVH~_LD>8ev=b`h6)qqF#lm-0Dy9se=fpVpaFoe zyG^d8MOTn1hNN8|06z+qqnP*pi! z4s#C%O}-)kz+5r9pencfJ*Y~l9NP2!V%B0S4x-Xx{(lb2r9RJH9!Bz20RX(vz6DJ7 z?#Pr^vHf8r2N2(hmVg=}HBqfK;0r1>L~AHD5sm?4ar`G$ULX`uDIi*~V55o@P%Quj zFSIwHv~~5JXivWm5#NdW-^YJwUT9k_fWS3n;F<~(t98m+E~9}gPCg4{M0w~J&B=cT z5MNRk6MwE4B0An3+arNjv86kxp9QXI(3y&D31;?_%Vy1m=7`sPyj|~>BQ*)8-|@|> z*#5e@>;_$+IbnO`7#Virz%danRiG+R)r?e8sz8WPY(dPqre3PpPlpZw_Nw^<*i`KN~uL7uwn6B)oa> z`?epM`V~Z+*i}cFD24fFkisEsS-TB0G6m9ZLzFXKXg_Jir2;VPF73!>mtO;8bc)d~ zdonhaLwLEe{v&WV;rgZQN$aUV;F_UaPF}_K1J?jt>n?lJQf))8V*9GPZ?)v90|5N) g-y8CO9Yg>>0es~)#PgR>2mk;807*qoM6N<$f_X}DApigX diff --git a/icon/48.png b/icon/48.png index e915300bd421c34bdd3731970bb9e601b1773f06..6a6e7b066b31a5a8dbe20f89263ffacfe5e62225 100644 GIT binary patch delta 1265 zcmVrEQt6j_$H-3SP*G;va?B( z%p4z*U3X_@H@kF`Ou;|oVb3{pbAEI0nS1UPRizV+TJ8+6Hh*BA%i89ejt1(IjEmz>sjVQBW(T&SyX)XCQt6pY5Wji7RRbB9~B$ zF}kKmfV?#`aP_+T^Qocoh6s1~ZAW~W-g$Ux{nfwRUrt&#H{W`i%@qR3TQdW~{Ddmo z)R40g&xGH0#DB8tS!PQ$o1X;HDq4-oXi$nf8(nn5*O1FAKsk*y>ZeJ>EQY!Ed7PrQ49 zyCY%-`n}|d>JLFeGhi4-4XUrteXBZy0c|jJZqqI18-G#_AX_xQ7kC1=8H!R4of?{c z1OSPe0ly4za;UORghLVkXI7A>vc=Ryw-T?ap$0OxdBwok4a^1Oe8ayuzLj3o2muI2 zd1RP`A$k!)pUycI*_E-A-*z|k#l(V!qjvw7BDx!x54i51>~oI_ez=I$cl+nu48nr} zkEf!0bAN^8*WF9GQIY#VrjYtvRrdg1gF`s(BO8P9irj_pdcf?VJd-P=KJHq!5t3Ht zflDk=xF#pL_9oLw7Q zvYE@+$;&G89MI0uO%;7{rILp>yU|=B`2ot2rGMg7cqM12f}7Z~s_k|6RezI9?>;TT}nBKUMe61nuRh3Wjmb*79K2`u{ zc1&-a_jMwoo9z*}ytuJ8C{_S-zJCXBy@|UAxHM)}4%8c)7ID@#+ueCvYSeN)B3@Lh zBFVJUM)@s&(2RNWNKpYeY!4Ys&)PF&x zq%bL{qau!|3`P|3>H%bnX75t%G%Q>a_pFgOKwltqWg~B{<_xsd_*QyL{qDh@HO2^# z4oAqbtet#2mH>IHvQzpw3k*iMRp6zJojeb#TLU4T8YQH(gTO4R zyR*gA7g4dXnt`fvcHgyxA;ZA$Dzd9t@(@!1fFnQk|2ds7A6V*UJGKA-OWg@U%mLcb bZ4dA-e$3dwK)7Z;00000NkvXXu0mjfy5Cz; delta 1250 zcmV<81ReXo3a|>0UVptwL_t(&fz_B%Xj@en$A8~x)~5FsG8_yhuEPyPK?QL`q+Ui} zWG{oE>tGHP_nz1NdwYeN95P5wNDv5^Z!$ zR)CCG9=`UM|I3My>GhUyw6`rQ)4NanyYAWz|JP&Q?B=_-*<2BTj8`5OZcnPRO^uwg zyc6whOIq~}n13@0j~i}JE+<3-z{!#FCZX~Z$mRuKJeToGI~%=P^SuG;xaaRc*|+#O zyqpj;h&ZwUk3AMrNJ_Kx`ur zOamMnDQy$skmY~o1Q|=^h9)~@ysCydkj%Q@8O&Z_K3p#_!MXEWS%aDgfMBM_M~NBH za}ZfN=T&4+GV6TZS?i0&!UadNgI|c~Uf^~p^vKP)e?$nPlUM_PaNbWMJQj*nRP_1u zl=D^REPpr3`W;A44P8>zeL$rlARG;ljp6!=Y(RK3R9r!MAw4zpX~*1JBukwKCOrQ| z-~#oaLLOyZV=Ixe0A+tF=YG;zyHPC(0C3zZ?N{^yAT743s2oV;oDc8XT?@M7W-gg^ zK30*JfmV)gs_4O$G7l~8M$=QyM<|EyR8NK1(|_5a@JDP})$(>am2*!kyw)r(B5$Op z+=dLA_iL=&o- zKqP^Z07-x*1lB6D7Bm4QQaN`3ji%-JD6ylHktKXdfcCB>I;2$2HDQ2^R~|N0z6VLR z`hVeT|4_e7s%_)pK}9cxcfrln69857b(_F`K|gN=qU!+fZJ*f~GGA3o5M2kD4}yEZ z+-MT+7xdzB&)?DP{ic&Rm33SF@-C?f&#z$VsPH7tc%_{}aJtD^Vos6W>8x`(9O(K8 zRF%#;muLPNdPv|^gd*jk{qhMAuRXC;8x|Pr&6d5)zWFT0stsNuv!6TRpi<9l=DqD=fE702MQ>8L<@=(M9CxMMSmpU zWD?Ivz^>F)=L&Rj5~p%*+_s&Dg^S_7e)AT{D>Poy%&N#()jV`}6^iKmR@M{;{R4gd zW*kU_!nL%iFYFnBj91zzgPa1kSi)BG&;tM`My4MQjGPtN-lR%H&WF|YRj6WOw?HG8 zWs90sk+I}e*J>Vm03e->UsqzIpj&^0LQT`AUOjUrUO1HnKZ0(dMf1=DBT!X38^0bi zGOF-THo3J@agRmvDN!tQVgUT~e%m^p ze#AL(R_3JlxQ`L7s#)7nRWX~bib;=1ql{Y>irOxOGt(4h(&K$I zu|b)SWZ4esVM=F#K#3a;+J`LKoJ}wDLM=pf4kQO)Y`4|89`}i-AZ#jzC>w<6;VcN| zWf&8f+c}tV45ye-8-pZv8Ri_1A(rnp{=)KH!mNs5g6)AZM}ct@YkUn0;8K|ztXpg5 zD|QGbF_vm)s-f-9?sD=(EIH)jlF~dPSj^3X-^B)er}Q@M}06 s1QR$Y1c^DKYWyFVvh!{^B93v<3;2+V_%J~d2tg7U!Bn7|P~(058_Fa_m;e9( delta 1420 zcmaJ>OH31C5Z*1_ZBbw~N-b?^QBXj%YUCj8E|O>vRMffO7+>*%nD}U8jO?!3 z*o+5_Zx6<3d?Y>Tp*{{CJec@GLkuyZ9s$#qf}-pEk4=anHkr;p-~2Q4?ab^Bb`5r& zds&ok>Z!;^jUAR{3CZ3iN<*SBs~R6s{$x~RPqMdu=1Ak&ag1E+vY1xaW|s8s=gN*i+ym=;d4`V zn%Mzqhc|V{@klJM7KG&mD6>%nE_fCY6RJ~j=PeORrdL4Nv%zZ#mPoh~!R*~AlgK9q!?XS&5Es za6DW^x*4#$i6H5uO`1jcJe`xbo0fBUA?$QPTX8$iMooPw9+I_~Ajr!V%NbHEK|$?O zELQ=ZMHGPxo(bl3;cOK_aE3^XAh=?wlF}swaJqgGGCNbm_EUeK)AThElnz6`vj#lU zw3%etuuGpf-BEfWU0e;VQYpy3eQ?}2OJe9c?=nPo6C}Cc&B3%I0E1<5`01~dMzRsJ znQRUsIy?`A_?^D88_-i8Xr}NQu(*I1oGn$4Z-cP1_pFsd^hQOD&|9eOsG+aa7a5 zc^~=@gM2Qkw*qAlI0}hMzr?DLCzE|$u&OG^k^Zs^;FufYmd|q@9?z|m@C#wK(5{6& zacHcj&1ES_bHkpKbOsDr=e-RPzrs_nIknMY&hvZm`(S>^speXoa2@=pNxPx# From 92f865726e4ed49a54f4023163fb6ec7d396f2a0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 12 Feb 2024 13:37:12 +0800 Subject: [PATCH 012/738] small edit --- bundle.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bundle.ts b/bundle.ts index 830ee6b..9b47de2 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,13 +1,11 @@ import { emit } from "./dev-deps.ts"; import { debounce } from "./dev-deps.ts"; -const SOURCE = "./src/main.ts"; -const DESTINATION = "./main.js"; - -const url = new URL(SOURCE, import.meta.url); +const SOURCE = new URL("./src/main.ts", import.meta.url); +const DESTINATION = new URL("./main.js", import.meta.url); async function build(options: emit.BundleOptions): Promise { - const result = await emit.bundle(url, options); + const result = await emit.bundle(SOURCE, options); const { code } = result; await Deno.writeTextFile(DESTINATION, code); } From eb75897151f9f4f8d5da6688d9c8a1ce17677a2a Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 12 Feb 2024 13:38:49 +0800 Subject: [PATCH 013/738] small edit --- bundle.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bundle.ts b/bundle.ts index 9b47de2..350f5cf 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,5 +1,4 @@ -import { emit } from "./dev-deps.ts"; -import { debounce } from "./dev-deps.ts"; +import { debounce, emit } from "./dev-deps.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./main.js", import.meta.url); From f2fa442f5e6419d9b396a665b962f253b7f65e86 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 2 Mar 2024 10:02:42 +0800 Subject: [PATCH 014/738] update dependencies --- dev-deps.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-deps.ts b/dev-deps.ts index e876059..e1295b9 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,2 +1,2 @@ -export * as emit from "https://deno.land/x/emit@0.35.0/mod.ts"; -export * as debounce from "https://deno.land/std@0.213.0/async/debounce.ts"; +export * as emit from "https://deno.land/x/emit@0.38.2/mod.ts"; +export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; From a7fbcbfa2081799129a4947523dc6d029c432efb Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 10:57:31 +0800 Subject: [PATCH 015/738] Handle multiple errors --- index.html | 4 +++- src/english-ast.ts | 0 src/main.ts | 22 ++++++++++++++++++++- src/output.ts | 49 ++++++++++++++++++++++++++++++---------------- 4 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 src/english-ast.ts diff --git a/index.html b/index.html index 753fe8c..8e4cc76 100644 --- a/index.html +++ b/index.html @@ -37,6 +37,7 @@

    ilo Token

      +
        Provide feedback
          @@ -54,7 +55,8 @@

          ilo Token

          and ping me (never_rare) at dedicated forum channel: ilo Token - Rule-based Toki Pona to English translatorilo Token - Rule-based Toki Pona to English + translator.
        • diff --git a/src/english-ast.ts b/src/english-ast.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/main.ts b/src/main.ts index 2733fa9..c269250 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,9 @@ document.addEventListener("DOMContentLoaded", () => { const input = document.getElementById("input") as HTMLTextAreaElement; const output = document.getElementById("output") as HTMLUListElement; const error = document.getElementById("error") as HTMLParagraphElement; + const errorList = document.getElementById( + "error-list", + ) as HTMLParagraphElement; const button = document.getElementById( "translate-button", ) as HTMLButtonElement; @@ -27,10 +30,27 @@ document.addEventListener("DOMContentLoaded", () => { while (output.children.length > 0) { output.removeChild(output.children[0]); } + while (errorList.children.length > 0) { + errorList.removeChild(errorList.children[0]); + } error.innerText = ""; const translations = translate(input.value); if (translations.isError()) { - error.innerText = translations.error?.message ?? "No error provided"; + const errors = translations.errors; + if (errors.length === 0) { + error.innerText = + "An unknown error has occurred (Errors should be known, please report this)"; + } else if (errors.length === 1) { + error.innerText = errors[0].message; + } else { + error.innerText = + "Multiple errors has been found, but only at least one could be helpful:"; + for (const errorMessage of errors) { + const list = document.createElement("li"); + list.innerText = errorMessage.message; + output.appendChild(list); + } + } } else { const set = new Set(); for (const translation of translations.output) { diff --git a/src/output.ts b/src/output.ts index f725ae0..7c00fa2 100644 --- a/src/output.ts +++ b/src/output.ts @@ -4,34 +4,42 @@ export class Output { /** Represents possibilities, considered error when the array is empty. */ output: Array; /** - * An optional error, should be supplied if and only if the array is empty. + * A list of errors */ - error: null | OutputError; + errors: Array = []; constructor(output?: undefined | null | Array | OutputError) { if (Array.isArray(output)) { this.output = output; - if (output.length === 0) { - this.error = new OutputError("no error provided"); - } else this.error = null; } else if (output instanceof OutputError) { this.output = []; - this.error = output; + this.errors.push(output); } else { this.output = []; - this.error = new OutputError(); } } - private setError(error: OutputError) { - if (this.output.length === 0 && !this.error) this.error = error; + private static newErrors(errors: Array): Output { + const output = new Output(); + output.errors = errors; + return output; + } + private pushError(error: OutputError): void { + if (this.isError()) { + this.errors.push(error); + } } private push(value: T): void { this.output.push(value); - this.error = null; + this.errors.length = 0; } - private append({ output, error }: Output): void { + private append({ output, errors }: Output): void { this.output = [...this.output, ...output]; - if (this.output.length > 0) this.error = null; - else this.error = error; + if (this.output.length > 0) { + this.errors.length = 0; + } else { + for (const item of errors) { + this.errors.push(item); + } + } } /** Returns true when the output array is empty */ isError(): boolean { @@ -51,14 +59,19 @@ export class Output { * function can throw OutputError; Other kinds of errors will be ignored. */ map(mapper: (value: T) => U): Output { - if (this.isError()) return new Output(this.error); + if (this.isError()) { + return Output.newErrors(this.errors); + } const wholeOutput = new Output(); for (const value of this.output) { try { wholeOutput.push(mapper(value)); } catch (error) { - if (error instanceof OutputError) this.setError(error); - else throw error; + if (error instanceof OutputError) { + this.pushError(error); + } else { + throw error; + } } } return wholeOutput; @@ -68,7 +81,9 @@ export class Output { * values and flattens them into single array for Output. */ flatMap(mapper: (value: T) => Output): Output { - if (this.isError()) return new Output(this.error); + if (this.isError()) { + return Output.newErrors(this.errors); + } const wholeOutput = new Output(); for (const value of this.output) wholeOutput.append(mapper(value)); return wholeOutput; From 5ffd9aa920bd7efcd3852f934d8f6308551a757d Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 10:58:20 +0800 Subject: [PATCH 016/738] copy error --- src/output.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/output.ts b/src/output.ts index 7c00fa2..e337bd7 100644 --- a/src/output.ts +++ b/src/output.ts @@ -60,7 +60,7 @@ export class Output { */ map(mapper: (value: T) => U): Output { if (this.isError()) { - return Output.newErrors(this.errors); + return Output.newErrors(this.errors.slice()); } const wholeOutput = new Output(); for (const value of this.output) { @@ -82,7 +82,7 @@ export class Output { */ flatMap(mapper: (value: T) => Output): Output { if (this.isError()) { - return Output.newErrors(this.errors); + return Output.newErrors(this.errors.slice()); } const wholeOutput = new Output(); for (const value of this.output) wholeOutput.append(mapper(value)); From 78dade9f4773907217257e69c591c2b5a144450d Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:09:08 +0800 Subject: [PATCH 017/738] update unreachable error --- src/error.ts | 7 ++----- src/parser.ts | 4 +++- src/translator.ts | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/error.ts b/src/error.ts index d9d67f6..d6dcd00 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,10 +1,7 @@ /** Represents Error used by `Output`. */ export class OutputError extends Error {} -/** - * Represents errors that cannot be seen. This includes errors expected to be - * unreached as well as errors expected to be covered by non-error outputs. - */ -export class UnreachableError extends OutputError { +/** Represents errors that cannot be reached. */ +export class UnreachableError extends Error { constructor() { super("This is an error you shouldn't see... Please report this error."); } diff --git a/src/parser.ts b/src/parser.ts index 14df87a..7b50ba5 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -92,7 +92,9 @@ function match(regex: RegExp): Parser { } else { const token = src.match(/(.*)(?:\s|$)/)?.[1]; if (token) return new Output(new UnrecognizedError(`"${token}"`)); - else return new Output(new UnreachableError()); + else { + throw new Error("unreachable"); + } } }); } diff --git a/src/translator.ts b/src/translator.ts index 98e042d..cb93df9 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -70,7 +70,7 @@ function wordUnitAs( new Array(word.count).fill(noun).join(" ") ); } else { - return new Output(new UnreachableError()); + throw new UnreachableError(); } } function modifierAs( From aa4ae214ea1e663c246fa5ccbf3de60b0a891903 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:10:38 +0800 Subject: [PATCH 018/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b02ae9..2a99a5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ You may need to force restart the page in order to use the latest version: shift ## 0.2.3 (On development) - Add icons. +- All possible errors will now be listed. ## 0.2.2 From cd5c30327b25d7a0e2bc3f046ff83e7059fe7895 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:15:42 +0800 Subject: [PATCH 019/738] handle unreachable errors --- src/error.ts | 2 +- src/main.ts | 56 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/error.ts b/src/error.ts index d6dcd00..0ba0847 100644 --- a/src/error.ts +++ b/src/error.ts @@ -3,7 +3,7 @@ export class OutputError extends Error {} /** Represents errors that cannot be reached. */ export class UnreachableError extends Error { constructor() { - super("This is an error you shouldn't see... Please report this error."); + super("Reached unreachable error."); } } /** Represents Error due to things not implemented yet. */ diff --git a/src/main.ts b/src/main.ts index c269250..2629b4b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import { UnreachableError } from "./error.ts"; import { translate } from "./translator.ts"; // Set to false when releasing, set to true when developing @@ -34,33 +35,42 @@ document.addEventListener("DOMContentLoaded", () => { errorList.removeChild(errorList.children[0]); } error.innerText = ""; - const translations = translate(input.value); - if (translations.isError()) { - const errors = translations.errors; - if (errors.length === 0) { - error.innerText = - "An unknown error has occurred (Errors should be known, please report this)"; - } else if (errors.length === 1) { - error.innerText = errors[0].message; + try { + const translations = translate(input.value); + if (translations.isError()) { + const errors = translations.errors; + if (errors.length === 0) { + error.innerText = + "An unknown error has occurred (Errors should be known, please report this)"; + } else if (errors.length === 1) { + error.innerText = errors[0].message; + } else { + error.innerText = + "Multiple errors has been found, but only at least one could be helpful:"; + for (const errorMessage of errors) { + const list = document.createElement("li"); + list.innerText = errorMessage.message; + output.appendChild(list); + } + } } else { - error.innerText = - "Multiple errors has been found, but only at least one could be helpful:"; - for (const errorMessage of errors) { - const list = document.createElement("li"); - list.innerText = errorMessage.message; - output.appendChild(list); + const set = new Set(); + for (const translation of translations.output) { + if (!set.has(translation)) { + const list = document.createElement("li"); + list.innerText = translation; + output.appendChild(list); + set.add(translation); + } } } - } else { - const set = new Set(); - for (const translation of translations.output) { - if (!set.has(translation)) { - const list = document.createElement("li"); - list.innerText = translation; - output.appendChild(list); - set.add(translation); - } + } catch (unreachableError) { + if (unreachableError instanceof Error) { + error.innerText = unreachableError.message; + } else { + error.innerText = unreachableError.toString(); } + error.innerText += " (This is bad, please report this)"; } }; button.addEventListener("click", listener); From f6026d6009de63e491ca7df6da66331a508eae5b Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:16:59 +0800 Subject: [PATCH 020/738] update unreachable error handling --- src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.ts b/src/main.ts index 2629b4b..33bae45 100644 --- a/src/main.ts +++ b/src/main.ts @@ -71,6 +71,7 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = unreachableError.toString(); } error.innerText += " (This is bad, please report this)"; + throw error; } }; button.addEventListener("click", listener); From 43c9129fa42bc86e89c40efca5f14ee061cd2d06 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:17:32 +0800 Subject: [PATCH 021/738] small change --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 33bae45..1d943e5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -71,7 +71,7 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = unreachableError.toString(); } error.innerText += " (This is bad, please report this)"; - throw error; + throw unreachableError; } }; button.addEventListener("click", listener); From 54daf153feb9de8cc78ae64f140e8bbce746dcb8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:27:55 +0800 Subject: [PATCH 022/738] implement covered errors --- src/error.ts | 2 ++ src/main.ts | 13 ++++++++++--- src/parser.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/error.ts b/src/error.ts index 0ba0847..211caea 100644 --- a/src/error.ts +++ b/src/error.ts @@ -6,6 +6,8 @@ export class UnreachableError extends Error { super("Reached unreachable error."); } } +/** Represents errors expected to be covered by other errors. */ +export class CoveredError extends OutputError {} /** Represents Error due to things not implemented yet. */ export class TodoError extends OutputError { constructor(token: string) { diff --git a/src/main.ts b/src/main.ts index 1d943e5..c4a98f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import { CoveredError } from "./error.ts"; import { UnreachableError } from "./error.ts"; import { translate } from "./translator.ts"; @@ -38,10 +39,16 @@ document.addEventListener("DOMContentLoaded", () => { try { const translations = translate(input.value); if (translations.isError()) { - const errors = translations.errors; + const errors = translations.errors.filter((x) => + !(x instanceof CoveredError) + ); if (errors.length === 0) { - error.innerText = - "An unknown error has occurred (Errors should be known, please report this)"; + if (translations.errors.length === 0) { + error.innerText = + "An unknown error has occurred (Errors should be known, please report this)"; + } else { + error.innerText = "TODO better error message"; + } } else if (errors.length === 1) { error.innerText = errors[0].message; } else { diff --git a/src/parser.ts b/src/parser.ts index 7b50ba5..6f37750 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -29,6 +29,7 @@ import { SENTENCES_RULE, WORD_UNIT_RULES, } from "./filter.ts"; +import { CoveredError } from "./error.ts"; /** A single parsing result. */ type ValueRest = { value: T; rest: string }; @@ -282,8 +283,11 @@ function number(): Parser> { many(specificWord("wan")), ).map((array) => { const output = array.flat(); - if (output.length >= 2) return output; - else throw new UnreachableError(); + if (output.length >= 2) { + return output; + } else { + throw new CoveredError(); + } }); } /** Parses multiple modifiers */ From 2a03a3f5eb730169e9d97489a22d5e0e9d6f29b1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:35:36 +0800 Subject: [PATCH 023/738] change unreachable error into covered error --- src/main.ts | 5 +++-- src/parser.ts | 4 ++-- src/translator.ts | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index c4a98f7..8aae6c8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,4 @@ import { CoveredError } from "./error.ts"; -import { UnreachableError } from "./error.ts"; import { translate } from "./translator.ts"; // Set to false when releasing, set to true when developing @@ -47,7 +46,9 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = "An unknown error has occurred (Errors should be known, please report this)"; } else { - error.innerText = "TODO better error message"; + error.innerText = + "Found errors not supposed to appear (please report this)"; + throw translations.errors[0]; } } else if (errors.length === 1) { error.innerText = errors[0].message; diff --git a/src/parser.ts b/src/parser.ts index 6f37750..a493ce2 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -460,7 +460,7 @@ function associatedPredicates( many(optionalComma().with(preposition())), ).map(([predicates, objects, prepositions]) => { if (!objects && prepositions.length === 0) { - throw new UnreachableError(); + throw new CoveredError(); } else { return { type: "associated", @@ -541,7 +541,7 @@ function clause(): Parser { } as Clause)), subjectPhrases().map((phrases) => { if (phrases.type === "single" && phrases.phrase.type === "quotation") { - throw new UnreachableError(); + throw new CoveredError(); } else { return { type: "phrases", phrases } as Clause; } diff --git a/src/translator.ts b/src/translator.ts index cb93df9..477e72b 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -11,8 +11,7 @@ import { Output } from "./output.ts"; import { parser } from "./parser.ts"; import { TodoError } from "./error.ts"; import { DEFINITION } from "./definition.ts"; -import { OutputError } from "./error.ts"; -import { UnreachableError } from "./error.ts"; +import { CoveredError, OutputError } from "./error.ts"; /** A special kind of Output that translators returns. */ export type TranslationOutput = Output; @@ -70,7 +69,7 @@ function wordUnitAs( new Array(word.count).fill(noun).join(" ") ); } else { - throw new UnreachableError(); + return new Output(new CoveredError()); } } function modifierAs( From 0de32bde625f782d987f1ca8d303a16cfeba1a65 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:39:20 +0800 Subject: [PATCH 024/738] update error handling --- src/main.ts | 9 ++++++--- style.css | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8aae6c8..0be91c8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -55,10 +55,13 @@ document.addEventListener("DOMContentLoaded", () => { } else { error.innerText = "Multiple errors has been found, but only at least one could be helpful:"; + const set = new Set(); for (const errorMessage of errors) { - const list = document.createElement("li"); - list.innerText = errorMessage.message; - output.appendChild(list); + if (!set.has(errorMessage.message)) { + const list = document.createElement("li"); + list.innerText = errorMessage.message; + errorList.appendChild(list); + } } } } else { diff --git a/style.css b/style.css index 1319f8c..5190612 100644 --- a/style.css +++ b/style.css @@ -19,7 +19,7 @@ a:visited { resize: vertical; width: 100%; } -#error { +#error #error-list { color: #b60000; } @media (min-width: 800px) { @@ -38,7 +38,7 @@ a:visited { a:visited { color: #b47de7; } - #error { + #error #error-list { color: #ff5e5e; } } From 4984a69634be7ef36771f987d584de2e6aa9c528 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:40:22 +0800 Subject: [PATCH 025/738] fix error --- style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style.css b/style.css index 5190612..a4af00c 100644 --- a/style.css +++ b/style.css @@ -19,7 +19,7 @@ a:visited { resize: vertical; width: 100%; } -#error #error-list { +#error, #error-list { color: #b60000; } @media (min-width: 800px) { @@ -38,7 +38,7 @@ a:visited { a:visited { color: #b47de7; } - #error #error-list { + #error, #error-list { color: #ff5e5e; } } From 0873a56e5e15fd62048c981ae695fa7c33a2413c Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:43:42 +0800 Subject: [PATCH 026/738] update deduplication codes --- src/main.ts | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/main.ts b/src/main.ts index 0be91c8..a84b33a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -38,9 +38,13 @@ document.addEventListener("DOMContentLoaded", () => { try { const translations = translate(input.value); if (translations.isError()) { - const errors = translations.errors.filter((x) => - !(x instanceof CoveredError) - ); + const errors = [ + ...new Set( + translations.errors.filter((x) => !(x instanceof CoveredError)).map( + (x) => x.message, + ), + ), + ]; if (errors.length === 0) { if (translations.errors.length === 0) { error.innerText = @@ -51,28 +55,21 @@ document.addEventListener("DOMContentLoaded", () => { throw translations.errors[0]; } } else if (errors.length === 1) { - error.innerText = errors[0].message; + error.innerText = errors[0]; } else { error.innerText = "Multiple errors has been found, but only at least one could be helpful:"; - const set = new Set(); for (const errorMessage of errors) { - if (!set.has(errorMessage.message)) { - const list = document.createElement("li"); - list.innerText = errorMessage.message; - errorList.appendChild(list); - } + const list = document.createElement("li"); + list.innerText = errorMessage; + errorList.appendChild(list); } } } else { - const set = new Set(); - for (const translation of translations.output) { - if (!set.has(translation)) { - const list = document.createElement("li"); - list.innerText = translation; - output.appendChild(list); - set.add(translation); - } + for (const translation of [...new Set(translations.output)]) { + const list = document.createElement("li"); + list.innerText = translation; + output.appendChild(list); } } } catch (unreachableError) { From 7600a1a94db16eff1399944bbf13bb4309fdf6ca Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:51:45 +0800 Subject: [PATCH 027/738] update error messages --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index a84b33a..9b8c2b8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -51,7 +51,7 @@ document.addEventListener("DOMContentLoaded", () => { "An unknown error has occurred (Errors should be known, please report this)"; } else { error.innerText = - "Found errors not supposed to appear (please report this)"; + "Uncovered covered errors (please report this)"; throw translations.errors[0]; } } else if (errors.length === 1) { From 085d896dbfca3fc159e132cee2882e31c16ff09c Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:52:09 +0800 Subject: [PATCH 028/738] update error messages --- src/main.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 9b8c2b8..25e57a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,8 +50,7 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = "An unknown error has occurred (Errors should be known, please report this)"; } else { - error.innerText = - "Uncovered covered errors (please report this)"; + error.innerText = "Uncovered covered errors (please report this)"; throw translations.errors[0]; } } else if (errors.length === 1) { @@ -78,7 +77,7 @@ document.addEventListener("DOMContentLoaded", () => { } else { error.innerText = unreachableError.toString(); } - error.innerText += " (This is bad, please report this)"; + error.innerText += " (please report this)"; throw unreachableError; } }; From a7e73a798e9464c3f5551600ecfc54d268e16329 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:55:21 +0800 Subject: [PATCH 029/738] update error messages --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 25e57a5..791bcf0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,7 +50,7 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = "An unknown error has occurred (Errors should be known, please report this)"; } else { - error.innerText = "Uncovered covered errors (please report this)"; + error.innerText = "Found errors expected to be covered by another error (please report this)"; throw translations.errors[0]; } } else if (errors.length === 1) { From c0cf3628839de0bddfcedd11a147f2bda9f085ab Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:57:51 +0800 Subject: [PATCH 030/738] upgrade error to unreachable error --- src/output.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index e337bd7..255ecda 100644 --- a/src/output.ts +++ b/src/output.ts @@ -50,7 +50,7 @@ export class Output { if (mapper(value)) { return value; } else { - throw new OutputError("no error provided"); + throw new Error("no error provided"); } }); } From aa05f23229a627e31654a2a78e3911202bcbcc66 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 11:59:34 +0800 Subject: [PATCH 031/738] update errors --- src/filter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/filter.ts b/src/filter.ts index 856b678..0568e0b 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -11,6 +11,7 @@ import { someObjectInMultiplePredicate, WordUnit, } from "./ast.ts"; +import { UnreachableError } from "./error.ts"; import { UnrecognizedError } from "./error.ts"; /** Array of filter rules for a word unit. */ @@ -122,7 +123,7 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ } else if (modifier.type === "pi") { return true; } else { - throw new Error("unreachable error"); + throw new UnreachableError(); } }; if (modifier.type === "pi") { From 1b1e23b68f90d3d103ab8e250ac000be7cdccda7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:01:20 +0800 Subject: [PATCH 032/738] use more CoveredError --- src/filter.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 0568e0b..e0315ba 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -11,8 +11,7 @@ import { someObjectInMultiplePredicate, WordUnit, } from "./ast.ts"; -import { UnreachableError } from "./error.ts"; -import { UnrecognizedError } from "./error.ts"; +import { CoveredError, UnreachableError, UnrecognizedError } from "./error.ts"; /** Array of filter rules for a word unit. */ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ @@ -117,7 +116,7 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ modifier.type === "default" || modifier.type === "proper words" || modifier.type === "quotation" ) { - return false; + throw new CoveredError(); } else if (modifier.type === "nanpa") { return someModifierInPhrase(modifier.phrase, false, checker); } else if (modifier.type === "pi") { @@ -287,12 +286,12 @@ function modifierIsNumeric(modifier: Modifier): boolean { (word.type === "default" && (word.word === "wan" || word.word === "tu")); } - return false; + throw new CoveredError(); } /** Helper function for checking if the modifiers is exactly just _ala_ or nothing. */ function modifiersIsAlaOrNone(modifiers: Array): boolean { if (modifiers.length > 1) { - return false; + throw new CoveredError(); } else if (modifiers.length === 1) { const [modifier] = modifiers; return modifier.type === "default" && modifier.word.type === "default" && @@ -302,13 +301,13 @@ function modifiersIsAlaOrNone(modifiers: Array): boolean { } function hasPrepositionInPhrase(phrase: Phrase): boolean { if (phrase.type === "default") { - return false; + throw new CoveredError(); } else if (phrase.type === "preposition") { return true; } else if (phrase.type === "preverb") { return hasPrepositionInPhrase(phrase.phrase); } else if (phrase.type === "quotation") { - return false; + throw new CoveredError(); } else { throw new Error("unreachable"); } From 7d431c4816690f476a0c999aa5b87028fa3ee20c Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:02:40 +0800 Subject: [PATCH 033/738] update document comments --- src/output.ts | 4 ++++ src/parser.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/output.ts b/src/output.ts index 255ecda..aa03149 100644 --- a/src/output.ts +++ b/src/output.ts @@ -45,6 +45,10 @@ export class Output { isError(): boolean { return this.output.length === 0; } + /** + * Filters outputs. Instead of returning false, OutputError must be thrown + * instead. + */ filter(mapper: (value: T) => boolean): Output { return this.map((value) => { if (mapper(value)) { diff --git a/src/parser.ts b/src/parser.ts index a493ce2..357a2cc 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -52,8 +52,8 @@ class Parser { ); } /** - * Filters outputs. The mapper may throw OutputError as well in place of - * returning false. + * Filters outputs. Instead of returning false, OutputError must be thrown + * instead. */ filter(mapper: (value: T) => boolean): Parser { return new Parser((src) => From ec9502b2e5ceb001fef06fd44c150b44d8a13023 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:04:28 +0800 Subject: [PATCH 034/738] revert autoreplace --- src/filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index e0315ba..240c966 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -301,13 +301,13 @@ function modifiersIsAlaOrNone(modifiers: Array): boolean { } function hasPrepositionInPhrase(phrase: Phrase): boolean { if (phrase.type === "default") { - throw new CoveredError(); + return false; } else if (phrase.type === "preposition") { return true; } else if (phrase.type === "preverb") { return hasPrepositionInPhrase(phrase.phrase); } else if (phrase.type === "quotation") { - throw new CoveredError(); + return false; } else { throw new Error("unreachable"); } From 77a6228e5338a628f7639da5d7579103055af707 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:04:59 +0800 Subject: [PATCH 035/738] revert more autoreplace --- src/filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 240c966..491352c 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -286,12 +286,12 @@ function modifierIsNumeric(modifier: Modifier): boolean { (word.type === "default" && (word.word === "wan" || word.word === "tu")); } - throw new CoveredError(); + return false; } /** Helper function for checking if the modifiers is exactly just _ala_ or nothing. */ function modifiersIsAlaOrNone(modifiers: Array): boolean { if (modifiers.length > 1) { - throw new CoveredError(); + return false; } else if (modifiers.length === 1) { const [modifier] = modifiers; return modifier.type === "default" && modifier.word.type === "default" && From cf0ce4f74ac47339528048a145aa4ececca4c969 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:06:13 +0800 Subject: [PATCH 036/738] revert more autoreplace --- src/filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter.ts b/src/filter.ts index 491352c..c70165c 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -116,7 +116,7 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ modifier.type === "default" || modifier.type === "proper words" || modifier.type === "quotation" ) { - throw new CoveredError(); + return false; } else if (modifier.type === "nanpa") { return someModifierInPhrase(modifier.phrase, false, checker); } else if (modifier.type === "pi") { From 87d3b52660d50f6b658fb6a72711f6affb541872 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:07:40 +0800 Subject: [PATCH 037/738] format --- src/filter.ts | 2 +- src/main.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index c70165c..9686a3a 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -11,7 +11,7 @@ import { someObjectInMultiplePredicate, WordUnit, } from "./ast.ts"; -import { CoveredError, UnreachableError, UnrecognizedError } from "./error.ts"; +import { UnreachableError, UnrecognizedError } from "./error.ts"; /** Array of filter rules for a word unit. */ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ diff --git a/src/main.ts b/src/main.ts index 791bcf0..6e4871f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,7 +50,8 @@ document.addEventListener("DOMContentLoaded", () => { error.innerText = "An unknown error has occurred (Errors should be known, please report this)"; } else { - error.innerText = "Found errors expected to be covered by another error (please report this)"; + error.innerText = + "Found errors expected to be covered by another error (please report this)"; throw translations.errors[0]; } } else if (errors.length === 1) { From 74e5179f0034d4fe2049e4711151a96b2ce2f6dd Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:22:02 +0800 Subject: [PATCH 038/738] small update --- src/output.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index aa03149..98188a6 100644 --- a/src/output.ts +++ b/src/output.ts @@ -32,7 +32,9 @@ export class Output { this.errors.length = 0; } private append({ output, errors }: Output): void { - this.output = [...this.output, ...output]; + for (const item of output) { + this.push(item); + } if (this.output.length > 0) { this.errors.length = 0; } else { From 2aaf92f8da6176ff3884a3e80a5695b68f2638c0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 12:22:30 +0800 Subject: [PATCH 039/738] small update --- src/output.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/output.ts b/src/output.ts index 98188a6..0bad661 100644 --- a/src/output.ts +++ b/src/output.ts @@ -35,9 +35,7 @@ export class Output { for (const item of output) { this.push(item); } - if (this.output.length > 0) { - this.errors.length = 0; - } else { + if (this.isError()) { for (const item of errors) { this.errors.push(item); } From 0ed1d7f8d7281cdbd91ef7c89d91b03dd5f91c46 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 14:11:27 +0800 Subject: [PATCH 040/738] handle error when bundling --- bundle.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bundle.ts b/bundle.ts index 350f5cf..1ac6baf 100644 --- a/bundle.ts +++ b/bundle.ts @@ -13,8 +13,12 @@ if (Deno.args[0] === "build") { } else if (Deno.args[0] === "watch") { const builder = debounce.debounce(async () => { console.log("Starting to build..."); - await build({ compilerOptions: { inlineSourceMap: true } }); - console.log("Building done!"); + try { + await build({ compilerOptions: { inlineSourceMap: true } }); + console.log("Building done!"); + } catch (error) { + console.error(error); + } }, 500); const watcher = Deno.watchFs("./src/"); builder(); From cfd0df453dea007ca9a12849a347665f8090eede Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 15:12:13 +0800 Subject: [PATCH 041/738] small change --- src/parser.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 357a2cc..8c00c6a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -560,13 +560,12 @@ function clause(): Parser { subjects, predicates, } as Clause)), - sequence( - specificWord("o").with(multiplePredicates(["o", "anu"])), - ).map(([predicates]) => ({ - type: "o clause", - subjects: null, - predicates, - } as Clause)), + specificWord("o").with(multiplePredicates(["o", "anu"])) + .map((predicates) => ({ + type: "o clause", + subjects: null, + predicates, + } as Clause)), sequence( subjectPhrases(), optionalComma().with(specificWord("o")).with( From a6d46db054ca16b7a0050cdc7c11a8a241294a01 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 15:39:04 +0800 Subject: [PATCH 042/738] small change --- src/output.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index 0bad661..343af63 100644 --- a/src/output.ts +++ b/src/output.ts @@ -37,7 +37,7 @@ export class Output { } if (this.isError()) { for (const item of errors) { - this.errors.push(item); + this.pushError(item); } } } From 84ee542b96ea1ce1d25405a91f4fe44705803ee2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:10:54 +0800 Subject: [PATCH 043/738] fix error message for match combinator --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 8c00c6a..fe254d0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -91,7 +91,7 @@ function match(regex: RegExp): Parser { } else if (src === "") { return new Output(new UnrecognizedError("Unexpected end of sentence")); } else { - const token = src.match(/(.*)(?:\s|$)/)?.[1]; + const token = src.match(/([^\s]*)/)?.[1]; if (token) return new Output(new UnrecognizedError(`"${token}"`)); else { throw new Error("unreachable"); From 4dd44228d9cf59d70bdfaa870ea1a26f6febac92 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:14:20 +0800 Subject: [PATCH 044/738] small update --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index fe254d0..d803262 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -91,7 +91,7 @@ function match(regex: RegExp): Parser { } else if (src === "") { return new Output(new UnrecognizedError("Unexpected end of sentence")); } else { - const token = src.match(/([^\s]*)/)?.[1]; + const token = src.match(/[^\s]*/)?.[0]; if (token) return new Output(new UnrecognizedError(`"${token}"`)); else { throw new Error("unreachable"); From 02d59cb240aacf5f9e66d276ea0771f194deb41e Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:26:19 +0800 Subject: [PATCH 045/738] implemented unexpected error --- src/error.ts | 6 ++++++ src/parser.ts | 39 +++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/error.ts b/src/error.ts index 211caea..b7d86c6 100644 --- a/src/error.ts +++ b/src/error.ts @@ -8,6 +8,12 @@ export class UnreachableError extends Error { } /** Represents errors expected to be covered by other errors. */ export class CoveredError extends OutputError {} +/** Represents Error with unexpected and expected elements. */ +export class UnexpectedError extends OutputError { + constructor(unexpected: string, expected: string) { + super(`Unexpected ${unexpected}. ${expected} were expected instead.`); + } +} /** Represents Error due to things not implemented yet. */ export class TodoError extends OutputError { constructor(token: string) { diff --git a/src/parser.ts b/src/parser.ts index d803262..0044554 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -10,7 +10,12 @@ import { Sentence, WordUnit, } from "./ast.ts"; -import { UnreachableError, UnrecognizedError } from "./error.ts"; +import { + OutputError, + UnexpectedError, + UnreachableError, + UnrecognizedError, +} from "./error.ts"; import { Output } from "./output.ts"; import { CONTENT_WORD, @@ -82,18 +87,19 @@ class Parser { * Uses Regular Expression to create parser. The parser outputs * RegExpMatchArray, which is what `string.match( ... )` returns. */ -function match(regex: RegExp): Parser { +function match(regex: RegExp, description: string): Parser { const newRegex = new RegExp("^" + regex.source, regex.flags); return new Parser((src) => { const match = src.match(newRegex); if (match) { return new Output([{ value: match, rest: src.slice(match[0].length) }]); } else if (src === "") { - return new Output(new UnrecognizedError("Unexpected end of sentence")); + return new Output(new UnexpectedError("end of sentence", description)); } else { const token = src.match(/[^\s]*/)?.[0]; - if (token) return new Output(new UnrecognizedError(`"${token}"`)); - else { + if (token) { + return new Output(new UnexpectedError(`"${token}"`, description)); + } else { throw new Error("unreachable"); } } @@ -224,7 +230,7 @@ function allAtLeastOnce(parser: Parser): Parser> { } /** Parses comma. */ function comma(): Parser { - return match(/,\s*/).map(() => ","); + return match(/,\s*/, "comma").map(() => ","); } /** Parses an optional comma. */ function optionalComma(): Parser { @@ -232,14 +238,16 @@ function optionalComma(): Parser { } /** Parses lowercase word. */ function word(): Parser { - return match(/([a-z]+)\s*/).map(([_, word]) => word); + return match(/([a-z]+)\s*/, "word").map(([_, word]) => word); } /** * Parses all at least one uppercase words and combines them all into single * string. This function is exhaustive like `all`. */ function properWords(): Parser { - return allAtLeastOnce(match(/([A-Z][a-z]*)\s*/).map(([_, word]) => word)).map( + return allAtLeastOnce( + match(/([A-Z][a-z]*)\s*/, "proper word").map(([_, word]) => word), + ).map( (array) => array.join(" "), ); } @@ -614,7 +622,9 @@ function sentence(): Parser { choice( eol().map(() => ""), lookAhead(closeQuotationMark()).map(() => ""), - match(/([.,:;?!])\s*/).map(([_, punctuation]) => punctuation), + match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => + punctuation + ), ), ).map(([clause, moreClauses, punctuation]) => ({ laClauses: [clause, ...moreClauses], @@ -623,11 +633,11 @@ function sentence(): Parser { } /** Parses opening quotation mark */ function openQuotationMark(): Parser { - return match(/(["“«「])\s*/).map(([_, mark]) => mark); + return match(/(["“«「])\s*/, "open quotation mark").map(([_, mark]) => mark); } /** Parses closing quotation mark */ function closeQuotationMark(): Parser { - return match(/(["”»」])\s*/).map(([_, mark]) => mark); + return match(/(["”»」])\s*/, "close quotation mark").map(([_, mark]) => mark); } /** Parses multiple sentences inside quotation mark */ function quotation(): Parser { @@ -654,8 +664,9 @@ function quotation(): Parser { } /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { - return match(/\s*/).with(allAtLeastOnce(sentence())).skip(eol()).filter( - filter(SENTENCES_RULE), - ).parser(src) + return match(/\s*/, "space").with(allAtLeastOnce(sentence())).skip(eol()) + .filter( + filter(SENTENCES_RULE), + ).parser(src) .map(({ value }) => value); } From 5544dd80e8545c081ec2c690c8254c14ca4f5824 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:34:34 +0800 Subject: [PATCH 046/738] format --- src/parser.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 0044554..886a23f 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -11,7 +11,6 @@ import { WordUnit, } from "./ast.ts"; import { - OutputError, UnexpectedError, UnreachableError, UnrecognizedError, From db6e0ac34058c745f636e50daf44f13774cd2aff Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:34:57 +0800 Subject: [PATCH 047/738] small update --- src/output.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/output.ts b/src/output.ts index 343af63..1af6153 100644 --- a/src/output.ts +++ b/src/output.ts @@ -31,12 +31,12 @@ export class Output { this.output.push(value); this.errors.length = 0; } - private append({ output, errors }: Output): void { - for (const item of output) { + private append(output: Output): void { + for (const item of output.output) { this.push(item); } - if (this.isError()) { - for (const item of errors) { + if (this.isError() && output.isError()) { + for (const item of output.errors) { this.pushError(item); } } From 6fd17c3300c13e402c9bd4da2b30257c2d561ce0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:44:42 +0800 Subject: [PATCH 048/738] implement unit testing --- deno.json | 4 ++++ src/output.ts | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/deno.json b/deno.json index 3d20008..e0e5011 100644 --- a/deno.json +++ b/deno.json @@ -13,5 +13,9 @@ }, "lint": { "include": ["./src/**/*.ts", "./bundle.ts", "./test-parser.ts"] + }, + "test": { + "include": ["./src/**/*.ts"], + "exclude": ["./src/main.ts"] } } diff --git a/src/output.ts b/src/output.ts index 1af6153..8dfec64 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,3 +1,4 @@ +import { assert } from "https://deno.land/std@0.140.0/_util/assert.ts"; import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { @@ -100,3 +101,11 @@ export class Output { return wholeOutput; } } +if (Deno) { + Deno.test("use all error", () => { + let errors = new Output(["1", "2", "3"]).flatMap((number) => + new Output(new OutputError(number)) + ).errors.map((error) => error.message); + assert(errors.length === 3); + }); +} From 260c367df3cb256326a03880838f32bcec208ee1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:45:02 +0800 Subject: [PATCH 049/738] small update --- src/output.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index 8dfec64..70d09e4 100644 --- a/src/output.ts +++ b/src/output.ts @@ -103,7 +103,7 @@ export class Output { } if (Deno) { Deno.test("use all error", () => { - let errors = new Output(["1", "2", "3"]).flatMap((number) => + const errors = new Output(["1", "2", "3"]).flatMap((number) => new Output(new OutputError(number)) ).errors.map((error) => error.message); assert(errors.length === 3); From b601176c29f839bf0bc9e13c74dfc99c0a954205 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 16:56:26 +0800 Subject: [PATCH 050/738] update feature detection --- src/output.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index 70d09e4..d117307 100644 --- a/src/output.ts +++ b/src/output.ts @@ -101,7 +101,7 @@ export class Output { return wholeOutput; } } -if (Deno) { +if (typeof Deno !== "undefined") { Deno.test("use all error", () => { const errors = new Output(["1", "2", "3"]).flatMap((number) => new Output(new OutputError(number)) From ec23fe4ffe499fda5098ad5128c6af0db6d8eaa0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 17:05:35 +0800 Subject: [PATCH 051/738] update unit tests --- dev-deps.ts | 1 + src/output.ts | 4 ++-- src/parser.ts | 12 ++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dev-deps.ts b/dev-deps.ts index e1295b9..68d84ac 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,2 +1,3 @@ export * as emit from "https://deno.land/x/emit@0.38.2/mod.ts"; export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; +export * as assert from "https://deno.land/std@0.220.0/assert/mod.ts"; \ No newline at end of file diff --git a/src/output.ts b/src/output.ts index d117307..f83e07f 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,4 +1,4 @@ -import { assert } from "https://deno.land/std@0.140.0/_util/assert.ts"; +import { assert } from "../dev-deps.ts"; import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { @@ -106,6 +106,6 @@ if (typeof Deno !== "undefined") { const errors = new Output(["1", "2", "3"]).flatMap((number) => new Output(new OutputError(number)) ).errors.map((error) => error.message); - assert(errors.length === 3); + assert.assertEquals (errors.length, 3); }); } diff --git a/src/parser.ts b/src/parser.ts index 886a23f..6ec74a7 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -34,6 +34,7 @@ import { WORD_UNIT_RULES, } from "./filter.ts"; import { CoveredError } from "./error.ts"; +import { assert } from "../dev-deps.ts"; /** A single parsing result. */ type ValueRest = { value: T; rest: string }; @@ -669,3 +670,14 @@ export function parser(src: string): Output> { ).parser(src) .map(({ value }) => value); } +if (typeof Deno !== "undefined") { + Deno.test("choice considers all error", () => { + const parser = choice( + match(/a/, "a").map(() => "a"), + match(/a/, "a").map(() => "a"), + match(/a/, "a").map(() => "a"), + ); + const errors = parser.parser("").errors.map((error) => error.message); + assert.assertEquals(errors.length, 3); + }); +} From c62391905026fc8ec4cd964984e5cf96963ba6e7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 24 Mar 2024 18:47:19 +0800 Subject: [PATCH 052/738] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0659b5..065906e 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ To stop this command, simply press Ctrl + C. ## About the source codes -With exception to `./src/main.ts`, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run`. +With exception to `./src/main.ts`, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. From 4bade5aad834f41d2c3c82137815d54ea17d3b54 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 07:40:11 +0800 Subject: [PATCH 053/738] use more unexpected errors --- src/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 6ec74a7..b45c638 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -113,7 +113,7 @@ function nothing(): Parser { function eol(): Parser { return new Parser((src) => { if (src === "") return new Output([{ value: null, rest: "" }]); - else return new Output(new UnrecognizedError(`"${src}"`)); + else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); }); } /** Parses without consuming the source string */ @@ -262,7 +262,7 @@ function wordFrom(set: Set, description: string): Parser { function specificWord(thatWord: string): Parser { return word().filter((thisWord) => { if (thatWord === thisWord) return true; - else throw new UnrecognizedError(`"${thisWord}" instead of "${thatWord}"`); + else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); } /** Parses word unit without numbers. */ From 51d44b0cea0dd5fe211d80bb60b6f0eed619c35b Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 07:49:11 +0800 Subject: [PATCH 054/738] use more unreachable error --- src/ast.ts | 10 ++++++---- src/filter.ts | 2 +- src/parser.ts | 2 +- src/translator.ts | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index 3c411f6..c7bec5e 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,3 +1,5 @@ +import { UnreachableError } from "./error.ts"; + /** Represents a word unit. */ export type WordUnit = | { type: "default"; word: string } @@ -121,7 +123,7 @@ export function someModifierInPhrase( } else if (phrase.type === "quotation") { return whenQuotation; } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } export function someModifierInMultiplePhrases( @@ -136,7 +138,7 @@ export function someModifierInMultiplePhrases( someModifierInMultiplePhrases(phrases, whenQuotation, checker) ); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } export function somePhraseInMultiplePhrases( @@ -150,7 +152,7 @@ export function somePhraseInMultiplePhrases( somePhraseInMultiplePhrases(phrases, checker) ); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } export function someObjectInMultiplePredicate( @@ -170,6 +172,6 @@ export function someObjectInMultiplePredicate( someObjectInMultiplePredicate(predicates, checker) ); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } diff --git a/src/filter.ts b/src/filter.ts index 9686a3a..b278e99 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -309,6 +309,6 @@ function hasPrepositionInPhrase(phrase: Phrase): boolean { } else if (phrase.type === "quotation") { return false; } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } diff --git a/src/parser.ts b/src/parser.ts index b45c638..9029489 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -100,7 +100,7 @@ function match(regex: RegExp, description: string): Parser { if (token) { return new Output(new UnexpectedError(`"${token}"`, description)); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } }); diff --git a/src/translator.ts b/src/translator.ts index 477e72b..b4f5304 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -11,7 +11,7 @@ import { Output } from "./output.ts"; import { parser } from "./parser.ts"; import { TodoError } from "./error.ts"; import { DEFINITION } from "./definition.ts"; -import { CoveredError, OutputError } from "./error.ts"; +import { CoveredError, OutputError, UnreachableError } from "./error.ts"; /** A special kind of Output that translators returns. */ export type TranslationOutput = Output; @@ -251,10 +251,10 @@ function translateMultiplePhrases( phrases.join([" ", conjunction, " "].join("")) ); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } else { - throw new Error("unreachable"); + throw new UnreachableError(); } } /** Translates a clause. */ @@ -268,7 +268,7 @@ function translateClause(clause: Clause): TranslationOutput { } else if (phrases.type === "anu") { return phrases.phrases.some(hasEn); } else { - throw new Error("unreachable"); + throw new UnreachableError(); } }; const phrases = clause.phrases; From 4fb916c503cc63755952b4f2d8ca1c6c23d0cefc Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 07:54:30 +0800 Subject: [PATCH 055/738] update test codes --- deno.json | 3 +-- src/output.ts | 5 ++++- src/parser.ts | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/deno.json b/deno.json index e0e5011..765be21 100644 --- a/deno.json +++ b/deno.json @@ -15,7 +15,6 @@ "include": ["./src/**/*.ts", "./bundle.ts", "./test-parser.ts"] }, "test": { - "include": ["./src/**/*.ts"], - "exclude": ["./src/main.ts"] + "include": ["./src/translator.ts"] } } diff --git a/src/output.ts b/src/output.ts index f83e07f..9e1cd5f 100644 --- a/src/output.ts +++ b/src/output.ts @@ -106,6 +106,9 @@ if (typeof Deno !== "undefined") { const errors = new Output(["1", "2", "3"]).flatMap((number) => new Output(new OutputError(number)) ).errors.map((error) => error.message); - assert.assertEquals (errors.length, 3); + assert.assertEquals(errors.length, 3); + assert.assertEquals(errors[0], "1"); + assert.assertEquals(errors[1], "2"); + assert.assertEquals(errors[2], "3"); }); } diff --git a/src/parser.ts b/src/parser.ts index 9029489..f92b03c 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -674,10 +674,13 @@ if (typeof Deno !== "undefined") { Deno.test("choice considers all error", () => { const parser = choice( match(/a/, "a").map(() => "a"), - match(/a/, "a").map(() => "a"), - match(/a/, "a").map(() => "a"), + match(/b/, "b").map(() => "b"), + match(/c/, "c").map(() => "c"), ); const errors = parser.parser("").errors.map((error) => error.message); assert.assertEquals(errors.length, 3); + assert.assertEquals(errors[0], new UnexpectedError("end of sentence", "a").message); + assert.assertEquals(errors[1], new UnexpectedError("end of sentence", "b").message); + assert.assertEquals(errors[2], new UnexpectedError("end of sentence", "c").message); }); } From 967e4a0298e2e677bba7f3273f9beafd400065ba Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 09:09:59 +0800 Subject: [PATCH 056/738] fix --- src/output.ts | 2 +- src/parser.ts | 26 +++++++++++++++++++------- src/translator.ts | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/output.ts b/src/output.ts index 9e1cd5f..1fdf11b 100644 --- a/src/output.ts +++ b/src/output.ts @@ -73,7 +73,7 @@ export class Output { wholeOutput.push(mapper(value)); } catch (error) { if (error instanceof OutputError) { - this.pushError(error); + wholeOutput.pushError(error); } else { throw error; } diff --git a/src/parser.ts b/src/parser.ts index f92b03c..edd197e 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -91,13 +91,13 @@ function match(regex: RegExp, description: string): Parser { const newRegex = new RegExp("^" + regex.source, regex.flags); return new Parser((src) => { const match = src.match(newRegex); - if (match) { + if (match !== null) { return new Output([{ value: match, rest: src.slice(match[0].length) }]); } else if (src === "") { return new Output(new UnexpectedError("end of sentence", description)); } else { const token = src.match(/[^\s]*/)?.[0]; - if (token) { + if (token !== undefined) { return new Output(new UnexpectedError(`"${token}"`, description)); } else { throw new UnreachableError(); @@ -254,8 +254,11 @@ function properWords(): Parser { /** Parses word only from `set`. */ function wordFrom(set: Set, description: string): Parser { return word().filter((word) => { - if (set.has(word)) return true; - else throw new UnrecognizedError(`"${word}" as ${description}`); + if (set.has(word)) { + return true; + } else { + throw new UnrecognizedError(`"${word}" as ${description}`); + } }); } /** Parses a specific word. */ @@ -679,8 +682,17 @@ if (typeof Deno !== "undefined") { ); const errors = parser.parser("").errors.map((error) => error.message); assert.assertEquals(errors.length, 3); - assert.assertEquals(errors[0], new UnexpectedError("end of sentence", "a").message); - assert.assertEquals(errors[1], new UnexpectedError("end of sentence", "b").message); - assert.assertEquals(errors[2], new UnexpectedError("end of sentence", "c").message); + assert.assertEquals( + errors[0], + new UnexpectedError("end of sentence", "a").message, + ); + assert.assertEquals( + errors[1], + new UnexpectedError("end of sentence", "b").message, + ); + assert.assertEquals( + errors[2], + new UnexpectedError("end of sentence", "c").message, + ); }); } diff --git a/src/translator.ts b/src/translator.ts index b4f5304..a7211fa 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -164,7 +164,7 @@ function defaultPhraseAs( [...modifiers.slice().reverse(), headWord].join(" "), ).map( (translation) => { - if (name) { + if (name !== undefined) { return `${translation} named ${name}`; } else { return translation; @@ -186,7 +186,7 @@ function defaultPhraseAs( [...modifiers.slice().reverse(), headWord].join(" "), ).map( (translation) => { - if (name) { + if (name !== undefined) { return `${translation} named ${name}`; } else { return translation; From b32dc7e7c749cfcf5d486fe1ce88260ef4f29e99 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 09:52:01 +0800 Subject: [PATCH 057/738] implement settings --- index.html | 33 +++++++++++++++++++++++++++++++++ src/main.ts | 15 +++++++++++++++ style.css | 12 +++++++----- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 8e4cc76..fcc3399 100644 --- a/index.html +++ b/index.html @@ -75,6 +75,39 @@

          ilo Token

          >

          + +

          + +

          +

          + +

          +

          + +

          +

          + +

          +

          + +

          +
          diff --git a/src/main.ts b/src/main.ts index 6e4871f..10e367a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,6 +18,15 @@ document.addEventListener("DOMContentLoaded", () => { const button = document.getElementById( "translate-button", ) as HTMLButtonElement; + const settingsButton = document.getElementById( + "settings-button", + ) as HTMLButtonElement; + const dialogBox = document.getElementById( + "dialog-box", + ) as HTMLDialogElement; + const confirmButton = document.getElementById( + "confirm-button", + ) as HTMLButtonElement; const version = document.getElementById("version") as HTMLAnchorElement; if (DEVELOPMENT) { version.innerText = `${VERSION} (On development)`; @@ -27,6 +36,12 @@ document.addEventListener("DOMContentLoaded", () => { }); version.innerText = `${VERSION} - Released ${date}`; } + settingsButton.addEventListener("click", () => { + dialogBox.showModal(); + }); + confirmButton.addEventListener("click", () => { + dialogBox.close(); + }); const listener = () => { while (output.children.length > 0) { output.removeChild(output.children[0]); diff --git a/style.css b/style.css index a4af00c..55e011d 100644 --- a/style.css +++ b/style.css @@ -19,9 +19,13 @@ a:visited { resize: vertical; width: 100%; } -#error, #error-list { +#error, +#error-list { color: #b60000; } +summary { + cursor: pointer; +} @media (min-width: 800px) { body { margin: 50px; @@ -38,10 +42,8 @@ a:visited { a:visited { color: #b47de7; } - #error, #error-list { + #error, + #error-list { color: #ff5e5e; } } -summary { - cursor: pointer; -} From 1cf6b7328ed37820fe027677dc8939594d8d55bc Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 09:53:40 +0800 Subject: [PATCH 058/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a99a5e..e8b70bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ You may need to force restart the page in order to use the latest version: shift - Add icons. - All possible errors will now be listed. +- Implement settings dialog, although it's still not functional. ## 0.2.2 From c5f32be81434c8c5dd7c92cb7fd9f37166cf5ec1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 09:58:52 +0800 Subject: [PATCH 059/738] update settings dialog --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index fcc3399..caf2677 100644 --- a/index.html +++ b/index.html @@ -82,7 +82,7 @@

          ilo Token

          >

          - +

      • >Verb tenses From ef4e92dfb3522c237ba099397f7df49da922eac7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 11:22:36 +0800 Subject: [PATCH 060/738] remove test codes --- dev-deps.ts | 3 +-- src/output.ts | 12 ------------ src/parser.ts | 24 ------------------------ 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/dev-deps.ts b/dev-deps.ts index 68d84ac..507a252 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,3 +1,2 @@ export * as emit from "https://deno.land/x/emit@0.38.2/mod.ts"; -export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; -export * as assert from "https://deno.land/std@0.220.0/assert/mod.ts"; \ No newline at end of file +export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; \ No newline at end of file diff --git a/src/output.ts b/src/output.ts index 1fdf11b..dfe228e 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,4 +1,3 @@ -import { assert } from "../dev-deps.ts"; import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { @@ -101,14 +100,3 @@ export class Output { return wholeOutput; } } -if (typeof Deno !== "undefined") { - Deno.test("use all error", () => { - const errors = new Output(["1", "2", "3"]).flatMap((number) => - new Output(new OutputError(number)) - ).errors.map((error) => error.message); - assert.assertEquals(errors.length, 3); - assert.assertEquals(errors[0], "1"); - assert.assertEquals(errors[1], "2"); - assert.assertEquals(errors[2], "3"); - }); -} diff --git a/src/parser.ts b/src/parser.ts index edd197e..73a6d13 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -34,7 +34,6 @@ import { WORD_UNIT_RULES, } from "./filter.ts"; import { CoveredError } from "./error.ts"; -import { assert } from "../dev-deps.ts"; /** A single parsing result. */ type ValueRest = { value: T; rest: string }; @@ -673,26 +672,3 @@ export function parser(src: string): Output> { ).parser(src) .map(({ value }) => value); } -if (typeof Deno !== "undefined") { - Deno.test("choice considers all error", () => { - const parser = choice( - match(/a/, "a").map(() => "a"), - match(/b/, "b").map(() => "b"), - match(/c/, "c").map(() => "c"), - ); - const errors = parser.parser("").errors.map((error) => error.message); - assert.assertEquals(errors.length, 3); - assert.assertEquals( - errors[0], - new UnexpectedError("end of sentence", "a").message, - ); - assert.assertEquals( - errors[1], - new UnexpectedError("end of sentence", "b").message, - ); - assert.assertEquals( - errors[2], - new UnexpectedError("end of sentence", "c").message, - ); - }); -} From bda7b386677bd9bbc233da43e73cf7d2a09482af Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 11:23:00 +0800 Subject: [PATCH 061/738] remove test codes --- deno.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/deno.json b/deno.json index 765be21..3d20008 100644 --- a/deno.json +++ b/deno.json @@ -13,8 +13,5 @@ }, "lint": { "include": ["./src/**/*.ts", "./bundle.ts", "./test-parser.ts"] - }, - "test": { - "include": ["./src/translator.ts"] } } From 8cb430f19902de9094cc8810d1c9a360298912df Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 12:33:32 +0800 Subject: [PATCH 062/738] implement silent combinator --- src/parser.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 73a6d13..4da5d08 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -81,6 +81,14 @@ class Parser { skip(parser: Parser): Parser { return sequence(this, parser).map(([output, _]) => output); } + /** Suppresses all error. */ + silent(): Parser { + return new Parser((src) => { + const output = this.parser(src); + output.errors.length = 0; + return output; + }); + } } /** * Uses Regular Expression to create parser. The parser outputs @@ -623,7 +631,7 @@ function sentence(): Parser { many(la().with(fullClause())), choice( eol().map(() => ""), - lookAhead(closeQuotationMark()).map(() => ""), + lookAhead(closeQuotationMark()).map(() => "").silent(), match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => punctuation ), From 29a434d006b18166b0effb3560d7204e184a5670 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 12:35:29 +0800 Subject: [PATCH 063/738] update silent combinator --- src/parser.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 4da5d08..0a668fd 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -83,11 +83,7 @@ class Parser { } /** Suppresses all error. */ silent(): Parser { - return new Parser((src) => { - const output = this.parser(src); - output.errors.length = 0; - return output; - }); + return new Parser((src) => new Output(this.parser(src).output)); } } /** From f7d68c19ee6d4dfa24485370efc74fb0e98d15bf Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 18:02:00 +0800 Subject: [PATCH 064/738] update settings --- index.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index caf2677..6848a0c 100644 --- a/index.html +++ b/index.html @@ -105,7 +105,14 @@

        ilo Token

        >

        - + +

        +

        + +

        From 31e7c40ac7d4b26fe9af7f110d309558aca65d7e Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 18:09:25 +0800 Subject: [PATCH 065/738] update settings --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 6848a0c..7ba36ec 100644 --- a/index.html +++ b/index.html @@ -89,7 +89,7 @@

        ilo Token

        >Singular and plural forms @@ -99,7 +99,7 @@

        ilo Token

        >Verb tenses From 71f1c3f49314dce15cc159cda19ab2f6359e66cb Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 18:14:34 +0800 Subject: [PATCH 066/738] add tooltip to X ala X partial parsing settings --- index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 7ba36ec..c3aa25f 100644 --- a/index.html +++ b/index.html @@ -104,10 +104,12 @@

        ilo Token

        -

        +

        From 431529032f13e585e33b18bc5799da646ef599d6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 25 Mar 2024 18:28:49 +0800 Subject: [PATCH 067/738] update settings --- index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index c3aa25f..c23aa16 100644 --- a/index.html +++ b/index.html @@ -76,6 +76,12 @@

        ilo Token

        +

        + Settings + (Help) +

        ilo Token >

        Use telo misikeke + error messages

        - +

        @@ -114,8 +118,8 @@

        title="This allows X ala X constructions to be parsed as series of modifiers along with the usual translation" > Allow partial parsing + of X ala X constructions (?)

        diff --git a/src/main.ts b/src/main.ts index 10e367a..8366894 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { CoveredError } from "./error.ts"; import { translate } from "./translator.ts"; +import { defaultSettings, RedundancySettings, settings } from "./settings.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; @@ -7,100 +8,220 @@ const DEVELOPMENT = true; const DATE_RELEASED = new Date("2024-2-1"); const VERSION = "v0.2.3"; -// TODO: maybe use worker -document.addEventListener("DOMContentLoaded", () => { - const input = document.getElementById("input") as HTMLTextAreaElement; - const output = document.getElementById("output") as HTMLUListElement; - const error = document.getElementById("error") as HTMLParagraphElement; - const errorList = document.getElementById( - "error-list", - ) as HTMLParagraphElement; - const button = document.getElementById( - "translate-button", - ) as HTMLButtonElement; - const settingsButton = document.getElementById( - "settings-button", - ) as HTMLButtonElement; - const dialogBox = document.getElementById( - "dialog-box", - ) as HTMLDialogElement; - const confirmButton = document.getElementById( - "confirm-button", - ) as HTMLButtonElement; - const version = document.getElementById("version") as HTMLAnchorElement; +type Elements = { + input: HTMLTextAreaElement; + output: HTMLUListElement; + error: HTMLParagraphElement; + errorList: HTMLParagraphElement; + translateButton: HTMLButtonElement; + settingsButton: HTMLButtonElement; + dialogBox: HTMLDialogElement; + confirmButton: HTMLButtonElement; + resetButton: HTMLButtonElement; + version: HTMLAnchorElement; + useTeloMisikeke: HTMLInputElement; + randomize: HTMLInputElement; + number: HTMLSelectElement; + tense: HTMLSelectElement; + xAlaXPartialParsing: HTMLInputElement; +}; +let elements: undefined | Elements; + +function loadElements(): void { + elements = { + input: document.getElementById("input") as HTMLTextAreaElement, + output: document.getElementById("output") as HTMLUListElement, + error: document.getElementById("error") as HTMLParagraphElement, + errorList: document.getElementById( + "error-list", + ) as HTMLParagraphElement, + translateButton: document.getElementById( + "translate-button", + ) as HTMLButtonElement, + settingsButton: document.getElementById( + "settings-button", + ) as HTMLButtonElement, + dialogBox: document.getElementById( + "dialog-box", + ) as HTMLDialogElement, + confirmButton: document.getElementById( + "confirm-button", + ) as HTMLButtonElement, + resetButton: document.getElementById("reset-button") as HTMLButtonElement, + version: document.getElementById("version") as HTMLAnchorElement, + useTeloMisikeke: document.getElementById( + "use-telo-misikeke", + ) as HTMLInputElement, + randomize: document.getElementById("randomize") as HTMLInputElement, + number: document.getElementById("number") as HTMLSelectElement, + tense: document.getElementById("tense") as HTMLSelectElement, + xAlaXPartialParsing: document.getElementById( + "x-ala-x-parsing", + ) as HTMLInputElement, + }; +} +function setVersion(): void { if (DEVELOPMENT) { - version.innerText = `${VERSION} (On development)`; + elements!.version.innerText = `${VERSION} (On development)`; } else { const date = DATE_RELEASED.toLocaleDateString(undefined, { dateStyle: "short", }); - version.innerText = `${VERSION} - Released ${date}`; + elements!.version.innerText = `${VERSION} - Released ${date}`; } - settingsButton.addEventListener("click", () => { - dialogBox.showModal(); - }); - confirmButton.addEventListener("click", () => { - dialogBox.close(); - }); - const listener = () => { - while (output.children.length > 0) { - output.removeChild(output.children[0]); - } - while (errorList.children.length > 0) { - errorList.removeChild(errorList.children[0]); +} +function clearOutput(): void { + while (elements!.output.children.length > 0) { + elements!.output.removeChild(elements!.output.children[0]); + } + while (elements!.errorList.children.length > 0) { + elements!.errorList.removeChild(elements!.errorList.children[0]); + } + elements!.error.innerText = ""; +} +function outputTranslations(output: Array): void { + for (const translation of output) { + const list = document.createElement("li"); + list.innerText = translation; + elements!.output.appendChild(list); + } +} +function outputErrors(errors: Array): void { + if (errors.length === 0) { + elements!.error.innerText = + "An unknown error has occurred (Errors should be known, please report this)"; + } else if (errors.length === 1) { + elements!.error.innerText = errors[0]; + } else { + elements!.error.innerText = + "Multiple errors has been found, but only at least one could be helpful:"; + for (const errorMessage of errors) { + const list = document.createElement("li"); + list.innerText = errorMessage; + elements!.errorList.appendChild(list); } - error.innerText = ""; - try { - const translations = translate(input.value); - if (translations.isError()) { - const errors = [ - ...new Set( - translations.errors.filter((x) => !(x instanceof CoveredError)).map( - (x) => x.message, - ), + } +} +function updateOutput(): void { + clearOutput(); + try { + const translations = translate(elements!.input.value); + if (translations.isError()) { + outputErrors([ + ...new Set( + translations.errors.filter((x) => !(x instanceof CoveredError)).map( + (x) => x.message, ), - ]; - if (errors.length === 0) { - if (translations.errors.length === 0) { - error.innerText = - "An unknown error has occurred (Errors should be known, please report this)"; - } else { - error.innerText = - "Found errors expected to be covered by another error (please report this)"; - throw translations.errors[0]; - } - } else if (errors.length === 1) { - error.innerText = errors[0]; - } else { - error.innerText = - "Multiple errors has been found, but only at least one could be helpful:"; - for (const errorMessage of errors) { - const list = document.createElement("li"); - list.innerText = errorMessage; - errorList.appendChild(list); - } - } - } else { - for (const translation of [...new Set(translations.output)]) { - const list = document.createElement("li"); - list.innerText = translation; - output.appendChild(list); - } - } - } catch (unreachableError) { - if (unreachableError instanceof Error) { - error.innerText = unreachableError.message; - } else { - error.innerText = unreachableError.toString(); + ), + ]); + } else { + const output = [...new Set(translations.output)]; + if (settings.randomize) { + output.sort(() => Math.random() - Math.random()); } - error.innerText += " (please report this)"; - throw unreachableError; + outputTranslations(output); } - }; - button.addEventListener("click", listener); - input.addEventListener("keydown", (event) => { + } catch (unreachableError) { + let error; + if (unreachableError instanceof Error) { + error = unreachableError.message; + } else { + error = unreachableError?.toString() + ""; + } + error += " (please report this)"; + outputErrors([error]); + throw unreachableError; + } +} +function loadSettings(): void { + function setBool< + T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + >(name: T, element: HTMLInputElement): void { + const x = localStorage.getItem(name); + let value: boolean; + if (x === null) { + value = defaultSettings[name]; + } else { + value = x === "true"; + } + settings[name] = value; + element.checked = value; + } + function setRedundancy( + name: T, + element: HTMLSelectElement, + ): void { + const x = localStorage.getItem(name) ?? defaultSettings[name]; + if (["both", "condensed", "default only"].includes(x)) { + settings[name] = x as RedundancySettings; + element.value = x; + } else { + settings[name] = defaultSettings[name]; + element.value = defaultSettings[name]; + } + } + setBool("useTeloMisikeke", elements!.useTeloMisikeke); + setBool("randomize", elements!.randomize); + setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setRedundancy("number", elements!.number); + setRedundancy("tense", elements!.tense); +} +function confirmSettings(): void { + function setBool< + T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + >(name: T, element: HTMLInputElement) { + const value = element.checked; + localStorage.setItem(name, value.toString()); + settings[name] = value; + } + function setRedundancy( + name: T, + element: HTMLSelectElement, + ): void { + const value = element.value as RedundancySettings; + localStorage.setItem(name, value.toString()); + settings[name] = value; + } + setBool("useTeloMisikeke", elements!.useTeloMisikeke); + setBool("randomize", elements!.randomize); + setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setRedundancy("number", elements!.number); + setRedundancy("tense", elements!.tense); +} +function resetSettings(): void { + function setBool< + T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + >(name: T, element: HTMLInputElement) { + element.checked = defaultSettings[name]; + } + function setRedundancy( + name: T, + element: HTMLSelectElement, + ): void { + element.value = defaultSettings[name]; + } + setBool("useTeloMisikeke", elements!.useTeloMisikeke); + setBool("randomize", elements!.randomize); + setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setRedundancy("number", elements!.number); + setRedundancy("tense", elements!.tense); +} +document.addEventListener("DOMContentLoaded", () => { + loadElements(); + setVersion(); + loadSettings(); + elements!.settingsButton.addEventListener("click", () => { + elements!.dialogBox.showModal(); + }); + elements!.confirmButton.addEventListener("click", () => { + confirmSettings(); + elements!.dialogBox.close(); + }); + elements!.resetButton.addEventListener("click", resetSettings); + elements!.translateButton.addEventListener("click", updateOutput); + elements!.input.addEventListener("keydown", (event) => { if (event.code === "Enter") { - listener(); + updateOutput(); event.preventDefault(); } }); diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..a0717c0 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,22 @@ +export type RedundancySettings = "both" | "condensed" | "default only"; +export type Settings = { + useTeloMisikeke: boolean; + randomize: boolean; + xAlaXPartialParsing: boolean; + number: RedundancySettings; + tense: RedundancySettings; +}; +export const defaultSettings: Settings = { + useTeloMisikeke: false, + randomize: false, + xAlaXPartialParsing: false, + number: "both", + tense: "both", +}; +export const settings: Settings = { + useTeloMisikeke: false, + randomize: false, + xAlaXPartialParsing: false, + number: "both", + tense: "both", +}; From 9317ff1a1f3e285a342ea1adf73f079512c72890 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:19:06 +0800 Subject: [PATCH 081/738] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a03a243..c50d4c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ You may need to force restart the page in order to use the latest version: shift - Add icons. - All possible errors will now be listed. -- Implement settings dialog, although it's still not functional. +- Implement settings dialog. Inside update (intended for developers): From 67dce964cae8dc33bb86262315f894d7472ee4c5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:21:21 +0800 Subject: [PATCH 082/738] respect settings for x ala x partial parsing --- src/lexer.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 8b5fd08..3e67ccb 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -15,6 +15,7 @@ import { } from "./parser-lib.ts"; import { TokenTree } from "./token-tree.ts"; import { CoveredError } from "./error.ts"; +import { settings } from "./settings.ts"; export type Lexer = Parser; @@ -161,7 +162,13 @@ function tokenTree(includeQuotation: boolean): Lexer { }), properWords().map((words) => ({ type: "proper word", words }) as TokenTree), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), - xAlaX().map((word) => ({ type: "x ala x", word }) as TokenTree), + lazy(() => { + if (settings) { + return xAlaX().map((word) => ({ type: "x ala x", word }) as TokenTree); + } else { + return error(new CoveredError()); + } + }), word().map((word) => ({ type: "word", word })), ); } From 298145333af322ac597343065852cb3891720109 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:22:17 +0800 Subject: [PATCH 083/738] fix --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 3e67ccb..7768eb4 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -163,7 +163,7 @@ function tokenTree(includeQuotation: boolean): Lexer { properWords().map((words) => ({ type: "proper word", words }) as TokenTree), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), lazy(() => { - if (settings) { + if (!settings.xAlaXPartialParsing) { return xAlaX().map((word) => ({ type: "x ala x", word }) as TokenTree); } else { return error(new CoveredError()); From 45f62019f44bc5608838d6c93b45930e025d7cda Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:29:04 +0800 Subject: [PATCH 084/738] remove unused import --- src/translator.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/translator.ts b/src/translator.ts index bd0ec92..3f5b455 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -9,12 +9,7 @@ import { } from "./ast.ts"; import { Output } from "./output.ts"; import { parser } from "./ast-parser.ts"; -import { - CoveredError, - OutputError, - TodoError, - UnreachableError, -} from "./error.ts"; +import { OutputError, TodoError, UnreachableError } from "./error.ts"; import { DEFINITION } from "./definition.ts"; /** A special kind of Output that translators returns. */ From 7d8f7a70ff830a63f623ff855d36413257a5ffe7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:32:34 +0800 Subject: [PATCH 085/738] add notes about ucsur --- README.md | 6 ++++++ src/ucsur.ts | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 065906e..9a9be8a 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,10 @@ To stop this command, simply press Ctrl + C. ## About the source codes +### Runtime agnostic + With exception to `./src/main.ts`, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. + +### UCSUR included + +Some parts of the code make use of sitelen pona UCSUR characters. To display properly, install an UCSUR font and change the font settings on your editor. [UCSUR Installation guides](https://github.com/neroist/sitelen-pona-ucsur-guide/). diff --git a/src/ucsur.ts b/src/ucsur.ts index ad3f639..a7248fb 100644 --- a/src/ucsur.ts +++ b/src/ucsur.ts @@ -1,4 +1,3 @@ -// This requires UCSUR font on your editor export const START_OF_CARTOUCHE = "\u{F1990}"; export const END_OF_CARTOUCHE = "\u{F1991}"; export const COMBINING_CARTOUCHE_EXTENSION = "\u{F1992}"; From 13b9c7c64d861526ea0cff0979a0e2cc6c4b5934 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 14:41:12 +0800 Subject: [PATCH 086/738] use nasin nanpa --- style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/style.css b/style.css index 55e011d..07c26e5 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,7 @@ +@font-face { + font-family: "nasin-nanpa"; + src: url("https://github.com/ETBCOR/nasin-nanpa/raw/main/versions/nasin-nanpa.otf"); +} body { margin: 10px; font-family: sans-serif; @@ -15,6 +19,7 @@ a:visited { color: #551a8b; } #input { + font-family: "nasin-nanpa", sans-serif; box-sizing: border-box; resize: vertical; width: 100%; From 3e398ed113739ec8c23c78bb937631c6582cfc71 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 16:19:50 +0800 Subject: [PATCH 087/738] basic support for ucsur --- src/lexer.ts | 25 +++++++-- src/ucsur.ts | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 7768eb4..6752a79 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -16,6 +16,7 @@ import { import { TokenTree } from "./token-tree.ts"; import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; +import { UCSUR_TO_LATIN } from "./ucsur.ts"; export type Lexer = Parser; @@ -60,8 +61,8 @@ function eol(): Lexer { else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); }); } -/** Parses lowercase word. */ -function word(): Lexer { +/** Parses lowercase latin word. */ +function latinWord(): Lexer { return match(/([a-z][a-zA-Z]*)\s*/, "word").map(([_, word]) => { if (/[A-Z]/.test(word)) { throw new UnrecognizedError(`"${word}"`); @@ -70,6 +71,21 @@ function word(): Lexer { } }); } +/** Parses UCSUR word. */ +function ucsurWord(): Lexer { + return match(/([^]{2})\s*/, "UCSUR word").map(([_, word]) => { + const latin = UCSUR_TO_LATIN[word]; + if (latin === undefined) { + throw new CoveredError(); + } else { + return word; + } + }); +} +/** Parses a word. */ +function word(): Lexer { + return choiceOnlyOne(ucsurWord(), latinWord()); +} /** * Parses all at least one uppercase words and combines them all into single * string. This function is exhaustive like `all`. @@ -83,7 +99,7 @@ function properWords(): Lexer { } /** Parses a specific word. */ function specificWord(thatWord: string): Lexer { - return word().filter((thisWord) => { + return choiceOnlyOne(ucsurWord(), word()).filter((thisWord) => { if (thatWord === thisWord) return true; else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); @@ -142,7 +158,8 @@ function comma(): Lexer { } /** Parses a punctuation. */ function punctuation(): Lexer { - return match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => + // UCSUR characters are two characters wide + return match(/([.,:;?!]|󱦜|󱦝)\s*/, "punctuation").map(([_, punctuation]) => punctuation ); } diff --git a/src/ucsur.ts b/src/ucsur.ts index a7248fb..a2af63c 100644 --- a/src/ucsur.ts +++ b/src/ucsur.ts @@ -12,3 +12,147 @@ export const START_OF_REVERSE_LONG_GLYPH = "\u{F199A}"; export const END_OF_REVERSE_LONG_GLYPH = "\u{F199B}"; export const MIDDLE_DOT = "\u{F199C}"; export const COLON = "\u{F199D}"; + +export const UCSUR_TO_LATIN: { [ucsur: string]: string } = { + "󱤀": "a", + "󱤁": "akesi", + "󱤂": "ala", + "󱤃": "alasa", + "󱤄": "ale", + "󱤅": "anpa", + "󱤆": "ante", + "󱤇": "anu", + "󱤈": "awen", + "󱤉": "e", + "󱤊": "en", + "󱤋": "esun", + "󱤌": "ijo", + "󱤍": "ike", + "󱤎": "ilo", + "󱤏": "insa", + "󱤐": "jaki", + "󱤑": "jan", + "󱤒": "jelo", + "󱤓": "jo", + "󱤔": "kala", + "󱤕": "kalama", + "󱤖": "kama", + "󱤗": "kasi", + "󱤘": "ken", + "󱤙": "kepeken", + "󱤚": "kili", + "󱤛": "kiwen", + "󱤜": "ko", + "󱤝": "kon", + "󱤞": "kule", + "󱤟": "kulupu", + "󱤠": "kute", + "󱤡": "la", + "󱤢": "lape", + "󱤣": "laso", + "󱤤": "lawa", + "󱤥": "len", + "󱤦": "lete", + "󱤧": "li", + "󱤨": "lili", + "󱤩": "linja", + "󱤪": "lipu", + "󱤫": "loje", + "󱤬": "lon", + "󱤭": "luka", + "󱤮": "lukin", + "󱤯": "lupa", + "󱤰": "ma", + "󱤱": "mama", + "󱤲": "mani", + "󱤳": "meli", + "󱤴": "mi", + "󱤵": "mije", + "󱤶": "moku", + "󱤷": "moli", + "󱤸": "monsi", + "󱤹": "mu", + "󱤺": "mun", + "󱤻": "musi", + "󱤼": "mute", + "󱤽": "nanpa", + "󱤾": "nasa", + "󱤿": "nasin", + "󱥀": "lupa", + "󱥁": "ni", + "󱥂": "nimi", + "󱥃": "noka", + "󱥄": "o", + "󱥅": "olin", + "󱥆": "ona", + "󱥇": "open", + "󱥈": "pakala", + "󱥉": "pali", + "󱥊": "palisa", + "󱥋": "pan", + "󱥌": "pana", + "󱥍": "pi", + "󱥎": "pilin", + "󱥏": "pimeja", + "󱥐": "pini", + "󱥑": "pipi", + "󱥒": "poka", + "󱥓": "poki", + "󱥔": "pona", + "󱥕": "pu", + "󱥖": "sama", + "󱥗": "seli", + "󱥘": "selo", + "󱥙": "seme", + "󱥚": "sewi", + "󱥛": "sijelo", + "󱥜": "sike", + "󱥝": "sin", + "󱥞": "sina", + "󱥟": "sinpin", + "󱥠": "sitelen", + "󱥡": "sona", + "󱥢": "soweli", + "󱥣": "suli", + "󱥤": "suno", + "󱥥": "supa", + "󱥦": "suwi", + "󱥧": "tan", + "󱥨": "taso", + "󱥩": "tawa", + "󱥪": "telo", + "󱥫": "tenpo", + "󱥬": "toki", + "󱥭": "tomo", + "󱥮": "tu", + "󱥯": "unpa", + "󱥰": "uta", + "󱥱": "utala", + "󱥲": "walo", + "󱥳": "wan", + "󱥴": "waso", + "󱥵": "wawa", + "󱥶": "weka", + "󱥷": "wile", + "󱥸": "namako", + "󱥹": "kin", + "󱥺": "oko", + "󱥻": "kipisi", + "󱥼": "leko", + "󱥽": "monsuta", + "󱥾": "tonsi", + "󱥿": "jasima", + "󱦀": "kijetesantakalu", + "󱦁": "soko", + "󱦂": "meso", + "󱦃": "epiku", + "󱦄": "kokosila", + "󱦅": "lanpan", + "󱦆": "n", + "󱦇": "misikeke", + "󱦈": "ku", + "󱦠": "pake", + "󱦡": "apeja", + "󱦢": "majuna", + "󱦣": "powe", +}; From 4a7b02839600db5c9ffd561a26cebc0d77e0a20f Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 16:21:55 +0800 Subject: [PATCH 088/738] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9a9be8a..cec3199 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,5 @@ With exception to `./src/main.ts`, every source codes in `./src/` are runtime ag ### UCSUR included Some parts of the code make use of sitelen pona UCSUR characters. To display properly, install an UCSUR font and change the font settings on your editor. [UCSUR Installation guides](https://github.com/neroist/sitelen-pona-ucsur-guide/). + +Also, take note that UCSUR characters are two characters wide. Be careful with string and regex manipulation. From fa1cd14b33a63145d2060cae6075f21d67d3c0c0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 16:26:16 +0800 Subject: [PATCH 089/738] allow error aggregation on choiceOnlyOne --- src/parser-lib.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 8699c87..2ff0ce8 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -98,8 +98,11 @@ export function choiceOnlyOne( ): Parser { return new Parser((src) => choices.reduce((output, parser) => { - if (output.isError()) return parser.parser(src); - else return output; + if (output.isError()) { + return Output.concat(output, parser.parser(src)); + } else { + return output; + } }, new Output>()) ); } From f96be3478eff9a347825285e7f965accafe11f1c Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 16:34:01 +0800 Subject: [PATCH 090/738] simplify null detection --- src/lexer.ts | 2 +- src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 6752a79..1e268c3 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -75,7 +75,7 @@ function latinWord(): Lexer { function ucsurWord(): Lexer { return match(/([^]{2})\s*/, "UCSUR word").map(([_, word]) => { const latin = UCSUR_TO_LATIN[word]; - if (latin === undefined) { + if (latin == null) { throw new CoveredError(); } else { return word; diff --git a/src/main.ts b/src/main.ts index 8366894..803d688 100644 --- a/src/main.ts +++ b/src/main.ts @@ -139,7 +139,7 @@ function loadSettings(): void { >(name: T, element: HTMLInputElement): void { const x = localStorage.getItem(name); let value: boolean; - if (x === null) { + if (x == null) { value = defaultSettings[name]; } else { value = x === "true"; From 05df4f4c5d426d831a996eddb2b81af39fae71c1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 17:37:56 +0800 Subject: [PATCH 091/738] implement cartouche --- src/lexer.ts | 101 ++++++++++++++++++++++++++++++++++++++++++++-- src/parser-lib.ts | 3 ++ 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 1e268c3..a75a491 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -16,10 +16,18 @@ import { import { TokenTree } from "./token-tree.ts"; import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; -import { UCSUR_TO_LATIN } from "./ucsur.ts"; +import { + COMBINING_CARTOUCHE_EXTENSION, + END_OF_CARTOUCHE, + START_OF_CARTOUCHE, + UCSUR_TO_LATIN, +} from "./ucsur.ts"; export type Lexer = Parser; +const VOWEL = /[aeiou]/; +const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; + /** Takes all parsers and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. @@ -54,6 +62,21 @@ function match( } }); } +function spaces(): Lexer { + return match(/\s*/, "space").map(([space]) => space); +} +function slice(length: number, description: string): Lexer { + return new Parser((src) => { + if (src.length < length) { + return new Output(new UnexpectedError(src, description)); + } else { + return new Output([{ + rest: src.slice(length), + value: src.slice(0, length), + }]); + } + }); +} /** Parses the end of line (or the end of sentence in context of Toki Pona) */ function eol(): Lexer { return new Parser((src) => { @@ -71,9 +94,24 @@ function latinWord(): Lexer { } }); } +function ucsur(): Lexer { + return slice(2, "UCSUR character").skip(spaces()); +} +function specificUcsurCharacter( + character: string, + description: string, +): Lexer { + return ucsur().filter((word) => { + if (word === character) { + return true; + } else { + throw new UnexpectedError(`"${word}"`, description); + } + }); +} /** Parses UCSUR word. */ function ucsurWord(): Lexer { - return match(/([^]{2})\s*/, "UCSUR word").map(([_, word]) => { + return ucsur().map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { throw new CoveredError(); @@ -124,6 +162,59 @@ function openQuotationMark(): Lexer { function closeQuotationMark(): Lexer { return match(/(["”»」])\s*/, "close quotation mark").map(([_, mark]) => mark); } +function cartoucheSpace(): Lexer { + return all(choiceOnlyOne( + match(/\s+/, "space").map(() => null), + specificUcsurCharacter( + COMBINING_CARTOUCHE_EXTENSION, + "combining cartouche extension", + ).map(() => null), + )).map(() => null); +} +function cartoucheElement(): Lexer { + return choiceOnlyOne( + ucsurWord().skip(cartoucheSpace()).skip( + specificUcsurCharacter("󱦝", "colon"), + ).skip(cartoucheSpace()), + sequence( + ucsurWord().skip(cartoucheSpace()), + allAtLeastOnce( + specificUcsurCharacter("󱦜", "colon").skip(cartoucheSpace()), + ).map( + (dots) => dots.length, + ), + ).map(([word, dots]) => { + let count = dots; + if (VOWEL.test(word[0])) { + count++; + } + const morae = word.match(MORAE)!; + if (morae.length < count) { + throw new UnrecognizedError("Excess dots"); + } + return morae.slice(0, count).join(""); + }), + ucsurWord().skip(cartoucheSpace()).map((word) => word[0]), + match(/([a-zA-Z])\s*/, "Latin letter").skip(cartoucheSpace()).map(( + [_, letter], + ) => letter), + ); +} +function cartouche(): Lexer { + return sequence( + specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche"), + cartoucheSpace().with(allAtLeastOnce(cartoucheElement())), + specificUcsurCharacter(END_OF_CARTOUCHE, "end of cartouche"), + ).map( + ([_, words, _1]) => { + const word = words.join(""); + return word[0].toUpperCase() + word.slice(1); + }, + ); +} +function cartouches(): Lexer { + return allAtLeastOnce(cartouche()).map((words) => words.join(" ")); +} /** Parses quotation. */ function quotation(): Lexer { return sequence( @@ -177,7 +268,9 @@ function tokenTree(includeQuotation: boolean): Lexer { return error(new CoveredError()); } }), - properWords().map((words) => ({ type: "proper word", words }) as TokenTree), + choiceOnlyOne(cartouches(), properWords()).map((words) => + ({ type: "proper word", words }) as TokenTree + ), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), lazy(() => { if (!settings.xAlaXPartialParsing) { @@ -193,7 +286,7 @@ function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } export function lex(src: string): Output> { - return match(/\s*/, "spaces").with(tokenTrees(true)).skip(eol()).parser(src) + return spaces().with(tokenTrees(true)).skip(eol()).parser(src) .map(( { value }, ) => value); diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 2ff0ce8..cfb0b96 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -110,6 +110,9 @@ export function choiceOnlyOne( export function optional(parser: Parser): Parser { return choice(parser, nothing()); } +export function optionalAll(parser: Parser): Parser { + return choiceOnlyOne(parser, nothing()); +} /** Takes all parsers and applies them one after another. */ // Typescript really struggles with inferring types when using this function export function sequence>( From d61f2b4d694f132f7c822bc9e1bbf9a1444cd123 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 17:41:26 +0800 Subject: [PATCH 092/738] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c50d4c8..ad91d8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ You may need to force restart the page in order to use the latest version: shift - Add icons. - All possible errors will now be listed. -- Implement settings dialog. +- Implement settings dialog ([More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help)). +- Implement UCSUR support! It supports: + - Cartouche with nasin sitelen kalama Inside update (intended for developers): From 5398a679f8ceca94ee6ce9aa71773a787581a2b2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 17:44:04 +0800 Subject: [PATCH 093/738] fix bug --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index a75a491..51ad06d 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -116,7 +116,7 @@ function ucsurWord(): Lexer { if (latin == null) { throw new CoveredError(); } else { - return word; + return latin; } }); } From 0989861378d746efd5b68174ad24b6f88f70f72f Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 17:46:11 +0800 Subject: [PATCH 094/738] reorder and update punctuation lexer --- src/lexer.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 51ad06d..600fe29 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -162,6 +162,23 @@ function openQuotationMark(): Lexer { function closeQuotationMark(): Lexer { return match(/(["”»」])\s*/, "close quotation mark").map(([_, mark]) => mark); } +/** Parses a comma. */ +function comma(): Lexer { + return match(/,\s*/, "comma").map(() => ","); +} +/** Parses a punctuation. */ +function punctuation(): Lexer { + // UCSUR characters are two characters wide + return match(/([.,:;?!]|󱦜|󱦝)\s*/, "punctuation").map(([_, punctuation]) => { + if (punctuation === "󱦜") { + return "."; + } else if (punctuation === "󱦝") { + return ":"; + } else { + return punctuation; + } + }); +} function cartoucheSpace(): Lexer { return all(choiceOnlyOne( match(/\s+/, "space").map(() => null), @@ -243,17 +260,6 @@ function quotation(): Lexer { }; }); } -/** Parses a comma. */ -function comma(): Lexer { - return match(/,\s*/, "comma").map(() => ","); -} -/** Parses a punctuation. */ -function punctuation(): Lexer { - // UCSUR characters are two characters wide - return match(/([.,:;?!]|󱦜|󱦝)\s*/, "punctuation").map(([_, punctuation]) => - punctuation - ); -} /** Parses a token tree. */ function tokenTree(includeQuotation: boolean): Lexer { return choiceOnlyOne( From 33ae96493317cba02d9272b08aa89a74ee5d2f05 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:00:09 +0800 Subject: [PATCH 095/738] implement variation selector --- src/lexer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 600fe29..bb94856 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -94,8 +94,13 @@ function latinWord(): Lexer { } }); } +function variationSelector(): Lexer { + return match(/[\uFE00-\uFE0F]/, "variation selector").map(([character]) => + character + ); +} function ucsur(): Lexer { - return slice(2, "UCSUR character").skip(spaces()); + return slice(2, "UCSUR character").skip(variationSelector()).skip(spaces()); } function specificUcsurCharacter( character: string, From 1b548ee1eeb6a34e562f5fe68c96f3bde2c0455e Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:13:45 +0800 Subject: [PATCH 096/738] update readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index cec3199..a614dda 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,15 @@ deno task watch To stop this command, simply press Ctrl + C. +## Running locally + +After building or watching, you can directly run `./index.html` using your favorite browser with some caveat however: + +- Settings won't be saved. +- UCSUR characters will display as tofu. + +This could be mitigated by making use of local server but I didn't do that, there's little need for that. + ## About the source codes ### Runtime agnostic From 8469d2a6908c5e03d8f1a454114723007be69199 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:31:18 +0800 Subject: [PATCH 097/738] remove deprecated combiner --- src/lexer.ts | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index bb94856..fb16f8d 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -184,24 +184,15 @@ function punctuation(): Lexer { } }); } -function cartoucheSpace(): Lexer { - return all(choiceOnlyOne( - match(/\s+/, "space").map(() => null), - specificUcsurCharacter( - COMBINING_CARTOUCHE_EXTENSION, - "combining cartouche extension", - ).map(() => null), - )).map(() => null); -} function cartoucheElement(): Lexer { return choiceOnlyOne( - ucsurWord().skip(cartoucheSpace()).skip( + ucsurWord().skip( specificUcsurCharacter("󱦝", "colon"), - ).skip(cartoucheSpace()), + ), sequence( - ucsurWord().skip(cartoucheSpace()), + ucsurWord(), allAtLeastOnce( - specificUcsurCharacter("󱦜", "colon").skip(cartoucheSpace()), + specificUcsurCharacter("󱦜", "colon"), ).map( (dots) => dots.length, ), @@ -216,8 +207,8 @@ function cartoucheElement(): Lexer { } return morae.slice(0, count).join(""); }), - ucsurWord().skip(cartoucheSpace()).map((word) => word[0]), - match(/([a-zA-Z])\s*/, "Latin letter").skip(cartoucheSpace()).map(( + ucsurWord().map((word) => word[0]), + match(/([a-zA-Z])\s*/, "Latin letter").map(( [_, letter], ) => letter), ); @@ -225,7 +216,7 @@ function cartoucheElement(): Lexer { function cartouche(): Lexer { return sequence( specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche"), - cartoucheSpace().with(allAtLeastOnce(cartoucheElement())), + allAtLeastOnce(cartoucheElement()), specificUcsurCharacter(END_OF_CARTOUCHE, "end of cartouche"), ).map( ([_, words, _1]) => { From b326cd970e14180a64df76c6348e98d821c803d8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:32:45 +0800 Subject: [PATCH 098/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad91d8e..64ca88f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement settings dialog ([More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help)). - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama + - (Deprecated characters and combiners are not supported ) Inside update (intended for developers): From a704f98cd389d459d7fa2b10a470359b9950ea47 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:33:22 +0800 Subject: [PATCH 099/738] small update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ca88f..736dd4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement settings dialog ([More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help)). - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - - (Deprecated characters and combiners are not supported ) + - (Deprecated characters and combiners are not supported) Inside update (intended for developers): From 30fbac8e055f90ae23426c12b7accc7c2e9ea518 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:43:07 +0800 Subject: [PATCH 100/738] small update --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index fb16f8d..0db58ab 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -142,7 +142,7 @@ function properWords(): Lexer { } /** Parses a specific word. */ function specificWord(thatWord: string): Lexer { - return choiceOnlyOne(ucsurWord(), word()).filter((thisWord) => { + return word().filter((thisWord) => { if (thatWord === thisWord) return true; else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); From 7df6b2e60dd50831d54d14829299a631a17a819e Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 18:49:43 +0800 Subject: [PATCH 101/738] remove unused import --- src/lexer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 0db58ab..ed1f785 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -17,7 +17,6 @@ import { TokenTree } from "./token-tree.ts"; import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; import { - COMBINING_CARTOUCHE_EXTENSION, END_OF_CARTOUCHE, START_OF_CARTOUCHE, UCSUR_TO_LATIN, From ed47a31abc239cb827665e2b1ebfdc7ddce356b1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 26 Mar 2024 19:48:31 +0800 Subject: [PATCH 102/738] simplify null checking --- src/lexer.ts | 4 ++-- src/translator.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index ed1f785..c960e78 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -47,13 +47,13 @@ function match( const newRegex = new RegExp("^" + regex.source, regex.flags); return new Parser((src) => { const match = src.match(newRegex); - if (match !== null) { + if (match != null) { return new Output([{ value: match, rest: src.slice(match[0].length) }]); } else if (src === "") { return new Output(new UnexpectedError("end of sentence", description)); } else { const token = src.match(/[^\s]*/)?.[0]; - if (token !== undefined) { + if (token != null) { return new Output(new UnexpectedError(`"${token}"`, description)); } else { throw new UnreachableError(); diff --git a/src/translator.ts b/src/translator.ts index 3f5b455..e3dee70 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -165,7 +165,7 @@ function defaultPhraseAs( [...modifiers.slice().reverse(), headWord].join(" "), ).map( (translation) => { - if (name !== undefined) { + if (name != null) { return `${translation} named ${name}`; } else { return translation; @@ -187,7 +187,7 @@ function defaultPhraseAs( [...modifiers.slice().reverse(), headWord].join(" "), ).map( (translation) => { - if (name !== undefined) { + if (name != null) { return `${translation} named ${name}`; } else { return translation; From 233e1ca84479d2e52b8b9c581aa2b9bd2056067e Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 04:53:00 +0800 Subject: [PATCH 103/738] make variation selector optional --- src/lexer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index c960e78..0233484 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -10,6 +10,7 @@ import { choiceOnlyOne, error, lazy, + optionalAll, Parser, sequence as rawSequence, } from "./parser-lib.ts"; @@ -99,7 +100,8 @@ function variationSelector(): Lexer { ); } function ucsur(): Lexer { - return slice(2, "UCSUR character").skip(variationSelector()).skip(spaces()); + return slice(2, "UCSUR character").skip(optionalAll(variationSelector())) + .skip(spaces()); } function specificUcsurCharacter( character: string, From 99dc17eaab13f039ea91e1467fc9d393bbf1b65c Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 05:55:06 +0800 Subject: [PATCH 104/738] disallow glyph variation on some parts --- src/lexer.ts | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 0233484..7be980c 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -22,6 +22,7 @@ import { START_OF_CARTOUCHE, UCSUR_TO_LATIN, } from "./ucsur.ts"; +import { nothing } from "./parser-lib.ts"; export type Lexer = Parser; @@ -99,15 +100,24 @@ function variationSelector(): Lexer { character ); } -function ucsur(): Lexer { - return slice(2, "UCSUR character").skip(optionalAll(variationSelector())) +function ucsur(allowVariation: boolean): Lexer { + return slice(2, "UCSUR character").skip( + lazy(() => { + if (allowVariation) { + return optionalAll(variationSelector()).map(() => null); + } else { + return nothing(); + } + }), + ) .skip(spaces()); } function specificUcsurCharacter( character: string, + allowVariation: boolean, description: string, ): Lexer { - return ucsur().filter((word) => { + return ucsur(allowVariation).filter((word) => { if (word === character) { return true; } else { @@ -117,7 +127,7 @@ function specificUcsurCharacter( } /** Parses UCSUR word. */ function ucsurWord(): Lexer { - return ucsur().map((word) => { + return ucsur(true).map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { throw new CoveredError(); @@ -174,26 +184,23 @@ function comma(): Lexer { } /** Parses a punctuation. */ function punctuation(): Lexer { - // UCSUR characters are two characters wide - return match(/([.,:;?!]|󱦜|󱦝)\s*/, "punctuation").map(([_, punctuation]) => { - if (punctuation === "󱦜") { - return "."; - } else if (punctuation === "󱦝") { - return ":"; - } else { - return punctuation; - } - }); + return choiceOnlyOne( + match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => + punctuation + ), + specificUcsurCharacter("󱦜", true, "middle dot").map(() => "."), + specificUcsurCharacter("󱦝", true, "middle dot").map(() => ":"), + ); } function cartoucheElement(): Lexer { return choiceOnlyOne( ucsurWord().skip( - specificUcsurCharacter("󱦝", "colon"), + specificUcsurCharacter("󱦝", true, "colon"), ), sequence( ucsurWord(), allAtLeastOnce( - specificUcsurCharacter("󱦜", "colon"), + specificUcsurCharacter("󱦜", true, "colon"), ).map( (dots) => dots.length, ), @@ -216,9 +223,9 @@ function cartoucheElement(): Lexer { } function cartouche(): Lexer { return sequence( - specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche"), + specificUcsurCharacter(START_OF_CARTOUCHE, false, "start of cartouche"), allAtLeastOnce(cartoucheElement()), - specificUcsurCharacter(END_OF_CARTOUCHE, "end of cartouche"), + specificUcsurCharacter(END_OF_CARTOUCHE, false, "end of cartouche"), ).map( ([_, words, _1]) => { const word = words.join(""); From 836c8cf092bd7029b91c8551e906739a1a27313c Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:00:02 +0800 Subject: [PATCH 105/738] allow more characters for sitelen kalama --- src/lexer.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 7be980c..41cc11c 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -195,12 +195,18 @@ function punctuation(): Lexer { function cartoucheElement(): Lexer { return choiceOnlyOne( ucsurWord().skip( - specificUcsurCharacter("󱦝", true, "colon"), + choiceOnlyOne( + match(/(:)\s*/, "full width colon").map(([_, dot]) => dot), + specificUcsurCharacter("󱦝", true, "colon"), + ), ), sequence( ucsurWord(), allAtLeastOnce( - specificUcsurCharacter("󱦜", true, "colon"), + choiceOnlyOne( + match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), + specificUcsurCharacter("󱦜", true, "colon"), + ), ).map( (dots) => dots.length, ), From 25ebe7c4ba591b0da095d0672660b5d02ad7bea7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:01:07 +0800 Subject: [PATCH 106/738] small update --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 41cc11c..5f0f7ee 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -196,7 +196,7 @@ function cartoucheElement(): Lexer { return choiceOnlyOne( ucsurWord().skip( choiceOnlyOne( - match(/(:)\s*/, "full width colon").map(([_, dot]) => dot), + match(/(\uff1a)\s*/, "full width colon").map(([_, dot]) => dot), specificUcsurCharacter("󱦝", true, "colon"), ), ), From 0ada73268d79acb08608be8a4bf3615ca893ea35 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:02:51 +0800 Subject: [PATCH 107/738] small update --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 5f0f7ee..3cb5062 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -205,7 +205,7 @@ function cartoucheElement(): Lexer { allAtLeastOnce( choiceOnlyOne( match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), - specificUcsurCharacter("󱦜", true, "colon"), + specificUcsurCharacter("󱦜", true, "middle dot"), ), ).map( (dots) => dots.length, From a33a02b0ca12a39853bcf87bb9dafed9927f7b90 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:14:50 +0800 Subject: [PATCH 108/738] disallow space on some part --- src/lexer.ts | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 3cb5062..d4ad620 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -100,24 +100,32 @@ function variationSelector(): Lexer { character ); } -function ucsur(allowVariation: boolean): Lexer { +function ucsur( + settings: { allowVariation: boolean; allowSpace: boolean }, +): Lexer { return slice(2, "UCSUR character").skip( lazy(() => { - if (allowVariation) { + if (settings.allowVariation) { return optionalAll(variationSelector()).map(() => null); } else { return nothing(); } }), ) - .skip(spaces()); + .skip(lazy(() => { + if (settings.allowSpace) { + return spaces().map(() => null); + } else { + return nothing(); + } + })); } function specificUcsurCharacter( character: string, - allowVariation: boolean, description: string, + settings: { allowVariation: boolean; allowSpace: boolean }, ): Lexer { - return ucsur(allowVariation).filter((word) => { + return ucsur(settings).filter((word) => { if (word === character) { return true; } else { @@ -127,7 +135,7 @@ function specificUcsurCharacter( } /** Parses UCSUR word. */ function ucsurWord(): Lexer { - return ucsur(true).map((word) => { + return ucsur({ allowVariation: true, allowSpace: true }).map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { throw new CoveredError(); @@ -188,8 +196,14 @@ function punctuation(): Lexer { match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => punctuation ), - specificUcsurCharacter("󱦜", true, "middle dot").map(() => "."), - specificUcsurCharacter("󱦝", true, "middle dot").map(() => ":"), + specificUcsurCharacter("󱦜", "middle dot", { + allowVariation: true, + allowSpace: true, + }).map(() => "."), + specificUcsurCharacter("󱦝", "middle dot", { + allowVariation: true, + allowSpace: true, + }).map(() => ":"), ); } function cartoucheElement(): Lexer { @@ -197,7 +211,10 @@ function cartoucheElement(): Lexer { ucsurWord().skip( choiceOnlyOne( match(/(\uff1a)\s*/, "full width colon").map(([_, dot]) => dot), - specificUcsurCharacter("󱦝", true, "colon"), + specificUcsurCharacter("󱦝", "colon", { + allowVariation: true, + allowSpace: true, + }), ), ), sequence( @@ -205,7 +222,10 @@ function cartoucheElement(): Lexer { allAtLeastOnce( choiceOnlyOne( match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), - specificUcsurCharacter("󱦜", true, "middle dot"), + specificUcsurCharacter("󱦜", "middle dot", { + allowVariation: true, + allowSpace: true, + }), ), ).map( (dots) => dots.length, @@ -229,9 +249,15 @@ function cartoucheElement(): Lexer { } function cartouche(): Lexer { return sequence( - specificUcsurCharacter(START_OF_CARTOUCHE, false, "start of cartouche"), + specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche", { + allowVariation: false, + allowSpace: false, + }), allAtLeastOnce(cartoucheElement()), - specificUcsurCharacter(END_OF_CARTOUCHE, false, "end of cartouche"), + specificUcsurCharacter(END_OF_CARTOUCHE, "end of cartouche", { + allowVariation: false, + allowSpace: false, + }), ).map( ([_, words, _1]) => { const word = words.join(""); From ba9a74a114a45f172307ab6adb1f942145b8f537 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:22:58 +0800 Subject: [PATCH 109/738] implement combined words lexer --- src/lexer.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/lexer.ts b/src/lexer.ts index d4ad620..6c2befc 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -19,10 +19,13 @@ import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; import { END_OF_CARTOUCHE, + SCALING_JOINER, + STACKING_JOINER, START_OF_CARTOUCHE, UCSUR_TO_LATIN, } from "./ucsur.ts"; import { nothing } from "./parser-lib.ts"; +import { CHAR_UPPERCASE_Z } from "https://deno.land/std@0.186.0/path/_constants.ts"; export type Lexer = Parser; @@ -144,6 +147,37 @@ function ucsurWord(): Lexer { } }); } +function joiner(): Lexer { + return choiceOnlyOne( + match(/\u200D/, "zero width joiner").map(([_, joiner]) => joiner), + specificUcsurCharacter(STACKING_JOINER, "stacking joiner", { + allowVariation: false, + allowSpace: false, + }), + specificUcsurCharacter(SCALING_JOINER, "scaling joiner", { + allowVariation: false, + allowSpace: false, + }), + ); +} +function combinedWords(): Lexer { + return sequence( + ucsur({ allowVariation: false, allowSpace: false }).map((word) => { + const latin = UCSUR_TO_LATIN[word]; + if (latin == null) { + throw new CoveredError(); + } else { + return latin; + } + }), + joiner(), + ucsurWord(), + ).map(([first, _, second]) => ({ + type: "combined words", + first, + second, + })); +} /** Parses a word. */ function word(): Lexer { return choiceOnlyOne(ucsurWord(), latinWord()); @@ -313,6 +347,7 @@ function tokenTree(includeQuotation: boolean): Lexer { choiceOnlyOne(cartouches(), properWords()).map((words) => ({ type: "proper word", words }) as TokenTree ), + combinedWords(), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), lazy(() => { if (!settings.xAlaXPartialParsing) { From 86c592d58f137dfab450ec215b3a55d0a9d6b399 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 06:26:22 +0800 Subject: [PATCH 110/738] refactor --- src/lexer.ts | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 6c2befc..a01b58f 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -137,8 +137,10 @@ function specificUcsurCharacter( }); } /** Parses UCSUR word. */ -function ucsurWord(): Lexer { - return ucsur({ allowVariation: true, allowSpace: true }).map((word) => { +function ucsurWord( + settings: { allowVariation: boolean; allowSpace: boolean }, +): Lexer { + return ucsur(settings).map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { throw new CoveredError(); @@ -147,6 +149,10 @@ function ucsurWord(): Lexer { } }); } +/** Parses UCSUR word. */ +function singleUcsurWord(): Lexer { + return ucsurWord({ allowVariation: true, allowSpace: true }); +} function joiner(): Lexer { return choiceOnlyOne( match(/\u200D/, "zero width joiner").map(([_, joiner]) => joiner), @@ -162,16 +168,9 @@ function joiner(): Lexer { } function combinedWords(): Lexer { return sequence( - ucsur({ allowVariation: false, allowSpace: false }).map((word) => { - const latin = UCSUR_TO_LATIN[word]; - if (latin == null) { - throw new CoveredError(); - } else { - return latin; - } - }), + ucsurWord({ allowVariation: false, allowSpace: false }), joiner(), - ucsurWord(), + ucsurWord({ allowVariation: false, allowSpace: true }), ).map(([first, _, second]) => ({ type: "combined words", first, @@ -180,7 +179,7 @@ function combinedWords(): Lexer { } /** Parses a word. */ function word(): Lexer { - return choiceOnlyOne(ucsurWord(), latinWord()); + return choiceOnlyOne(singleUcsurWord(), latinWord()); } /** * Parses all at least one uppercase words and combines them all into single @@ -242,7 +241,7 @@ function punctuation(): Lexer { } function cartoucheElement(): Lexer { return choiceOnlyOne( - ucsurWord().skip( + singleUcsurWord().skip( choiceOnlyOne( match(/(\uff1a)\s*/, "full width colon").map(([_, dot]) => dot), specificUcsurCharacter("󱦝", "colon", { @@ -252,7 +251,7 @@ function cartoucheElement(): Lexer { ), ), sequence( - ucsurWord(), + singleUcsurWord(), allAtLeastOnce( choiceOnlyOne( match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), @@ -275,7 +274,7 @@ function cartoucheElement(): Lexer { } return morae.slice(0, count).join(""); }), - ucsurWord().map((word) => word[0]), + singleUcsurWord().map((word) => word[0]), match(/([a-zA-Z])\s*/, "Latin letter").map(( [_, letter], ) => letter), From 7632d448a7a40755f1814e556fb8a22c7a524f1a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:00:11 +0800 Subject: [PATCH 111/738] change some x ala x into X ala X --- src/ast-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e5af265..1037136 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -148,11 +148,11 @@ function wordUnit(word: Set, description: string): AstParser { count: words.length + 1, } as WordUnit)) ), - tokenTree("x ala x").map((tokenTree) => { + tokenTree("X ala X").map((tokenTree) => { if (tokenTree.type === "x ala x") { return { type: "x ala x", word: tokenTree.word } as WordUnit; } else { - throw new UnexpectedError(tokenTree.type, "x ala x"); + throw new UnexpectedError(tokenTree.type, "X ala X"); } }), wordFrom(word, description).then((word) => From 4f07d7c96a3e5f91f5107128ec72022b74800159 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:12:10 +0800 Subject: [PATCH 112/738] implement allUntilEnd combinator for better error messages --- src/ast-parser.ts | 16 +++++++++++++++- src/lexer.ts | 10 +++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 1037136..02de978 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -33,6 +33,7 @@ import { all, allAtLeastOnce, choice, + choiceOnlyOne, lazy, many, manyAtLeastOnce, @@ -61,6 +62,19 @@ function eol(): AstParser { else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); }); } +function allUntilEnd(parser: AstParser): AstParser> { + return choiceOnlyOne( + sequence(parser, lazy(() => allUntilEnd(parser))).map(( + [first, rest], + ) => [first, ...rest]), + eol().map(() => []), + ); +} +function allUntilEndAtLeastOnce(parser: AstParser): AstParser> { + return sequence(parser, allUntilEnd(parser)).map(( + [first, rest], + ) => [first, ...rest]); +} /** Parses a single token tree. */ function tokenTree(description: string): AstParser { return new Parser((src) => { @@ -530,7 +544,7 @@ export function quotation(): AstParser { /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { return lex(src).flatMap((src) => - allAtLeastOnce(sentence()).skip(eol()) + allUntilEndAtLeastOnce(sentence()) .filter( filter(SENTENCES_RULE), ).parser(src) diff --git a/src/lexer.ts b/src/lexer.ts index a01b58f..0f27cf4 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -88,6 +88,14 @@ function eol(): Lexer { else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); }); } +function allUntilEnd(parser: Lexer): Lexer> { + return choiceOnlyOne( + sequence(parser, lazy(() => allUntilEnd(parser))).map(( + [first, rest], + ) => [first, ...rest]), + eol().map(() => []), + ); +} /** Parses lowercase latin word. */ function latinWord(): Lexer { return match(/([a-z][a-zA-Z]*)\s*/, "word").map(([_, word]) => { @@ -362,7 +370,7 @@ function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } export function lex(src: string): Output> { - return spaces().with(tokenTrees(true)).skip(eol()).parser(src) + return spaces().with(allUntilEnd(tokenTree(true))).parser(src) .map(( { value }, ) => value); From 2c8cb3450cdcc1dbd9543768f04c7872ab4fa4bf Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:12:40 +0800 Subject: [PATCH 113/738] fix error message for eol parser --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 02de978..2825e19 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -59,7 +59,7 @@ function sequence>( function eol(): AstParser { return new Parser((src) => { if (src.length === 0) return new Output([{ value: null, rest: [] }]); - else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); + else return new Output(new UnexpectedError(src[0].type, "end of sentence")); }); } function allUntilEnd(parser: AstParser): AstParser> { From e535c682ea55fcb0eab674437892c2fd15b75e75 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:15:29 +0800 Subject: [PATCH 114/738] this turns out to be unhelpful --- src/ast-parser.ts | 16 +--------------- src/lexer.ts | 10 +--------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 2825e19..83bb08e 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -33,7 +33,6 @@ import { all, allAtLeastOnce, choice, - choiceOnlyOne, lazy, many, manyAtLeastOnce, @@ -62,19 +61,6 @@ function eol(): AstParser { else return new Output(new UnexpectedError(src[0].type, "end of sentence")); }); } -function allUntilEnd(parser: AstParser): AstParser> { - return choiceOnlyOne( - sequence(parser, lazy(() => allUntilEnd(parser))).map(( - [first, rest], - ) => [first, ...rest]), - eol().map(() => []), - ); -} -function allUntilEndAtLeastOnce(parser: AstParser): AstParser> { - return sequence(parser, allUntilEnd(parser)).map(( - [first, rest], - ) => [first, ...rest]); -} /** Parses a single token tree. */ function tokenTree(description: string): AstParser { return new Parser((src) => { @@ -544,7 +530,7 @@ export function quotation(): AstParser { /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { return lex(src).flatMap((src) => - allUntilEndAtLeastOnce(sentence()) + allAtLeastOnce(sentence()).skip(eol()) .filter( filter(SENTENCES_RULE), ).parser(src) diff --git a/src/lexer.ts b/src/lexer.ts index 0f27cf4..293a919 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -88,14 +88,6 @@ function eol(): Lexer { else return new Output(new UnexpectedError(`"${src}"`, "end of sentence")); }); } -function allUntilEnd(parser: Lexer): Lexer> { - return choiceOnlyOne( - sequence(parser, lazy(() => allUntilEnd(parser))).map(( - [first, rest], - ) => [first, ...rest]), - eol().map(() => []), - ); -} /** Parses lowercase latin word. */ function latinWord(): Lexer { return match(/([a-z][a-zA-Z]*)\s*/, "word").map(([_, word]) => { @@ -370,7 +362,7 @@ function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } export function lex(src: string): Output> { - return spaces().with(allUntilEnd(tokenTree(true))).parser(src) + return spaces().with(all(tokenTree(true))).skip(eol()).parser(src) .map(( { value }, ) => value); From 02a36bac1d24e7f45a446aae38cc24a6a05606e5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:18:38 +0800 Subject: [PATCH 115/738] who put this here, oh it's me... must have been an accident --- src/lexer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 293a919..640d3da 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -25,7 +25,6 @@ import { UCSUR_TO_LATIN, } from "./ucsur.ts"; import { nothing } from "./parser-lib.ts"; -import { CHAR_UPPERCASE_Z } from "https://deno.land/std@0.186.0/path/_constants.ts"; export type Lexer = Parser; From 73e1f189684391f2cc8be9a1e6edf7b946884bc2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 27 Mar 2024 09:24:59 +0800 Subject: [PATCH 116/738] formatting fix --- src/lexer.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 640d3da..c401bbc 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -113,14 +113,13 @@ function ucsur( return nothing(); } }), - ) - .skip(lazy(() => { - if (settings.allowSpace) { - return spaces().map(() => null); - } else { - return nothing(); - } - })); + ).skip(lazy(() => { + if (settings.allowSpace) { + return spaces().map(() => null); + } else { + return nothing(); + } + })); } function specificUcsurCharacter( character: string, From fbf5e2a3578b69c3410fb0566f2b1dceec7f3985 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 09:28:09 +0800 Subject: [PATCH 117/738] update documentations --- src/ast-parser.ts | 21 +++++++++------------ src/ast.ts | 18 ++++++++++++++++++ src/definition.ts | 2 ++ src/english-ast.ts | 1 + src/error.ts | 2 ++ src/filter.ts | 9 ++++++++- src/lexer.ts | 33 +++++++++++++++++++++++++-------- src/main.ts | 3 +++ src/output.ts | 7 ++++--- src/parser-lib.ts | 16 +++++++++++++--- src/settings.ts | 3 +++ src/token-tree.ts | 28 +++++++++++++++++++++++++++- src/ucsur.ts | 3 +++ src/vocabulary.ts | 6 +++--- 14 files changed, 121 insertions(+), 31 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 83bb08e..d129f15 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -1,3 +1,5 @@ +/** Module for AST Parser. It is responsible for turning an array of token tress into AST. */ + import { Clause, FullClause, @@ -85,7 +87,7 @@ function comma(): AstParser { function optionalComma(): AstParser { return optional(comma()); } -/** Parses lowercase word. */ +/** Parses a toki pona word. */ function word(): AstParser { return tokenTree("word").map((tokenTree) => { if (tokenTree.type === "word") { @@ -95,10 +97,7 @@ function word(): AstParser { } }); } -/** - * Parses all at least one uppercase words and combines them all into single - * string. This function is exhaustive like `all`. - */ +/** Parses proper words spanning multiple words. */ function properWords(): AstParser { return tokenTree("proper word").map((tokenTree) => { if (tokenTree.type === "proper word") { @@ -108,10 +107,7 @@ function properWords(): AstParser { } }); } -/** - * Parses all at least one uppercase words and combines them all into single - * string. This function is exhaustive like `all`. - */ +/** Parses a toki pona */ function punctuation(): AstParser { return tokenTree("punctuation").map((tokenTree) => { if (tokenTree.type === "punctuation") { @@ -182,7 +178,7 @@ function number(): AstParser> { } }); } -/** Parses multiple modifiers */ +/** Parses multiple modifiers. */ function modifiers(): AstParser> { return sequence( many( @@ -230,7 +226,7 @@ function modifiers(): AstParser> { filter(MODIFIERS_RULES), ); } -/** Parses phrases including preverbial phrases. */ +/** Parses phrases. */ function phrase(): AstParser { return choice( sequence(number(), lazy(modifiers)).map(( @@ -474,7 +470,7 @@ function clause(): AstParser { } as Clause)), ).filter(filter(CLAUSE_RULE)); } -/** Parses a single clause including precaluse and postclause. */ +/** Parses a single clause including preclause and postclause. */ function fullClause(): AstParser { return sequence( optional(wordUnit(new Set(["taso"]), '"taso"').skip(optionalComma())), @@ -512,6 +508,7 @@ function sentence(): AstParser { punctuation, })); } +/** Parses a quotation. */ export function quotation(): AstParser { return tokenTree("quotation").flatMapValue((tokenTree) => { if (tokenTree.type === "quotation") { diff --git a/src/ast.ts b/src/ast.ts index c7bec5e..37f3f64 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,3 +1,5 @@ +/** Module for describing Toki Pona AST. */ + import { UnreachableError } from "./error.ts"; /** Represents a word unit. */ @@ -102,6 +104,10 @@ export type Quotation = { leftMark: string; rightMark: string; }; +/** + * Helper function for checking whether some modifier passes the test + * function. + */ export function someModifierInPhrase( phrase: Phrase, whenQuotation: boolean, @@ -126,6 +132,10 @@ export function someModifierInPhrase( throw new UnreachableError(); } } +/** + * Helper function for checking whether some modifier passes the test + * function. + */ export function someModifierInMultiplePhrases( phrases: MultiplePhrases, whenQuotation: boolean, @@ -141,6 +151,10 @@ export function someModifierInMultiplePhrases( throw new UnreachableError(); } } +/** + * Helper function for checking whether some phrase passes the test + * function. + */ export function somePhraseInMultiplePhrases( phrases: MultiplePhrases, checker: (modifier: Phrase) => boolean, @@ -155,6 +169,10 @@ export function somePhraseInMultiplePhrases( throw new UnreachableError(); } } +/** + * Helper function for checking whether some object phrase passes the test + * function. + */ export function someObjectInMultiplePredicate( predicate: MultiplePredicates, checker: (object: Phrase) => boolean, diff --git a/src/definition.ts b/src/definition.ts index a77abe5..6a7ab42 100644 --- a/src/definition.ts +++ b/src/definition.ts @@ -1,3 +1,5 @@ +/** Module for describing word to word translations. */ + // TODO: avoid certain adjectives to be modified by adverb: this applies to // possessive adjectives: my, our, your, etc. diff --git a/src/english-ast.ts b/src/english-ast.ts index e69de29..0b6ffe4 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -0,0 +1 @@ +/** Module for describing English AST. */ diff --git a/src/error.ts b/src/error.ts index b7d86c6..bd5fee8 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,3 +1,5 @@ +/** Module for Error datatypes. */ + /** Represents Error used by `Output`. */ export class OutputError extends Error {} /** Represents errors that cannot be reached. */ diff --git a/src/filter.ts b/src/filter.ts index 48aad1e..af9fc25 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -1,3 +1,5 @@ +/** Module describing filter rules integrated within AST Parser. */ + import { Clause, FullClause, @@ -288,7 +290,9 @@ function modifierIsNumeric(modifier: Modifier): boolean { } return false; } -/** Helper function for checking if the modifiers is exactly just _ala_ or nothing. */ +/** + * Helper function for checking if the modifiers is exactly just _ala_ or nothing. + */ function modifiersIsAlaOrNone(modifiers: Array): boolean { if (modifiers.length > 1) { return false; @@ -299,6 +303,9 @@ function modifiersIsAlaOrNone(modifiers: Array): boolean { } return true; } +/** + * Helper function for determining whether the phrase has a preposition inside. + */ function hasPrepositionInPhrase(phrase: Phrase): boolean { if (phrase.type === "default") { return false; diff --git a/src/lexer.ts b/src/lexer.ts index c401bbc..66aaac6 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -1,3 +1,11 @@ +/** + * Module for lexer. It is responsible for turning string into array of token + * tress. It also latinizes UCSUR characters. + * + * Note: the words lexer and parser are used interchangeably since they both + * have the same capabilities. + */ + import { Output } from "./output.ts"; import { UnexpectedError, @@ -31,7 +39,7 @@ export type Lexer = Parser; const VOWEL = /[aeiou]/; const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; -/** Takes all parsers and applies them one after another. */ +/** Takes all parser and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. function sequence>( @@ -65,9 +73,11 @@ function match( } }); } +/** parses space. */ function spaces(): Lexer { return match(/\s*/, "space").map(([space]) => space); } +/** parses a string of consistent length. */ function slice(length: number, description: string): Lexer { return new Parser((src) => { if (src.length < length) { @@ -97,11 +107,13 @@ function latinWord(): Lexer { } }); } +/** Parses variation selector. */ function variationSelector(): Lexer { return match(/[\uFE00-\uFE0F]/, "variation selector").map(([character]) => character ); } +/** Parses an UCSUR character with optional variation selector and space. */ function ucsur( settings: { allowVariation: boolean; allowSpace: boolean }, ): Lexer { @@ -121,6 +133,7 @@ function ucsur( } })); } +/** Parses a specific UCSUR character. */ function specificUcsurCharacter( character: string, description: string, @@ -147,10 +160,11 @@ function ucsurWord( } }); } -/** Parses UCSUR word. */ +/** Parses a single UCSUR word. */ function singleUcsurWord(): Lexer { return ucsurWord({ allowVariation: true, allowSpace: true }); } +/** Parses a joiner. */ function joiner(): Lexer { return choiceOnlyOne( match(/\u200D/, "zero width joiner").map(([_, joiner]) => joiner), @@ -164,6 +178,7 @@ function joiner(): Lexer { }), ); } +/** Parses combined words. */ function combinedWords(): Lexer { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), @@ -175,14 +190,11 @@ function combinedWords(): Lexer { second, })); } -/** Parses a word. */ +/** Parses a word, either UCSUR or latin. */ function word(): Lexer { return choiceOnlyOne(singleUcsurWord(), latinWord()); } -/** - * Parses all at least one uppercase words and combines them all into single - * string. This function is exhaustive like `all`. - */ +/** Parses proper words spanning multiple words. */ function properWords(): Lexer { return allAtLeastOnce( match(/([A-Z][a-zA-Z]*)\s*/, "proper word").map(([_, word]) => word), @@ -190,7 +202,7 @@ function properWords(): Lexer { (array) => array.join(" "), ); } -/** Parses a specific word. */ +/** Parses a specific word, either UCSUR or latin. */ function specificWord(thatWord: string): Lexer { return word().filter((thisWord) => { if (thatWord === thisWord) return true; @@ -237,6 +249,7 @@ function punctuation(): Lexer { }).map(() => ":"), ); } +/** Parses cartouche element and returns the phonemes or letters it represents. */ function cartoucheElement(): Lexer { return choiceOnlyOne( singleUcsurWord().skip( @@ -278,6 +291,7 @@ function cartoucheElement(): Lexer { ) => letter), ); } +/** Parses a single cartouche. */ function cartouche(): Lexer { return sequence( specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche", { @@ -296,6 +310,7 @@ function cartouche(): Lexer { }, ); } +/** Parses multiple cartouches. */ function cartouches(): Lexer { return allAtLeastOnce(cartouche()).map((words) => words.join(" ")); } @@ -356,9 +371,11 @@ function tokenTree(includeQuotation: boolean): Lexer { word().map((word) => ({ type: "word", word })), ); } +/** Parses multiple token trees. */ function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } +/** Parses multiple token trees. */ export function lex(src: string): Output> { return spaces().with(all(tokenTree(true))).skip(eol()).parser(src) .map(( diff --git a/src/main.ts b/src/main.ts index 803d688..57ca7e4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,5 @@ +/** Module for main execution. */ + import { CoveredError } from "./error.ts"; import { translate } from "./translator.ts"; import { defaultSettings, RedundancySettings, settings } from "./settings.ts"; @@ -25,6 +27,7 @@ type Elements = { tense: HTMLSelectElement; xAlaXPartialParsing: HTMLInputElement; }; +/** A map of all HTML elements that are used here. */ let elements: undefined | Elements; function loadElements(): void { diff --git a/src/output.ts b/src/output.ts index dfe228e..d724f64 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,11 +1,11 @@ +/** Module containing the Output data type. */ + import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { /** Represents possibilities, considered error when the array is empty. */ output: Array; - /** - * A list of errors - */ + /** A list of all aggregated errors. */ errors: Array = []; constructor(output?: undefined | null | Array | OutputError) { if (Array.isArray(output)) { @@ -92,6 +92,7 @@ export class Output { for (const value of this.output) wholeOutput.append(mapper(value)); return wholeOutput; } + /** Combines all outputs. */ static concat(...outputs: Array>): Output { const wholeOutput = new Output(); for (const output of outputs) { diff --git a/src/parser-lib.ts b/src/parser-lib.ts index cfb0b96..32dd878 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -1,3 +1,8 @@ +/** + * A generic module for parser and parser combinator. It is used by both lexer + * and AST parser. + */ + import { OutputError } from "./error.ts"; import { Output } from "./output.ts"; @@ -21,6 +26,7 @@ export class Parser { })) ); } + /** TODO better comment. */ flatMapValue(mapper: (value: U) => Output): Parser { return new Parser((src) => this.parser(src).flatMap(({ value, rest }) => @@ -75,7 +81,7 @@ export function lookAhead(parser: Parser): Parser { } /** * Lazily evaluates the parser function only when needed. Useful for recursive - * parsers. + * parsers as well as precomputed parsers. */ export function lazy(parser: () => Parser): Parser { return new Parser((src) => parser().parser(src)); @@ -90,8 +96,8 @@ export function choice(...choices: Array>): Parser { ); } /** - * Tries to evaluate each parsers one at a time and only returns the first - * Output without error. + * Tries to evaluate each parsers one at a time and only only use the output of + * the parser that is successful. */ export function choiceOnlyOne( ...choices: Array> @@ -110,6 +116,10 @@ export function choiceOnlyOne( export function optional(parser: Parser): Parser { return choice(parser, nothing()); } +/** + * Like `optional` but when the parser is successful, it doesn't consider + * parsing nothing. + */ export function optionalAll(parser: Parser): Parser { return choiceOnlyOne(parser, nothing()); } diff --git a/src/settings.ts b/src/settings.ts index a0717c0..e6f0de0 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,3 +1,6 @@ +/** Module for translation settings stored as a global state */ + +/** */ export type RedundancySettings = "both" | "condensed" | "default only"; export type Settings = { useTeloMisikeke: boolean; diff --git a/src/token-tree.ts b/src/token-tree.ts index 6e12657..b903e8d 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -1,3 +1,11 @@ +/** + * Module describing token tree. It is like token but some token tree type also + * stores array of token trees hence the name token tree. + */ + +/** + * Represents token tree. + */ export type TokenTree = | { type: "word"; word: string } | { @@ -8,9 +16,14 @@ export type TokenTree = | { type: "long container"; before: Array; - word: string; + word: longContainerHead; after: Array; } + | { + type: "long space container"; + word: longContainerHead; + spaceLength: number; + } | { type: "multiple a"; count: number } | { type: "x ala x"; word: string } | { type: "proper word"; words: string } @@ -22,3 +35,16 @@ export type TokenTree = } | { type: "comma" } | { type: "punctuation"; punctuation: string }; +/** + * Represents the word used as long container. + */ +export type longContainerHead = + | { + type: "word"; + word: string; + } + | { + type: "combined words"; + first: string; + second: string; + }; diff --git a/src/ucsur.ts b/src/ucsur.ts index a2af63c..928de00 100644 --- a/src/ucsur.ts +++ b/src/ucsur.ts @@ -1,3 +1,6 @@ +/** Module for constants and other helper items for UCSUR. */ + +/** */ export const START_OF_CARTOUCHE = "\u{F1990}"; export const END_OF_CARTOUCHE = "\u{F1991}"; export const COMBINING_CARTOUCHE_EXTENSION = "\u{F1992}"; diff --git a/src/vocabulary.ts b/src/vocabulary.ts index 360d39c..ac4b4ad 100644 --- a/src/vocabulary.ts +++ b/src/vocabulary.ts @@ -1,4 +1,6 @@ -/** Particles. */ +/** Module for accepted words. */ + +/** */ export const PARTICLES = new Set([ "a", "ala", @@ -12,7 +14,6 @@ export const PARTICLES = new Set([ "pi", "taso", ]); -/** Content words. */ export const CONTENT_WORD = new Set([ "akesi", "ala", @@ -143,7 +144,6 @@ export const PREVERB = new Set([ "sona", "wile", ]); -/** Prepositions. */ export const PREPOSITION = new Set([ "kepeken", "lon", From 7c47c8bb14e86695cb585c8c260c3c31ceef81c6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 09:34:15 +0800 Subject: [PATCH 118/738] disallow multiline text --- src/lexer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lexer.ts b/src/lexer.ts index 66aaac6..2371a7d 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -377,6 +377,9 @@ function tokenTrees(includeQuotation: boolean): Lexer> { } /** Parses multiple token trees. */ export function lex(src: string): Output> { + if (/\n/.test(src.trim())) { + return new Output(new UnrecognizedError("multiline text")); + } return spaces().with(all(tokenTree(true))).skip(eol()).parser(src) .map(( { value }, From e626b895ce85b9b51cc5a29ade463eb08c3d4e87 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 09:36:27 +0800 Subject: [PATCH 119/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 736dd4b..7528314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - (Deprecated characters and combiners are not supported) +- The parser will no longer recognize multiline text. Inside update (intended for developers): From cb914906a4bc3cadc53d1b22a3d02e381ec51db9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 09:48:12 +0800 Subject: [PATCH 120/738] small update --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 57ca7e4..e230319 100644 --- a/src/main.ts +++ b/src/main.ts @@ -224,8 +224,8 @@ document.addEventListener("DOMContentLoaded", () => { elements!.translateButton.addEventListener("click", updateOutput); elements!.input.addEventListener("keydown", (event) => { if (event.code === "Enter") { - updateOutput(); event.preventDefault(); + updateOutput(); } }); }); From b18c245832bd8aeaa0219593b0751a441e8a17e3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 11:08:07 +0800 Subject: [PATCH 121/738] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a614dda..fb2ac7f 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,4 @@ With exception to `./src/main.ts`, every source codes in `./src/` are runtime ag Some parts of the code make use of sitelen pona UCSUR characters. To display properly, install an UCSUR font and change the font settings on your editor. [UCSUR Installation guides](https://github.com/neroist/sitelen-pona-ucsur-guide/). -Also, take note that UCSUR characters are two characters wide. Be careful with string and regex manipulation. +Also, take note that UCSUR characters are two characters wide in JavaScript string . Be careful with string and regex manipulation. From 8780a6c344562d3aff95bb68df1df625d6eef687 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 11:14:34 +0800 Subject: [PATCH 122/738] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7528314..5b1d0cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - (Deprecated characters and combiners are not supported) -- The parser will no longer recognize multiline text. +- Multiline text will no longer be recognized. Inside update (intended for developers): From 4f74ccdcedd832639d6a5c39eef1b691dea7e2fb Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 16:48:15 +0800 Subject: [PATCH 123/738] integrate telo misikeke --- .gitignore | 2 + bundle.ts | 8 +- deno.json | 16 +- deps.ts | 1 + dev-deps.ts | 3 +- src/main.ts | 23 ++- telo-misikeke/build.ts | 46 ++++++ telo-misikeke/linku-data.ts | 259 +++++++++++++++++++++++++++++++++ telo-misikeke/telo-misikeke.ts | 16 ++ 9 files changed, 362 insertions(+), 12 deletions(-) create mode 100644 deps.ts create mode 100644 telo-misikeke/build.ts create mode 100644 telo-misikeke/linku-data.ts create mode 100644 telo-misikeke/telo-misikeke.ts diff --git a/.gitignore b/.gitignore index a9b203a..4e37a0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ main.js +telo-misikeke/rules.js +telo-misikeke/Parser.js diff --git a/bundle.ts b/bundle.ts index 1ac6baf..17dc1e9 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,4 +1,4 @@ -import { debounce, emit } from "./dev-deps.ts"; +import { debounce, emit, teloMisikeke } from "./dev-deps.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./main.js", import.meta.url); @@ -9,7 +9,11 @@ async function build(options: emit.BundleOptions): Promise { await Deno.writeTextFile(DESTINATION, code); } if (Deno.args[0] === "build") { + console.log("Building telo misikeke..."); + await teloMisikeke.build(); + console.log("Building main.js..."); await build({ minify: true }); + console.log("Building done!"); } else if (Deno.args[0] === "watch") { const builder = debounce.debounce(async () => { console.log("Starting to build..."); @@ -20,7 +24,7 @@ if (Deno.args[0] === "build") { console.error(error); } }, 500); - const watcher = Deno.watchFs("./src/"); + const watcher = Deno.watchFs(["./src/", "./telo-misikeke"]); builder(); for await (const _ of watcher) { builder(); diff --git a/deno.json b/deno.json index 3d20008..05afe4b 100644 --- a/deno.json +++ b/deno.json @@ -9,9 +9,21 @@ "watch": "deno run --allow-read --allow-write --allow-env --allow-net ./bundle.ts watch" }, "fmt": { - "include": ["./src/**/*.ts", "./bundle.ts", "./test-parser.ts"] + "include": [ + "./src/**/*.ts", + "./telo-misikeke/**/*", + "./bundle.ts", + "./dev-deps.ts", + "./deps.ts" + ] }, "lint": { - "include": ["./src/**/*.ts", "./bundle.ts", "./test-parser.ts"] + "include": [ + "./src/**/*.ts", + "./telo-misikeke/**/*", + "./bundle.ts", + "./dev-deps.ts", + "./deps.ts" + ] } } diff --git a/deps.ts b/deps.ts new file mode 100644 index 0000000..6f1850e --- /dev/null +++ b/deps.ts @@ -0,0 +1 @@ +export * as teloMisikeke from "./telo-misikeke/telo-misikeke.ts"; diff --git a/dev-deps.ts b/dev-deps.ts index 507a252..514173a 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,2 +1,3 @@ export * as emit from "https://deno.land/x/emit@0.38.2/mod.ts"; -export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; \ No newline at end of file +export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; +export * as teloMisikeke from "./telo-misikeke/build.ts"; diff --git a/src/main.ts b/src/main.ts index e230319..6cdb2c1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { CoveredError } from "./error.ts"; import { translate } from "./translator.ts"; import { defaultSettings, RedundancySettings, settings } from "./settings.ts"; +import { teloMisikeke } from "../deps.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; @@ -107,16 +108,24 @@ function outputErrors(errors: Array): void { } function updateOutput(): void { clearOutput(); + const source = elements!.input.value; try { - const translations = translate(elements!.input.value); + const translations = translate(source); if (translations.isError()) { - outputErrors([ - ...new Set( - translations.errors.filter((x) => !(x instanceof CoveredError)).map( - (x) => x.message, + let error: Array = []; + if (settings.useTeloMisikeke) { + error = teloMisikeke.errors(source); + } + if (error.length === 0) { + error = [ + ...new Set( + translations.errors.filter((x) => !(x instanceof CoveredError)).map( + (x) => x.message, + ), ), - ), - ]); + ]; + } + outputErrors(error); } else { const output = [...new Set(translations.output)]; if (settings.randomize) { diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts new file mode 100644 index 0000000..131ff80 --- /dev/null +++ b/telo-misikeke/build.ts @@ -0,0 +1,46 @@ +/** Build codes for telo misikeke source codes. */ + +/** */ +const SOURCE = [ + [ + "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/rules.js?ref_type=heads&inline=false", + "./rules.js", + "build_rules", + ], + [ + "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/Parser.js?ref_type=heads&inline=false", + "./Parser.js", + "ParserWithCallbacks", + ], +] as const; +async function buildFile( + source: string, + destination: string, + exportItem: string, +): Promise { + // fetch source code + let file = await (await fetch(source)).text(); + + // add `export` keyword + file = file.replace( + new RegExp(`function\\s+${exportItem}`), + `export function ${exportItem}`, + ); + + // remove module.export + const seachText = "if ( typeof ( module ) != 'undefined' )"; + const regex = new RegExp( + seachText.replaceAll(/([()])/g, "\\$1").replaceAll(" ", "\\s*") + "[^]*$", + ); + file = file.replace(regex, ""); + + //write the code + await Deno.writeTextFile(new URL(destination, import.meta.url), file); +} +export async function build(): Promise { + await Promise.all( + SOURCE.map(([source, destination, exportItem]) => + buildFile(source, destination, exportItem) + ), + ); +} diff --git a/telo-misikeke/linku-data.ts b/telo-misikeke/linku-data.ts new file mode 100644 index 0000000..fe66f58 --- /dev/null +++ b/telo-misikeke/linku-data.ts @@ -0,0 +1,259 @@ +/** Transformed Linku data required for telo misikeke. */ +export const DATA = [ + ["a", "core"], + ["aka", "obscure"], + ["akesi", "core"], + ["ako", "obscure"], + ["ala", "core"], + ["alasa", "core"], + ["ale", "core"], + ["alente", "obscure"], + ["ali", "uncommon"], + ["alu", "obscure"], + ["anpa", "core"], + ["ante", "core"], + ["anu", "core"], + ["apeja", "uncommon"], + ["awase", "obscure"], + ["awen", "core"], + ["e", "core"], + ["eki", "obscure"], + ["en", "core"], + ["enko", "obscure"], + ["epiku", "uncommon"], + ["esun", "core"], + ["ete", "obscure"], + ["ewe", "obscure"], + ["i", "obscure"], + ["ijo", "core"], + ["ike", "core"], + ["iki", "obscure"], + ["ilo", "core"], + ["insa", "core"], + ["ipi", "obscure"], + ["isipin", "rare"], + ["itomi", "obscure"], + ["jaki", "core"], + ["jaku", "obscure"], + ["jalan", "obscure"], + ["jami", "obscure"], + ["jan", "core"], + ["jans", "obscure"], + ["jasima", "uncommon"], + ["je", "obscure"], + ["jelo", "core"], + ["jo", "core"], + ["jonke", "obscure"], + ["ju", "obscure"], + ["jule", "obscure"], + ["jume", "obscure"], + ["kala", "core"], + ["kalama", "core"], + ["kalamARR", "obscure"], + ["kalijopilale", "obscure"], + ["kama", "core"], + ["kamalawala", "obscure"], + ["kan", "obscure"], + ["kapa", "obscure"], + ["kapesi", "rare"], + ["kasi", "core"], + ["ke", "obscure"], + ["ken", "core"], + ["kepeken", "core"], + ["kepen", "obscure"], + ["kese", "obscure"], + ["ki", "obscure"], + ["kijetesantakalu", "widespread"], + ["kiki", "uncommon"], + ["kili", "core"], + ["kin", "widespread"], + ["kipisi", "widespread"], + ["kisa", "obscure"], + ["kiwen", "core"], + ["ko", "core"], + ["kokosila", "uncommon"], + ["kon", "core"], + ["konsi", "obscure"], + ["konwe", "obscure"], + ["kosan", "obscure"], + ["ku", "widespread"], + ["kule", "core"], + ["kulijo", "obscure"], + ["kulu", "obscure"], + ["kulupu", "core"], + ["kuntu", "obscure"], + ["kute", "core"], + ["kutopoma", "obscure"], + ["la", "core"], + ["lanpan", "common"], + ["lape", "core"], + ["laso", "core"], + ["lawa", "core"], + ["leko", "widespread"], + ["len", "core"], + ["lete", "core"], + ["li", "core"], + ["lijokuku", "obscure"], + ["likujo", "obscure"], + ["lili", "core"], + ["linja", "core"], + ["linluwi", "uncommon"], + ["lipu", "core"], + ["lo", "obscure"], + ["loje", "core"], + ["loka", "obscure"], + ["lokon", "obscure"], + ["lon", "core"], + ["lu", "obscure"], + ["luka", "core"], + ["lukin", "core"], + ["lupa", "core"], + ["ma", "core"], + ["majuna", "uncommon"], + ["mama", "core"], + ["mani", "core"], + ["meli", "widespread"], + ["melome", "obscure"], + ["meso", "common"], + ["mi", "core"], + ["mije", "widespread"], + ["mijomi", "obscure"], + ["misa", "rare"], + ["misikeke", "common"], + ["moku", "core"], + ["moli", "core"], + ["molusa", "obscure"], + ["monsi", "core"], + ["monsuta", "widespread"], + ["mu", "core"], + ["mulapisu", "obscure"], + ["mun", "core"], + ["musi", "core"], + ["mute", "core"], + ["n", "common"], + ["nalanja", "obscure"], + ["namako", "widespread"], + ["nanpa", "core"], + ["nasa", "core"], + ["nasin", "core"], + ["natu", "obscure"], + ["neja", "obscure"], + ["nele", "obscure"], + ["nena", "core"], + ["ni", "core"], + ["nimi", "core"], + ["nimisin", "rare"], + ["nja", "obscure"], + ["noka", "core"], + ["nu", "obscure"], + ["o", "core"], + ["ojuta", "obscure"], + ["oke", "rare"], + ["okepuma", "obscure"], + ["oki", "obscure"], + ["oko", "common"], + ["olin", "core"], + ["omekalike", "obscure"], + ["omekapo", "rare"], + ["omen", "obscure"], + ["ona", "core"], + ["oni", "obscure"], + ["open", "core"], + ["owe", "obscure"], + ["pa", "obscure"], + ["pakala", "core"], + ["pake", "rare"], + ["pali", "core"], + ["palisa", "core"], + ["pan", "core"], + ["pana", "core"], + ["pasila", "obscure"], + ["pata", "obscure"], + ["peta", "obscure"], + ["peto", "obscure"], + ["pi", "core"], + ["pika", "obscure"], + ["pilin", "core"], + ["pimeja", "core"], + ["Pingo", "obscure"], + ["pini", "core"], + ["pipi", "core"], + ["pipo", "obscure"], + ["po", "obscure"], + ["poka", "core"], + ["poki", "core"], + ["polinpin", "obscure"], + ["pomotolo", "obscure"], + ["pona", "core"], + ["poni", "obscure"], + ["powe", "uncommon"], + ["pu", "core"], + ["puwa", "rare"], + ["sama", "core"], + ["samu", "obscure"], + ["san", "obscure"], + ["seli", "core"], + ["selo", "core"], + ["seme", "core"], + ["sewi", "core"], + ["sijelo", "core"], + ["sike", "core"], + ["sikomo", "obscure"], + ["sin", "core"], + ["sina", "core"], + ["sinpin", "core"], + ["sipi", "obscure"], + ["sitelen", "core"], + ["slape", "obscure"], + ["soko", "widespread"], + ["sona", "core"], + ["soto", "obscure"], + ["soweli", "core"], + ["su", "obscure"], + ["suke", "obscure"], + ["suli", "core"], + ["suno", "core"], + ["supa", "core"], + ["sutopatikuna", "obscure"], + ["suwi", "core"], + ["taki", "rare"], + ["tan", "core"], + ["taso", "core"], + ["tawa", "core"], + ["te", "rare"], + ["teje", "obscure"], + ["telo", "core"], + ["ten", "obscure"], + ["tenpo", "core"], + ["to", "rare"], + ["tokana", "obscure"], + ["toki", "core"], + ["toma", "obscure"], + ["tomo", "core"], + ["tonsi", "widespread"], + ["tu", "core"], + ["tuli", "obscure"], + ["u", "obscure"], + ["umesu", "obscure"], + ["unpa", "core"], + ["unu", "rare"], + ["usawi", "rare"], + ["uta", "core"], + ["utala", "core"], + ["wa", "rare"], + ["waleja", "obscure"], + ["walo", "core"], + ["wan", "core"], + ["waso", "core"], + ["wasoweli", "obscure"], + ["wawa", "core"], + ["wawajete", "obscure"], + ["we", "obscure"], + ["weka", "core"], + ["wekama", "obscure"], + ["wi", "obscure"], + ["wile", "core"], + ["wuwojiti", "rare"], + ["yupekosi", "rare"], + ["yutu", "obscure"], +]; diff --git a/telo-misikeke/telo-misikeke.ts b/telo-misikeke/telo-misikeke.ts new file mode 100644 index 0000000..76cc8e9 --- /dev/null +++ b/telo-misikeke/telo-misikeke.ts @@ -0,0 +1,16 @@ +/** Glue code for telo misikeke */ + +import { ParserWithCallbacks } from "./Parser.js"; +import { build_rules } from "./rules.js"; +import { DATA } from "./linku-data.ts"; + +// deno-lint-ignore no-explicit-any +const RULES: any = build_rules(DATA); + +/** Gets all telo misikeke error messages. */ +export function errors(text: string): Array { + return new ParserWithCallbacks(RULES, false) + .tokenize(text) + .filter((token) => RULES[token.ruleName].category === "error") + .map((token) => `"${token.text}" ${RULES[token.ruleName].message}`); +} From 7d725598ae6efbb4ab9210db27bc5dda1468dc14 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 16:49:22 +0800 Subject: [PATCH 124/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b1d0cf..49601e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - (Deprecated characters and combiners are not supported) +- ilo Token can now use telo misikeke for error messages. - Multiline text will no longer be recognized. Inside update (intended for developers): From e74b1840d973a43b370cf294c492a283dcbc9534 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 17:14:50 +0800 Subject: [PATCH 125/738] improve error messages --- src/ast-parser.ts | 24 +++++++++++++----------- src/token-tree.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index d129f15..e0da4cf 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -44,6 +44,7 @@ import { } from "./parser-lib.ts"; import { TokenTree } from "./token-tree.ts"; import { lex } from "./lexer.ts"; +import { describe } from "./token-tree.ts"; export type AstParser = Parser, T>; @@ -79,7 +80,7 @@ function comma(): AstParser { if (tokenTree.type === "comma") { return ","; } else { - throw new UnexpectedError(tokenTree.type, "comma"); + throw new UnexpectedError(describe(tokenTree), "comma"); } }); } @@ -93,7 +94,7 @@ function word(): AstParser { if (tokenTree.type === "word") { return tokenTree.word; } else { - throw new UnexpectedError(tokenTree.type, "word"); + throw new UnexpectedError(describe(tokenTree), "word"); } }); } @@ -103,7 +104,7 @@ function properWords(): AstParser { if (tokenTree.type === "proper word") { return tokenTree.words; } else { - throw new UnexpectedError(tokenTree.type, "proper words"); + throw new UnexpectedError(describe(tokenTree), "proper words"); } }); } @@ -113,7 +114,7 @@ function punctuation(): AstParser { if (tokenTree.type === "punctuation") { return tokenTree.punctuation; } else { - throw new UnexpectedError(tokenTree.type, "punctuation"); + throw new UnexpectedError(describe(tokenTree), "punctuation"); } }); } @@ -148,7 +149,7 @@ function wordUnit(word: Set, description: string): AstParser { if (tokenTree.type === "x ala x") { return { type: "x ala x", word: tokenTree.word } as WordUnit; } else { - throw new UnexpectedError(tokenTree.type, "X ala X"); + throw new UnexpectedError(describe(tokenTree), "X ala X"); } }), wordFrom(word, description).then((word) => @@ -513,14 +514,15 @@ export function quotation(): AstParser { return tokenTree("quotation").flatMapValue((tokenTree) => { if (tokenTree.type === "quotation") { return all(sentence()).skip(eol()).parser(tokenTree.tokenTree).map( - ({ value }) => ({ - sentences: value, - leftMark: tokenTree.leftMark, - rightMark: tokenTree.rightMark, - }), + ({ value }) => + ({ + sentences: value, + leftMark: tokenTree.leftMark, + rightMark: tokenTree.rightMark, + }) as Quotation, ); } else { - return new Output(new UnexpectedError(tokenTree.type, "quotation")); + return new Output(new UnexpectedError(describe(tokenTree), "quotation")); } }); } diff --git a/src/token-tree.ts b/src/token-tree.ts index b903e8d..5cb116a 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -3,6 +3,8 @@ * stores array of token trees hence the name token tree. */ +import { UnreachableError } from "./error.ts"; + /** * Represents token tree. */ @@ -48,3 +50,29 @@ export type longContainerHead = first: string; second: string; }; +export function describe(tokenTree: TokenTree): string { + if (tokenTree.type === "word") { + return `"${tokenTree.word}"`; + } else if (tokenTree.type === "combined words") { + return `"${tokenTree.first} ${tokenTree.second}"`; + } else if ( + tokenTree.type === "long container" || + tokenTree.type === "long space container" + ) { + return "long glyph"; + } else if (tokenTree.type === "multiple a") { + return new Array(tokenTree.count).fill("a").join(" "); + } else if (tokenTree.type === "x ala x") { + return `"${tokenTree.word} ala ${tokenTree.word}"`; + } else if (tokenTree.type === "proper word") { + return tokenTree.words; + } else if (tokenTree.type === "quotation") { + return "quotation"; + } else if (tokenTree.type === "comma") { + return "comma"; + } else if (tokenTree.type === "punctuation") { + return "punctuation mark"; + } else { + throw new UnreachableError(); + } +} From 97e3b75651431f4e499e8512facb1b11bc88d18e Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 17:18:58 +0800 Subject: [PATCH 126/738] small change --- src/token-tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token-tree.ts b/src/token-tree.ts index 5cb116a..45505be 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -65,7 +65,7 @@ export function describe(tokenTree: TokenTree): string { } else if (tokenTree.type === "x ala x") { return `"${tokenTree.word} ala ${tokenTree.word}"`; } else if (tokenTree.type === "proper word") { - return tokenTree.words; + return "proper word or cartouche"; } else if (tokenTree.type === "quotation") { return "quotation"; } else if (tokenTree.type === "comma") { From 0b9ab6d8c5350f31f7ae2b8821a95408dbe010cd Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 17:54:24 +0800 Subject: [PATCH 127/738] update readme --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fb2ac7f..2cab451 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,25 @@ A rule-based Toki Pona to English translator that translates into multiple sente [Try it](https://neverrare.github.io/ilo-token/) +## Dependencies + +You'll need the following in order to run commands: + +- [Deno](https://deno.com/) + ## Building This builds `./main.js` as a minified file ready for production use. -You'll need [Deno](https://deno.com/). Run the following command. - ``` deno task build ``` ## Watching -This builds `./main.js` as a non-minified file with source mapping, intended for testing and debugging. This command also watches the source codes in `./src/` path and rebuilds `./main.js` whenever there are changes. +Before running this command, you'll need to run `deno task build` first. You'll only need to run this command once. -You'll need [Deno](https://deno.com/). Run the following command. +This builds `./main.js` as a non-minified file with source mapping, intended for testing and debugging. This command also watches the source codes in `./src/` path and rebuilds `./main.js` whenever there are changes. ``` deno task watch From 2e575ec2f0038fe31f10c575b738c93f3384d12b Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 28 Mar 2024 18:18:23 +0800 Subject: [PATCH 128/738] small update --- telo-misikeke/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts index 131ff80..45c8729 100644 --- a/telo-misikeke/build.ts +++ b/telo-misikeke/build.ts @@ -30,7 +30,7 @@ async function buildFile( // remove module.export const seachText = "if ( typeof ( module ) != 'undefined' )"; const regex = new RegExp( - seachText.replaceAll(/([()])/g, "\\$1").replaceAll(" ", "\\s*") + "[^]*$", + seachText.replaceAll(/[()]/g, "\\$&").replaceAll(" ", "\\s*") + "[^]*$", ); file = file.replace(regex, ""); From 0336b07e15b96b5c7da3a1dd28211d6c21b68789 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 06:04:59 +0800 Subject: [PATCH 129/738] update build scripts --- telo-misikeke/build.ts | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts index 45c8729..fb2d3d7 100644 --- a/telo-misikeke/build.ts +++ b/telo-misikeke/build.ts @@ -2,30 +2,29 @@ /** */ const SOURCE = [ - [ - "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/rules.js?ref_type=heads&inline=false", - "./rules.js", - "build_rules", - ], - [ - "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/Parser.js?ref_type=heads&inline=false", - "./Parser.js", - "ParserWithCallbacks", - ], + { + source: + "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/rules.js?ref_type=heads&inline=false", + destination: new URL("./rules.js", import.meta.url), + exportItem: "build_rules", + }, + { + source: + "https://gitlab.com/telo-misikeke/telo-misikeke.gitlab.io/-/raw/main/public/Parser.js?ref_type=heads&inline=false", + destination: new URL("./Parser.js", import.meta.url), + exportItem: "ParserWithCallbacks", + }, ] as const; async function buildFile( source: string, - destination: string, + destination: URL, exportItem: string, ): Promise { // fetch source code let file = await (await fetch(source)).text(); // add `export` keyword - file = file.replace( - new RegExp(`function\\s+${exportItem}`), - `export function ${exportItem}`, - ); + file = file.replace(new RegExp(`function\\s+${exportItem}`), "export $&"); // remove module.export const seachText = "if ( typeof ( module ) != 'undefined' )"; @@ -35,12 +34,12 @@ async function buildFile( file = file.replace(regex, ""); //write the code - await Deno.writeTextFile(new URL(destination, import.meta.url), file); + await Deno.writeTextFile(destination, file); } export async function build(): Promise { await Promise.all( - SOURCE.map(([source, destination, exportItem]) => - buildFile(source, destination, exportItem) + SOURCE.map((file) => + buildFile(file.source, file.destination, file.exportItem) ), ); } From cf2d0fe01e97030eb14599fd926dd54d3cfacc5b Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 06:38:06 +0800 Subject: [PATCH 130/738] small update --- bundle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle.ts b/bundle.ts index 17dc1e9..210263b 100644 --- a/bundle.ts +++ b/bundle.ts @@ -24,7 +24,7 @@ if (Deno.args[0] === "build") { console.error(error); } }, 500); - const watcher = Deno.watchFs(["./src/", "./telo-misikeke"]); + const watcher = Deno.watchFs(["./src/", "./telo-misikeke/"]); builder(); for await (const _ of watcher) { builder(); From e611f6ab2f4e14e1a096a1fc6eb516680fd074fe Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 07:09:37 +0800 Subject: [PATCH 131/738] update changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49601e1..3281d84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,14 @@ You may need to force restart the page in order to use the latest version: shift ## 0.2.3 (On development) -- Add icons. +- Implement settings dialog. [More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help). - All possible errors will now be listed. -- Implement settings dialog ([More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help)). +- ilo Token can now use telo misikeke for error messages. You can turn this on from the settings dialog. - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - (Deprecated characters and combiners are not supported) -- ilo Token can now use telo misikeke for error messages. - Multiline text will no longer be recognized. +- Add icons. Inside update (intended for developers): From a57b14d96dd80e16101eb71a66d8a33bbc798089 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 07:18:08 +0800 Subject: [PATCH 132/738] simplify token parsing --- src/ast-parser.ts | 65 +++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e0da4cf..65eb393 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -74,49 +74,36 @@ function tokenTree(description: string): AstParser { } }); } -/** Parses comma. */ -function comma(): AstParser { - return tokenTree("comma").map((tokenTree) => { - if (tokenTree.type === "comma") { - return ","; +function specificTokenTree( + type: T, +): AstParser { + return tokenTree(type).map((tokenTree) => { + if (tokenTree.type === type) { + return tokenTree as TokenTree & { type: T }; } else { - throw new UnexpectedError(describe(tokenTree), "comma"); + throw new UnexpectedError(describe(tokenTree), type); } }); } +/** Parses comma. */ +function comma(): AstParser { + return specificTokenTree("comma").map(() => ","); +} /** Parses an optional comma. */ function optionalComma(): AstParser { return optional(comma()); } /** Parses a toki pona word. */ function word(): AstParser { - return tokenTree("word").map((tokenTree) => { - if (tokenTree.type === "word") { - return tokenTree.word; - } else { - throw new UnexpectedError(describe(tokenTree), "word"); - } - }); + return specificTokenTree("word").map(({ word }) => word); } /** Parses proper words spanning multiple words. */ function properWords(): AstParser { - return tokenTree("proper word").map((tokenTree) => { - if (tokenTree.type === "proper word") { - return tokenTree.words; - } else { - throw new UnexpectedError(describe(tokenTree), "proper words"); - } - }); + return specificTokenTree("proper word").map(({ words }) => words); } /** Parses a toki pona */ function punctuation(): AstParser { - return tokenTree("punctuation").map((tokenTree) => { - if (tokenTree.type === "punctuation") { - return tokenTree.punctuation; - } else { - throw new UnexpectedError(describe(tokenTree), "punctuation"); - } - }); + return specificTokenTree("punctuation").map(({ punctuation }) => punctuation); } /** Parses word only from `set`. */ function wordFrom(set: Set, description: string): AstParser { @@ -511,20 +498,16 @@ function sentence(): AstParser { } /** Parses a quotation. */ export function quotation(): AstParser { - return tokenTree("quotation").flatMapValue((tokenTree) => { - if (tokenTree.type === "quotation") { - return all(sentence()).skip(eol()).parser(tokenTree.tokenTree).map( - ({ value }) => - ({ - sentences: value, - leftMark: tokenTree.leftMark, - rightMark: tokenTree.rightMark, - }) as Quotation, - ); - } else { - return new Output(new UnexpectedError(describe(tokenTree), "quotation")); - } - }); + return specificTokenTree("quotation").flatMapValue((tokenTree) => + all(sentence()).skip(eol()).parser(tokenTree.tokenTree).map( + ({ value }) => + ({ + sentences: value, + leftMark: tokenTree.leftMark, + rightMark: tokenTree.rightMark, + }) as Quotation, + ) + ); } /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { From 53afc095e82b9990c8af0eb9a0c0c35716f38363 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 07:30:09 +0800 Subject: [PATCH 133/738] small update --- src/ast-parser.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 65eb393..5ab462b 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -132,13 +132,9 @@ function wordUnit(word: Set, description: string): AstParser { count: words.length + 1, } as WordUnit)) ), - tokenTree("X ala X").map((tokenTree) => { - if (tokenTree.type === "x ala x") { - return { type: "x ala x", word: tokenTree.word } as WordUnit; - } else { - throw new UnexpectedError(describe(tokenTree), "X ala X"); - } - }), + specificTokenTree("x ala x").map(({ word }) => + ({ type: "x ala x", word }) as WordUnit + ), wordFrom(word, description).then((word) => specificWord("ala").with(specificWord(word)) ).map(( From 02055a80c4d480b0c1682fdcdc5f076e5baa41ed Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 09:25:02 +0800 Subject: [PATCH 134/738] update error messages --- src/main.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 6cdb2c1..e55f8f0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -97,8 +97,7 @@ function outputErrors(errors: Array): void { } else if (errors.length === 1) { elements!.error.innerText = errors[0]; } else { - elements!.error.innerText = - "Multiple errors has been found, but only at least one could be helpful:"; + elements!.error.innerText = "Multiple errors has been found:"; for (const errorMessage of errors) { const list = document.createElement("li"); list.innerText = errorMessage; From fb673ae7fb926a7c4ae36b7ca9ecc270048b5bff Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 09:26:31 +0800 Subject: [PATCH 135/738] small update --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index e55f8f0..7d5ca34 100644 --- a/src/main.ts +++ b/src/main.ts @@ -133,7 +133,7 @@ function updateOutput(): void { outputTranslations(output); } } catch (unreachableError) { - let error; + let error: string; if (unreachableError instanceof Error) { error = unreachableError.message; } else { From 096c4974a9a7448dd09d515ca870d60edb40c55f Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 10:18:45 +0800 Subject: [PATCH 136/738] implement lexer for combined words with more than 3 words --- src/lexer.ts | 10 +++++----- src/token-tree.ts | 8 +++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 2371a7d..79e55db 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -182,12 +182,12 @@ function joiner(): Lexer { function combinedWords(): Lexer { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), - joiner(), - ucsurWord({ allowVariation: false, allowSpace: true }), - ).map(([first, _, second]) => ({ + allAtLeastOnce( + joiner().skip(ucsurWord({ allowVariation: false, allowSpace: true })), + ), + ).map(([first, rest]) => ({ type: "combined words", - first, - second, + words: [first, ...rest], })); } /** Parses a word, either UCSUR or latin. */ diff --git a/src/token-tree.ts b/src/token-tree.ts index 45505be..10b4979 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -12,8 +12,7 @@ export type TokenTree = | { type: "word"; word: string } | { type: "combined words"; - first: string; - second: string; + words: Array; } | { type: "long container"; @@ -47,14 +46,13 @@ export type longContainerHead = } | { type: "combined words"; - first: string; - second: string; + words: Array; }; export function describe(tokenTree: TokenTree): string { if (tokenTree.type === "word") { return `"${tokenTree.word}"`; } else if (tokenTree.type === "combined words") { - return `"${tokenTree.first} ${tokenTree.second}"`; + return `"${tokenTree.words.join(" ")}"`; } else if ( tokenTree.type === "long container" || tokenTree.type === "long space container" From 3d98acb5ded42c8ff5bf925ccc36393088ac4c72 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 10:58:02 +0800 Subject: [PATCH 137/738] make some computation eager --- src/lexer.ts | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 79e55db..1b2627f 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -117,21 +117,19 @@ function variationSelector(): Lexer { function ucsur( settings: { allowVariation: boolean; allowSpace: boolean }, ): Lexer { - return slice(2, "UCSUR character").skip( - lazy(() => { - if (settings.allowVariation) { - return optionalAll(variationSelector()).map(() => null); - } else { - return nothing(); - } - }), - ).skip(lazy(() => { - if (settings.allowSpace) { - return spaces().map(() => null); - } else { - return nothing(); - } - })); + let variationParser: Lexer; + if (settings.allowVariation) { + variationParser = optionalAll(variationSelector()).map(() => null); + } else { + variationParser = nothing(); + } + let spaceParser: Lexer; + if (settings.allowSpace) { + spaceParser = spaces().map(() => null); + } else { + spaceParser = nothing(); + } + return slice(2, "UCSUR character").skip(variationParser).skip(spaceParser); } /** Parses a specific UCSUR character. */ function specificUcsurCharacter( @@ -344,30 +342,32 @@ function quotation(): Lexer { } /** Parses a token tree. */ function tokenTree(includeQuotation: boolean): Lexer { + let quotationParser: Lexer; + if (includeQuotation) { + quotationParser = quotation(); + } else { + quotationParser = error(new CoveredError()); + } + let xAlaXParser: Lexer; + if (settings.xAlaXPartialParsing) { + xAlaXParser = error(new CoveredError()); + } else { + xAlaXParser = xAlaX().map((word) => + ({ type: "x ala x", word }) as TokenTree + ); + } return choiceOnlyOne( punctuation().map((punctuation) => ({ type: "punctuation", punctuation }) as TokenTree ), comma().map(() => ({ type: "comma" }) as TokenTree), - lazy(() => { - if (includeQuotation) { - return quotation(); - } else { - return error(new CoveredError()); - } - }), + quotationParser, choiceOnlyOne(cartouches(), properWords()).map((words) => ({ type: "proper word", words }) as TokenTree ), combinedWords(), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), - lazy(() => { - if (!settings.xAlaXPartialParsing) { - return xAlaX().map((word) => ({ type: "x ala x", word }) as TokenTree); - } else { - return error(new CoveredError()); - } - }), + xAlaXParser, word().map((word) => ({ type: "word", word })), ); } From c442bdd32e46a01884f69d21699a957a7813c9f3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 10:58:15 +0800 Subject: [PATCH 138/738] update document --- src/parser-lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 32dd878..eb8678f 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -81,7 +81,7 @@ export function lookAhead(parser: Parser): Parser { } /** * Lazily evaluates the parser function only when needed. Useful for recursive - * parsers as well as precomputed parsers. + * parsers. */ export function lazy(parser: () => Parser): Parser { return new Parser((src) => parser().parser(src)); From 8c3b237b9db4426ff26e4b39faa1ff29c3c8f93d Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 11:16:55 +0800 Subject: [PATCH 139/738] simplify implementation of sequence combinator --- src/parser-lib.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index eb8678f..0973b1e 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -129,18 +129,15 @@ export function sequence>( ...sequence: { [I in keyof U]: Parser } & { length: U["length"] } ): Parser { // We resorted to using `any` types here, make sure it works properly - return new Parser((src) => - sequence.reduce( - (output, parser) => - output.flatMap(({ value, rest }) => - parser.parser(rest).map(({ value: newValue, rest }) => ({ - value: [...value, newValue], - rest, - })) - ), + // deno-lint-ignore no-explicit-any + return (sequence as Array).reduce( + (parser, newParser) => // deno-lint-ignore no-explicit-any - new Output>([{ value: [], rest: src }]), - ) + parser.then((value: any) => + // deno-lint-ignore no-explicit-any + newParser.map((newValue: any) => [...value, newValue]) + ), + nothing().map(() => []), ); } /** From 60fa70939bf5b3c3743ae41f0afaa73372985aee Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 11:53:02 +0800 Subject: [PATCH 140/738] update choiceOnlyOne combinator --- src/parser-lib.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 0973b1e..6000627 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -69,6 +69,10 @@ export class Parser { export function error(error: OutputError): Parser { return new Parser(() => new Output(error)); } +/** Parser that always outputs an empty output. */ +export function empty(): Parser { + return new Parser(() => new Output()); +} /** Parses nothing and leaves the source string intact. */ export function nothing(): Parser { return new Parser((src) => new Output([{ value: null, rest: src }])); @@ -102,15 +106,20 @@ export function choice(...choices: Array>): Parser { export function choiceOnlyOne( ...choices: Array> ): Parser { - return new Parser((src) => - choices.reduce((output, parser) => { + if (choices.length === 0) { + return empty(); + } else { + const first = choices[0]; + const rest = choiceOnlyOne(...choices.slice(1)); + return new Parser((src) => { + const output = first.parser(src); if (output.isError()) { - return Output.concat(output, parser.parser(src)); + return Output.concat(output, rest.parser(src)); } else { return output; } - }, new Output>()) - ); + }); + } } /** Combines `parser` and the `nothing` parser, and output `null | T`. */ export function optional(parser: Parser): Parser { From 251b9be656f9a5405a9e1ce6c7e1905e35b31f8f Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 12:09:40 +0800 Subject: [PATCH 141/738] simplify implementation of choiceOnlyOne combinator --- src/parser-lib.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 6000627..f08e25f 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -106,20 +106,15 @@ export function choice(...choices: Array>): Parser { export function choiceOnlyOne( ...choices: Array> ): Parser { - if (choices.length === 0) { - return empty(); - } else { - const first = choices[0]; - const rest = choiceOnlyOne(...choices.slice(1)); - return new Parser((src) => { - const output = first.parser(src); + return choices.reduce((parser, newParser) => + new Parser((src) => { + const output = parser.parser(src); if (output.isError()) { - return Output.concat(output, rest.parser(src)); + return Output.concat(output, newParser.parser(src)); } else { return output; } - }); - } + }), empty()); } /** Combines `parser` and the `nothing` parser, and output `null | T`. */ export function optional(parser: Parser): Parser { From b86dce9f3e04437c398589b02399abcd644f624f Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 12:41:25 +0800 Subject: [PATCH 142/738] make use of reduce right --- src/parser-lib.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index f08e25f..f83ede0 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -106,7 +106,7 @@ export function choice(...choices: Array>): Parser { export function choiceOnlyOne( ...choices: Array> ): Parser { - return choices.reduce((parser, newParser) => + return choices.reduceRight((newParser, parser) => new Parser((src) => { const output = parser.parser(src); if (output.isError()) { @@ -134,12 +134,12 @@ export function sequence>( ): Parser { // We resorted to using `any` types here, make sure it works properly // deno-lint-ignore no-explicit-any - return (sequence as Array).reduce( - (parser, newParser) => + return (sequence as Array).reduceRight( + (newParser, parser) => // deno-lint-ignore no-explicit-any parser.then((value: any) => // deno-lint-ignore no-explicit-any - newParser.map((newValue: any) => [...value, newValue]) + newParser.map((newValue: any) => [value, ...newValue]) ), nothing().map(() => []), ); From 1f64fbb134db0b683811fca648489af10185d9d8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 13:10:41 +0800 Subject: [PATCH 143/738] avoid recalculation of parsers --- src/ast-parser.ts | 9 ++++----- src/lexer.ts | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 5ab462b..cb8a251 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -505,13 +505,12 @@ export function quotation(): AstParser { ) ); } +const FULL_PARSER = allAtLeastOnce(sentence()) + .skip(eol()) + .filter(filter(SENTENCES_RULE)); /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { return lex(src).flatMap((src) => - allAtLeastOnce(sentence()).skip(eol()) - .filter( - filter(SENTENCES_RULE), - ).parser(src) - .map(({ value }) => value) + FULL_PARSER.parser(src).map(({ value }) => value) ); } diff --git a/src/lexer.ts b/src/lexer.ts index 1b2627f..af1457e 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -375,12 +375,13 @@ function tokenTree(includeQuotation: boolean): Lexer { function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } +const FULL_PARSER = spaces().with(all(tokenTree(true))).skip(eol()); /** Parses multiple token trees. */ export function lex(src: string): Output> { if (/\n/.test(src.trim())) { return new Output(new UnrecognizedError("multiline text")); } - return spaces().with(all(tokenTree(true))).skip(eol()).parser(src) + return FULL_PARSER.parser(src) .map(( { value }, ) => value); From 99af9686fb256d9578de38800ce6bd585ddb9ef4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 29 Mar 2024 18:28:41 +0800 Subject: [PATCH 144/738] make class properties readonly --- src/output.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/output.ts b/src/output.ts index d724f64..4ef163e 100644 --- a/src/output.ts +++ b/src/output.ts @@ -4,9 +4,9 @@ import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { /** Represents possibilities, considered error when the array is empty. */ - output: Array; + readonly output: Array; /** A list of all aggregated errors. */ - errors: Array = []; + readonly errors: Array = []; constructor(output?: undefined | null | Array | OutputError) { if (Array.isArray(output)) { this.output = output; @@ -19,7 +19,9 @@ export class Output { } private static newErrors(errors: Array): Output { const output = new Output(); - output.errors = errors; + for (const error of errors) { + output.pushError(error); + } return output; } private pushError(error: OutputError): void { @@ -64,7 +66,7 @@ export class Output { */ map(mapper: (value: T) => U): Output { if (this.isError()) { - return Output.newErrors(this.errors.slice()); + return Output.newErrors(this.errors); } const wholeOutput = new Output(); for (const value of this.output) { @@ -86,7 +88,7 @@ export class Output { */ flatMap(mapper: (value: T) => Output): Output { if (this.isError()) { - return Output.newErrors(this.errors.slice()); + return Output.newErrors(this.errors); } const wholeOutput = new Output(); for (const value of this.output) wholeOutput.append(mapper(value)); From f296e99438f1e7df8873c2d9116708378504f969 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:00:47 +0800 Subject: [PATCH 145/738] implement parsers for combined words --- src/ast-parser.ts | 98 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index cb8a251..60a22dc 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -48,6 +48,13 @@ import { describe } from "./token-tree.ts"; export type AstParser = Parser, T>; +function nullableAsArray(value?: null | undefined | T): Array { + if (value == null) { + return []; + } else { + return [value]; + } +} /** Takes all parsers and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. @@ -145,6 +152,37 @@ function wordUnit(word: Set, description: string): AstParser { ) => ({ type: "default", word } as WordUnit)), ).filter(filter(WORD_UNIT_RULES)); } +function binaryWords( + word: Set, + description: string, +): AstParser<[string, string]> { + return specificTokenTree("combined words").map(({ words }) => { + if (words.length > 2) { + throw new UnrecognizedError(`combined words of ${words.length} words`); + } else if (!word.has(words[0])) { + throw new UnrecognizedError(`"${words[0]}" as ${description}`); + } else if (!CONTENT_WORD.has(words[1])) { + throw new UnrecognizedError(`"${words[1]}" as content word`); + } else { + return words as [string, string]; + } + }); +} +function optionalCombined( + word: Set, + description: string, +): AstParser<[WordUnit, null | string]> { + return choice( + wordUnit(word, description).map((wordUnit) => + [wordUnit, null] as [WordUnit, null | string] + ), + binaryWords(word, description).map(( + [first, second], + ) => + [{ type: "default", word: first }, second] as [WordUnit, null | string] + ), + ); +} /** Parses number words in order. */ function number(): AstParser> { return sequence( @@ -220,16 +258,28 @@ function phrase(): AstParser { headWord: { type: "numbers", numbers }, modifiers, } as Phrase)), + binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => + ({ + type: "preverb", + preverb: { type: "default", word: preverb }, + modifiers: [], + phrase: { + type: "default", + headWord: { type: "default", word: phrase }, + modifiers: [], + }, + }) as Phrase + ), sequence( - wordUnit(PREVERB, "preverb"), + optionalCombined(PREVERB, "preverb"), lazy(modifiers), lazy(phrase), ).map(( - [preverb, modifiers, phrase], + [[preverb, modifier], modifiers, phrase], ) => ({ type: "preverb", preverb, - modifiers, + modifiers: [...nullableAsArray(modifier), ...modifiers], phrase, } as Phrase)), lazy(preposition).map((preposition) => ({ @@ -237,12 +287,12 @@ function phrase(): AstParser { preposition, } as Phrase)), sequence( - wordUnit(CONTENT_WORD, "headword"), + optionalCombined(CONTENT_WORD, "headword"), lazy(modifiers), - ).map(([headWord, modifiers]) => ({ + ).map(([[headWord, modifier], modifiers]) => ({ type: "default", headWord, - modifiers, + modifiers: [...nullableAsArray(modifier), ...modifiers], } as Phrase)), quotation().map(( quotation, @@ -306,15 +356,33 @@ function subjectPhrases(): AstParser { } /** Parses prepositional phrase. */ function preposition(): AstParser { - return sequence( - wordUnit(PREPOSITION, "preposition"), - modifiers(), - nestedPhrases(["anu"]), - ).map(([preposition, modifiers, phrases]) => ({ - preposition, - modifiers, - phrases, - })).filter(filter(PREPOSITION_RULE)); + return choice( + binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => + ({ + preposition: { type: "default", word: preposition }, + modifiers: [], + phrases: { + type: "single", + phrase: { + type: "default", + headWord: { type: "default", word: phrase }, + modifiers: [], + }, + }, + }) as Preposition + ), + sequence( + optionalCombined(PREPOSITION, "preposition"), + modifiers(), + nestedPhrases(["anu"]), + ).map(([[preposition, modifier], modifiers, phrases]) => + ({ + preposition, + modifiers: [...nullableAsArray(modifier), ...modifiers], + phrases, + }) as Preposition + ), + ).filter(filter(PREPOSITION_RULE)); } /** * Parses associated predicates whose predicates only uses top level operator. From 9dc48166c924996b0083b08ea0d2b599a72529b1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:03:59 +0800 Subject: [PATCH 146/738] rename combined words to combined glyphs --- src/ast-parser.ts | 4 ++-- src/lexer.ts | 8 ++++---- src/token-tree.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 60a22dc..ed2ebf1 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -156,9 +156,9 @@ function binaryWords( word: Set, description: string, ): AstParser<[string, string]> { - return specificTokenTree("combined words").map(({ words }) => { + return specificTokenTree("combined glyphs").map(({ words }) => { if (words.length > 2) { - throw new UnrecognizedError(`combined words of ${words.length} words`); + throw new UnrecognizedError(`combined glyphs of ${words.length} words`); } else if (!word.has(words[0])) { throw new UnrecognizedError(`"${words[0]}" as ${description}`); } else if (!CONTENT_WORD.has(words[1])) { diff --git a/src/lexer.ts b/src/lexer.ts index af1457e..cf7f8e2 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -176,15 +176,15 @@ function joiner(): Lexer { }), ); } -/** Parses combined words. */ -function combinedWords(): Lexer { +/** Parses combined glyphs. */ +function combinedGlyphs(): Lexer { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), allAtLeastOnce( joiner().skip(ucsurWord({ allowVariation: false, allowSpace: true })), ), ).map(([first, rest]) => ({ - type: "combined words", + type: "combined glyphs", words: [first, ...rest], })); } @@ -365,7 +365,7 @@ function tokenTree(includeQuotation: boolean): Lexer { choiceOnlyOne(cartouches(), properWords()).map((words) => ({ type: "proper word", words }) as TokenTree ), - combinedWords(), + combinedGlyphs(), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), xAlaXParser, word().map((word) => ({ type: "word", word })), diff --git a/src/token-tree.ts b/src/token-tree.ts index 10b4979..5b9085a 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -11,7 +11,7 @@ import { UnreachableError } from "./error.ts"; export type TokenTree = | { type: "word"; word: string } | { - type: "combined words"; + type: "combined glyphs"; words: Array; } | { @@ -51,8 +51,8 @@ export type longContainerHead = export function describe(tokenTree: TokenTree): string { if (tokenTree.type === "word") { return `"${tokenTree.word}"`; - } else if (tokenTree.type === "combined words") { - return `"${tokenTree.words.join(" ")}"`; + } else if (tokenTree.type === "combined glyphs") { + return `combined glyphs "${tokenTree.words.join(" ")}"`; } else if ( tokenTree.type === "long container" || tokenTree.type === "long space container" From 52f5fe824c18d62e47c773646eeba16f8be04642 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:09:01 +0800 Subject: [PATCH 147/738] rename token tree names --- src/token-tree.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/token-tree.ts b/src/token-tree.ts index 5b9085a..6a8188c 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -15,16 +15,20 @@ export type TokenTree = words: Array; } | { - type: "long container"; + type: "long glyph"; before: Array; - word: longContainerHead; + word: longGlyphHead; after: Array; } | { - type: "long space container"; - word: longContainerHead; + type: "long glyph space"; + word: longGlyphHead; spaceLength: number; } + | { + type: "long lon"; + words: Array; + } | { type: "multiple a"; count: number } | { type: "x ala x"; word: string } | { type: "proper word"; words: string } @@ -37,9 +41,9 @@ export type TokenTree = | { type: "comma" } | { type: "punctuation"; punctuation: string }; /** - * Represents the word used as long container. + * Represents the word used as long glyph. */ -export type longContainerHead = +export type longGlyphHead = | { type: "word"; word: string; @@ -54,8 +58,8 @@ export function describe(tokenTree: TokenTree): string { } else if (tokenTree.type === "combined glyphs") { return `combined glyphs "${tokenTree.words.join(" ")}"`; } else if ( - tokenTree.type === "long container" || - tokenTree.type === "long space container" + tokenTree.type === "long glyph" || + tokenTree.type === "long glyph space" ) { return "long glyph"; } else if (tokenTree.type === "multiple a") { From 29aa3a3108e649e1a35246ed464f6632024aae7f Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:15:35 +0800 Subject: [PATCH 148/738] fix bug in combined glyphs parser --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index cf7f8e2..0406f1a 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -181,7 +181,7 @@ function combinedGlyphs(): Lexer { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), allAtLeastOnce( - joiner().skip(ucsurWord({ allowVariation: false, allowSpace: true })), + joiner().with(ucsurWord({ allowVariation: false, allowSpace: true })), ), ).map(([first, rest]) => ({ type: "combined glyphs", From 6c782acad413bf93a14de5401bef4a57cc7278c8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:22:15 +0800 Subject: [PATCH 149/738] fix bug --- src/ast-parser.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index ed2ebf1..fb532e4 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -55,6 +55,15 @@ function nullableAsArray(value?: null | undefined | T): Array { return [value]; } } +function wordAsModifier(word: string): Modifier { + return { + type: "default", + word: { + type: "default", + word, + }, + }; +} /** Takes all parsers and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. @@ -279,7 +288,10 @@ function phrase(): AstParser { ) => ({ type: "preverb", preverb, - modifiers: [...nullableAsArray(modifier), ...modifiers], + modifiers: [ + ...nullableAsArray(modifier).map(wordAsModifier), + ...modifiers, + ], phrase, } as Phrase)), lazy(preposition).map((preposition) => ({ @@ -292,7 +304,10 @@ function phrase(): AstParser { ).map(([[headWord, modifier], modifiers]) => ({ type: "default", headWord, - modifiers: [...nullableAsArray(modifier), ...modifiers], + modifiers: [ + ...nullableAsArray(modifier).map(wordAsModifier), + ...modifiers, + ], } as Phrase)), quotation().map(( quotation, @@ -378,7 +393,10 @@ function preposition(): AstParser { ).map(([[preposition, modifier], modifiers, phrases]) => ({ preposition, - modifiers: [...nullableAsArray(modifier), ...modifiers], + modifiers: [ + ...nullableAsArray(modifier).map(wordAsModifier), + ...modifiers, + ], phrases, }) as Preposition ), From e422d5baba51075430f3f133cbe3a73307bba68d Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 14:48:55 +0800 Subject: [PATCH 150/738] simplify combined word parser --- src/ast-parser.ts | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index fb532e4..a9090b4 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -48,22 +48,6 @@ import { describe } from "./token-tree.ts"; export type AstParser = Parser, T>; -function nullableAsArray(value?: null | undefined | T): Array { - if (value == null) { - return []; - } else { - return [value]; - } -} -function wordAsModifier(word: string): Modifier { - return { - type: "default", - word: { - type: "default", - word, - }, - }; -} /** Takes all parsers and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. @@ -180,15 +164,18 @@ function binaryWords( function optionalCombined( word: Set, description: string, -): AstParser<[WordUnit, null | string]> { +): AstParser<[WordUnit, Array]> { return choice( wordUnit(word, description).map((wordUnit) => - [wordUnit, null] as [WordUnit, null | string] + [wordUnit, []] as [WordUnit, Array] ), binaryWords(word, description).map(( [first, second], ) => - [{ type: "default", word: first }, second] as [WordUnit, null | string] + [{ type: "default", word: first }, [{ + type: "default", + word: { type: "default", word: second }, + }]] as [WordUnit, Array] ), ); } @@ -289,7 +276,7 @@ function phrase(): AstParser { type: "preverb", preverb, modifiers: [ - ...nullableAsArray(modifier).map(wordAsModifier), + ...modifier, ...modifiers, ], phrase, @@ -305,7 +292,7 @@ function phrase(): AstParser { type: "default", headWord, modifiers: [ - ...nullableAsArray(modifier).map(wordAsModifier), + ...modifier, ...modifiers, ], } as Phrase)), @@ -394,7 +381,7 @@ function preposition(): AstParser { ({ preposition, modifiers: [ - ...nullableAsArray(modifier).map(wordAsModifier), + ...modifier, ...modifiers, ], phrases, From 7e005a3f98bceaea53f11d40b55046e4da8886e9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:06:05 +0800 Subject: [PATCH 151/738] add note --- src/lexer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lexer.ts b/src/lexer.ts index 0406f1a..22f1456 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -237,6 +237,7 @@ function punctuation(): Lexer { match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => punctuation ), + // NOTE: maybe these are unnecessary specificUcsurCharacter("󱦜", "middle dot", { allowVariation: true, allowSpace: true, From 36f65ced409a8ba39f0f5544c4aa054171b64ba8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:07:33 +0800 Subject: [PATCH 152/738] fix space allowance in combined glyphs --- src/lexer.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 22f1456..b35d413 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -181,12 +181,13 @@ function combinedGlyphs(): Lexer { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), allAtLeastOnce( - joiner().with(ucsurWord({ allowVariation: false, allowSpace: true })), + joiner().with(ucsurWord({ allowVariation: false, allowSpace: false })), ), - ).map(([first, rest]) => ({ - type: "combined glyphs", - words: [first, ...rest], - })); + ).skip(spaces()) + .map(([first, rest]) => ({ + type: "combined glyphs", + words: [first, ...rest], + })); } /** Parses a word, either UCSUR or latin. */ function word(): Lexer { From 875d68875dd03e136027369e3340d0dd5d5a939f Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:11:29 +0800 Subject: [PATCH 153/738] remove unneeded lazy --- src/lexer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index b35d413..5892d4e 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -17,7 +17,6 @@ import { allAtLeastOnce, choiceOnlyOne, error, - lazy, optionalAll, Parser, sequence as rawSequence, @@ -318,7 +317,7 @@ function cartouches(): Lexer { function quotation(): Lexer { return sequence( openQuotationMark(), - lazy(() => tokenTrees(false)), + tokenTrees(false), closeQuotationMark(), ).map(([leftMark, tokenTree, rightMark]) => { if (leftMark === '"' || leftMark === "“") { From 8e351a8806178654029302a42012af15e2a50917 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:17:09 +0800 Subject: [PATCH 154/738] avoid unnecessary recalculation in quotation parser --- src/ast-parser.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index a9090b4..392f778 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -565,10 +565,13 @@ function sentence(): AstParser { punctuation, })); } +const FULL_PARSER = allAtLeastOnce(sentence()) + .skip(eol()) + .filter(filter(SENTENCES_RULE)); /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => - all(sentence()).skip(eol()).parser(tokenTree.tokenTree).map( + FULL_PARSER.parser(tokenTree.tokenTree).map( ({ value }) => ({ sentences: value, @@ -578,9 +581,6 @@ export function quotation(): AstParser { ) ); } -const FULL_PARSER = allAtLeastOnce(sentence()) - .skip(eol()) - .filter(filter(SENTENCES_RULE)); /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { return lex(src).flatMap((src) => From f10ae358b61cd89643557925427c8fd11e19c910 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:18:38 +0800 Subject: [PATCH 155/738] remove unused import --- src/ast-parser.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 392f778..e24a14d 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -32,7 +32,6 @@ import { WORD_UNIT_RULES, } from "./filter.ts"; import { - all, allAtLeastOnce, choice, lazy, From ee7520f60eb13655b8fbdee04fb1b89b9890e0c4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:20:41 +0800 Subject: [PATCH 156/738] update quotation parser --- src/ast-parser.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e24a14d..5c89dca 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -32,6 +32,7 @@ import { WORD_UNIT_RULES, } from "./filter.ts"; import { + all, allAtLeastOnce, choice, lazy, @@ -564,13 +565,13 @@ function sentence(): AstParser { punctuation, })); } -const FULL_PARSER = allAtLeastOnce(sentence()) +const FULL_INNER_QUOTATION_PARSER = all(sentence()) .skip(eol()) .filter(filter(SENTENCES_RULE)); /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => - FULL_PARSER.parser(tokenTree.tokenTree).map( + FULL_INNER_QUOTATION_PARSER.parser(tokenTree.tokenTree).map( ({ value }) => ({ sentences: value, @@ -580,6 +581,9 @@ export function quotation(): AstParser { ) ); } +const FULL_PARSER = allAtLeastOnce(sentence()) + .skip(eol()) + .filter(filter(SENTENCES_RULE)); /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { return lex(src).flatMap((src) => From d4a61a1ec6f073258a22ed55ff166e67f81eb725 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:36:14 +0800 Subject: [PATCH 157/738] small update --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index 5892d4e..c54e246 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -376,7 +376,7 @@ function tokenTree(includeQuotation: boolean): Lexer { function tokenTrees(includeQuotation: boolean): Lexer> { return all(tokenTree(includeQuotation)); } -const FULL_PARSER = spaces().with(all(tokenTree(true))).skip(eol()); +const FULL_PARSER = spaces().with(tokenTrees(true)).skip(eol()); /** Parses multiple token trees. */ export function lex(src: string): Output> { if (/\n/.test(src.trim())) { From cad406678d518c8958d9f5727ebcbcef30d6abd4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:37:37 +0800 Subject: [PATCH 158/738] allow space on cartouche --- src/lexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index c54e246..102acfa 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -295,12 +295,12 @@ function cartouche(): Lexer { return sequence( specificUcsurCharacter(START_OF_CARTOUCHE, "start of cartouche", { allowVariation: false, - allowSpace: false, + allowSpace: true, }), allAtLeastOnce(cartoucheElement()), specificUcsurCharacter(END_OF_CARTOUCHE, "end of cartouche", { allowVariation: false, - allowSpace: false, + allowSpace: true, }), ).map( ([_, words, _1]) => { From fe17190008ba5e2bbab05c390ea07b97ea4daf48 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 15:39:08 +0800 Subject: [PATCH 159/738] force letters inside cartouche to be lowercase aside from the first --- src/lexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 102acfa..b116c15 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -285,9 +285,9 @@ function cartoucheElement(): Lexer { return morae.slice(0, count).join(""); }), singleUcsurWord().map((word) => word[0]), - match(/([a-zA-Z])\s*/, "Latin letter").map(( + match(/([a-zA-Z]+)\s*/, "Latin letter").map(( [_, letter], - ) => letter), + ) => letter.toLowerCase()), ); } /** Parses a single cartouche. */ From a1e1b6d3401b6bd5b41c38de8428ee5ca25fcafb Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 16:49:52 +0800 Subject: [PATCH 160/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3281d84..097f5bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ You may need to force restart the page in order to use the latest version: shift - ilo Token can now use telo misikeke for error messages. You can turn this on from the settings dialog. - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama + - Combined glyphs - (Deprecated characters and combiners are not supported) - Multiline text will no longer be recognized. - Add icons. From 55611c88fb7887f9f54905c58cbaf412e9ec2d9b Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 30 Mar 2024 16:55:56 +0800 Subject: [PATCH 161/738] small update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cab451..facc89c 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,4 @@ With exception to `./src/main.ts`, every source codes in `./src/` are runtime ag Some parts of the code make use of sitelen pona UCSUR characters. To display properly, install an UCSUR font and change the font settings on your editor. [UCSUR Installation guides](https://github.com/neroist/sitelen-pona-ucsur-guide/). -Also, take note that UCSUR characters are two characters wide in JavaScript string . Be careful with string and regex manipulation. +Also, take note that UCSUR characters are two characters wide in JavaScript string. Be careful with string and regex manipulation. From b70513b616473aa2e2dc88e9d45a307fca86136b Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 12:46:08 +0800 Subject: [PATCH 162/738] simplify build script --- telo-misikeke/build.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts index fb2d3d7..3c39e69 100644 --- a/telo-misikeke/build.ts +++ b/telo-misikeke/build.ts @@ -23,15 +23,8 @@ async function buildFile( // fetch source code let file = await (await fetch(source)).text(); - // add `export` keyword - file = file.replace(new RegExp(`function\\s+${exportItem}`), "export $&"); - - // remove module.export - const seachText = "if ( typeof ( module ) != 'undefined' )"; - const regex = new RegExp( - seachText.replaceAll(/[()]/g, "\\$&").replaceAll(" ", "\\s*") + "[^]*$", - ); - file = file.replace(regex, ""); + // add `export` + file = file + `export{${exportItem}};`; //write the code await Deno.writeTextFile(destination, file); From f04177ba93f9d2a7872f7c210918b8fcc4c144b4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 12:48:59 +0800 Subject: [PATCH 163/738] change script type to module --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 87dfce4..dc18802 100644 --- a/index.html +++ b/index.html @@ -129,4 +129,4 @@

        - + From 5573994b574000c47502895ca426929385440bf5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 12:55:38 +0800 Subject: [PATCH 164/738] revert --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index dc18802..87dfce4 100644 --- a/index.html +++ b/index.html @@ -129,4 +129,4 @@

        - + From 1166c71e0291b7034234409ab6ff608b77b16952 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 12:59:16 +0800 Subject: [PATCH 165/738] change output type to "classic" --- bundle.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bundle.ts b/bundle.ts index 210263b..43949fd 100644 --- a/bundle.ts +++ b/bundle.ts @@ -12,13 +12,16 @@ if (Deno.args[0] === "build") { console.log("Building telo misikeke..."); await teloMisikeke.build(); console.log("Building main.js..."); - await build({ minify: true }); + await build({ minify: true, type: "classic" }); console.log("Building done!"); } else if (Deno.args[0] === "watch") { const builder = debounce.debounce(async () => { console.log("Starting to build..."); try { - await build({ compilerOptions: { inlineSourceMap: true } }); + await build({ + compilerOptions: { inlineSourceMap: true }, + type: "classic", + }); console.log("Building done!"); } catch (error) { console.error(error); From 9e97099011bdcecf5d96577aa764007e32dcfd00 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 13:19:39 +0800 Subject: [PATCH 166/738] change position of script tag --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 87dfce4..a86c471 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,7 @@ + ilo Token @@ -129,4 +130,3 @@

        - From 8b7c37fada253c17eef0a629408ca171506b1964 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 13:23:21 +0800 Subject: [PATCH 167/738] small update --- src/ast-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 5c89dca..d07faeb 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -565,13 +565,13 @@ function sentence(): AstParser { punctuation, })); } -const FULL_INNER_QUOTATION_PARSER = all(sentence()) +const INNER_QUOTATION_PARSER = all(sentence()) .skip(eol()) .filter(filter(SENTENCES_RULE)); /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => - FULL_INNER_QUOTATION_PARSER.parser(tokenTree.tokenTree).map( + INNER_QUOTATION_PARSER.parser(tokenTree.tokenTree).map( ({ value }) => ({ sentences: value, From eabb859f837d682b03f0f45f968e992b318dbb3b Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 14:43:03 +0800 Subject: [PATCH 168/738] implement long glyph lexer --- src/lexer.ts | 158 +++++++++++++++++++++++++++++++++++++++++----- src/token-tree.ts | 19 ++---- 2 files changed, 146 insertions(+), 31 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index b116c15..d165a47 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -26,9 +26,13 @@ import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; import { END_OF_CARTOUCHE, + END_OF_LONG_GLYPH, + END_OF_REVERSE_LONG_GLYPH, SCALING_JOINER, STACKING_JOINER, START_OF_CARTOUCHE, + START_OF_LONG_GLYPH, + START_OF_REVERSE_LONG_GLYPH, UCSUR_TO_LATIN, } from "./ucsur.ts"; import { nothing } from "./parser-lib.ts"; @@ -146,9 +150,9 @@ function specificUcsurCharacter( } /** Parses UCSUR word. */ function ucsurWord( - settings: { allowVariation: boolean; allowSpace: boolean }, + trailingSettings: { allowVariation: boolean; allowSpace: boolean }, ): Lexer { - return ucsur(settings).map((word) => { + return ucsur(trailingSettings).map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { throw new CoveredError(); @@ -176,17 +180,14 @@ function joiner(): Lexer { ); } /** Parses combined glyphs. */ -function combinedGlyphs(): Lexer { +function combinedGlyphs(): Lexer> { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), allAtLeastOnce( joiner().with(ucsurWord({ allowVariation: false, allowSpace: false })), ), ).skip(spaces()) - .map(([first, rest]) => ({ - type: "combined glyphs", - words: [first, ...rest], - })); + .map(([first, rest]) => [first, ...rest]); } /** Parses a word, either UCSUR or latin. */ function word(): Lexer { @@ -314,10 +315,12 @@ function cartouches(): Lexer { return allAtLeastOnce(cartouche()).map((words) => words.join(" ")); } /** Parses quotation. */ -function quotation(): Lexer { +function quotation( + allowLongGlyph: boolean, +): Lexer { return sequence( openQuotationMark(), - tokenTrees(false), + tokenTrees({ allowQuotation: false, allowLongGlyph }), closeQuotationMark(), ).map(([leftMark, tokenTree, rightMark]) => { if (leftMark === '"' || leftMark === "“") { @@ -341,14 +344,130 @@ function quotation(): Lexer { }; }); } +function longContainer( + left: string, + right: string, + inside: Lexer, +): Lexer { + const description: { [character: string]: string } = { + [START_OF_LONG_GLYPH]: "start of long glyph", + [END_OF_LONG_GLYPH]: "end of long glyph", + [START_OF_REVERSE_LONG_GLYPH]: "start of reverse long glyph", + [END_OF_REVERSE_LONG_GLYPH]: "end of reverse long glyph", + }; + return sequence( + specificUcsurCharacter(left, description[left], { + allowSpace: true, + allowVariation: false, + }), + inside, + specificUcsurCharacter(right, description[right], { + allowSpace: true, + allowVariation: false, + }), + ).map(([_, inside, _1]) => inside); +} +function longCharacterContainer( + allowQuotation: boolean, + left: string, + right: string, +): Lexer> { + return longContainer( + left, + right, + allAtLeastOnce(tokenTree({ allowQuotation, allowLongGlyph: false })), + ); +} +function longSpaceContainer(): Lexer { + return longContainer( + START_OF_LONG_GLYPH, + END_OF_LONG_GLYPH, + match(/\s+/, "space").map(([space]) => space.length), + ); +} +function longGlyphHead(): Lexer> { + return choiceOnlyOne( + combinedGlyphs(), + ucsurWord({ allowSpace: false, allowVariation: false }).map(( + word, + ) => [word]), + ); +} +function characterLongGlyph( + allowQuotation: boolean, +): Lexer { + return sequence( + optionalAll( + longCharacterContainer( + allowQuotation, + START_OF_LONG_GLYPH, + END_OF_LONG_GLYPH, + ), + ), + longGlyphHead(), + optionalAll( + longCharacterContainer( + allowQuotation, + START_OF_REVERSE_LONG_GLYPH, + END_OF_REVERSE_LONG_GLYPH, + ), + ), + ).map(([beforeNull, words, afterNull]) => { + const before = beforeNull ?? []; + const after = afterNull ?? []; + if (before.length === 0 && after.length === 0) { + throw new CoveredError(); + } + return { + type: "long glyph", + before: before ?? [], + words, + after: after ?? [], + }; + }); +} +function longSpaceGlyph(): Lexer { + return sequence( + longGlyphHead(), + longSpaceContainer(), + ).map(([words, spaceLength]) => ({ + type: "long glyph space", + words, + spaceLength, + })); +} +function longLon( + allowQuotation: boolean, +): Lexer> { + return longCharacterContainer( + allowQuotation, + START_OF_REVERSE_LONG_GLYPH, + END_OF_LONG_GLYPH, + ); +} +function longGlyph(allowQuotation: boolean): Lexer { + return choiceOnlyOne( + characterLongGlyph(allowQuotation) as Lexer, + longSpaceGlyph() as Lexer, + longLon(allowQuotation).map((words) => ({ type: "underline lon", words })), + ); +} /** Parses a token tree. */ -function tokenTree(includeQuotation: boolean): Lexer { +function tokenTree( + nestingSettings: { allowQuotation: boolean; allowLongGlyph: boolean }, +): Lexer { let quotationParser: Lexer; - if (includeQuotation) { - quotationParser = quotation(); + if (nestingSettings.allowQuotation) { + quotationParser = quotation(nestingSettings.allowLongGlyph); } else { quotationParser = error(new CoveredError()); } + let longGlyphParser: Lexer; + if (nestingSettings.allowLongGlyph) { + longGlyphParser = longGlyph(nestingSettings.allowQuotation); + } else { + longGlyphParser = error(new CoveredError()); + } let xAlaXParser: Lexer; if (settings.xAlaXPartialParsing) { xAlaXParser = error(new CoveredError()); @@ -363,20 +482,27 @@ function tokenTree(includeQuotation: boolean): Lexer { ), comma().map(() => ({ type: "comma" }) as TokenTree), quotationParser, + longGlyphParser, choiceOnlyOne(cartouches(), properWords()).map((words) => ({ type: "proper word", words }) as TokenTree ), - combinedGlyphs(), + combinedGlyphs().map((words) => + ({ type: "combined glyphs", words }) as TokenTree + ), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), xAlaXParser, word().map((word) => ({ type: "word", word })), ); } /** Parses multiple token trees. */ -function tokenTrees(includeQuotation: boolean): Lexer> { - return all(tokenTree(includeQuotation)); +function tokenTrees( + nestingSettings: { allowQuotation: boolean; allowLongGlyph: boolean }, +): Lexer> { + return all(tokenTree(nestingSettings)); } -const FULL_PARSER = spaces().with(tokenTrees(true)).skip(eol()); +const FULL_PARSER = spaces() + .with(tokenTrees({ allowQuotation: true, allowLongGlyph: true })) + .skip(eol()); /** Parses multiple token trees. */ export function lex(src: string): Output> { if (/\n/.test(src.trim())) { diff --git a/src/token-tree.ts b/src/token-tree.ts index 6a8188c..0381b2c 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -17,16 +17,16 @@ export type TokenTree = | { type: "long glyph"; before: Array; - word: longGlyphHead; + words: Array; after: Array; } | { type: "long glyph space"; - word: longGlyphHead; + words: Array; spaceLength: number; } | { - type: "long lon"; + type: "underline lon"; words: Array; } | { type: "multiple a"; count: number } @@ -40,18 +40,7 @@ export type TokenTree = } | { type: "comma" } | { type: "punctuation"; punctuation: string }; -/** - * Represents the word used as long glyph. - */ -export type longGlyphHead = - | { - type: "word"; - word: string; - } - | { - type: "combined words"; - words: Array; - }; + export function describe(tokenTree: TokenTree): string { if (tokenTree.type === "word") { return `"${tokenTree.word}"`; From 8163208a17559bfe05b36c2938179a6fb049b902 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 31 Mar 2024 14:57:08 +0800 Subject: [PATCH 169/738] add notes and change space handling --- src/lexer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index d165a47..c3afc7f 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -344,6 +344,7 @@ function quotation( }; }); } +// TODO: better space handling function longContainer( left: string, right: string, @@ -357,12 +358,12 @@ function longContainer( }; return sequence( specificUcsurCharacter(left, description[left], { - allowSpace: true, + allowSpace: false, allowVariation: false, }), inside, specificUcsurCharacter(right, description[right], { - allowSpace: true, + allowSpace: false, allowVariation: false, }), ).map(([_, inside, _1]) => inside); From 6373fe6bfc71e9167d66594c3561d65a47e1d139 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 07:58:26 +0800 Subject: [PATCH 170/738] add explicit li as additional information on ast --- src/ast-parser.ts | 2 ++ src/ast.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index d07faeb..64a8df3 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -476,6 +476,7 @@ function clause(): AstParser { }, }, predicates, + explicitLi: false, } as Clause)), sequence( preposition(), @@ -504,6 +505,7 @@ function clause(): AstParser { type: "li clause", subjects, predicates, + explicitLi: true, } as Clause)), specificWord("o").with(multiplePredicates(["o", "anu"])) .map((predicates) => ({ diff --git a/src/ast.ts b/src/ast.ts index 37f3f64..eab55f0 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -76,6 +76,7 @@ export type Clause = type: "li clause"; subjects: MultiplePhrases; predicates: MultiplePredicates; + explicitLi: boolean; } | { type: "o clause"; From 2ad2f601645113ed9e67e3451ae404b968f00fdd Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 08:02:34 +0800 Subject: [PATCH 171/738] this looks better --- src/ast-parser.ts | 236 ++++++++++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 101 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 64a8df3..885693f 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -126,11 +126,13 @@ function specificWord(thatWord: string): AstParser { function wordUnit(word: Set, description: string): AstParser { return choice( wordFrom(word, description).then((word) => - manyAtLeastOnce(specificWord(word)).map((words) => ({ - type: "reduplication", - word, - count: words.length + 1, - } as WordUnit)) + manyAtLeastOnce(specificWord(word)).map((words) => + ({ + type: "reduplication", + word, + count: words.length + 1, + }) as WordUnit + ) ), specificTokenTree("x ala x").map(({ word }) => ({ type: "x ala x", word }) as WordUnit @@ -139,10 +141,10 @@ function wordUnit(word: Set, description: string): AstParser { specificWord("ala").with(specificWord(word)) ).map(( word, - ) => ({ type: "x ala x", word } as WordUnit)), + ) => ({ type: "x ala x", word }) as WordUnit), wordFrom(word, description).map(( word, - ) => ({ type: "default", word } as WordUnit)), + ) => ({ type: "default", word }) as WordUnit), ).filter(filter(WORD_UNIT_RULES)); } function binaryWords( @@ -201,24 +203,28 @@ function modifiers(): AstParser> { return sequence( many( choice( - wordUnit(CONTENT_WORD, "modifier").map((word) => ({ - type: "default", - word, - } as Modifier)).filter(filter(MODIFIER_RULES)), + wordUnit(CONTENT_WORD, "modifier").map((word) => + ({ + type: "default", + word, + }) as Modifier + ).filter(filter(MODIFIER_RULES)), properWords().map(( words, - ) => ({ type: "proper words", words } as Modifier)).filter( + ) => ({ type: "proper words", words }) as Modifier).filter( filter(MODIFIER_RULES), ), number().map(( numbers, - ) => ({ - type: "default", - word: { type: "numbers", numbers }, - } as Modifier)).filter(filter(MODIFIER_RULES)), + ) => + ({ + type: "default", + word: { type: "numbers", numbers }, + }) as Modifier + ).filter(filter(MODIFIER_RULES)), quotation().map(( quotation, - ) => ({ type: "quotation", quotation } as Modifier)).filter( + ) => ({ type: "quotation", quotation }) as Modifier).filter( filter(MODIFIER_RULES), ), ), @@ -226,17 +232,21 @@ function modifiers(): AstParser> { many( sequence(wordUnit(new Set(["nanpa"]), '"nanpa"'), phrase()).map(( [nanpa, phrase], - ) => ({ - type: "nanpa", - nanpa, - phrase, - } as Modifier)).filter(filter(MODIFIER_RULES)), + ) => + ({ + type: "nanpa", + nanpa, + phrase, + }) as Modifier + ).filter(filter(MODIFIER_RULES)), ), many( - specificWord("pi").with(phrase()).map((phrase) => ({ - type: "pi", - phrase, - } as Modifier)).filter(filter(MODIFIER_RULES)), + specificWord("pi").with(phrase()).map((phrase) => + ({ + type: "pi", + phrase, + }) as Modifier + ).filter(filter(MODIFIER_RULES)), ), ).map(( [modifiers, nanpaModifiers, piModifiers], @@ -249,11 +259,13 @@ function phrase(): AstParser { return choice( sequence(number(), lazy(modifiers)).map(( [numbers, modifiers], - ) => ({ - type: "default", - headWord: { type: "numbers", numbers }, - modifiers, - } as Phrase)), + ) => + ({ + type: "default", + headWord: { type: "numbers", numbers }, + modifiers, + }) as Phrase + ), binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => ({ type: "preverb", @@ -272,33 +284,39 @@ function phrase(): AstParser { lazy(phrase), ).map(( [[preverb, modifier], modifiers, phrase], - ) => ({ - type: "preverb", - preverb, - modifiers: [ - ...modifier, - ...modifiers, - ], - phrase, - } as Phrase)), - lazy(preposition).map((preposition) => ({ - type: "preposition", - preposition, - } as Phrase)), + ) => + ({ + type: "preverb", + preverb, + modifiers: [ + ...modifier, + ...modifiers, + ], + phrase, + }) as Phrase + ), + lazy(preposition).map((preposition) => + ({ + type: "preposition", + preposition, + }) as Phrase + ), sequence( optionalCombined(CONTENT_WORD, "headword"), lazy(modifiers), - ).map(([[headWord, modifier], modifiers]) => ({ - type: "default", - headWord, - modifiers: [ - ...modifier, - ...modifiers, - ], - } as Phrase)), + ).map(([[headWord, modifier], modifiers]) => + ({ + type: "default", + headWord, + modifiers: [ + ...modifier, + ...modifiers, + ], + }) as Phrase + ), quotation().map(( quotation, - ) => ({ type: "quotation", quotation } as Phrase)), + ) => ({ type: "quotation", quotation }) as Phrase), ).filter(filter(PHRASE_RULE)); } /** @@ -310,7 +328,7 @@ function nestedPhrasesOnly( ): AstParser { if (nestingRule.length === 0) { return phrase().map( - (phrase) => ({ type: "single", phrase } as MultiplePhrases), + (phrase) => ({ type: "single", phrase }) as MultiplePhrases, ); } else { const [first, ...rest] = nestingRule; @@ -339,7 +357,7 @@ function nestedPhrases( ): AstParser { if (nestingRule.length === 0) { return phrase().map( - (phrase) => ({ type: "single", phrase } as MultiplePhrases), + (phrase) => ({ type: "single", phrase }) as MultiplePhrases, ); } else { return choice( @@ -425,7 +443,7 @@ function multiplePredicates( associatedPredicates([]), phrase().map(( predicate, - ) => ({ type: "single", predicate } as MultiplePredicates)), + ) => ({ type: "single", predicate }) as MultiplePredicates), ); } else { const [first, ...rest] = nestingRule; @@ -450,10 +468,12 @@ function multiplePredicates( ), ), ), - ).map(([group, moreGroups]) => ({ - type, - predicates: [group, ...moreGroups], - } as MultiplePredicates)), + ).map(([group, moreGroups]) => + ({ + type, + predicates: [group, ...moreGroups], + }) as MultiplePredicates + ), multiplePredicates(rest), ); } @@ -464,27 +484,31 @@ function clause(): AstParser { sequence( wordFrom(SPECIAL_SUBJECT, "mi/sina subject"), multiplePredicates(["li", "anu"]), - ).map(([subject, predicates]) => ({ - type: "li clause", - subjects: { - type: "single", - phrase: { - type: "default", - headWord: { type: "default", word: subject }, - alaQuestion: false, - modifiers: [], + ).map(([subject, predicates]) => + ({ + type: "li clause", + subjects: { + type: "single", + phrase: { + type: "default", + headWord: { type: "default", word: subject }, + alaQuestion: false, + modifiers: [], + }, }, - }, - predicates, - explicitLi: false, - } as Clause)), + predicates, + explicitLi: false, + }) as Clause + ), sequence( preposition(), many(optionalComma().with(preposition())), - ).map(([preposition, morePreposition]) => ({ - type: "prepositions", - prepositions: [preposition, ...morePreposition], - } as Clause)), + ).map(([preposition, morePreposition]) => + ({ + type: "prepositions", + prepositions: [preposition, ...morePreposition], + }) as Clause + ), subjectPhrases().map((phrases) => { if (phrases.type === "single" && phrases.phrase.type === "quotation") { throw new CoveredError(); @@ -492,41 +516,51 @@ function clause(): AstParser { return { type: "phrases", phrases } as Clause; } }), - subjectPhrases().skip(specificWord("o")).map((phrases) => ({ - type: "o vocative", - phrases, - } as Clause)), + subjectPhrases().skip(specificWord("o")).map((phrases) => + ({ + type: "o vocative", + phrases, + }) as Clause + ), sequence( subjectPhrases(), optionalComma().with(specificWord("li")).with( multiplePredicates(["li", "anu"]), ), - ).map(([subjects, predicates]) => ({ - type: "li clause", - subjects, - predicates, - explicitLi: true, - } as Clause)), - specificWord("o").with(multiplePredicates(["o", "anu"])) - .map((predicates) => ({ - type: "o clause", - subjects: null, + ).map(([subjects, predicates]) => + ({ + type: "li clause", + subjects, predicates, - } as Clause)), + explicitLi: true, + }) as Clause + ), + specificWord("o").with(multiplePredicates(["o", "anu"])) + .map((predicates) => + ({ + type: "o clause", + subjects: null, + predicates, + }) as Clause + ), sequence( subjectPhrases(), optionalComma().with(specificWord("o")).with( multiplePredicates(["o", "anu"]), ), - ).map(([subjects, predicates]) => ({ - type: "o clause", - subjects: subjects, - predicates, - } as Clause)), - quotation().map((quotation) => ({ - type: "quotation", - quotation, - } as Clause)), + ).map(([subjects, predicates]) => + ({ + type: "o clause", + subjects: subjects, + predicates, + }) as Clause + ), + quotation().map((quotation) => + ({ + type: "quotation", + quotation, + }) as Clause + ), ).filter(filter(CLAUSE_RULE)); } /** Parses a single clause including preclause and postclause. */ From 069f3dfcc725a4b1a9354b29b9735bb5a13935d4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 09:36:22 +0800 Subject: [PATCH 172/738] improve space handling --- src/lexer.ts | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index c3afc7f..f1e0df6 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -344,7 +344,8 @@ function quotation( }; }); } -// TODO: better space handling +// spaces after the first glyph aren't parsed and so must be manually added on +// the `inside` parser function longContainer( left: string, right: string, @@ -376,7 +377,9 @@ function longCharacterContainer( return longContainer( left, right, - allAtLeastOnce(tokenTree({ allowQuotation, allowLongGlyph: false })), + spaces().with( + allAtLeastOnce(tokenTree({ allowQuotation, allowLongGlyph: false })), + ), ); } function longSpaceContainer(): Lexer { @@ -384,8 +387,9 @@ function longSpaceContainer(): Lexer { START_OF_LONG_GLYPH, END_OF_LONG_GLYPH, match(/\s+/, "space").map(([space]) => space.length), - ); + ).skip(spaces()); } +// This doesn't parses space on the right and so must be manually `skip` function longGlyphHead(): Lexer> { return choiceOnlyOne( combinedGlyphs(), @@ -413,24 +417,25 @@ function characterLongGlyph( END_OF_REVERSE_LONG_GLYPH, ), ), - ).map(([beforeNull, words, afterNull]) => { - const before = beforeNull ?? []; - const after = afterNull ?? []; - if (before.length === 0 && after.length === 0) { - throw new CoveredError(); - } - return { - type: "long glyph", - before: before ?? [], - words, - after: after ?? [], - }; - }); + ).skip(spaces()) + .map(([beforeNull, words, afterNull]) => { + const before = beforeNull ?? []; + const after = afterNull ?? []; + if (before.length === 0 && after.length === 0) { + throw new CoveredError(); + } + return { + type: "long glyph", + before: before ?? [], + words, + after: after ?? [], + }; + }); } function longSpaceGlyph(): Lexer { return sequence( longGlyphHead(), - longSpaceContainer(), + longSpaceContainer().skip(spaces()), ).map(([words, spaceLength]) => ({ type: "long glyph space", words, @@ -444,7 +449,7 @@ function longLon( allowQuotation, START_OF_REVERSE_LONG_GLYPH, END_OF_LONG_GLYPH, - ); + ).skip(spaces()); } function longGlyph(allowQuotation: boolean): Lexer { return choiceOnlyOne( From 980836e04140895efe7120bbfb53776e43fb7063 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 09:38:02 +0800 Subject: [PATCH 173/738] this isn't needed globally --- src/lexer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index f1e0df6..84f56b3 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -39,9 +39,6 @@ import { nothing } from "./parser-lib.ts"; export type Lexer = Parser; -const VOWEL = /[aeiou]/; -const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; - /** Takes all parser and applies them one after another. */ // Had to redeclare this function, Typescript really struggles with inferring // types when using `sequence`. @@ -275,6 +272,8 @@ function cartoucheElement(): Lexer { (dots) => dots.length, ), ).map(([word, dots]) => { + const VOWEL = /[aeiou]/; + const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; let count = dots; if (VOWEL.test(word[0])) { count++; From e327168f3f79b8165ab663b9cd91f83e4583eba2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 09:41:21 +0800 Subject: [PATCH 174/738] update comments --- src/lexer.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 84f56b3..8ba9a47 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -343,8 +343,8 @@ function quotation( }; }); } -// spaces after the first glyph aren't parsed and so must be manually added on -// the `inside` parser +// spaces after the first glyph and the last glyph aren't parsed and so must be +// manually added by the caller if needed function longContainer( left: string, right: string, @@ -388,7 +388,8 @@ function longSpaceContainer(): Lexer { match(/\s+/, "space").map(([space]) => space.length), ).skip(spaces()); } -// This doesn't parses space on the right and so must be manually `skip` +// This doesn't parses space on the right and so must be manually added by the +// caller if needed function longGlyphHead(): Lexer> { return choiceOnlyOne( combinedGlyphs(), From 38c756603c5719f57ebbd59b48485e2929b233ea Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 09:58:26 +0800 Subject: [PATCH 175/738] simplify parser calls --- src/ast-parser.ts | 8 +++----- src/lexer.ts | 5 +---- src/parser-lib.ts | 5 ++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 885693f..38f80c7 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -607,8 +607,8 @@ const INNER_QUOTATION_PARSER = all(sentence()) /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => - INNER_QUOTATION_PARSER.parser(tokenTree.tokenTree).map( - ({ value }) => + INNER_QUOTATION_PARSER.parse(tokenTree.tokenTree).map( + (value) => ({ sentences: value, leftMark: tokenTree.leftMark, @@ -622,7 +622,5 @@ const FULL_PARSER = allAtLeastOnce(sentence()) .filter(filter(SENTENCES_RULE)); /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { - return lex(src).flatMap((src) => - FULL_PARSER.parser(src).map(({ value }) => value) - ); + return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/lexer.ts b/src/lexer.ts index 8ba9a47..0ad0bfe 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -514,8 +514,5 @@ export function lex(src: string): Output> { if (/\n/.test(src.trim())) { return new Output(new UnrecognizedError("multiline text")); } - return FULL_PARSER.parser(src) - .map(( - { value }, - ) => value); + return FULL_PARSER.parse(src); } diff --git a/src/parser-lib.ts b/src/parser-lib.ts index f83ede0..d66dbc8 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -60,9 +60,8 @@ export class Parser { skip(parser: Parser): Parser { return sequence(this, parser).map(([output, _]) => output); } - /** Suppresses all error. */ - silent(): Parser { - return new Parser((src) => new Output(this.parser(src).output)); + parse(src: T): Output { + return this.parser(src).map(({ value }) => value); } } /** Parser that always outputs an error. */ From 37c26c5f82f2354e353b6496a5952d6e314e8dde Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 10:02:36 +0800 Subject: [PATCH 176/738] change statement order --- src/main.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index 7d5ca34..483422b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -110,7 +110,13 @@ function updateOutput(): void { const source = elements!.input.value; try { const translations = translate(source); - if (translations.isError()) { + if (!translations.isError()) { + const output = [...new Set(translations.output)]; + if (settings.randomize) { + output.sort(() => Math.random() - Math.random()); + } + outputTranslations(output); + } else { let error: Array = []; if (settings.useTeloMisikeke) { error = teloMisikeke.errors(source); @@ -125,12 +131,6 @@ function updateOutput(): void { ]; } outputErrors(error); - } else { - const output = [...new Set(translations.output)]; - if (settings.randomize) { - output.sort(() => Math.random() - Math.random()); - } - outputTranslations(output); } } catch (unreachableError) { let error: string; From 96a4177c35f29b90b78bc422920a188e61931efe Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 10:51:32 +0800 Subject: [PATCH 177/738] improve space handling --- src/lexer.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 0ad0bfe..fed62c6 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -176,15 +176,17 @@ function joiner(): Lexer { }), ); } -/** Parses combined glyphs. */ +/** + * Parses combined glyphs. The spaces after aren't parsed and so must be + * manually added by the caller. + */ function combinedGlyphs(): Lexer> { return sequence( ucsurWord({ allowVariation: false, allowSpace: false }), allAtLeastOnce( joiner().with(ucsurWord({ allowVariation: false, allowSpace: false })), ), - ).skip(spaces()) - .map(([first, rest]) => [first, ...rest]); + ).map(([first, rest]) => [first, ...rest]); } /** Parses a word, either UCSUR or latin. */ function word(): Lexer { @@ -492,7 +494,7 @@ function tokenTree( choiceOnlyOne(cartouches(), properWords()).map((words) => ({ type: "proper word", words }) as TokenTree ), - combinedGlyphs().map((words) => + combinedGlyphs().skip(spaces()).map((words) => ({ type: "combined glyphs", words }) as TokenTree ), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), From 200ae3408880018a51bba296e08e070292f28b30 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 11:34:32 +0800 Subject: [PATCH 178/738] implement long pi parser --- src/ast-parser.ts | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 38f80c7..fed5065 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -198,6 +198,40 @@ function number(): AstParser> { } }); } +const INNER_PI_PARSER = phrase().skip(eol()); +function pi(): AstParser { + return choice( + specificTokenTree("long glyph").flatMapValue( + (longGlyph) => { + if (longGlyph.before.length !== 0) { + return new Output( + new UnexpectedError("reverse long glyph", "long pi"), + ); + } + if (longGlyph.words.length !== 1 || longGlyph.words[0] !== "pi") { + return new Output( + new UnexpectedError( + describe({ type: "combined glyphs", words: longGlyph.words }), + "pi", + ), + ); + } + return INNER_PI_PARSER.map((phrase) => + ({ + type: "pi", + phrase, + }) as Modifier & { type: "pi" } + ).parse(longGlyph.after); + }, + ), + specificWord("pi").with(phrase()).map((phrase) => + ({ + type: "pi", + phrase, + }) as Modifier & { type: "pi" } + ), + ).filter(filter(MODIFIER_RULES)); +} /** Parses multiple modifiers. */ function modifiers(): AstParser> { return sequence( @@ -240,14 +274,7 @@ function modifiers(): AstParser> { }) as Modifier ).filter(filter(MODIFIER_RULES)), ), - many( - specificWord("pi").with(phrase()).map((phrase) => - ({ - type: "pi", - phrase, - }) as Modifier - ).filter(filter(MODIFIER_RULES)), - ), + many(pi()), ).map(( [modifiers, nanpaModifiers, piModifiers], ) => [...modifiers, ...nanpaModifiers, ...piModifiers]).filter( From 18b5c11b0668dc689b70d42664e0c47073eb14e9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 11:40:05 +0800 Subject: [PATCH 179/738] fix bug in parsing long glyph --- src/lexer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index fed62c6..ea81ce3 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -407,16 +407,16 @@ function characterLongGlyph( optionalAll( longCharacterContainer( allowQuotation, - START_OF_LONG_GLYPH, - END_OF_LONG_GLYPH, + START_OF_REVERSE_LONG_GLYPH, + END_OF_REVERSE_LONG_GLYPH, ), ), longGlyphHead(), optionalAll( longCharacterContainer( allowQuotation, - START_OF_REVERSE_LONG_GLYPH, - END_OF_REVERSE_LONG_GLYPH, + START_OF_LONG_GLYPH, + END_OF_LONG_GLYPH, ), ), ).skip(spaces()) From e3464b47888af7966e734a392653b6bf3cc9a847 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 1 Apr 2024 11:44:04 +0800 Subject: [PATCH 180/738] update eol description --- src/ast-parser.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index fed5065..a2c5076 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -58,10 +58,12 @@ function sequence>( return rawSequence, T>(...sequence as any); } /** Parses the end of line (or the end of sentence in context of Toki Pona) */ -function eol(): AstParser { +function eol(description: string): AstParser { return new Parser((src) => { if (src.length === 0) return new Output([{ value: null, rest: [] }]); - else return new Output(new UnexpectedError(src[0].type, "end of sentence")); + else {return new Output( + new UnexpectedError(src[0].type, description), + );} }); } /** Parses a single token tree. */ @@ -198,7 +200,7 @@ function number(): AstParser> { } }); } -const INNER_PI_PARSER = phrase().skip(eol()); +const INNER_PI_PARSER = phrase().skip(eol("end of long glyph")); function pi(): AstParser { return choice( specificTokenTree("long glyph").flatMapValue( @@ -620,7 +622,7 @@ function sentence(): AstParser { fullClause(), many(la().with(fullClause())), choice( - eol().map(() => ""), + eol("end of sentence").map(() => ""), punctuation(), ), ).map(([clause, moreClauses, punctuation]) => ({ @@ -629,7 +631,7 @@ function sentence(): AstParser { })); } const INNER_QUOTATION_PARSER = all(sentence()) - .skip(eol()) + .skip(eol("end of sentence")) .filter(filter(SENTENCES_RULE)); /** Parses a quotation. */ export function quotation(): AstParser { @@ -645,7 +647,7 @@ export function quotation(): AstParser { ); } const FULL_PARSER = allAtLeastOnce(sentence()) - .skip(eol()) + .skip(eol("end of sentence")) .filter(filter(SENTENCES_RULE)); /** A multiple Toki Pona sentence parser. */ export function parser(src: string): Output> { From 8f8efad3c5b66673de5a2a35f9efdaa476b37f08 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 2 Apr 2024 05:14:56 +0800 Subject: [PATCH 181/738] implement long X ala X parser --- src/ast-parser.ts | 69 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index a2c5076..aaf91fe 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -124,6 +124,66 @@ function specificWord(thatWord: string): AstParser { else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); } +function xAlaX( + word: Set, + description: string, +): AstParser { + return choice( + specificTokenTree("long glyph").map((longGlyph) => { + // TODO: reduce duplication + if (longGlyph.words.length !== 1 || longGlyph.words[0] !== "ala") { + throw new UnexpectedError( + describe({ type: "combined glyphs", words: longGlyph.words }), + '"ala"', + ); + } + if (longGlyph.before.length !== 1) { + if (longGlyph.before.length === 0) { + throw new UnexpectedError( + "forward long glyph", + "long glyph on both sides", + ); + } else { + throw new UnexpectedError( + describe(longGlyph.before[0]), + "end of long glyph", + ); + } + } + const leftGlyph = longGlyph.before[0]; + if (leftGlyph.type !== "word") { + throw new UnexpectedError(describe(leftGlyph), "word"); + } + const word = leftGlyph.word; + if (longGlyph.after.length !== 1) { + if (longGlyph.after.length === 0) { + throw new UnexpectedError( + "backwards long glyph", + "long glyph on both sides", + ); + } else { + throw new UnexpectedError( + describe(longGlyph.after[0]), + "end of long glyph", + ); + } + } + const rightGlyph = longGlyph.after[0]; + if (rightGlyph.type !== "word" || rightGlyph.word !== word) { + throw new UnexpectedError(describe(leftGlyph), `"${word}"`); + } + return { type: "x ala x", word } as WordUnit & { type: "x ala x" }; + }), + specificTokenTree("x ala x").map(({ word }) => + ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" } + ), + wordFrom(word, description).then((word) => + specificWord("ala").with(specificWord(word)) + ).map(( + word, + ) => ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" }), + ); +} /** Parses word unit without numbers. */ function wordUnit(word: Set, description: string): AstParser { return choice( @@ -136,14 +196,7 @@ function wordUnit(word: Set, description: string): AstParser { }) as WordUnit ) ), - specificTokenTree("x ala x").map(({ word }) => - ({ type: "x ala x", word }) as WordUnit - ), - wordFrom(word, description).then((word) => - specificWord("ala").with(specificWord(word)) - ).map(( - word, - ) => ({ type: "x ala x", word }) as WordUnit), + xAlaX(word, description), wordFrom(word, description).map(( word, ) => ({ type: "default", word }) as WordUnit), From 196f3d4bc85f33311b782b69ce72731bcbf80de7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 2 Apr 2024 05:24:15 +0800 Subject: [PATCH 182/738] implement underline lon --- src/ast-parser.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index aaf91fe..e8f7cea 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -253,7 +253,6 @@ function number(): AstParser> { } }); } -const INNER_PI_PARSER = phrase().skip(eol("end of long glyph")); function pi(): AstParser { return choice( specificTokenTree("long glyph").flatMapValue( @@ -271,7 +270,7 @@ function pi(): AstParser { ), ); } - return INNER_PI_PARSER.map((phrase) => + return INNER_PHRASE_PARSER.map((phrase) => ({ type: "pi", phrase, @@ -336,6 +335,7 @@ function modifiers(): AstParser> { filter(MODIFIERS_RULES), ); } +const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); /** Parses phrases. */ function phrase(): AstParser { return choice( @@ -459,6 +459,18 @@ function subjectPhrases(): AstParser { /** Parses prepositional phrase. */ function preposition(): AstParser { return choice( + specificTokenTree("underline lon").flatMapValue((tokenTrees) => + INNER_PHRASE_PARSER.parse(tokenTrees.words) + ).map((phrase) => + ({ + preposition: { type: "default", word: "lon" }, + modifiers: [], + phrases: { + type: "single", + phrase, + }, + }) as Preposition + ), binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => ({ preposition: { type: "default", word: preposition }, From 7cd93d915088dc4b3d95736465e6c4c352950055 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 2 Apr 2024 05:27:18 +0800 Subject: [PATCH 183/738] simplify calls to flatMapValue --- src/ast-parser.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e8f7cea..194076c 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -255,7 +255,7 @@ function number(): AstParser> { } function pi(): AstParser { return choice( - specificTokenTree("long glyph").flatMapValue( + specificTokenTree("long glyph").flatMapValue( (longGlyph) => { if (longGlyph.before.length !== 0) { return new Output( @@ -270,13 +270,13 @@ function pi(): AstParser { ), ); } - return INNER_PHRASE_PARSER.map((phrase) => - ({ - type: "pi", - phrase, - }) as Modifier & { type: "pi" } - ).parse(longGlyph.after); + return INNER_PHRASE_PARSER.parse(longGlyph.after); }, + ).map((phrase) => + ({ + type: "pi", + phrase, + }) as Modifier & { type: "pi" } ), specificWord("pi").with(phrase()).map((phrase) => ({ From f1207a5428c4012fe67bdff1ebfa881ef592c8cc Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 3 Apr 2024 08:11:54 +0800 Subject: [PATCH 184/738] implement long preposition parser --- src/ast-parser.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 194076c..694d0f3 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -471,6 +471,39 @@ function preposition(): AstParser { }, }) as Preposition ), + specificTokenTree("long glyph").flatMapValue((tokenTrees) => { + if (tokenTrees.before.length !== 0) { + return new Output( + new UnexpectedError("reverse long glyph", "forward long glyph"), + ); + } + if (tokenTrees.words.length > 2) { + return new Output( + new UnrecognizedError( + `combined glyphs of ${tokenTrees.words.length} words`, + ), + ); + } + const word = tokenTrees.words[0]; + if (!PREPOSITION.has(word)) { + return new Output( + new UnrecognizedError(`"${word}" as preposition`), + ); + } + const modifiers = tokenTrees.words.slice(1).map((word) => + ({ type: "default", word: { type: "default", word } }) as Modifier + ); + return INNER_PHRASE_PARSER.parse(tokenTrees.after).map((phrase) => + ({ + preposition: { type: "default", word }, + modifiers, + phrases: { + type: "single", + phrase, + }, + }) as Preposition + ); + }), binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => ({ preposition: { type: "default", word: preposition }, From d2682a242efb7aefce45a0ebd7d752aa343f317f Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 4 Apr 2024 14:47:04 +0800 Subject: [PATCH 185/738] implement parser for "a" --- src/ast-parser.ts | 74 +++++++++++++++++++++++++++++++++-------------- src/ast.ts | 40 +++++++++++++++++++++---- src/filter.ts | 12 ++++---- src/lexer.ts | 11 +++++++ src/token-tree.ts | 1 + src/translator.ts | 46 +++++++++++++++-------------- 6 files changed, 130 insertions(+), 54 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 694d0f3..c2287cc 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -3,10 +3,13 @@ import { Clause, FullClause, + Marker, Modifier, MultiplePhrases, MultiplePredicates, Phrase, + Postclause, + Preclause, Preposition, Quotation, Sentence, @@ -124,6 +127,17 @@ function specificWord(thatWord: string): AstParser { else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); } +function marker(): AstParser { + return choice( + specificTokenTree("multiple a").map(({ count }) => + ({ type: "multiple a", count }) as Marker + ), + specificTokenTree("long a").map(({ length }) => + ({ type: "long a", length }) as Marker + ), + specificWord("a").map(() => ({ type: "a" }) as Marker), + ); +} function xAlaX( word: Set, description: string, @@ -197,9 +211,9 @@ function wordUnit(word: Set, description: string): AstParser { ) ), xAlaX(word, description), - wordFrom(word, description).map(( - word, - ) => ({ type: "default", word }) as WordUnit), + sequence(wordFrom(word, description), marker()).map( + ([word, marker]) => ({ type: "default", word, marker }) as WordUnit, + ), ).filter(filter(WORD_UNIT_RULES)); } function binaryWords( @@ -351,11 +365,11 @@ function phrase(): AstParser { binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => ({ type: "preverb", - preverb: { type: "default", word: preverb }, + preverb: { type: "default", word: preverb, marker: null }, modifiers: [], phrase: { type: "default", - headWord: { type: "default", word: phrase }, + headWord: { type: "default", word: phrase, marker: null }, modifiers: [], }, }) as Phrase @@ -463,7 +477,7 @@ function preposition(): AstParser { INNER_PHRASE_PARSER.parse(tokenTrees.words) ).map((phrase) => ({ - preposition: { type: "default", word: "lon" }, + preposition: { type: "default", word: "lon", marker: null }, modifiers: [], phrases: { type: "single", @@ -506,13 +520,13 @@ function preposition(): AstParser { }), binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => ({ - preposition: { type: "default", word: preposition }, + preposition: { type: "default", word: preposition, marker: null }, modifiers: [], phrases: { type: "single", phrase: { type: "default", - headWord: { type: "default", word: phrase }, + headWord: { type: "default", word: phrase, marker: null }, modifiers: [], }, }, @@ -618,7 +632,7 @@ function clause(): AstParser { type: "single", phrase: { type: "default", - headWord: { type: "default", word: subject }, + headWord: { type: "default", word: subject, marker: null }, alaQuestion: false, modifiers: [], }, @@ -690,21 +704,39 @@ function clause(): AstParser { ), ).filter(filter(CLAUSE_RULE)); } +function preclause(): AstParser { + return choice( + marker().map((marker) => ({ type: "marker", marker }) as Preclause), + wordUnit(new Set(["taso"]), '"taso"').map((taso) => + ({ type: "taso", taso }) as Preclause + ), + ); +} +function postclause(): AstParser { + return choice( + marker().map((marker) => ({ type: "marker", marker }) as Postclause), + specificWord("anu").with(wordUnit(new Set(["seme"]), '"seme"')).map( + (seme) => ({ type: "anu seme", seme }) as Postclause, + ), + ); +} /** Parses a single clause including preclause and postclause. */ function fullClause(): AstParser { - return sequence( - optional(wordUnit(new Set(["taso"]), '"taso"').skip(optionalComma())), - clause(), - optional( - optionalComma().with(specificWord("anu")).with( - wordUnit(new Set(["seme"]), '"seme"'), - ), + return choice( + sequence( + optional(preclause().skip(optionalComma())), + clause(), + optional(optionalComma().with(specificWord("anu"))), + ).map(([preclause, clause, postclause]) => + ({ + type: "default", + preclause, + clause, + postclause, + }) as FullClause ), - ).map(([taso, clause, anuSeme]) => ({ - taso, - anuSeme, - clause, - })).filter(filter(FULL_CLAUSE_RULE)); + marker().map((marker) => ({ type: "marker", marker }) as FullClause), + ).filter(filter(FULL_CLAUSE_RULE)); } /** parses _la_ with optional comma around. */ function la(): AstParser { diff --git a/src/ast.ts b/src/ast.ts index eab55f0..6bda29b 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -2,9 +2,13 @@ import { UnreachableError } from "./error.ts"; +export type Marker = + | { type: "a" } + | { type: "long a"; length: number } + | { type: "multiple a"; count: number }; /** Represents a word unit. */ export type WordUnit = - | { type: "default"; word: string } + | { type: "default"; word: string; marker: null | Marker } | { type: "x ala x"; word: string } | { type: "reduplication"; word: string; count: number } | { type: "numbers"; numbers: Array }; @@ -91,12 +95,36 @@ export type Clause = type: "quotation"; quotation: Quotation; }; +export type Preclause = + | { + type: "taso"; + taso: WordUnit; + } + | { + type: "marker"; + marker: Marker; + }; +export type Postclause = + | { + type: "anu seme"; + seme: WordUnit; + } + | { + type: "marker"; + marker: Marker; + }; /** Represents a clause including preclause and postclause. */ -export type FullClause = { - taso: null | WordUnit; - anuSeme: null | WordUnit; - clause: Clause; -}; +export type FullClause = + | { + type: "default"; + preclause: null | Preclause; + postclause: null | Postclause; + clause: Clause; + } + | { + type: "marker"; + marker: Marker; + }; /** Represents a single full sentence. */ export type Sentence = { laClauses: Array; punctuation: string }; /** Represents quotation. */ diff --git a/src/filter.ts b/src/filter.ts index af9fc25..7d85ab4 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -257,12 +257,12 @@ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ ]; export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // Prevent "taso ala taso" - (fullClause) => { - if (fullClause.taso && fullClause.taso.type === "x ala x") { - throw new UnrecognizedError('"taso ala taso"'); - } - return true; - }, + // (fullClause) => { + // if (fullClause.taso && fullClause.taso.type === "x ala x") { + // throw new UnrecognizedError('"taso ala taso"'); + // } + // return true; + // }, ]; /** Array of filter rules for multiple sentences. */ export const SENTENCES_RULE: Array<(sentences: Array) => boolean> = [ diff --git a/src/lexer.ts b/src/lexer.ts index ea81ce3..776561c 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -213,6 +213,16 @@ function multipleA(): Lexer { [a, as], ) => [a, ...as].length); } +function longA(): Lexer { + return match(/(a+)\s*/, "long a").map(([_, a]) => { + const length = a.length; + if (length > 1) { + return length; + } else { + throw new CoveredError(); + } + }); +} /** Parses X ala X constructions. */ function xAlaX(): Lexer { return word().then((word) => @@ -497,6 +507,7 @@ function tokenTree( combinedGlyphs().skip(spaces()).map((words) => ({ type: "combined glyphs", words }) as TokenTree ), + longA().map((length) => ({ type: "long a", length }) as TokenTree), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), xAlaXParser, word().map((word) => ({ type: "word", word })), diff --git a/src/token-tree.ts b/src/token-tree.ts index 0381b2c..ba70c68 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -30,6 +30,7 @@ export type TokenTree = words: Array; } | { type: "multiple a"; count: number } + | { type: "long a"; length: number } | { type: "x ala x"; word: string } | { type: "proper word"; words: string } | { diff --git a/src/translator.ts b/src/translator.ts index e3dee70..0b76194 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,3 +1,5 @@ +// This module is bound to be replaced with a translator that uses English AST + import { Clause } from "./ast.ts"; import { FullClause, @@ -299,28 +301,30 @@ function translateClause(clause: Clause): TranslationOutput { } /** Translates a full clause. */ function translateFullClause(fullClause: FullClause): TranslationOutput { - let but = ""; - const taso = fullClause.taso; - if (taso) { - if (taso.type === "default") { - but = "but "; - } else if (taso.type === "reduplication") { - but = new Array(taso.count).fill("but ").join(""); - } - } - let isntIt = ""; - const anuSeme = fullClause.anuSeme; - if (anuSeme) { - if (anuSeme.type === "default") { - isntIt = ", isn't it"; - } else if (anuSeme.type === "reduplication") { - // TODO: better translation - isntIt = new Array(anuSeme.count).fill(", isn't it").join(""); - } + // let but = ""; + // const taso = fullClause.taso; + // if (taso) { + // if (taso.type === "default") { + // but = "but "; + // } else if (taso.type === "reduplication") { + // but = new Array(taso.count).fill("but ").join(""); + // } + // } + // let isntIt = ""; + // const anuSeme = fullClause.anuSeme; + // if (anuSeme) { + // if (anuSeme.type === "default") { + // isntIt = ", isn't it"; + // } else if (anuSeme.type === "reduplication") { + // // TODO: better translation + // isntIt = new Array(anuSeme.count).fill(", isn't it").join(""); + // } + // } + if (fullClause.type === "default") { + return translateClause(fullClause.clause); + } else { + return new Output(new TodoError("translation for a")); } - return translateClause(fullClause.clause).map((clause) => - [but, clause, isntIt].join("") - ); } /** Translates a single sentence. */ function translateSentence(sentence: Sentence): TranslationOutput { From 9b53de735d597183628f029f988fa3ef507eb0f4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 4 Apr 2024 15:54:55 +0800 Subject: [PATCH 186/738] rename --- src/{definition.ts => old-definition.ts} | 0 src/translator.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{definition.ts => old-definition.ts} (100%) diff --git a/src/definition.ts b/src/old-definition.ts similarity index 100% rename from src/definition.ts rename to src/old-definition.ts diff --git a/src/translator.ts b/src/translator.ts index 0b76194..12184f4 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -12,7 +12,7 @@ import { import { Output } from "./output.ts"; import { parser } from "./ast-parser.ts"; import { OutputError, TodoError, UnreachableError } from "./error.ts"; -import { DEFINITION } from "./definition.ts"; +import { DEFINITION } from "./old-definition.ts"; /** A special kind of Output that translators returns. */ export type TranslationOutput = Output; From 224883b8b94f9f6bcbbf288804e6404d93c5aa14 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 4 Apr 2024 16:11:53 +0800 Subject: [PATCH 187/738] start new dictionary --- src/dictionary.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/dictionary.ts diff --git a/src/dictionary.ts b/src/dictionary.ts new file mode 100644 index 0000000..a7ba429 --- /dev/null +++ b/src/dictionary.ts @@ -0,0 +1,40 @@ +export type Definition = + | { + type: "noun"; + singular: null | string; + plural: null | string; + condensed: string; + } + | { + type: "pronoun"; + subject: string; + object: string; + possessive: string; + } + | { + type: "adjective"; + adjective: string; + kind: never; + } + | { + type: "adverb"; + adverb: string; + } + | { + type: "verb"; + transitive: boolean; + past: string; + present: string; + condensed: string; + usePreposition: null | string; + withPreposition: null | never; + } + | { + type: "gerund"; + gerund: string; + } + | { + type: "modifier as preposition"; + preposition: never; + }; +export const DICTIONARY: { [word: string]: Array } = {}; From be09f20e5ec0843fb1fe6c72fe91054073785a94 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 13:12:38 +0800 Subject: [PATCH 188/738] implement new dictionary --- src/dictionary.ts | 1091 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1087 insertions(+), 4 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index a7ba429..0d45358 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,3 +1,890 @@ +export const SPECIAL_CONTENT_WORD = new Set([ + "ala", + "mu", + "ni", + "pu", + "seme", +]); +export const DICTIONARY: { [word: string]: Array } = { + akesi: [ + noun("reptile(s)"), + noun("amphibian(s)"), + adjective("reptilian", "qualifier"), + adjective("amphibian", "qualifier"), + ], + alasa: [ + verb("hunt(ed)"), + verb("search(ed)"), + gerund("searching"), + gerund("hunting"), + // TODO: preverb + ], + ale: [ + numeral(100), + singularNoun("everything"), + singularNoun("anything"), + singularNoun("entirety"), + quantifier("all"), + quantifier("every"), + adverb("completely"), + ], + ali: [], // Will be duplicated with "ale" + anpa: [ + singularNoun("bottom"), + singularNoun("below"), + singularNoun("floor"), + // TODO: adjectives + ], + ante: [ + noun("change(s)"), + noun("difference(s)"), + noun("modification(s)"), + adjective("different", "opinion"), + adjective("other", "opinion"), + adjective("altered", "opinion"), + ], + awen: [ + intransitiveVerb("wait(ed)"), + intransitiveVerb("stay(ed)"), + intransitiveVerb("endure(d)"), + verb("keep/kept"), + verb("protect(ed)"), + gerund("waiting"), + gerund("staying"), + gerund("enduring"), + gerund("keeping"), + gerund("protecting"), + // TODO: preverb + ], + esun: [ + noun("shop(s)"), + verb("trade(d)"), + verb("barter(ed)"), + verb("exchange(d)"), + verb("swap(ped)"), + verb("buy/bought"), + verb("sell/sold"), + gerund("trading"), + gerund("bartering"), + gerund("exchanging"), + gerund("swapping"), + gerund("buying"), + gerund("selling"), + ], + ijo: [ + noun("phenomenon(s)"), + noun("object(s)"), + noun("matter(s)"), + ], + ike: [ + adjectiveNounPhrase( + [adjective("negative", "opinion")], + noun("quality/qualities"), + ), + adjective("bad", "opinion"), + adjective("unpleasant", "opinion"), + adjective("harmful", "opinion"), + adjective("unneeded", "opinion"), + adverb("badly"), + adverb("unpleasantly"), + adverb("harmfully"), + ], + ilo: [ + noun("tool(s)"), + noun("implement(s)"), + noun("machine(s)"), + ], + insa: [ + singularNoun("centre"), + noun("content(s)"), + singularNoun("inside"), + ], + jaki: [ + noun("obscenity/obscenities"), + adjective("disgusting", "opinion"), + adjective("sickly", "opinion"), + adjective("toxic", "opinion"), + adjective("unclean", "opinion"), + ], + jan: [ + singularNoun("human being"), + noun("person/people"), + singularNoun("somebody"), // This is technically a pronoun + ], + jelo: [ + singularNoun("yellow"), + adjectiveNounPhrase( + [adjective("lime", "color")], + singularNoun("yellow"), + ), + adjectiveNounPhrase( + [adjective("yellowish", "color")], + singularNoun("orange"), + ), + adjective("yellow", "color"), + adjective("golden", "color"), + adverbAdjectivePhrase([adverb("lime")], adjective("yellow", "color")), + adverbAdjectivePhrase([adverb("yellowish")], adjective("orange", "color")), + ], + jo: [ + verb("have/had"), + verb("carry/carried"), + verb("contain(ed)"), + verb("hold/held"), + gerund("having"), + gerund("carrying"), + gerund("containing"), + gerund("holding"), + ], + kala: [ + noun("fish(es)"), + adjectiveNounPhrase([adjective("sea", "qualifier")], noun("creature(s)")), + gerund("swimming"), + intransitiveVerb("swim"), + ], + kalama: [ + noun("sound(s)"), + intransitiveVerb("sound(ed)"), + ], + kama: [ + noun("future"), + adjective("future", "age"), + verb("summon(ed)"), + intransitiveVerb("arrive"), + gerund("arriving"), + gerund("summoning"), + ], + kasi: [ + noun("plant(s)"), + noun("herb(s)"), + noun("leaf/leaves"), + ], + ken: [ + noun("ability/abilities"), + noun("possibility/possibilities"), + // TODO: preverb + ], + kepeken: [ + // TODO: preposition + ], + kili: [ + noun("fruit(s)"), + noun("vegetable(s)"), + noun("mushroom(s)"), + ], + kiwen: [ + adjectiveNounPhrase( + [adjective("hard", "physical quality")], + noun("object(s)"), + ), + noun("metal(s)"), + noun("rock(s)"), + noun("stone(s)"), + adjective("hard", "physical quality"), + ], + ko: [ + singularNoun("semi-solid"), + singularNoun("paste"), + singularNoun("powder"), + singularNoun("goo"), + singularNoun("sand"), + singularNoun("soil"), + singularNoun("clay"), + adjective("squishy", "physical quality"), + adjective("moldable", "physical quality"), + ], + kon: [ + singularNoun("air"), + singularNoun("breath"), + singularNoun("essence"), + singularNoun("spirit"), + adjectiveNounPhrase( + [adjective("hidden", "physical quality")], + singularNoun("reality"), + ), + adjectiveNounPhrase( + [adjective("unseen", "physical quality")], + singularNoun("agent"), + ), + ], + kule: [ + noun("color(s)"), + noun("pigment(s)"), + noun("category/categories"), + noun("flavor(s)"), + singularNoun("queerness"), + adjective("colorful", "color"), + adjective("queer", "qualifier"), + adverb("colorfully"), + ], + kulupu: [ + noun("community/communities"), + noun("company/companies"), + noun("group(s)"), + noun("nation(s)"), + noun("society/societies"), + noun("tribe(s)"), + ], + kute: [ + noun("ear(s)"), + gerund("listening"), + verb("listen(ed)", "at"), + ], + laso: [ + singularNoun("turquoise"), + singularNoun("blue"), + singularNoun("green"), + singularNoun("cyan"), + singularNoun("indigo"), + adjectiveNounPhrase([adjective("lime", "color")], singularNoun("green")), + adjective("turquoise", "color"), + adjective("blue", "color"), + adjective("green", "color"), + adjective("cyan", "color"), + adjective("indigo", "color"), + adverbAdjectivePhrase([adverb("lime")], adjective("green", "color")), + ], + lawa: [ + noun("head(s)"), + noun("mind(s)"), + noun("guide(s)"), + noun("plan(s)"), + noun("rule(s)"), + verb("control(led)"), + verb("direct(ed)"), + verb("guide(d)"), + verb("lead/led"), + verb("own(ed"), + verb("regulate(d)"), + verb("rule(d)"), + ], + len: [ + noun("clothing(s)"), + noun("fabric(s)"), + gerund("hiding"), + adjective("hidden", "origin"), + verb("cover(ed)"), + ], + lete: [ + singularNoun("coldness"), + adjective("cool", "physical quality"), + adjective("cold", "physical quality"), + adjective("frozen", "physical quality"), + verb("freeze/froze"), + ], + lili: [ + // TODO: "piece of" prefix + singularNoun("smallness"), + adjective("small", "physical quality"), + adjective("short", "physical quality"), + adjective("young", "age"), + quantifier("few"), + ], + linja: [ + adjectiveNounPhrase([ + adjective("long", "physical quality"), + adjective("flexible", "physical quality"), + ], noun("thing(s)")), + noun("cord(s)"), + singularNoun("hair"), + noun("rope(s)"), + noun("line(s)"), + noun("connection(s)"), + compoundAdjective([ + adjective("long", "physical quality"), + adjective("flexible", "physical quality"), + ]), + ], + lipu: [ + adjectiveNounPhrase( + [adjective("flat", "physical quality")], + noun("object(s)"), + ), + noun("book(s)"), + noun("document(s)"), + noun("card(s)"), + noun("paper(s)"), + noun("record(s)"), + noun("website(s)"), + adjective("flat", "physical quality"), + ], + loje: [ + singularNoun("red"), + singularNoun("magenta"), + singularNoun("scarlet"), + singularNoun("pink"), + singularNoun("rust-color"), + adjectiveNounPhrase( + [adjective("reddish", "color")], + singularNoun("orange"), + ), + adjective("red", "color"), + adjective("magenta", "color"), + adjective("scarlet", "color"), + adjective("pink", "color"), + adjective("rust-color", "color"), + adverbAdjectivePhrase([adverb("reddish")], adjective("orange", "color")), + ], + lon: [ + singularNoun("truth"), + adjective("real", "opinion"), + gerund("existing"), + adverb("truthfully"), + verb("exist"), + // TODO: preposition + ], + luka: [ + numeral(5), + noun("hand(s)"), + noun("arm(s)"), + adjectiveNounPhrase([adjective("tactile", "qualifier")], noun("organ(s)")), + adjectiveNounPhrase([adjective("grasping", "qualifier")], noun("organ(s)")), + ], + lukin: [ + noun("appearance(s)"), + noun("visual(s)"), + noun("eye(s)"), + adjectiveNounPhrase([adjective("seeing", "qualifier")], noun("organ(s)")), + verb("look(ed)", "at"), + verb("read"), + verb("watch(ed)"), + ], + lupa: [ + noun("hole(s)"), + singularNoun("pit"), + noun("cave(s)"), + singularNoun("doorway"), + noun("window(s)"), + noun("portal(s)"), + ], + ma: [ + singularNoun("earth"), + singularNoun("land"), + singularNoun("world"), + noun("country/countries"), + noun("territory/territories"), + singularNoun("soil"), + ], + mama: [ + noun("parent(s)"), + noun("ancestor(s)"), + noun("creator(s)"), + noun("originator(s)"), + noun("caretaker(s)"), + noun("sustainer(s)"), + noun("guardian(s)"), + ], + mani: [ + singularNoun("money"), + singularNoun("cash"), + pluralNoun("savings"), + singularNoun("wealth"), + adjectiveNounPhrase([ + adjective("large", "size"), + adjective("domestic", "qualifier"), + ], noun("animal(s)")), + ], + meli: [ + noun("woman/women"), + // noun("female(s)"), // this sounds dehumanizing + adjectiveNounPhrase( + [adjective("feminine", "qualifier")], + noun("person/people"), + ), + noun("wife/wives"), + ], + mi: [ + // TODO: pronouns + ], + mije: [ + noun("man/men"), + // noun("male(s)"), // this sounds dehumanizing + adjectiveNounPhrase( + [adjective("masculine", "qualifier")], + noun("person/people"), + ), + noun("husband(s)"), + ], + moku: [ + noun("food(s)"), + noun("drink(s)"), + verb("eat/ate"), + verb("drink/drank"), + verb("consume(d)"), + verb("ingest(ed)"), + ], + moli: [ + noun("death"), + adjective("dead", "age"), + verb("kill(ed)"), + ], + monsi: [ + singularNoun("back"), + singularNoun("behind"), + singularNoun("rear"), + ], + mun: [ + singularNoun("moon"), + adjectiveNounPhrase( + [adjective("night sky", "origin")], + noun("object(s)"), + ), + noun("star(s)"), + adjectiveNounPhrase( + [adjective("celestial", "origin")], + noun("body/bodies"), + ), + ], + musi: [ + singularNoun("fun"), + noun("game(s)"), + noun("entertainment(s)"), + noun("art(s)"), + adjective("fun", "opinion"), + adjective("amusing", "opinion"), + adjective("interesting", "opinion"), + adjective("comical", "opinion"), + adjective("silly", "opinion"), + verbObjectPhrase(verb("have/had"), singularNoun("fun")), + ], + mute: [ + numeral(20), + quantifier("many"), + quantifier("several"), + adverb("very"), + ], + nanpa: [ + noun("number(s)"), + ], + nasa: [ + singularNoun("silliness"), + singularNoun("strangeness"), + adjective("unusual", "opinion"), + adjective("strange", "opinion"), + adjective("silly", "opinion"), + adjective("drunk", "opinion"), + adjective("intoxicated", "opinion"), + adverb("strangely"), + ], + nasin: [ + noun("way(s)"), + noun("custom(s)"), + noun("doctrine(s)"), + noun("method(s)"), + noun("path(s)"), + ], + nena: [ + noun("bump(s)"), + noun("hill(s)"), + noun("mountain(s)"), + noun("nose(s)"), + noun("protuberance(s)"), + ], + nimi: [ + noun("name(s)"), + noun("word(s)"), + ], + noka: [ + noun("foot/feet"), + noun("leg(s)"), + noun("root(s)"), + adjectiveNounPhrase( + [adjective("locomotive", "qualifier")], + noun("organ(s)"), + ), + ], + olin: [ + singularNoun("affection"), + singularNoun("appreciation"), + singularNoun("respect"), + noun("relationship(s)"), + adjective("platonic", "qualifier"), + adjective("romantic", "qualifier"), + adjective("familial", "qualifier"), + verb("respect(ed)"), + // TODO: to have a strong emotional bond (with) + ], + ona: [ + // TODO: pronouns + ], + open: [ + noun("beginning(s)"), + singularNoun("start"), + adjective("open", "physical quality"), + adjective("turned on", "qualifier"), + verb("start(ed)"), + verb("turn(ed) on"), + ], + pakala: [ + singularNoun("mess"), + noun("damage(s)"), + adjective("botched", "opinion"), + adjective("broken", "opinion"), + verb("botch(ed)"), + verb("break/broke"), + verb("damage(d)"), + verb("harm(ed)"), + verb("mess(ed) up"), + ], + pan: [ + noun("grain(s)"), + adjectiveNounPhrase([adjective("starchy", "material")], noun("food(s)")), + adjectiveNounPhrase([adjective("baked", "qualifier")], pluralNoun("goods")), + ], + pana: [ + verb("give/gave"), + verb("send/sent"), + verb("emit(ted)"), + verb("provide(d)"), + verb("put"), + verb("release(d)"), + gerund("giving"), + gerund("sending"), + gerund("emitting"), + gerund("providing"), + gerund("putting"), + gerund("releasing"), + ], + pali: [ + verb("build"), + verb("make/made"), + verb("prepare(d)"), + intransitiveVerb("do/did"), + intransitiveVerb("work/worked"), + gerund("building"), + gerund("making"), + gerund("preparing"), + gerund("doing"), + gerund("working"), + ], + palisa: [ + adjectiveNounPhrase([ + adjective("long", "physical quality"), + adjective("hard", "physical quality"), + ], noun("thing(s)")), + noun("branch(es)"), + noun("rod(s)"), + noun("stick(s)"), + ], + pilin: [ + noun("heart(s)"), + noun("feeling(s)"), + verb("touch(ed)"), + verb("feel/felt"), + ], + pimeja: [ + singularNoun("darkness"), + adjectiveNounPhrase([adjective("dark", "color")], noun("color(s)")), + adjective("dark", "color"), + adjective("unlit", "color"), + adjective("black", "color"), + adjective("purple", "color"), + adjective("brown", "color"), + ], + pini: [ + singularNoun("past"), + verb("end(ed)"), + ], + pipi: [ + noun("insect(s)"), + noun("bug(s)"), + ], + poka: [ + noun("hip(s)"), + noun("side(s)"), + noun("vicinity/vicinities"), + adjective("nearby", "origin"), + ], + poki: [ + noun("container(s)"), + ], + pona: [ + singularNoun("goodness"), + singularNoun("simplicity"), + adjective("good", "opinion"), + adjective("positive", "opinion"), + adjective("useful", "opinion"), + adjective("friendly", "opinion"), + adjective("peaceful", "opinion"), + adjective("simple", "opinion"), + adverb("nicely"), + ], + sama: [ + noun("similarity/similarities"), + noun("sibling(s)"), + noun("peer(s)"), + noun("fellow(s)"), + adjective("same", "opinion"), + adjective("similar", "opinion"), + ], + seli: [ + singularNoun("fire"), + adjectiveNounPhrase( + [adjective("cooking", "qualifier")], + noun("element(s)"), + ), + adjectiveNounPhrase( + [adjective("chemical", "qualifier")], + noun("reaction(s)"), + ), + adjectiveNounPhrase( + [adjective("heat", "qualifier")], + noun("source(s)"), + ), + adjective("hot", "material"), + verb("heat(ed)"), + ], + selo: [ + adjectiveNounPhrase([adjective("outer", "origin")], noun("form(s)")), + adjectiveNounPhrase([adjective("outer", "origin")], noun("layer(s)")), + singularNoun("skin"), + noun("boundary/boundaries"), + ], + sewi: [ + // TODO: area above, something elevated + adjectiveNounPhrase([adjective("highest", "origin")], noun("part(s)")), + verb("elevate(d)"), + adjective("awe-inspiring", "opinion"), + adjective("divine", "opinion"), + adjective("sacred", "opinion"), + adjective("supernatural", "opinion"), + ], + sijelo: [ + noun("body/bodies"), + adjectiveNounPhrase([adjective("physical", "qualifier")], noun("state(s)")), + singularNoun("torso"), + ], + sike: [ + adjectiveNounPhrase( + [adjective("round", "physical quality")], + noun("thing(s)"), + ), + noun("cycle(s)"), + adjective("round", "physical quality"), + ], + sin: [ + singularNoun("newness"), + adjective("new", "age"), + adjective("fresh", "opinion"), + adjective("additional", "origin"), + adjective("extra", "origin"), + quantifier("another"), // It is a determiner. But is it a quantifier? + adverb("newly"), + ], + sina: [ + // TODO: pronouns + ], + sinpin: [ + noun("face(s)"), + noun("wall(s)"), + adjective("foremost", "origin"), + ], + sitelen: [ + noun("image(s)"), + noun("picture(s)"), + noun("representation(s)"), + noun("symbol(s)"), + noun("mark(s)"), + noun("writing(s)"), + ], + sona: [ + singularNoun("knowledge"), + ], + soweli: [ + adjectiveNounPhrase( + [adjective("fuzzy", "physical quality")], + noun("creature(s)"), + ), + adjectiveNounPhrase( + [adjective("land", "origin")], + noun("animal(s)"), + ), + noun("beast(s)"), + ], + suli: [ + singularNoun("hugeness"), + singularNoun("importance"), + adjective("big", "size"), + adjective("heavy", "size"), + adjective("important", "opinion"), + adjective("adult", "age"), + ], + suno: [ + singularNoun("sun"), + noun("light(s)"), + singularNoun("brightness"), + singularNoun("glow"), + singularNoun("radiance"), + adjectiveNounPhrase([adjective("light", "qualifier")], noun("source(s)")), + adjective("shining", "color"), + ], + supa: [ + adjectiveNounPhrase( + [adjective("horizontal", "physical quality")], + noun("surface(s)"), + ), + ], + suwi: [ + singularNoun("sweetness"), + singularNoun("cuteness"), + singularNoun("innocence"), + adjective("sweet", "material"), + adjective("cute", "opinion"), + adjective("innocent", "opinion"), + adverb("sweetly"), + ], + tan: [ + noun("origin(s)"), + noun("cause(s)"), + ], + taso: [ + quantifier("only"), + ], + tawa: [ + noun("motion(s)"), + singularNoun("travel"), + verb("shake(d)"), + intransitiveVerb("walk(ed)"), + gerund("walking"), + gerund("shaking"), + ], + telo: [ + singularNoun("water"), + singularNoun("liquid"), + adjectiveNounPhrase([adjective("wet", "material")], noun("substance(s)")), + noun("beverage(s)"), + adjective("liquid", "material"), + adjective("wet", "material"), + ], + tenpo: [ + singularNoun("time"), + singularNoun("duration"), + singularNoun("moment"), + noun("occasion(s)"), + noun("period(s)"), + noun("situation(s)"), + ], + toki: [ + noun("communication(s)"), + noun("language(s)"), + verb("communicate(d)", "about"), + // TODO: hello interjection + ], + tomo: [ + noun("building(s)"), + singularNoun("home"), + noun("house(s)"), + noun("room(s)"), + ], + tonsi: [ + adjectiveNounPhrase( + [adjective("non-binary", "qualifier")], + noun("person/people"), + ), + adjectiveNounPhrase( + [adjective("gender nonconforming", "qualifier")], + noun("person/people"), + ), + adjectiveNounPhrase( + [adjective("genderqueer", "qualifier")], + noun("person/people"), + ), + adjectiveNounPhrase( + [adjective("transgender", "qualifier")], + noun("person/people"), + ), + adjective("non-binary", "qualifier"), + adjective("gender nonconforming", "qualifier"), + adjective("genderqueer", "qualifier"), + adjective("transgender", "qualifier"), + ], + tu: [ + numeral(2), + verb("separate(d)"), + verb("divide(d)"), + verb("split"), + verb("multiply/multiplied"), + verb("duplicate(d)"), + ], + unpa: [ + adjective("sexual", "qualifier"), + adverb("sexually"), + verbObjectPhrase(verb("have/had", "with"), singularNoun("sex")), + ], + uta: [ + singularNoun("mouth"), + pluralNoun("lips"), + adjectiveNounPhrase( + [adjective("oral", "qualifier")], + noun("cavity/cavities"), + ), + noun("jaw(s)"), + ], + utala: [ + noun("battle(s)"), + noun("challenge(s)"), + verb("compete(d)", "against"), + verb("struggle(d)", "against"), + ], + walo: [ + adjectiveNounPhrase([adjective("light", "color")], noun("color(s)")), + singularNoun("white"), + adjectiveNounPhrase([adjective("light", "color")], singularNoun("gray")), + ], + wan: [ + numeral(1), + adjective("singular", "opinion"), + verb("combine(d)"), + verb("mix(ed)"), + verb("fuse(d)"), + ], + waso: [ + noun("bird(s)"), + adjectiveNounPhrase( + [adjective("flying", "qualifier")], + noun("creature(s)"), + ), + adjectiveNounPhrase( + [adjective("winged", "qualifier")], + noun("animal(s)"), + ), + intransitiveVerb("fly"), + gerund("flying"), + ], + wawa: [ + singularNoun("power"), + singularNoun("confidence"), + singularNoun("energy"), + singularNoun("intensity"), + adjective("strong", "opinion"), + adjective("powerful", "opinion"), + adjective("confident", "opinion"), + adjective("energetic", "opinion"), + adjective("intense", "opinion"), + adverb("powefully"), + ], + weka: [ + adjective("absent", "origin"), + adjective("away", "origin"), + adjective("ignored", "opinion"), + intransitiveVerb("leave"), + ], + wile: [ + noun("want(s)"), + noun("need(s)"), + ], +}; +DICTIONARY.ali = DICTIONARY.ale; + +export type AdjectiveType = + | "opinion" + | "size" + | "physical quality" + | "age" + | "color" + | "origin" + | "material" + | "qualifier"; export type Definition = | { type: "noun"; @@ -5,6 +892,11 @@ export type Definition = plural: null | string; condensed: string; } + | { + type: "adjective noun phrase"; + adjectives: Array; + noun: Definition & { type: "noun" }; + } | { type: "pronoun"; subject: string; @@ -14,7 +906,24 @@ export type Definition = | { type: "adjective"; adjective: string; - kind: never; + kind: AdjectiveType; + } + | { + type: "compound adjective"; + adjectives: Array; + } + | { + type: "adverb adjective phrase"; + adverbs: Array; + adjective: Definition & { type: "adjective" }; + } + | { + type: "quantifier"; + quantifier: string; + } + | { + type: "numeral"; + number: number; } | { type: "adverb"; @@ -22,12 +931,21 @@ export type Definition = } | { type: "verb"; - transitive: boolean; past: string; present: string; condensed: string; usePreposition: null | string; - withPreposition: null | never; + } + | { + type: "verb object phrase"; + verb: Definition & { type: "verb" }; + object: Definition & { type: "noun" }; + } + | { + type: "intransitive verb"; + past: string; + present: string; + condensed: string; } | { type: "gerund"; @@ -37,4 +955,169 @@ export type Definition = type: "modifier as preposition"; preposition: never; }; -export const DICTIONARY: { [word: string]: Array } = {}; +function noun(word: string): Definition & { type: "noun" } { + const paren = word.match(/([a-z]*)\(([a-z]*)\)/); + let singular: string; + let plural: string; + if (paren != null) { + const [_, first, second] = paren; + singular = first; + plural = first + second; + } else if (word.includes("/")) { + [singular, plural] = word.split("/"); + } else { + throw new Error(`${word} must either contain parenthesis or slash`); + } + return { + type: "noun", + singular, + plural, + condensed: word, + }; +} +function singularNoun(word: string): Definition & { type: "noun" } { + return { + type: "noun", + singular: word, + plural: null, + condensed: word, + }; +} +function pluralNoun(word: string): Definition & { type: "noun" } { + return { + type: "noun", + singular: null, + plural: word, + condensed: word, + }; +} +function adjectiveNounPhrase( + adjectives: Array, + noun: Definition & { type: "noun" }, +): Definition & { type: "adjective noun phrase" } { + return { + type: "adjective noun phrase", + adjectives, + noun, + }; +} +function adverbAdjectivePhrase( + adverbs: Array, + adjective: Definition & { type: "adjective" }, +): Definition & { type: "adverb adjective phrase" } { + return { + type: "adverb adjective phrase", + adverbs, + adjective, + }; +} +function compoundAdjective( + adjectives: Array, +): Definition & { type: "compound adjective" } { + return { + type: "compound adjective", + adjectives, + }; +} +function verb( + word: string, + usePreposition?: null | undefined | string, +): Definition & { type: "verb" } { + const paren = word.match(/([a-z]*)\(([a-z]*)\)(| [a-z]*)/); + let present: string; + let past: string; + if (paren != null) { + const [_, first, second, third] = paren; + present = first + third; + past = first + second + third; + } else { + const slash = word.match(/([a-z*])\/([a-z]*)(| [a-z]*)/); + if (slash != null) { + const [_, first, second, third] = slash; + present = first + third; + past = second + third; + } else { + present = word; + past = word; + } + } + return { + type: "verb", + present, + past, + condensed: word, + usePreposition: usePreposition ?? null, + }; +} +function verbObjectPhrase( + verb: Definition & { type: "verb" }, + object: Definition & { type: "noun" }, +): Definition & { type: "verb object phrase" } { + return { + type: "verb object phrase", + verb, + object, + }; +} +function intransitiveVerb( + word: string, +): Definition & { type: "intransitive verb" } { + const paren = word.match(/([a-z]*)\(([a-z]*)\)/); + let present: string; + let past: string; + if (paren != null) { + const [_, first, second] = paren; + present = first; + past = first + second; + } else { + const slash = word.match(/([a-z*])\/([a-z]*)(| [a-z]*)/); + if (slash != null) { + const [_, first, second, third] = slash; + present = first + third; + past = second + third; + } else { + present = word; + past = word; + } + } + return { + type: "intransitive verb", + present, + past, + condensed: word, + }; +} +function gerund(word: string): Definition & { type: "gerund" } { + return { + type: "gerund", + gerund: word, + }; +} +function adjective( + word: string, + kind: AdjectiveType, +): Definition & { type: "adjective" } { + return { + type: "adjective", + adjective: word, + kind, + }; +} +function numeral(number: number): Definition & { type: "numeral" } { + return { + type: "numeral", + number, + }; +} +function adverb(word: string): Definition & { type: "adverb" } { + return { + type: "adverb", + adverb: word, + }; +} +function quantifier(word: string): Definition & { type: "quantifier" } { + return { + type: "quantifier", + quantifier: word, + }; +} From 0d149c9388a354a65e18d261fcff237056301bc0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:03:22 +0800 Subject: [PATCH 189/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 097f5bf..3c170a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ You may need to force restart the page in order to use the latest version: shift Inside update (intended for developers): - Implement lexer. +- Overhaul dictionary. ## 0.2.2 From cad5ef80b320513b74d2ae8cf1ebed6253f773a6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:09:01 +0800 Subject: [PATCH 190/738] define pronouns --- src/dictionary.ts | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 0d45358..e30b08a 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -394,7 +394,15 @@ export const DICTIONARY: { [word: string]: Array } = { noun("wife/wives"), ], mi: [ - // TODO: pronouns + { + type: "pronoun", + singularSubject: "I", + singularObject: "me", + singularPossessive: "my", + pluralSubject: "we", + pluralObject: "us", + pluralPossessive: "our", + }, ], mije: [ noun("man/men"), @@ -505,7 +513,15 @@ export const DICTIONARY: { [word: string]: Array } = { // TODO: to have a strong emotional bond (with) ], ona: [ - // TODO: pronouns + { + type: "pronoun", + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "they", + pluralObject: "them", + pluralPossessive: "their", + }, ], open: [ noun("beginning(s)"), @@ -672,7 +688,15 @@ export const DICTIONARY: { [word: string]: Array } = { adverb("newly"), ], sina: [ - // TODO: pronouns + { + type: "pronoun", + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "you", + pluralObject: "you", + pluralPossessive: "your", + }, ], sinpin: [ noun("face(s)"), @@ -899,9 +923,12 @@ export type Definition = } | { type: "pronoun"; - subject: string; - object: string; - possessive: string; + singularSubject: null | string; + singularObject: null | string; + singularPossessive: null | string; + pluralSubject: string; + pluralObject: string; + pluralPossessive: string; } | { type: "adjective"; From 27d34d35a43fb883bda8a26626207b8f53d1b0e4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:11:17 +0800 Subject: [PATCH 191/738] fix noun forms --- src/dictionary.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index e30b08a..e3c238d 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -147,7 +147,7 @@ export const DICTIONARY: { [word: string]: Array } = { intransitiveVerb("sound(ed)"), ], kama: [ - noun("future"), + singularNoun("future"), adjective("future", "age"), verb("summon(ed)"), intransitiveVerb("arrive"), @@ -422,7 +422,7 @@ export const DICTIONARY: { [word: string]: Array } = { verb("ingest(ed)"), ], moli: [ - noun("death"), + singularNoun("death"), adjective("dead", "age"), verb("kill(ed)"), ], From c805aa4a0a96f946c1a1c2e641a8d9ba593bdff3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:16:48 +0800 Subject: [PATCH 192/738] separate pronoun as special case --- src/dictionary.ts | 76 +++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index e3c238d..9c0eb53 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -5,6 +5,32 @@ export const SPECIAL_CONTENT_WORD = new Set([ "pu", "seme", ]); +export const PRONOUN: { [word: string]: Pronoun } = { + mi: { + singularSubject: "I", + singularObject: "me", + singularPossessive: "my", + pluralSubject: "we", + pluralObject: "us", + pluralPossessive: "our", + }, + sina: { + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "you", + pluralObject: "you", + pluralPossessive: "your", + }, + ona: { + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "they", + pluralObject: "them", + pluralPossessive: "their", + }, +}; export const DICTIONARY: { [word: string]: Array } = { akesi: [ noun("reptile(s)"), @@ -393,17 +419,6 @@ export const DICTIONARY: { [word: string]: Array } = { ), noun("wife/wives"), ], - mi: [ - { - type: "pronoun", - singularSubject: "I", - singularObject: "me", - singularPossessive: "my", - pluralSubject: "we", - pluralObject: "us", - pluralPossessive: "our", - }, - ], mije: [ noun("man/men"), // noun("male(s)"), // this sounds dehumanizing @@ -512,17 +527,6 @@ export const DICTIONARY: { [word: string]: Array } = { verb("respect(ed)"), // TODO: to have a strong emotional bond (with) ], - ona: [ - { - type: "pronoun", - singularSubject: null, - singularObject: null, - singularPossessive: null, - pluralSubject: "they", - pluralObject: "them", - pluralPossessive: "their", - }, - ], open: [ noun("beginning(s)"), singularNoun("start"), @@ -687,17 +691,6 @@ export const DICTIONARY: { [word: string]: Array } = { quantifier("another"), // It is a determiner. But is it a quantifier? adverb("newly"), ], - sina: [ - { - type: "pronoun", - singularSubject: null, - singularObject: null, - singularPossessive: null, - pluralSubject: "you", - pluralObject: "you", - pluralPossessive: "your", - }, - ], sinpin: [ noun("face(s)"), noun("wall(s)"), @@ -909,6 +902,14 @@ export type AdjectiveType = | "origin" | "material" | "qualifier"; +export type Pronoun = { + singularSubject: null | string; + singularObject: null | string; + singularPossessive: null | string; + pluralSubject: string; + pluralObject: string; + pluralPossessive: string; +}; export type Definition = | { type: "noun"; @@ -921,15 +922,6 @@ export type Definition = adjectives: Array; noun: Definition & { type: "noun" }; } - | { - type: "pronoun"; - singularSubject: null | string; - singularObject: null | string; - singularPossessive: null | string; - pluralSubject: string; - pluralObject: string; - pluralPossessive: string; - } | { type: "adjective"; adjective: string; From f95bc8c4ea4e6078221958735bb82e42cf6a76b0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:18:01 +0800 Subject: [PATCH 193/738] add lili as special case --- src/dictionary.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index 9c0eb53..fab5c36 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -4,6 +4,7 @@ export const SPECIAL_CONTENT_WORD = new Set([ "ni", "pu", "seme", + "lili", ]); export const PRONOUN: { [word: string]: Pronoun } = { mi: { From 7fc2cbd332cba756ab3183c122d75c44df8adcec Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 14:31:18 +0800 Subject: [PATCH 194/738] implement preposition --- src/dictionary.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index fab5c36..1ae7cf0 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -32,6 +32,13 @@ export const PRONOUN: { [word: string]: Pronoun } = { pluralPossessive: "their", }, }; +export const PREPOSITION: { [word: string]: Array } = { + kepeken: ["using"], + lon: ["at"], + sama: ["similar to"], + tan: ["from", "because of"], + tawa: ["towards", "in perspective of"], +}; export const DICTIONARY: { [word: string]: Array } = { akesi: [ noun("reptile(s)"), From 29fda521be1d04df3e6cfc9b41e5b453fbb09533 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:01:06 +0800 Subject: [PATCH 195/738] add preverb scratch file --- preverbs.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 preverbs.md diff --git a/preverbs.md b/preverbs.md new file mode 100644 index 0000000..cc2a258 --- /dev/null +++ b/preverbs.md @@ -0,0 +1,49 @@ +- alasa - try to, attempt +- awen - to continue to, to keep +- kama - to become, manage to, succeed in +- ken - to be able to, be allowed to, can, may +- lukin - try to +- open - to start +- pini - to stop +- sona - know how to +- wile - must, need, require, should, want, wish + +| alasa/lukin | noun | adjective | verb | +| ----------- | ------------------ | ----------------- | --------------- | +| noun phrase | trying to be @noun | trying to be @adj | trying to @verb | +| verb phrase | try to be @noun | @try to be @adj | try to @verb | + +| awen | noun | adjective | verb | +| ----------- | ---------------------- | --------------------- | ------------------- | +| noun phrase | continuing to be @noun | continuing to be @adj | continuing to @verb | +| verb phrase | continue to be @noun | continue to be @adj | continue to @verb | + +| kama | noun | adjective | verb | +| ----------- | -------------- | ------------- | ---- | +| noun phrase | becoming @noun | becoming @adj | | +| verb phrase | | | | + +| ken | noun | adjective | verb | +| ----------- | ---------------- | --------------- | ------------- | +| noun phrase | able to be @noun | able to be @adj | able to @verb | +| verb phrase | | | | + +| open | noun | adjective | verb | +| ----------- | -------------------- | ------------------- | ----------------- | +| noun phrase | starting to be @noun | starting to be @adj | starting to @verb | +| verb phrase | | | | + +| pini | noun | adjective | verb | +| ----------- | -------------------- | ------------------- | ----------------- | +| noun phrase | stopping to be @noun | stopping to be @adj | stopping to @verb | +| verb phrase | | | | + +| sona | noun | adjective | verb | +| ----------- | ----------------------- | ---------------------- | -------------------- | +| noun phrase | knowing how to be @noun | knowing how to be @adj | knowing how to @verb | +| verb phrase | know how to be @noun | know how to be @adj | know how to @adj | + +| wile | noun | adjective | verb | +| ----------- | ------------------- | ------------------ | ---------------- | +| noun phrase | wanting to be @noun | wanting to be @adj | wanting to @verb | +| verb phrase | want to be @noun | want to be @adj | want to @verb | From 4fbae3707e290a37ce840d9308a85d3abdb4da6c Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:19:31 +0800 Subject: [PATCH 196/738] remove vocabulary.ts --- src/ast-parser.ts | 9 +-- src/dictionary.ts | 40 ++++++++++-- src/vocabulary.ts | 155 ---------------------------------------------- 3 files changed, 38 insertions(+), 166 deletions(-) delete mode 100644 src/vocabulary.ts diff --git a/src/ast-parser.ts b/src/ast-parser.ts index c2287cc..2102224 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -17,12 +17,7 @@ import { } from "./ast.ts"; import { CoveredError, UnexpectedError, UnrecognizedError } from "./error.ts"; import { Output } from "./output.ts"; -import { - CONTENT_WORD, - PREPOSITION, - PREVERB, - SPECIAL_SUBJECT, -} from "./vocabulary.ts"; +import { CONTENT_WORD, PREPOSITION, PREVERB } from "./dictionary.ts"; import { CLAUSE_RULE, filter, @@ -623,7 +618,7 @@ function multiplePredicates( function clause(): AstParser { return choice( sequence( - wordFrom(SPECIAL_SUBJECT, "mi/sina subject"), + wordFrom(new Set(["mi", "sina"]), "mi/sina subject"), multiplePredicates(["li", "anu"]), ).map(([subject, predicates]) => ({ diff --git a/src/dictionary.ts b/src/dictionary.ts index 1ae7cf0..2db138e 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,3 +1,16 @@ +export const PARTICLES = new Set([ + "a", + "ala", + "anu", + "e", + "en", + "la", + "li", + "nanpa", + "o", + "pi", + "taso", +]); export const SPECIAL_CONTENT_WORD = new Set([ "ala", "mu", @@ -6,7 +19,7 @@ export const SPECIAL_CONTENT_WORD = new Set([ "seme", "lili", ]); -export const PRONOUN: { [word: string]: Pronoun } = { +export const PRONOUN_DEFINITION: { [word: string]: Pronoun } = { mi: { singularSubject: "I", singularObject: "me", @@ -32,14 +45,25 @@ export const PRONOUN: { [word: string]: Pronoun } = { pluralPossessive: "their", }, }; -export const PREPOSITION: { [word: string]: Array } = { +export const PREPOSITION_DEFINITION: { [word: string]: Array } = { kepeken: ["using"], lon: ["at"], sama: ["similar to"], tan: ["from", "because of"], tawa: ["towards", "in perspective of"], }; -export const DICTIONARY: { [word: string]: Array } = { +export const PREVERB_DEFINITION: { [word: string]: Array } = { + alasa: [], + awen: [], + kama: [], + ken: [], + lukin: [], + open: [], + pini: [], + sona: [], + wile: [], +}; +export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { akesi: [ noun("reptile(s)"), noun("amphibian(s)"), @@ -899,7 +923,7 @@ export const DICTIONARY: { [word: string]: Array } = { noun("need(s)"), ], }; -DICTIONARY.ali = DICTIONARY.ale; +CONTENT_WORD_DEFINITION.ali = CONTENT_WORD_DEFINITION.ale; export type AdjectiveType = | "opinion" @@ -982,6 +1006,14 @@ export type Definition = type: "modifier as preposition"; preposition: never; }; +export const CONTENT_WORD = new Set([ + ...SPECIAL_CONTENT_WORD, + ...Object.keys(PRONOUN_DEFINITION), + ...Object.keys(CONTENT_WORD_DEFINITION), +]); +export const PREVERB = new Set(Object.keys(PREVERB_DEFINITION)); +export const PREPOSITION = new Set(Object.keys(PREPOSITION_DEFINITION)); + function noun(word: string): Definition & { type: "noun" } { const paren = word.match(/([a-z]*)\(([a-z]*)\)/); let singular: string; diff --git a/src/vocabulary.ts b/src/vocabulary.ts deleted file mode 100644 index ac4b4ad..0000000 --- a/src/vocabulary.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** Module for accepted words. */ - -/** */ -export const PARTICLES = new Set([ - "a", - "ala", - "anu", - "e", - "en", - "la", - "li", - "nanpa", - "o", - "pi", - "taso", -]); -export const CONTENT_WORD = new Set([ - "akesi", - "ala", - "alasa", - "ale", - "ali", - "anpa", - "ante", - "awen", - "esun", - "ijo", - "ike", - "ilo", - "insa", - "jaki", - "jan", - "jelo", - "jo", - "kala", - "kalama", - "kama", - "kasi", - "ken", - "kepeken", - "kili", - "kiwen", - "ko", - "kon", - "kule", - "kulupu", - "kute", - "lape", - "laso", - "lawa", - "len", - "lete", - "lili", - "linja", - "lipu", - "loje", - "lon", - "luka", - "lukin", - "lupa", - "ma", - "mama", - "mani", - "meli", - "mi", - "mije", - "moku", - "moli", - "monsi", - "mu", - "mun", - "musi", - "mute", - "nanpa", - "nasa", - "nasin", - "nena", - "ni", - "nimi", - "noka", - "olin", - "ona", - "open", - "pakala", - "pali", - "palisa", - "pan", - "pana", - "pilin", - "pimeja", - "pini", - "pipi", - "poka", - "poki", - "pona", - "pu", - "sama", - "seli", - "selo", - "seme", - "sewi", - "sijelo", - "sike", - "sin", - "sina", - "sinpin", - "sitelen", - "sona", - "soweli", - "suli", - "suno", - "supa", - "suwi", - "tan", - "taso", - "tawa", - "telo", - "tenpo", - "toki", - "tomo", - "tonsi", - "tu", - "unpa", - "uta", - "utala", - "walo", - "wan", - "waso", - "wawa", - "weka", - "wile", -]); -/** Special subjects that doesn't use _li_ */ -export const SPECIAL_SUBJECT = new Set(["mi", "sina"]); -export const NUMBER = new Set(["wan", "tu", "luka", "mute", "ale", "ali"]); -export const PREVERB = new Set([ - "alasa", - "awen", - "kama", - "ken", - "lukin", - "open", - "pini", - "sona", - "wile", -]); -export const PREPOSITION = new Set([ - "kepeken", - "lon", - "sama", - "tan", - "tawa", -]); -/** Full vocabulary. */ -export const VOCABULARY = new Set([...PARTICLES, ...CONTENT_WORD]); From 21db9d8e021b39e3bbfead9d25ef335625aa0e88 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:20:20 +0800 Subject: [PATCH 197/738] actually use postclause parser --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 2102224..893cb20 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -721,7 +721,7 @@ function fullClause(): AstParser { sequence( optional(preclause().skip(optionalComma())), clause(), - optional(optionalComma().with(specificWord("anu"))), + optional(optionalComma().with(postclause())), ).map(([preclause, clause, postclause]) => ({ type: "default", From 908ed4a796c35f4556c5dd7b7d5bfed358c2240b Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:21:33 +0800 Subject: [PATCH 198/738] remove unused definition type --- src/dictionary.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 2db138e..b9c1dfa 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1001,10 +1001,6 @@ export type Definition = | { type: "gerund"; gerund: string; - } - | { - type: "modifier as preposition"; - preposition: never; }; export const CONTENT_WORD = new Set([ ...SPECIAL_CONTENT_WORD, From d3a58b8fe518c7d9730f4cac5b743064b7318801 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:26:09 +0800 Subject: [PATCH 199/738] update verb definition --- src/dictionary.ts | 48 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index b9c1dfa..d805f17 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -985,19 +985,13 @@ export type Definition = past: string; present: string; condensed: string; - usePreposition: null | string; + object: boolean | string; } | { type: "verb object phrase"; verb: Definition & { type: "verb" }; object: Definition & { type: "noun" }; } - | { - type: "intransitive verb"; - past: string; - present: string; - condensed: string; - } | { type: "gerund"; gerund: string; @@ -1074,10 +1068,9 @@ function compoundAdjective( adjectives, }; } -function verb( +function parseVerb( word: string, - usePreposition?: null | undefined | string, -): Definition & { type: "verb" } { +): { past: string; present: string; condensed: string } { const paren = word.match(/([a-z]*)\(([a-z]*)\)(| [a-z]*)/); let present: string; let past: string; @@ -1096,12 +1089,19 @@ function verb( past = word; } } + return { past, present, condensed: word }; +} +function verb( + word: string, + usePreposition?: null | undefined | string, +): Definition & { type: "verb" } { + const { past, present, condensed } = parseVerb(word); return { type: "verb", present, past, condensed: word, - usePreposition: usePreposition ?? null, + object: usePreposition ?? true, }; } function verbObjectPhrase( @@ -1116,30 +1116,14 @@ function verbObjectPhrase( } function intransitiveVerb( word: string, -): Definition & { type: "intransitive verb" } { - const paren = word.match(/([a-z]*)\(([a-z]*)\)/); - let present: string; - let past: string; - if (paren != null) { - const [_, first, second] = paren; - present = first; - past = first + second; - } else { - const slash = word.match(/([a-z*])\/([a-z]*)(| [a-z]*)/); - if (slash != null) { - const [_, first, second, third] = slash; - present = first + third; - past = second + third; - } else { - present = word; - past = word; - } - } +): Definition & { type: "verb" } { + const { past, present, condensed } = parseVerb(word); return { - type: "intransitive verb", + type: "verb", present, past, - condensed: word, + condensed, + object: false, }; } function gerund(word: string): Definition & { type: "gerund" } { From 4bb3a2e38673632938f53d7b04b1127ee66fc167 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 15:36:30 +0800 Subject: [PATCH 200/738] combine gerund and verb definitions --- src/dictionary.ts | 208 +++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 123 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index d805f17..f15447e 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -71,10 +71,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("amphibian", "qualifier"), ], alasa: [ - verb("hunt(ed)"), - verb("search(ed)"), - gerund("searching"), - gerund("hunting"), + verb("hunt(ed)", "hunting"), + verb("search(ed)", "searching"), // TODO: preverb ], ale: [ @@ -102,32 +100,21 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("altered", "opinion"), ], awen: [ - intransitiveVerb("wait(ed)"), - intransitiveVerb("stay(ed)"), - intransitiveVerb("endure(d)"), - verb("keep/kept"), - verb("protect(ed)"), - gerund("waiting"), - gerund("staying"), - gerund("enduring"), - gerund("keeping"), - gerund("protecting"), + intransitiveVerb("wait(ed)", "waiting"), + intransitiveVerb("stay(ed)", "staying"), + intransitiveVerb("endure(d)", "enduring"), + verb("keep/kept", "keeping"), + verb("protect(ed)", "protecting"), // TODO: preverb ], esun: [ noun("shop(s)"), - verb("trade(d)"), - verb("barter(ed)"), - verb("exchange(d)"), - verb("swap(ped)"), - verb("buy/bought"), - verb("sell/sold"), - gerund("trading"), - gerund("bartering"), - gerund("exchanging"), - gerund("swapping"), - gerund("buying"), - gerund("selling"), + verb("trade(d)", "trading"), + verb("barter(ed)", "bartering"), + verb("exchange(d)", "exchanging"), + verb("swap(ped)", "swapping"), + verb("buy/bought", "buying"), + verb("sell/sold", "selling"), ], ijo: [ noun("phenomenon(s)"), @@ -185,32 +172,25 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adverbAdjectivePhrase([adverb("yellowish")], adjective("orange", "color")), ], jo: [ - verb("have/had"), - verb("carry/carried"), - verb("contain(ed)"), - verb("hold/held"), - gerund("having"), - gerund("carrying"), - gerund("containing"), - gerund("holding"), + verb("have/had", "having"), + verb("carry/carried", "carrying"), + verb("contain(ed)", "containing"), + verb("hold/held", "holding"), ], kala: [ noun("fish(es)"), adjectiveNounPhrase([adjective("sea", "qualifier")], noun("creature(s)")), - gerund("swimming"), - intransitiveVerb("swim"), + intransitiveVerb("swim", "swimming"), ], kalama: [ noun("sound(s)"), - intransitiveVerb("sound(ed)"), + intransitiveVerb("sound(ed)", "sounding"), ], kama: [ singularNoun("future"), adjective("future", "age"), - verb("summon(ed)"), - intransitiveVerb("arrive"), - gerund("arriving"), - gerund("summoning"), + verb("summon(ed)", "summoning"), + intransitiveVerb("arrive", "arriving"), ], kasi: [ noun("plant(s)"), @@ -285,8 +265,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], kute: [ noun("ear(s)"), - gerund("listening"), - verb("listen(ed)", "at"), + verb("listen(ed)", "listening", "at"), ], laso: [ singularNoun("turquoise"), @@ -308,27 +287,26 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("guide(s)"), noun("plan(s)"), noun("rule(s)"), - verb("control(led)"), - verb("direct(ed)"), - verb("guide(d)"), - verb("lead/led"), - verb("own(ed"), - verb("regulate(d)"), - verb("rule(d)"), + verb("control(led)", "controlling"), + verb("direct(ed)", "directing"), + verb("guide(d)", "guiding"), + verb("lead/led", "leading"), + verb("own(ed)", "owning"), + verb("regulate(d)", "regulating"), + verb("rule(d)", "ruling"), ], len: [ noun("clothing(s)"), noun("fabric(s)"), - gerund("hiding"), adjective("hidden", "origin"), - verb("cover(ed)"), + verb("cover(ed)", "covering"), ], lete: [ singularNoun("coldness"), adjective("cool", "physical quality"), adjective("cold", "physical quality"), adjective("frozen", "physical quality"), - verb("freeze/froze"), + verb("freeze/froze", "freezing"), ], lili: [ // TODO: "piece of" prefix @@ -386,9 +364,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { lon: [ singularNoun("truth"), adjective("real", "opinion"), - gerund("existing"), adverb("truthfully"), - verb("exist"), + verb("exist", "existing"), // TODO: preposition ], luka: [ @@ -403,9 +380,9 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("visual(s)"), noun("eye(s)"), adjectiveNounPhrase([adjective("seeing", "qualifier")], noun("organ(s)")), - verb("look(ed)", "at"), - verb("read"), - verb("watch(ed)"), + verb("look(ed)", "looking", "at"), + verb("read", "reading"), + verb("watch(ed)", "watchingF"), ], lupa: [ noun("hole(s)"), @@ -463,15 +440,15 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { moku: [ noun("food(s)"), noun("drink(s)"), - verb("eat/ate"), - verb("drink/drank"), - verb("consume(d)"), - verb("ingest(ed)"), + verb("eat/ate", "eating"), + verb("drink/drank", "drinking"), + verb("consume(d)", "consuming"), + verb("ingest(ed)", "ingesting"), ], moli: [ singularNoun("death"), adjective("dead", "age"), - verb("kill(ed)"), + verb("kill(ed)", "killing"), ], monsi: [ singularNoun("back"), @@ -500,7 +477,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("interesting", "opinion"), adjective("comical", "opinion"), adjective("silly", "opinion"), - verbObjectPhrase(verb("have/had"), singularNoun("fun")), + verbObjectPhrase(verb("have/had", "having"), singularNoun("fun")), ], mute: [ numeral(20), @@ -556,7 +533,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("platonic", "qualifier"), adjective("romantic", "qualifier"), adjective("familial", "qualifier"), - verb("respect(ed)"), + verb("respect(ed)", "respecting"), // TODO: to have a strong emotional bond (with) ], open: [ @@ -564,19 +541,19 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("start"), adjective("open", "physical quality"), adjective("turned on", "qualifier"), - verb("start(ed)"), - verb("turn(ed) on"), + verb("start(ed)", "starting"), + verb("turn(ed) on", "turning on"), ], pakala: [ singularNoun("mess"), noun("damage(s)"), adjective("botched", "opinion"), adjective("broken", "opinion"), - verb("botch(ed)"), - verb("break/broke"), - verb("damage(d)"), - verb("harm(ed)"), - verb("mess(ed) up"), + verb("botch(ed)", "botching"), + verb("break/broke", "breaking"), + verb("damage(d)", "damaging"), + verb("harm(ed)", "harming"), + verb("mess(ed) up", "messing up"), ], pan: [ noun("grain(s)"), @@ -584,30 +561,19 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjectiveNounPhrase([adjective("baked", "qualifier")], pluralNoun("goods")), ], pana: [ - verb("give/gave"), - verb("send/sent"), - verb("emit(ted)"), - verb("provide(d)"), - verb("put"), - verb("release(d)"), - gerund("giving"), - gerund("sending"), - gerund("emitting"), - gerund("providing"), - gerund("putting"), - gerund("releasing"), + verb("give/gave", "giving"), + verb("send/sent", "sending"), + verb("emit(ted)", "emitting"), + verb("provide(d)", "providing"), + verb("put", "putting"), + verb("release(d)", "releasing"), ], pali: [ - verb("build"), - verb("make/made"), - verb("prepare(d)"), - intransitiveVerb("do/did"), - intransitiveVerb("work/worked"), - gerund("building"), - gerund("making"), - gerund("preparing"), - gerund("doing"), - gerund("working"), + verb("build", "building"), + verb("make/made", "making"), + verb("prepare(d)", "preparing"), + intransitiveVerb("do/did", "doing"), + intransitiveVerb("work/worked", "working"), ], palisa: [ adjectiveNounPhrase([ @@ -621,8 +587,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { pilin: [ noun("heart(s)"), noun("feeling(s)"), - verb("touch(ed)"), - verb("feel/felt"), + verb("touch(ed)", "touching"), + verb("feel/felt", "feeling"), ], pimeja: [ singularNoun("darkness"), @@ -635,7 +601,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], pini: [ singularNoun("past"), - verb("end(ed)"), + verb("end(ed)", "ending"), ], pipi: [ noun("insect(s)"), @@ -684,7 +650,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("source(s)"), ), adjective("hot", "material"), - verb("heat(ed)"), + verb("heat(ed)", "heating"), ], selo: [ adjectiveNounPhrase([adjective("outer", "origin")], noun("form(s)")), @@ -695,7 +661,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { sewi: [ // TODO: area above, something elevated adjectiveNounPhrase([adjective("highest", "origin")], noun("part(s)")), - verb("elevate(d)"), + verb("elevate(d)", "elavating"), adjective("awe-inspiring", "opinion"), adjective("divine", "opinion"), adjective("sacred", "opinion"), @@ -792,10 +758,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { tawa: [ noun("motion(s)"), singularNoun("travel"), - verb("shake(d)"), - intransitiveVerb("walk(ed)"), - gerund("walking"), - gerund("shaking"), + verb("shake(d)", "shaking"), + intransitiveVerb("walk(ed)", "walking"), ], telo: [ singularNoun("water"), @@ -816,7 +780,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { toki: [ noun("communication(s)"), noun("language(s)"), - verb("communicate(d)", "about"), + verb("communicate(d)", "communicating", "about"), // TODO: hello interjection ], tomo: [ @@ -849,16 +813,16 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], tu: [ numeral(2), - verb("separate(d)"), - verb("divide(d)"), - verb("split"), - verb("multiply/multiplied"), - verb("duplicate(d)"), + verb("separate(d)", "separating"), + verb("divide(d)", "dividing"), + verb("split", "splitting"), + verb("multiply/multiplied", "multiplying"), + verb("duplicate(d)", "duplicating"), ], unpa: [ adjective("sexual", "qualifier"), adverb("sexually"), - verbObjectPhrase(verb("have/had", "with"), singularNoun("sex")), + verbObjectPhrase(verb("have/had", "having", "with"), singularNoun("sex")), ], uta: [ singularNoun("mouth"), @@ -883,9 +847,9 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { wan: [ numeral(1), adjective("singular", "opinion"), - verb("combine(d)"), - verb("mix(ed)"), - verb("fuse(d)"), + verb("combine(d)", "combining"), + verb("mix(ed)", "mixing"), + verb("fuse(d)", "fusing"), ], waso: [ noun("bird(s)"), @@ -897,8 +861,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { [adjective("winged", "qualifier")], noun("animal(s)"), ), - intransitiveVerb("fly"), - gerund("flying"), + intransitiveVerb("fly", "flying"), ], wawa: [ singularNoun("power"), @@ -916,7 +879,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("absent", "origin"), adjective("away", "origin"), adjective("ignored", "opinion"), - intransitiveVerb("leave"), + intransitiveVerb("leave", "leaving"), ], wile: [ noun("want(s)"), @@ -985,6 +948,7 @@ export type Definition = past: string; present: string; condensed: string; + gerund: string; object: boolean | string; } | { @@ -1093,6 +1057,7 @@ function parseVerb( } function verb( word: string, + gerund: string, usePreposition?: null | undefined | string, ): Definition & { type: "verb" } { const { past, present, condensed } = parseVerb(word); @@ -1100,7 +1065,8 @@ function verb( type: "verb", present, past, - condensed: word, + condensed, + gerund, object: usePreposition ?? true, }; } @@ -1116,6 +1082,7 @@ function verbObjectPhrase( } function intransitiveVerb( word: string, + gerund: string, ): Definition & { type: "verb" } { const { past, present, condensed } = parseVerb(word); return { @@ -1123,15 +1090,10 @@ function intransitiveVerb( present, past, condensed, + gerund, object: false, }; } -function gerund(word: string): Definition & { type: "gerund" } { - return { - type: "gerund", - gerund: word, - }; -} function adjective( word: string, kind: AdjectiveType, From f4492c3dac4602a5a2d7049eb463590e00eedd7f Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 16:04:35 +0800 Subject: [PATCH 201/738] expand words --- src/dictionary.ts | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index f15447e..1c3bc28 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -4,8 +4,10 @@ export const PARTICLES = new Set([ "anu", "e", "en", + "kin", "la", "li", + "n", "nanpa", "o", "pi", @@ -18,6 +20,11 @@ export const SPECIAL_CONTENT_WORD = new Set([ "pu", "seme", "lili", + "kokosila", + "kijetesantakalu", + "ku", + "su", + "jasima", ]); export const PRONOUN_DEFINITION: { [word: string]: Pronoun } = { mi: { @@ -107,6 +114,12 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("protect(ed)", "protecting"), // TODO: preverb ], + epiku: [ + adjective("epic", "opinion"), + adjective("cool", "opinion"), + adjective("awesome", "opinion"), + adjective("amazing", "opinion"), + ], esun: [ noun("shop(s)"), verb("trade(d)", "trading"), @@ -156,6 +169,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("person/people"), singularNoun("somebody"), // This is technically a pronoun ], + jasima: [ + noun("reflection(s)"), + noun("mirror(s)"), + verb("reflect(ed)", "reflecting"), + verb("resound(ed)", "resounding"), + verb("mirror(ed)", "mirroring"), + ], jelo: [ singularNoun("yellow"), adjectiveNounPhrase( @@ -210,6 +230,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("vegetable(s)"), noun("mushroom(s)"), ], + kipisi: [ + adjective("sharp", "physical quality"), + verb("split(ted)", "splitting"), + verb("cut", "cutting"), + verb("slice(d)", "slicing"), + verb("sever(ed)", "severing"), + ], kiwen: [ adjectiveNounPhrase( [adjective("hard", "physical quality")], @@ -267,6 +294,19 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("ear(s)"), verb("listen(ed)", "listening", "at"), ], + lanpan: [ + verb("take/took", "taking"), + verb("seize(d)", "seizing"), + verb("catch(ed)", "catching"), + verb("receive(d)", "receiving"), + verb("get/got", "getting"), + ], + lape: [ + singularNoun("sleep"), + singularNoun("rest"), + intransitiveVerb("sleep/slept", "sleeping"), + intransitiveVerb("rest(ed)", "resting"), + ], laso: [ singularNoun("turquoise"), singularNoun("blue"), @@ -295,6 +335,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("regulate(d)", "regulating"), verb("rule(d)", "ruling"), ], + leko: [ + pluralNoun("stairs"), + noun("square(s)"), + noun("block(s)"), + noun("corner(s)"), + noun("cube(s)"), + ], len: [ noun("clothing(s)"), noun("fabric(s)"), @@ -400,6 +447,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("territory/territories"), singularNoun("soil"), ], + majuna: [ + adjective("old", "age"), + adjective("aged", "age"), + adjective("ancient", "age"), + ], mama: [ noun("parent(s)"), noun("ancestor(s)"), @@ -428,6 +480,12 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ), noun("wife/wives"), ], + meso: [ + noun("midpoint(s)"), + adjective("midpoint", "opinion"), + adjective("medium", "size"), + adjective("mediocre", "opinion"), + ], mije: [ noun("man/men"), // noun("male(s)"), // this sounds dehumanizing @@ -437,6 +495,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ), noun("husband(s)"), ], + misikeke: [ + noun("medicine(s)"), + adjective("medical", "qualifier"), + ], moku: [ noun("food(s)"), noun("drink(s)"), @@ -455,6 +517,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("behind"), singularNoun("rear"), ], + monsuta: [ + noun("monster(s)"), + noun("predator(s)"), + noun("threat(s)"), + noun("danger(s)"), + adjective("scary", "opinion"), + ], mun: [ singularNoun("moon"), adjectiveNounPhrase( @@ -525,6 +594,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("organ(s)"), ), ], + oko: [], // Will be duplicated with "lukin" olin: [ singularNoun("affection"), singularNoun("appreciation"), @@ -702,6 +772,9 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("mark(s)"), noun("writing(s)"), ], + soko: [ + noun("fungus/fungi"), + ], sona: [ singularNoun("knowledge"), ], @@ -887,6 +960,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], }; CONTENT_WORD_DEFINITION.ali = CONTENT_WORD_DEFINITION.ale; +CONTENT_WORD_DEFINITION.oko = CONTENT_WORD_DEFINITION.lukin; export type AdjectiveType = | "opinion" From 02ed789ce099388eee6c1ba732701da37ff70d5d Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 16:06:15 +0800 Subject: [PATCH 202/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c170a4..ace0a5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ You may need to force restart the page in order to use the latest version: shift - Cartouche with nasin sitelen kalama - Combined glyphs - (Deprecated characters and combiners are not supported) +- The vocabulary has been expanded to nimi ku suli plus nimi su! (Some are still unimplemented) - Multiline text will no longer be recognized. - Add icons. From acf014a1d12ffc83c0806a5b635511104945ac30 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 16:53:55 +0800 Subject: [PATCH 203/738] alphabetized special content words --- src/dictionary.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 1c3bc28..59499bf 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -15,16 +15,16 @@ export const PARTICLES = new Set([ ]); export const SPECIAL_CONTENT_WORD = new Set([ "ala", + "jasima", + "kijetesantakalu", + "kokosila", + "ku", + "lili", "mu", "ni", "pu", "seme", - "lili", - "kokosila", - "kijetesantakalu", - "ku", "su", - "jasima", ]); export const PRONOUN_DEFINITION: { [word: string]: Pronoun } = { mi: { From 3e25c73f1d14af84705459a919d6328a0ded4a94 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 16:56:09 +0800 Subject: [PATCH 204/738] remove unneeded todos --- src/dictionary.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 59499bf..a52f08f 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -80,7 +80,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { alasa: [ verb("hunt(ed)", "hunting"), verb("search(ed)", "searching"), - // TODO: preverb ], ale: [ numeral(100), @@ -112,7 +111,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { intransitiveVerb("endure(d)", "enduring"), verb("keep/kept", "keeping"), verb("protect(ed)", "protecting"), - // TODO: preverb ], epiku: [ adjective("epic", "opinion"), @@ -220,11 +218,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ken: [ noun("ability/abilities"), noun("possibility/possibilities"), - // TODO: preverb - ], - kepeken: [ - // TODO: preposition ], + kepeken: [], kili: [ noun("fruit(s)"), noun("vegetable(s)"), @@ -356,7 +351,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("freeze/froze", "freezing"), ], lili: [ - // TODO: "piece of" prefix singularNoun("smallness"), adjective("small", "physical quality"), adjective("short", "physical quality"), @@ -413,7 +407,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("real", "opinion"), adverb("truthfully"), verb("exist", "existing"), - // TODO: preposition ], luka: [ numeral(5), From a745a435d17404f138eb2ead8f650abe94121301 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 16:56:40 +0800 Subject: [PATCH 205/738] add definition for ala --- src/dictionary.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index a52f08f..6209c35 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -77,6 +77,9 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("reptilian", "qualifier"), adjective("amphibian", "qualifier"), ], + ala: [ + numeral(0), + ], alasa: [ verb("hunt(ed)", "hunting"), verb("search(ed)", "searching"), From 61759145ae0aaa33da316a24015179cd36f172f7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 17:04:36 +0800 Subject: [PATCH 206/738] update dictionary --- src/dictionary.ts | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 6209c35..423a0a7 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -14,14 +14,15 @@ export const PARTICLES = new Set([ "taso", ]); export const SPECIAL_CONTENT_WORD = new Set([ - "ala", - "jasima", + "ala", // not + "jasima", // opposite of "kijetesantakalu", "kokosila", "ku", - "lili", + "lili", // piece of "mu", "ni", + "olin", // have a strong emotional bond with "pu", "seme", "su", @@ -79,6 +80,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], ala: [ numeral(0), + quantifier("no"), ], alasa: [ verb("hunt(ed)", "hunting"), @@ -98,7 +100,15 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("bottom"), singularNoun("below"), singularNoun("floor"), - // TODO: adjectives + // TODO: bowing down + adjective("downward", "origin"), + adjective("humble", "opinion"), + adjective("lowly", "opinion"), + adjective("dependent", "opinion"), + adjective("low", "size"), + adjective("lower", "origin"), + adjective("bottom", "origin"), + adjective("down", "origin"), ], ante: [ noun("change(s)"), @@ -600,7 +610,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("romantic", "qualifier"), adjective("familial", "qualifier"), verb("respect(ed)", "respecting"), - // TODO: to have a strong emotional bond (with) ], open: [ noun("beginning(s)"), @@ -620,6 +629,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("damage(d)", "damaging"), verb("harm(ed)", "harming"), verb("mess(ed) up", "messing up"), + interjection("fuck"), ], pan: [ noun("grain(s)"), @@ -850,7 +860,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("communication(s)"), noun("language(s)"), verb("communicate(d)", "communicating", "about"), - // TODO: hello interjection + interjection("hello"), ], tomo: [ noun("building(s)"), @@ -1029,6 +1039,10 @@ export type Definition = | { type: "gerund"; gerund: string; + } + | { + type: "interjection"; + interjection: string; }; export const CONTENT_WORD = new Set([ ...SPECIAL_CONTENT_WORD, @@ -1192,3 +1206,9 @@ function quantifier(word: string): Definition & { type: "quantifier" } { quantifier: word, }; } +function interjection(word: string): Definition & { type: "interjection" } { + return { + type: "interjection", + interjection: word, + }; +} From 95143f6df2a711815221038325a8fc113d4acace Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 17:14:16 +0800 Subject: [PATCH 207/738] fix typo --- src/dictionary.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 423a0a7..1afc86f 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -737,7 +737,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { sewi: [ // TODO: area above, something elevated adjectiveNounPhrase([adjective("highest", "origin")], noun("part(s)")), - verb("elevate(d)", "elavating"), + verb("elevate(d)", "elevating"), adjective("awe-inspiring", "opinion"), adjective("divine", "opinion"), adjective("sacred", "opinion"), @@ -952,7 +952,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("confident", "opinion"), adjective("energetic", "opinion"), adjective("intense", "opinion"), - adverb("powefully"), + adverb("powerfully"), ], weka: [ adjective("absent", "origin"), From 038920bc3253e526e7cb056ad8d5a3dc66b1f940 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 17:18:11 +0800 Subject: [PATCH 208/738] add anu as content word --- src/dictionary.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index 1afc86f..55f9dc3 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -118,6 +118,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("other", "opinion"), adjective("altered", "opinion"), ], + anu: [ + verb("choose/chose", "choosing"), + intransitiveVerb("decide(d)", "deciding"), + ], awen: [ intransitiveVerb("wait(ed)", "waiting"), intransitiveVerb("stay(ed)", "staying"), From 9c2d9e90032d5b05ecb48747831067cfcf5e0f58 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 17:29:25 +0800 Subject: [PATCH 209/738] add comments --- src/dictionary.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dictionary.ts b/src/dictionary.ts index 55f9dc3..d30f2ec 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,3 +1,6 @@ +/** Module for describing word to word translations. */ + +/** */ export const PARTICLES = new Set([ "a", "ala", From 4020b9040dbf31911b96c07ac045c6fdd88a7cd4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 5 Apr 2024 17:31:52 +0800 Subject: [PATCH 210/738] change new version --- CHANGELOG.md | 2 +- src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ace0a5d..307d832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ You may need to force restart the page in order to use the latest version: shift + click the restart button; or ctrl + shift + R. -## 0.2.3 (On development) +## 0.3.0 (On development) - Implement settings dialog. [More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help). - All possible errors will now be listed. diff --git a/src/main.ts b/src/main.ts index 483422b..3e718c9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,7 @@ import { teloMisikeke } from "../deps.ts"; const DEVELOPMENT = true; // Don't forget these two when releasing const DATE_RELEASED = new Date("2024-2-1"); -const VERSION = "v0.2.3"; +const VERSION = "v0.3.0"; type Elements = { input: HTMLTextAreaElement; From bc50ac61bb342c71b567e39c68b6e8574555ad7e Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 6 Apr 2024 08:53:33 +0800 Subject: [PATCH 211/738] overhaul dictionary yet again --- src/dictionary.ts | 137 +++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 63 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index d30f2ec..21fc568 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -30,32 +30,6 @@ export const SPECIAL_CONTENT_WORD = new Set([ "seme", "su", ]); -export const PRONOUN_DEFINITION: { [word: string]: Pronoun } = { - mi: { - singularSubject: "I", - singularObject: "me", - singularPossessive: "my", - pluralSubject: "we", - pluralObject: "us", - pluralPossessive: "our", - }, - sina: { - singularSubject: null, - singularObject: null, - singularPossessive: null, - pluralSubject: "you", - pluralObject: "you", - pluralPossessive: "your", - }, - ona: { - singularSubject: null, - singularObject: null, - singularPossessive: null, - pluralSubject: "they", - pluralObject: "them", - pluralPossessive: "their", - }, -}; export const PREPOSITION_DEFINITION: { [word: string]: Array } = { kepeken: ["using"], lon: ["at"], @@ -84,6 +58,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ala: [ numeral(0), quantifier("no"), + singularNoun("nothing"), // This is technically a pronoun ], alasa: [ verb("hunt(ed)", "hunting"), @@ -119,7 +94,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("modification(s)"), adjective("different", "opinion"), adjective("other", "opinion"), - adjective("altered", "opinion"), + verb("change(d)", "changing"), + verb("alter(ed)", "altering"), ], anu: [ verb("choose/chose", "choosing"), @@ -171,9 +147,9 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("machine(s)"), ], insa: [ - singularNoun("centre"), + singularNoun("center"), noun("content(s)"), - singularNoun("inside"), + noun("inside(s)"), ], jaki: [ noun("obscenity/obscenities"), @@ -210,7 +186,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adverbAdjectivePhrase([adverb("yellowish")], adjective("orange", "color")), ], jo: [ - verb("have/had", "having"), + // verb("have/had", "having"), verb("carry/carried", "carrying"), verb("contain(ed)", "containing"), verb("hold/held", "holding"), @@ -254,13 +230,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], kiwen: [ adjectiveNounPhrase( - [adjective("hard", "physical quality")], + [adjective("hard", "material")], noun("object(s)"), ), noun("metal(s)"), noun("rock(s)"), noun("stone(s)"), - adjective("hard", "physical quality"), + adjective("hard", "material"), ], ko: [ singularNoun("semi-solid"), @@ -270,21 +246,21 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("sand"), singularNoun("soil"), singularNoun("clay"), - adjective("squishy", "physical quality"), - adjective("moldable", "physical quality"), + adjective("squishy", "material"), + adjective("moldable", "material"), ], kon: [ singularNoun("air"), singularNoun("breath"), - singularNoun("essence"), - singularNoun("spirit"), + noun("essence(s)"), + noun("spirit(s)"), adjectiveNounPhrase( [adjective("hidden", "physical quality")], - singularNoun("reality"), + noun("reality/realities"), ), adjectiveNounPhrase( [adjective("unseen", "physical quality")], - singularNoun("agent"), + noun("agent(s)"), ), ], kule: [ @@ -314,7 +290,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("seize(d)", "seizing"), verb("catch(ed)", "catching"), verb("receive(d)", "receiving"), - verb("get/got", "getting"), + // verb("get/got", "getting"), ], lape: [ singularNoun("sleep"), @@ -362,6 +338,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("fabric(s)"), adjective("hidden", "origin"), verb("cover(ed)", "covering"), + verb("hide/hid", "hiding"), ], lete: [ singularNoun("coldness"), @@ -372,15 +349,15 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], lili: [ singularNoun("smallness"), - adjective("small", "physical quality"), - adjective("short", "physical quality"), + adjective("small", "size"), + adjective("short", "size"), adjective("young", "age"), quantifier("few"), ], linja: [ adjectiveNounPhrase([ - adjective("long", "physical quality"), - adjective("flexible", "physical quality"), + adjective("long", "size"), + adjective("flexible", "material"), ], noun("thing(s)")), noun("cord(s)"), singularNoun("hair"), @@ -388,13 +365,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("line(s)"), noun("connection(s)"), compoundAdjective([ - adjective("long", "physical quality"), - adjective("flexible", "physical quality"), + adjective("long", "size"), + adjective("flexible", "material"), ]), ], lipu: [ adjectiveNounPhrase( - [adjective("flat", "physical quality")], + [adjective("flat", "size")], noun("object(s)"), ), noun("book(s)"), @@ -403,7 +380,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("paper(s)"), noun("record(s)"), noun("website(s)"), - adjective("flat", "physical quality"), + adjective("flat", "size"), ], loje: [ singularNoun("red"), @@ -426,7 +403,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("truth"), adjective("real", "opinion"), adverb("truthfully"), - verb("exist", "existing"), + verb("exist(ed)", "existing"), ], luka: [ numeral(5), @@ -442,13 +419,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjectiveNounPhrase([adjective("seeing", "qualifier")], noun("organ(s)")), verb("look(ed)", "looking", "at"), verb("read", "reading"), - verb("watch(ed)", "watchingF"), + verb("watch(ed)", "watching"), ], lupa: [ noun("hole(s)"), singularNoun("pit"), noun("cave(s)"), - singularNoun("doorway"), + noun("doorway(s)"), noun("window(s)"), noun("portal(s)"), ], @@ -499,6 +476,17 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("medium", "size"), adjective("mediocre", "opinion"), ], + mi: [ + { + type: "pronoun", + singularSubject: "I", + singularObject: "me", + singularPossessive: "my", + pluralSubject: "we", + pluralObject: "us", + pluralPossessive: "our", + }, + ], mije: [ noun("man/men"), // noun("male(s)"), // this sounds dehumanizing @@ -618,6 +606,17 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("familial", "qualifier"), verb("respect(ed)", "respecting"), ], + ona: [ + { + type: "pronoun", + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "they", + pluralObject: "them", + pluralPossessive: "their", + }, + ], open: [ noun("beginning(s)"), singularNoun("start"), @@ -660,8 +659,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], palisa: [ adjectiveNounPhrase([ - adjective("long", "physical quality"), - adjective("hard", "physical quality"), + adjective("long", "size"), + adjective("hard", "material"), ], noun("thing(s)")), noun("branch(es)"), noun("rod(s)"), @@ -684,6 +683,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], pini: [ singularNoun("past"), + adjective("past", "age"), verb("end(ed)", "ending"), ], pipi: [ @@ -772,6 +772,17 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { quantifier("another"), // It is a determiner. But is it a quantifier? adverb("newly"), ], + sina: [ + { + type: "pronoun", + singularSubject: null, + singularObject: null, + singularPossessive: null, + pluralSubject: "you", + pluralObject: "you", + pluralPossessive: "your", + }, + ], sinpin: [ noun("face(s)"), noun("wall(s)"), @@ -815,7 +826,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("light(s)"), singularNoun("brightness"), singularNoun("glow"), - singularNoun("radiance"), + noun("radiance(s)"), adjectiveNounPhrase([adjective("light", "qualifier")], noun("source(s)")), adjective("shining", "color"), ], @@ -858,7 +869,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { tenpo: [ singularNoun("time"), singularNoun("duration"), - singularNoun("moment"), + noun("moment(s)"), noun("occasion(s)"), noun("period(s)"), noun("situation(s)"), @@ -984,14 +995,6 @@ export type AdjectiveType = | "origin" | "material" | "qualifier"; -export type Pronoun = { - singularSubject: null | string; - singularObject: null | string; - singularPossessive: null | string; - pluralSubject: string; - pluralObject: string; - pluralPossessive: string; -}; export type Definition = | { type: "noun"; @@ -1004,6 +1007,15 @@ export type Definition = adjectives: Array; noun: Definition & { type: "noun" }; } + | { + type: "pronoun"; + singularSubject: null | string; + singularObject: null | string; + singularPossessive: null | string; + pluralSubject: string; + pluralObject: string; + pluralPossessive: string; + } | { type: "adjective"; adjective: string; @@ -1053,7 +1065,6 @@ export type Definition = }; export const CONTENT_WORD = new Set([ ...SPECIAL_CONTENT_WORD, - ...Object.keys(PRONOUN_DEFINITION), ...Object.keys(CONTENT_WORD_DEFINITION), ]); export const PREVERB = new Set(Object.keys(PREVERB_DEFINITION)); From 6084f99ddbbfb610759d63818a9c5671dfdde41b Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 6 Apr 2024 10:31:25 +0800 Subject: [PATCH 212/738] update contributing --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75f7eb2..6f212c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. If you want to propose a large change however, please open an issue first (or comment on an already existing issue page), and wait for my signal before beginning to work. +You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. ## The wiki From 2e757e098b00626f2fa54296e7bf5cd6d8059cb6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 7 Apr 2024 19:14:51 +0800 Subject: [PATCH 213/738] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 307d832..e1ffa68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ You may need to force restart the page in order to use the latest version: shift - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - Combined glyphs + - Long glyphs - (Deprecated characters and combiners are not supported) - The vocabulary has been expanded to nimi ku suli plus nimi su! (Some are still unimplemented) - Multiline text will no longer be recognized. From 0ed7ba747435830408e05c394c99cf2c87432899 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 8 Apr 2024 11:38:49 +0800 Subject: [PATCH 214/738] small update --- src/dictionary.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 21fc568..2d4be05 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -194,7 +194,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { kala: [ noun("fish(es)"), adjectiveNounPhrase([adjective("sea", "qualifier")], noun("creature(s)")), - intransitiveVerb("swim", "swimming"), + intransitiveVerb("swim/swam", "swimming"), ], kalama: [ noun("sound(s)"), @@ -565,7 +565,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("strange", "opinion"), adjective("silly", "opinion"), adjective("drunk", "opinion"), - adjective("intoxicated", "opinion"), + // adjective("intoxicated", "opinion"), + verb("intoxicate", "intoxicating"), adverb("strangely"), ], nasin: [ @@ -628,7 +629,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { pakala: [ singularNoun("mess"), noun("damage(s)"), - adjective("botched", "opinion"), adjective("broken", "opinion"), verb("botch(ed)", "botching"), verb("break/broke", "breaking"), @@ -975,7 +975,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { weka: [ adjective("absent", "origin"), adjective("away", "origin"), - adjective("ignored", "opinion"), + // adjective("ignored", "opinion"), + verb("ignore(d)", "ignoring"), intransitiveVerb("leave", "leaving"), ], wile: [ From e088d0a971d033f2d880f4ffd1cc5173f3546b90 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 8 Apr 2024 15:51:13 +0800 Subject: [PATCH 215/738] add settings for anu as content word --- index.html | 6 ++++++ src/filter.ts | 17 +++++++++++++++++ src/main.ts | 25 ++++++++++++++++++++++--- src/settings.ts | 3 +++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index a86c471..5586eb4 100644 --- a/index.html +++ b/index.html @@ -123,6 +123,12 @@

        of X ala X constructions (?)

        +

        + +

        diff --git a/src/filter.ts b/src/filter.ts index 7d85ab4..1a81a3d 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -14,6 +14,7 @@ import { WordUnit, } from "./ast.ts"; import { UnreachableError, UnrecognizedError } from "./error.ts"; +import { settings } from "./settings.ts"; /** Array of filter rules for a word unit. */ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ @@ -34,6 +35,22 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ } return true; }, + // disallow "anu" as content word only when turned off in settings + (wordUnit) => { + if (settings.anuAsContentWord) { + return true; + } + const isAnu = ( + wordUnit.type === "default" || + wordUnit.type === "reduplication" || + wordUnit.type === "x ala x" + ) && wordUnit.word === "anu"; + if (isAnu) { + throw new UnrecognizedError("anu as content word"); + } else { + return true; + } + }, ]; /** Array of filter rules for a single modifier. */ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ diff --git a/src/main.ts b/src/main.ts index 3e718c9..c333328 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,6 +27,7 @@ type Elements = { number: HTMLSelectElement; tense: HTMLSelectElement; xAlaXPartialParsing: HTMLInputElement; + anuAsContentWord: HTMLInputElement; }; /** A map of all HTML elements that are used here. */ let elements: undefined | Elements; @@ -62,6 +63,9 @@ function loadElements(): void { xAlaXPartialParsing: document.getElementById( "x-ala-x-parsing", ) as HTMLInputElement, + anuAsContentWord: document.getElementById( + "anu-as-content-word", + ) as HTMLInputElement, }; } function setVersion(): void { @@ -146,7 +150,11 @@ function updateOutput(): void { } function loadSettings(): void { function setBool< - T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + T extends + | "useTeloMisikeke" + | "randomize" + | "xAlaXPartialParsing" + | "anuAsContentWord", >(name: T, element: HTMLInputElement): void { const x = localStorage.getItem(name); let value: boolean; @@ -174,12 +182,17 @@ function loadSettings(): void { setBool("useTeloMisikeke", elements!.useTeloMisikeke); setBool("randomize", elements!.randomize); setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setBool("anuAsContentWord", elements!.anuAsContentWord); setRedundancy("number", elements!.number); setRedundancy("tense", elements!.tense); } function confirmSettings(): void { function setBool< - T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + T extends + | "useTeloMisikeke" + | "randomize" + | "xAlaXPartialParsing" + | "anuAsContentWord", >(name: T, element: HTMLInputElement) { const value = element.checked; localStorage.setItem(name, value.toString()); @@ -196,12 +209,17 @@ function confirmSettings(): void { setBool("useTeloMisikeke", elements!.useTeloMisikeke); setBool("randomize", elements!.randomize); setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setBool("anuAsContentWord", elements!.anuAsContentWord); setRedundancy("number", elements!.number); setRedundancy("tense", elements!.tense); } function resetSettings(): void { function setBool< - T extends "useTeloMisikeke" | "randomize" | "xAlaXPartialParsing", + T extends + | "useTeloMisikeke" + | "randomize" + | "xAlaXPartialParsing" + | "anuAsContentWord", >(name: T, element: HTMLInputElement) { element.checked = defaultSettings[name]; } @@ -214,6 +232,7 @@ function resetSettings(): void { setBool("useTeloMisikeke", elements!.useTeloMisikeke); setBool("randomize", elements!.randomize); setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); + setBool("anuAsContentWord", elements!.anuAsContentWord); setRedundancy("number", elements!.number); setRedundancy("tense", elements!.tense); } diff --git a/src/settings.ts b/src/settings.ts index e6f0de0..ffb7d4d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -6,6 +6,7 @@ export type Settings = { useTeloMisikeke: boolean; randomize: boolean; xAlaXPartialParsing: boolean; + anuAsContentWord: boolean; number: RedundancySettings; tense: RedundancySettings; }; @@ -13,6 +14,7 @@ export const defaultSettings: Settings = { useTeloMisikeke: false, randomize: false, xAlaXPartialParsing: false, + anuAsContentWord: false, number: "both", tense: "both", }; @@ -20,6 +22,7 @@ export const settings: Settings = { useTeloMisikeke: false, randomize: false, xAlaXPartialParsing: false, + anuAsContentWord: false, number: "both", tense: "both", }; From cbfdf63432adf7137ee9b8797388293a07feb9d2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 8 Apr 2024 16:03:56 +0800 Subject: [PATCH 216/738] make marker optional for word unit --- src/ast-parser.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 893cb20..310d8b8 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -206,7 +206,7 @@ function wordUnit(word: Set, description: string): AstParser { ) ), xAlaX(word, description), - sequence(wordFrom(word, description), marker()).map( + sequence(wordFrom(word, description), optional(marker())).map( ([word, marker]) => ({ type: "default", word, marker }) as WordUnit, ), ).filter(filter(WORD_UNIT_RULES)); @@ -778,3 +778,6 @@ const FULL_PARSER = allAtLeastOnce(sentence()) export function parser(src: string): Output> { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } +console.log( + wordUnit(CONTENT_WORD, "content word").parse([{ type: "word", word: "ijo" }]), +); From a71a3221d7062cc32a2f7a0e9488ec40f95319cc Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 14:33:37 +0800 Subject: [PATCH 217/738] improve readme --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index facc89c..d9112ea 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A rule-based Toki Pona to English translator that translates into multiple sente [Try it](https://neverrare.github.io/ilo-token/) +**It is work in progress!** [We welcome contributors however!](./CONTRIBUTING.md) + ## Dependencies You'll need the following in order to run commands: @@ -12,7 +14,7 @@ You'll need the following in order to run commands: ## Building -This builds `./main.js` as a minified file ready for production use. +This fetches dependencies needed and builds `./main.js` as a minified file ready for production use. ``` deno task build @@ -20,7 +22,7 @@ deno task build ## Watching -Before running this command, you'll need to run `deno task build` first. You'll only need to run this command once. +Before running this command, you'll need to run `deno task build` first. This is because `deno task watch` doesn't fetch dependencies. You'll only need to run this command once. This builds `./main.js` as a non-minified file with source mapping, intended for testing and debugging. This command also watches the source codes in `./src/` path and rebuilds `./main.js` whenever there are changes. From d34445bbb7ea3f9431fafe167924eb764401502d Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 14:34:02 +0800 Subject: [PATCH 218/738] small update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9112ea..f782f89 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ You'll need the following in order to run commands: ## Building -This fetches dependencies needed and builds `./main.js` as a minified file ready for production use. +This fetches more dependencies needed and builds `./main.js` as a minified file ready for production use. ``` deno task build From 899cb3e3e68d8a91e548994c12b669a06703f081 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 14:38:19 +0800 Subject: [PATCH 219/738] update contributing --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f212c3..c5e73c7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,8 @@ You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. +Unless you want to edit `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. + ## The wiki The wiki provides useful information for contributors, although it's not complete yet. Check it out: [Visit wiki](https://github.com/neverRare/ilo-token/wiki). From 81f494e102b37018fc5a2751eae1927edf345f86 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 14:51:49 +0800 Subject: [PATCH 220/738] small update --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5e73c7..a2b85d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. -Unless you want to edit `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. +Unless you want to edit the dictionary in `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. ## The wiki From c63114ebdcdac89b97ff2ce807288a4dfc09f175 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 14:59:25 +0800 Subject: [PATCH 221/738] small update --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a2b85d8..d41caea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,6 @@ You can do the usual github stuff: Open issue if there's an issue or you have a Unless you want to edit the dictionary in `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. -## The wiki +## [The wiki](https://github.com/neverRare/ilo-token/wiki) -The wiki provides useful information for contributors, although it's not complete yet. Check it out: [Visit wiki](https://github.com/neverRare/ilo-token/wiki). +The wiki provides useful information for contributors, although it's not complete yet. Check it out! From c98a534cfc2f510095fa346d57be7445ce6483ff Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 12 Apr 2024 15:11:04 +0800 Subject: [PATCH 222/738] small update --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d41caea..aaa5b62 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,8 @@ You can do the usual github stuff: Open issue if there's an issue or you have a Unless you want to edit the dictionary in `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. +If you to edit the dictionary, just go ahead and contribute! We provide useful guidelines in the wiki! + ## [The wiki](https://github.com/neverRare/ilo-token/wiki) The wiki provides useful information for contributors, although it's not complete yet. Check it out! From abb4d67fef57012db3a8931ab5040fe2339e2d04 Mon Sep 17 00:00:00 2001 From: Koko Date: Sat, 13 Apr 2024 11:47:49 +0800 Subject: [PATCH 223/738] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1f168bd --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +contactneverrare@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. From 2fd9a7bea6e1cf318b7b50b52eb78d30689e08cb Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 14 Apr 2024 13:32:51 +0800 Subject: [PATCH 224/738] update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ffa68..edc66b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,13 @@ You may need to force restart the page in order to use the latest version: shift + click the restart button; or ctrl + shift + R. +

        +On development changelog + ## 0.3.0 (On development) +The latest on development version can be accessed by building the source code. + - Implement settings dialog. [More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help). - All possible errors will now be listed. - ilo Token can now use telo misikeke for error messages. You can turn this on from the settings dialog. @@ -21,6 +26,8 @@ Inside update (intended for developers): - Implement lexer. - Overhaul dictionary. +
        + ## 0.2.2 Update missed links. From d4ba87447af5b13890f1f91a3929178104f26478 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 16 Apr 2024 11:45:25 +0800 Subject: [PATCH 225/738] encode past particle forms --- src/dictionary.ts | 188 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 149 insertions(+), 39 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 2d4be05..cb3d1cf 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,6 +1,7 @@ /** Module for describing word to word translations. */ -/** */ +import { UnreachableError } from "./error.ts"; + export const PARTICLES = new Set([ "a", "ala", @@ -98,7 +99,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("alter(ed)", "altering"), ], anu: [ - verb("choose/chose", "choosing"), + verb({ + presentPast: "choose/chose", + pastParticiple: "chosen", + gerund: "choosing", + }), intransitiveVerb("decide(d)", "deciding"), ], awen: [ @@ -204,7 +209,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("future"), adjective("future", "age"), verb("summon(ed)", "summoning"), - intransitiveVerb("arrive", "arriving"), + intransitiveVerb("arrive(d)", "arriving"), ], kasi: [ noun("plant(s)"), @@ -283,10 +288,18 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], kute: [ noun("ear(s)"), - verb("listen(ed)", "listening", "at"), + verb({ + presentPast: "listen(ed)", + gerund: "listening", + usePreposition: "at", + }), ], lanpan: [ - verb("take/took", "taking"), + verb({ + presentPast: "take/took", + pastParticiple: "taken", + gerund: "taking", + }), verb("seize(d)", "seizing"), verb("catch(ed)", "catching"), verb("receive(d)", "receiving"), @@ -338,14 +351,22 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("fabric(s)"), adjective("hidden", "origin"), verb("cover(ed)", "covering"), - verb("hide/hid", "hiding"), + verb({ + presentPast: "hide/hid", + pastParticiple: "hidden", + gerund: "hiding", + }), ], lete: [ singularNoun("coldness"), adjective("cool", "physical quality"), adjective("cold", "physical quality"), adjective("frozen", "physical quality"), - verb("freeze/froze", "freezing"), + verb({ + presentPast: "freeze/froze", + pastParticiple: "frozen", + gerund: "freezing", + }), ], lili: [ singularNoun("smallness"), @@ -417,7 +438,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("visual(s)"), noun("eye(s)"), adjectiveNounPhrase([adjective("seeing", "qualifier")], noun("organ(s)")), - verb("look(ed)", "looking", "at"), + verb({ + presentPast: "look(ed)", + gerund: "looking", + usePreposition: "at", + }), verb("read", "reading"), verb("watch(ed)", "watching"), ], @@ -503,8 +528,16 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { moku: [ noun("food(s)"), noun("drink(s)"), - verb("eat/ate", "eating"), - verb("drink/drank", "drinking"), + verb({ + presentPast: "eat/ate", + pastParticiple: "eaten", + gerund: "eating", + }), + verb({ + presentPast: "drink/drank", + pastParticiple: "drinked", + gerund: "drinking", + }), verb("consume(d)", "consuming"), verb("ingest(ed)", "ingesting"), ], @@ -566,7 +599,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("silly", "opinion"), adjective("drunk", "opinion"), // adjective("intoxicated", "opinion"), - verb("intoxicate", "intoxicating"), + verb("intoxicate(d)", "intoxicating"), adverb("strangely"), ], nasin: [ @@ -629,9 +662,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { pakala: [ singularNoun("mess"), noun("damage(s)"), - adjective("broken", "opinion"), + // adjective("broken", "opinion"), verb("botch(ed)", "botching"), - verb("break/broke", "breaking"), + verb({ + presentPast: "break/broke", + pastParticiple: "broken", + gerund: "breaking", + }), verb("damage(d)", "damaging"), verb("harm(ed)", "harming"), verb("mess(ed) up", "messing up"), @@ -654,7 +691,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("build", "building"), verb("make/made", "making"), verb("prepare(d)", "preparing"), - intransitiveVerb("do/did", "doing"), + intransitiveVerb({ + presentPast: "do/did", + pastParticiple: "done", + gerund: "doing", + }), intransitiveVerb("work/worked", "working"), ], palisa: [ @@ -877,7 +918,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { toki: [ noun("communication(s)"), noun("language(s)"), - verb("communicate(d)", "communicating", "about"), + verb({ + presentPast: "communicate(d)", + gerund: "communicating", + usePreposition: "about", + }), interjection("hello"), ], tomo: [ @@ -919,7 +964,14 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { unpa: [ adjective("sexual", "qualifier"), adverb("sexually"), - verbObjectPhrase(verb("have/had", "having", "with"), singularNoun("sex")), + verbObjectPhrase( + verb({ + presentPast: "have/had", + gerund: "having", + usePreposition: "with", + }), + singularNoun("sex"), + ), ], uta: [ singularNoun("mouth"), @@ -958,7 +1010,11 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { [adjective("winged", "qualifier")], noun("animal(s)"), ), - intransitiveVerb("fly", "flying"), + intransitiveVerb({ + presentPast: "fly/flew", + pastParticiple: "flown", + gerund: "flying", + }), ], wawa: [ singularNoun("power"), @@ -1045,9 +1101,10 @@ export type Definition = } | { type: "verb"; - past: string; present: string; + past: string; condensed: string; + pastParticiple: string; gerund: string; object: boolean | string; } @@ -1158,20 +1215,44 @@ function parseVerb( } return { past, present, condensed: word }; } +type VerbOption = { + presentPast: string; + pastParticiple?: null | undefined | string; + gerund: string; + usePreposition?: null | undefined | string; +}; +function verb(word: string, gerund: string): Definition & { type: "verb" }; +function verb(option: VerbOption): Definition & { type: "verb" }; function verb( - word: string, - gerund: string, - usePreposition?: null | undefined | string, + word: string | VerbOption, + gerund?: string, ): Definition & { type: "verb" } { - const { past, present, condensed } = parseVerb(word); - return { - type: "verb", - present, - past, - condensed, - gerund, - object: usePreposition ?? true, - }; + if (typeof word === "string") { + if (typeof gerund !== "string") { + throw new UnreachableError(); + } + const { past, present, condensed } = parseVerb(word); + return { + type: "verb", + present, + past, + pastParticiple: past, + condensed, + gerund, + object: true, + }; + } else { + const { past, present, condensed } = parseVerb(word.presentPast); + return { + type: "verb", + present, + past, + pastParticiple: word.pastParticiple ?? past, + condensed, + gerund: word.gerund, + object: word.usePreposition ?? true, + }; + } } function verbObjectPhrase( verb: Definition & { type: "verb" }, @@ -1183,19 +1264,48 @@ function verbObjectPhrase( object, }; } +type IntransitiveVerbOption = { + presentPast: string; + pastParticiple: string; + gerund: string; +}; function intransitiveVerb( word: string, gerund: string, +): Definition & { type: "verb" }; +function intransitiveVerb( + option: IntransitiveVerbOption, +): Definition & { type: "verb" }; +function intransitiveVerb( + word: string | IntransitiveVerbOption, + gerund?: string, ): Definition & { type: "verb" } { - const { past, present, condensed } = parseVerb(word); - return { - type: "verb", - present, - past, - condensed, - gerund, - object: false, - }; + if (typeof word === "string") { + if (typeof gerund !== "string") { + throw new UnreachableError(); + } + const { past, present, condensed } = parseVerb(word); + return { + type: "verb", + present, + past, + pastParticiple: past, + condensed, + gerund, + object: false, + }; + } else { + const { past, present, condensed } = parseVerb(word.presentPast); + return { + type: "verb", + present, + past, + pastParticiple: word.pastParticiple, + condensed, + gerund: word.gerund, + object: false, + }; + } } function adjective( word: string, From 27337f597603912548585fe8b25b3a609f704e22 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 16 Apr 2024 11:52:19 +0800 Subject: [PATCH 226/738] small improvement --- src/dictionary.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index cb3d1cf..723cc6e 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -349,7 +349,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { len: [ noun("clothing(s)"), noun("fabric(s)"), - adjective("hidden", "origin"), + // adjective("hidden", "origin"), verb("cover(ed)", "covering"), verb({ presentPast: "hide/hid", @@ -361,7 +361,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("coldness"), adjective("cool", "physical quality"), adjective("cold", "physical quality"), - adjective("frozen", "physical quality"), + // adjective("frozen", "physical quality"), + verb("cool(ed)", "cooling"), verb({ presentPast: "freeze/froze", pastParticiple: "frozen", @@ -655,9 +656,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("beginning(s)"), singularNoun("start"), adjective("open", "physical quality"), - adjective("turned on", "qualifier"), + // adjective("turned on", "qualifier"), verb("start(ed)", "starting"), verb("turn(ed) on", "turning on"), + verb("open(ed)", "opening"), ], pakala: [ singularNoun("mess"), From 9396b0c25f3f11e625ef8bff12881bb81344b2a9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 07:07:47 +0800 Subject: [PATCH 227/738] olin aint so special --- src/dictionary.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 723cc6e..3478271 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -26,7 +26,6 @@ export const SPECIAL_CONTENT_WORD = new Set([ "lili", // piece of "mu", "ni", - "olin", // have a strong emotional bond with "pu", "seme", "su", @@ -639,6 +638,17 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("platonic", "qualifier"), adjective("romantic", "qualifier"), adjective("familial", "qualifier"), + verbObjectPhrase( + verb({ + presentPast: "have/had", + gerund: "having", + usePreposition: "with", + }), + adjectiveNounPhrase([ + adjective("strong", "opinion"), + adjective("emotional", "qualifier"), + ], singularNoun("bond")), + ), verb("respect(ed)", "respecting"), ], ona: [ @@ -1113,7 +1123,7 @@ export type Definition = | { type: "verb object phrase"; verb: Definition & { type: "verb" }; - object: Definition & { type: "noun" }; + object: Definition & { type: "noun" | "adjective noun phrase" }; } | { type: "gerund"; @@ -1258,7 +1268,7 @@ function verb( } function verbObjectPhrase( verb: Definition & { type: "verb" }, - object: Definition & { type: "noun" }, + object: Definition & { type: "noun" | "adjective noun phrase" }, ): Definition & { type: "verb object phrase" } { return { type: "verb object phrase", From 98988c9764adc9ff876db3e9e3574408eed2d23a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 07:11:44 +0800 Subject: [PATCH 228/738] small improvement --- src/dictionary.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 3478271..8210218 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -997,8 +997,16 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { utala: [ noun("battle(s)"), noun("challenge(s)"), - verb("compete(d)", "against"), - verb("struggle(d)", "against"), + verb({ + presentPast: "compete(d)", + gerund: "competing", + usePreposition: "against", + }), + verb({ + presentPast: "struggle(d)", + gerund: "struggling", + usePreposition: "against", + }), ], walo: [ adjectiveNounPhrase([adjective("light", "color")], noun("color(s)")), From 8c8cdd98817f7d8eb0cc92d1e89b46037fdc9858 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 07:19:05 +0800 Subject: [PATCH 229/738] add "bowing down" for anpa --- src/dictionary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 8210218..c1dcc82 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -78,7 +78,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("bottom"), singularNoun("below"), singularNoun("floor"), - // TODO: bowing down adjective("downward", "origin"), adjective("humble", "opinion"), adjective("lowly", "opinion"), @@ -87,6 +86,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("lower", "origin"), adjective("bottom", "origin"), adjective("down", "origin"), + verb("bow(ed) down", "bowing down"), ], ante: [ noun("change(s)"), From eb9c21707287606797c75d05153e071cb3055a49 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 07:57:20 +0800 Subject: [PATCH 230/738] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edc66b6..a6fdb93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ You may need to force restart the page in order to use the latest version: shift ## 0.3.0 (On development) -The latest on development version can be accessed by building the source code. +The latest on-development version can be accessed by building the source code. - Implement settings dialog. [More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help). - All possible errors will now be listed. From 26c062fb523159f1c3a1f3eedbbf6b21a60a4e30 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 15:38:07 +0800 Subject: [PATCH 231/738] improve kalama definition --- src/dictionary.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index c1dcc82..852e138 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -202,7 +202,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], kalama: [ noun("sound(s)"), - intransitiveVerb("sound(ed)", "sounding"), + // intransitiveVerb("sound(ed)", "sounding"), + verbObjectPhrase(verb("make/made", "making"), noun("sound(s)")), ], kama: [ singularNoun("future"), From 980918c6f8e1ed31ef82c54dde33b68115694557 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 15:47:21 +0800 Subject: [PATCH 232/738] add words with special cases in content word dictionary --- src/dictionary.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 852e138..922ed3a 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -220,7 +220,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("ability/abilities"), noun("possibility/possibilities"), ], - kepeken: [], + kepeken: [ + verb("use(d)", "using "), + ], + kijetesantakalu: [], // Special case kili: [ noun("fruit(s)"), noun("vegetable(s)"), @@ -254,6 +257,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("squishy", "material"), adjective("moldable", "material"), ], + kokosila: [], // Special case kon: [ singularNoun("air"), singularNoun("breath"), @@ -268,6 +272,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("agent(s)"), ), ], + ku: [], // Special case kule: [ noun("color(s)"), noun("pigment(s)"), @@ -559,6 +564,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("danger(s)"), adjective("scary", "opinion"), ], + mu: [], // Special case mun: [ singularNoun("moon"), adjectiveNounPhrase( @@ -617,6 +623,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("nose(s)"), noun("protuberance(s)"), ], + ni: [], // Special case nimi: [ noun("name(s)"), noun("word(s)"), @@ -764,6 +771,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("simple", "opinion"), adverb("nicely"), ], + pu: [], // Special case sama: [ noun("similarity/similarities"), noun("sibling(s)"), @@ -795,6 +803,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("skin"), noun("boundary/boundaries"), ], + seme: [], // Special case sewi: [ // TODO: area above, something elevated adjectiveNounPhrase([adjective("highest", "origin")], noun("part(s)")), @@ -867,6 +876,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ), noun("beast(s)"), ], + su: [], // Special case suli: [ singularNoun("hugeness"), singularNoun("importance"), From 41421dd9dffd8b3097d5b0633941ff6ffcfd9f1a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 16:47:14 +0800 Subject: [PATCH 233/738] reenable taso ala taso filter --- src/filter.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 1a81a3d..41bb6f0 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -274,12 +274,16 @@ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ ]; export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // Prevent "taso ala taso" - // (fullClause) => { - // if (fullClause.taso && fullClause.taso.type === "x ala x") { - // throw new UnrecognizedError('"taso ala taso"'); - // } - // return true; - // }, + (fullClause) => { + if ( + fullClause.type === "default" && fullClause.preclause && + fullClause.preclause.type === "taso" && + fullClause.preclause.taso.type === "x ala x" + ) { + throw new UnrecognizedError('"taso ala taso"'); + } + return true; + }, ]; /** Array of filter rules for multiple sentences. */ export const SENTENCES_RULE: Array<(sentences: Array) => boolean> = [ From e6f67ea06245c38695366ad3686aa6881f6dcb88 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 17 Apr 2024 16:54:26 +0800 Subject: [PATCH 234/738] small update --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 310d8b8..3852cf8 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -139,7 +139,7 @@ function xAlaX( ): AstParser { return choice( specificTokenTree("long glyph").map((longGlyph) => { - // TODO: reduce duplication + // TODO: reduce code duplication if (longGlyph.words.length !== 1 || longGlyph.words[0] !== "ala") { throw new UnexpectedError( describe({ type: "combined glyphs", words: longGlyph.words }), From c39de7ed6dfcfe5a356bc3a88e46d2fae421a4a7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 08:41:46 +0800 Subject: [PATCH 235/738] major formatting --- src/ast-parser.ts | 500 +++++++++++++++++++---------------------- src/ast.ts | 17 +- src/dictionary.ts | 109 ++------- src/lexer.ts | 159 +++++++------ src/main.ts | 6 +- src/parser-lib.ts | 43 ++-- src/translator.ts | 122 +++++----- telo-misikeke/build.ts | 5 +- 8 files changed, 416 insertions(+), 545 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 3852cf8..d7f9e6d 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -40,9 +40,8 @@ import { Parser, sequence as rawSequence, } from "./parser-lib.ts"; -import { TokenTree } from "./token-tree.ts"; +import { describe, TokenTree } from "./token-tree.ts"; import { lex } from "./lexer.ts"; -import { describe } from "./token-tree.ts"; export type AstParser = Parser, T>; @@ -124,12 +123,10 @@ function specificWord(thatWord: string): AstParser { } function marker(): AstParser { return choice( - specificTokenTree("multiple a").map(({ count }) => - ({ type: "multiple a", count }) as Marker - ), - specificTokenTree("long a").map(({ length }) => - ({ type: "long a", length }) as Marker - ), + specificTokenTree("multiple a") + .map(({ count }) => ({ type: "multiple a", count }) as Marker), + specificTokenTree("long a") + .map(({ length }) => ({ type: "long a", length }) as Marker), specificWord("a").map(() => ({ type: "a" }) as Marker), ); } @@ -183,14 +180,15 @@ function xAlaX( } return { type: "x ala x", word } as WordUnit & { type: "x ala x" }; }), - specificTokenTree("x ala x").map(({ word }) => - ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" } - ), - wordFrom(word, description).then((word) => - specificWord("ala").with(specificWord(word)) - ).map(( - word, - ) => ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" }), + specificTokenTree("x ala x") + .map(({ word }) => + ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" } + ), + wordFrom(word, description) + .then((word) => specificWord("ala").with(specificWord(word))) + .map((word) => + ({ type: "x ala x", word }) as WordUnit & { type: "x ala x" } + ), ); } /** Parses word unit without numbers. */ @@ -206,10 +204,10 @@ function wordUnit(word: Set, description: string): AstParser { ) ), xAlaX(word, description), - sequence(wordFrom(word, description), optional(marker())).map( - ([word, marker]) => ({ type: "default", word, marker }) as WordUnit, - ), - ).filter(filter(WORD_UNIT_RULES)); + sequence(wordFrom(word, description), optional(marker())) + .map(([word, marker]) => ({ type: "default", word, marker }) as WordUnit), + ) + .filter(filter(WORD_UNIT_RULES)); } function binaryWords( word: Set, @@ -232,17 +230,18 @@ function optionalCombined( description: string, ): AstParser<[WordUnit, Array]> { return choice( - wordUnit(word, description).map((wordUnit) => - [wordUnit, []] as [WordUnit, Array] - ), - binaryWords(word, description).map(( - [first, second], - ) => - [{ type: "default", word: first }, [{ - type: "default", - word: { type: "default", word: second }, - }]] as [WordUnit, Array] - ), + wordUnit(word, description) + .map((wordUnit) => [wordUnit, []] as [WordUnit, Array]), + binaryWords(word, description) + .map(([first, second]) => + [ + { type: "default", word: first }, + [{ + type: "default", + word: { type: "default", word: second }, + }], + ] as [WordUnit, Array] + ), ); } /** Parses number words in order. */ @@ -253,14 +252,15 @@ function number(): AstParser> { many(specificWord("luka")), many(specificWord("tu")), many(specificWord("wan")), - ).map((array) => { - const output = array.flat(); - if (output.length >= 2) { - return output; - } else { - throw new CoveredError(); - } - }); + ) + .map((array) => { + const output = array.flat(); + if (output.length >= 2) { + return output; + } else { + throw new CoveredError(); + } + }); } function pi(): AstParser { return choice( @@ -281,82 +281,66 @@ function pi(): AstParser { } return INNER_PHRASE_PARSER.parse(longGlyph.after); }, - ).map((phrase) => - ({ - type: "pi", - phrase, - }) as Modifier & { type: "pi" } - ), - specificWord("pi").with(phrase()).map((phrase) => - ({ - type: "pi", - phrase, - }) as Modifier & { type: "pi" } - ), - ).filter(filter(MODIFIER_RULES)); + ) + .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }), + specificWord("pi") + .with(phrase()) + .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }), + ) + .filter(filter(MODIFIER_RULES)); } /** Parses multiple modifiers. */ function modifiers(): AstParser> { return sequence( many( choice( - wordUnit(CONTENT_WORD, "modifier").map((word) => - ({ - type: "default", - word, - }) as Modifier - ).filter(filter(MODIFIER_RULES)), - properWords().map(( - words, - ) => ({ type: "proper words", words }) as Modifier).filter( - filter(MODIFIER_RULES), - ), - number().map(( - numbers, - ) => - ({ - type: "default", - word: { type: "numbers", numbers }, - }) as Modifier - ).filter(filter(MODIFIER_RULES)), - quotation().map(( - quotation, - ) => ({ type: "quotation", quotation }) as Modifier).filter( - filter(MODIFIER_RULES), - ), + wordUnit(CONTENT_WORD, "modifier") + .map((word) => ({ type: "default", word }) as Modifier) + .filter(filter(MODIFIER_RULES)), + properWords() + .map((words) => ({ type: "proper words", words }) as Modifier) + .filter(filter(MODIFIER_RULES)), + number() + .map((numbers) => + ({ + type: "default", + word: { type: "numbers", numbers }, + }) as Modifier + ) + .filter(filter(MODIFIER_RULES)), + quotation() + .map((quotation) => ({ type: "quotation", quotation }) as Modifier) + .filter(filter(MODIFIER_RULES)), ), ), many( - sequence(wordUnit(new Set(["nanpa"]), '"nanpa"'), phrase()).map(( - [nanpa, phrase], - ) => - ({ - type: "nanpa", - nanpa, - phrase, - }) as Modifier - ).filter(filter(MODIFIER_RULES)), + sequence(wordUnit(new Set(["nanpa"]), '"nanpa"'), phrase()) + .map(([nanpa, phrase]) => + ({ type: "nanpa", nanpa, phrase }) as Modifier + ) + .filter(filter(MODIFIER_RULES)), ), many(pi()), - ).map(( - [modifiers, nanpaModifiers, piModifiers], - ) => [...modifiers, ...nanpaModifiers, ...piModifiers]).filter( - filter(MODIFIERS_RULES), - ); + ) + .map(([modifiers, nanpaModifiers, piModifiers]) => [ + ...modifiers, + ...nanpaModifiers, + ...piModifiers, + ]) + .filter(filter(MODIFIERS_RULES)); } const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); /** Parses phrases. */ function phrase(): AstParser { return choice( - sequence(number(), lazy(modifiers)).map(( - [numbers, modifiers], - ) => - ({ - type: "default", - headWord: { type: "numbers", numbers }, - modifiers, - }) as Phrase - ), + sequence(number(), lazy(modifiers)) + .map(([numbers, modifiers]) => + ({ + type: "default", + headWord: { type: "numbers", numbers }, + modifiers, + }) as Phrase + ), binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => ({ type: "preverb", @@ -373,42 +357,32 @@ function phrase(): AstParser { optionalCombined(PREVERB, "preverb"), lazy(modifiers), lazy(phrase), - ).map(( - [[preverb, modifier], modifiers, phrase], - ) => - ({ - type: "preverb", - preverb, - modifiers: [ - ...modifier, - ...modifiers, - ], - phrase, - }) as Phrase - ), - lazy(preposition).map((preposition) => - ({ - type: "preposition", - preposition, - }) as Phrase - ), + ) + .map(([[preverb, modifier], modifiers, phrase]) => + ({ + type: "preverb", + preverb, + modifiers: [...modifier, ...modifiers], + phrase, + }) as Phrase + ), + lazy(preposition) + .map((preposition) => ({ type: "preposition", preposition }) as Phrase), sequence( optionalCombined(CONTENT_WORD, "headword"), lazy(modifiers), - ).map(([[headWord, modifier], modifiers]) => - ({ - type: "default", - headWord, - modifiers: [ - ...modifier, - ...modifiers, - ], - }) as Phrase - ), - quotation().map(( - quotation, - ) => ({ type: "quotation", quotation }) as Phrase), - ).filter(filter(PHRASE_RULE)); + ) + .map(([[headWord, modifier], modifiers]) => + ({ + type: "default", + headWord, + modifiers: [...modifier, ...modifiers], + }) as Phrase + ), + quotation() + .map((quotation) => ({ type: "quotation", quotation }) as Phrase), + ) + .filter(filter(PHRASE_RULE)); } /** * Parses nested phrases with given nesting rule, only accepting the top level @@ -418,9 +392,8 @@ function nestedPhrasesOnly( nestingRule: Array<"en" | "li" | "o" | "e" | "anu">, ): AstParser { if (nestingRule.length === 0) { - return phrase().map( - (phrase) => ({ type: "single", phrase }) as MultiplePhrases, - ); + return phrase() + .map((phrase) => ({ type: "single", phrase }) as MultiplePhrases); } else { const [first, ...rest] = nestingRule; let type: "and conjunction" | "anu"; @@ -436,10 +409,11 @@ function nestedPhrasesOnly( nestedPhrases(rest), ), ), - ).map(([group, moreGroups]) => ({ - type, - phrases: [group, ...moreGroups], - })); + ) + .map(([group, moreGroups]) => ({ + type, + phrases: [group, ...moreGroups], + })); } } /** Parses nested phrases with given nesting rule. */ @@ -447,9 +421,8 @@ function nestedPhrases( nestingRule: Array<"en" | "li" | "o" | "e" | "anu">, ): AstParser { if (nestingRule.length === 0) { - return phrase().map( - (phrase) => ({ type: "single", phrase }) as MultiplePhrases, - ); + return phrase() + .map((phrase) => ({ type: "single", phrase }) as MultiplePhrases); } else { return choice( nestedPhrasesOnly(nestingRule), @@ -468,18 +441,15 @@ function subjectPhrases(): AstParser { /** Parses prepositional phrase. */ function preposition(): AstParser { return choice( - specificTokenTree("underline lon").flatMapValue((tokenTrees) => - INNER_PHRASE_PARSER.parse(tokenTrees.words) - ).map((phrase) => - ({ - preposition: { type: "default", word: "lon", marker: null }, - modifiers: [], - phrases: { - type: "single", - phrase, - }, - }) as Preposition - ), + specificTokenTree("underline lon") + .flatMapValue((tokenTrees) => INNER_PHRASE_PARSER.parse(tokenTrees.words)) + .map((phrase) => + ({ + preposition: { type: "default", word: "lon", marker: null }, + modifiers: [], + phrases: { type: "single", phrase }, + }) as Preposition + ), specificTokenTree("long glyph").flatMapValue((tokenTrees) => { if (tokenTrees.before.length !== 0) { return new Output( @@ -499,19 +469,20 @@ function preposition(): AstParser { new UnrecognizedError(`"${word}" as preposition`), ); } - const modifiers = tokenTrees.words.slice(1).map((word) => - ({ type: "default", word: { type: "default", word } }) as Modifier - ); - return INNER_PHRASE_PARSER.parse(tokenTrees.after).map((phrase) => - ({ - preposition: { type: "default", word }, - modifiers, - phrases: { - type: "single", - phrase, - }, - }) as Preposition - ); + const modifiers = tokenTrees.words + .slice(1) + .map((word) => + ({ type: "default", word: { type: "default", word } }) as Modifier + ); + return INNER_PHRASE_PARSER + .parse(tokenTrees.after) + .map((phrase) => + ({ + preposition: { type: "default", word }, + modifiers, + phrases: { type: "single", phrase }, + }) as Preposition + ); }), binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => ({ @@ -531,17 +502,16 @@ function preposition(): AstParser { optionalCombined(PREPOSITION, "preposition"), modifiers(), nestedPhrases(["anu"]), - ).map(([[preposition, modifier], modifiers, phrases]) => - ({ - preposition, - modifiers: [ - ...modifier, - ...modifiers, - ], - phrases, - }) as Preposition - ), - ).filter(filter(PREPOSITION_RULE)); + ) + .map(([[preposition, modifier], modifiers, phrases]) => + ({ + preposition, + modifiers: [...modifier, ...modifiers], + phrases, + }) as Preposition + ), + ) + .filter(filter(PREPOSITION_RULE)); } /** * Parses associated predicates whose predicates only uses top level operator. @@ -557,18 +527,14 @@ function associatedPredicates( ), ), many(optionalComma().with(preposition())), - ).map(([predicates, objects, prepositions]) => { - if (!objects && prepositions.length === 0) { - throw new CoveredError(); - } else { - return { - type: "associated", - predicates, - objects, - prepositions, - }; - } - }); + ) + .map(([predicates, objects, prepositions]) => { + if (!objects && prepositions.length === 0) { + throw new CoveredError(); + } else { + return { type: "associated", predicates, objects, prepositions }; + } + }); } /** Parses multiple predicates without _li_ nor _o_ at the beginning. */ function multiplePredicates( @@ -577,9 +543,9 @@ function multiplePredicates( if (nestingRule.length === 0) { return choice( associatedPredicates([]), - phrase().map(( - predicate, - ) => ({ type: "single", predicate }) as MultiplePredicates), + phrase().map((predicate) => + ({ type: "single", predicate }) as MultiplePredicates + ), ); } else { const [first, ...rest] = nestingRule; @@ -604,12 +570,10 @@ function multiplePredicates( ), ), ), - ).map(([group, moreGroups]) => - ({ - type, - predicates: [group, ...moreGroups], - }) as MultiplePredicates - ), + ) + .map(([group, moreGroups]) => + ({ type, predicates: [group, ...moreGroups] }) as MultiplePredicates + ), multiplePredicates(rest), ); } @@ -620,31 +584,33 @@ function clause(): AstParser { sequence( wordFrom(new Set(["mi", "sina"]), "mi/sina subject"), multiplePredicates(["li", "anu"]), - ).map(([subject, predicates]) => - ({ - type: "li clause", - subjects: { - type: "single", - phrase: { - type: "default", - headWord: { type: "default", word: subject, marker: null }, - alaQuestion: false, - modifiers: [], + ) + .map(([subject, predicates]) => + ({ + type: "li clause", + subjects: { + type: "single", + phrase: { + type: "default", + headWord: { type: "default", word: subject, marker: null }, + alaQuestion: false, + modifiers: [], + }, }, - }, - predicates, - explicitLi: false, - }) as Clause - ), + predicates, + explicitLi: false, + }) as Clause + ), sequence( preposition(), many(optionalComma().with(preposition())), - ).map(([preposition, morePreposition]) => - ({ - type: "prepositions", - prepositions: [preposition, ...morePreposition], - }) as Clause - ), + ) + .map(([preposition, morePreposition]) => + ({ + type: "prepositions", + prepositions: [preposition, ...morePreposition], + }) as Clause + ), subjectPhrases().map((phrases) => { if (phrases.type === "single" && phrases.phrase.type === "quotation") { throw new CoveredError(); @@ -652,67 +618,56 @@ function clause(): AstParser { return { type: "phrases", phrases } as Clause; } }), - subjectPhrases().skip(specificWord("o")).map((phrases) => - ({ - type: "o vocative", - phrases, - }) as Clause - ), + subjectPhrases() + .skip(specificWord("o")) + .map((phrases) => ({ type: "o vocative", phrases }) as Clause), sequence( subjectPhrases(), optionalComma().with(specificWord("li")).with( multiplePredicates(["li", "anu"]), ), - ).map(([subjects, predicates]) => - ({ - type: "li clause", - subjects, - predicates, - explicitLi: true, - }) as Clause - ), - specificWord("o").with(multiplePredicates(["o", "anu"])) - .map((predicates) => + ) + .map(([subjects, predicates]) => ({ - type: "o clause", - subjects: null, + type: "li clause", + subjects, predicates, + explicitLi: true, }) as Clause ), + specificWord("o") + .with(multiplePredicates(["o", "anu"])) + .map((predicates) => + ({ type: "o clause", subjects: null, predicates }) as Clause + ), sequence( subjectPhrases(), optionalComma().with(specificWord("o")).with( multiplePredicates(["o", "anu"]), ), - ).map(([subjects, predicates]) => - ({ - type: "o clause", - subjects: subjects, - predicates, - }) as Clause - ), + ) + .map(([subjects, predicates]) => + ({ type: "o clause", subjects, predicates }) as Clause + ), quotation().map((quotation) => - ({ - type: "quotation", - quotation, - }) as Clause + ({ type: "quotation", quotation }) as Clause ), - ).filter(filter(CLAUSE_RULE)); + ) + .filter(filter(CLAUSE_RULE)); } function preclause(): AstParser { return choice( marker().map((marker) => ({ type: "marker", marker }) as Preclause), - wordUnit(new Set(["taso"]), '"taso"').map((taso) => - ({ type: "taso", taso }) as Preclause - ), + wordUnit(new Set(["taso"]), '"taso"') + .map((taso) => ({ type: "taso", taso }) as Preclause), ); } function postclause(): AstParser { return choice( marker().map((marker) => ({ type: "marker", marker }) as Postclause), - specificWord("anu").with(wordUnit(new Set(["seme"]), '"seme"')).map( - (seme) => ({ type: "anu seme", seme }) as Postclause, - ), + specificWord("anu") + .with(wordUnit(new Set(["seme"]), '"seme"')) + .map((seme) => ({ type: "anu seme", seme }) as Postclause), ); } /** Parses a single clause including preclause and postclause. */ @@ -722,16 +677,13 @@ function fullClause(): AstParser { optional(preclause().skip(optionalComma())), clause(), optional(optionalComma().with(postclause())), - ).map(([preclause, clause, postclause]) => - ({ - type: "default", - preclause, - clause, - postclause, - }) as FullClause - ), + ) + .map(([preclause, clause, postclause]) => + ({ type: "default", preclause, clause, postclause }) as FullClause + ), marker().map((marker) => ({ type: "marker", marker }) as FullClause), - ).filter(filter(FULL_CLAUSE_RULE)); + ) + .filter(filter(FULL_CLAUSE_RULE)); } /** parses _la_ with optional comma around. */ function la(): AstParser { @@ -750,10 +702,11 @@ function sentence(): AstParser { eol("end of sentence").map(() => ""), punctuation(), ), - ).map(([clause, moreClauses, punctuation]) => ({ - laClauses: [clause, ...moreClauses], - punctuation, - })); + ) + .map(([clause, moreClauses, punctuation]) => ({ + laClauses: [clause, ...moreClauses], + punctuation, + })); } const INNER_QUOTATION_PARSER = all(sentence()) .skip(eol("end of sentence")) @@ -761,14 +714,15 @@ const INNER_QUOTATION_PARSER = all(sentence()) /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => - INNER_QUOTATION_PARSER.parse(tokenTree.tokenTree).map( - (value) => + INNER_QUOTATION_PARSER + .parse(tokenTree.tokenTree) + .map((value) => ({ sentences: value, leftMark: tokenTree.leftMark, rightMark: tokenTree.rightMark, - }) as Quotation, - ) + }) as Quotation + ) ); } const FULL_PARSER = allAtLeastOnce(sentence()) diff --git a/src/ast.ts b/src/ast.ts index 6bda29b..564df5d 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -173,9 +173,10 @@ export function someModifierInMultiplePhrases( if (phrases.type === "single") { return someModifierInPhrase(phrases.phrase, whenQuotation, checker); } else if (phrases.type === "and conjunction" || phrases.type === "anu") { - return phrases.phrases.some((phrases) => - someModifierInMultiplePhrases(phrases, whenQuotation, checker) - ); + return phrases.phrases + .some((phrases) => + someModifierInMultiplePhrases(phrases, whenQuotation, checker) + ); } else { throw new UnreachableError(); } @@ -191,9 +192,8 @@ export function somePhraseInMultiplePhrases( if (phrases.type === "single") { return checker(phrases.phrase); } else if (phrases.type === "and conjunction" || phrases.type === "anu") { - return phrases.phrases.some((phrases) => - somePhraseInMultiplePhrases(phrases, checker) - ); + return phrases.phrases + .some((phrases) => somePhraseInMultiplePhrases(phrases, checker)); } else { throw new UnreachableError(); } @@ -215,9 +215,8 @@ export function someObjectInMultiplePredicate( return false; } } else if (predicate.type === "and conjunction" || predicate.type === "anu") { - return predicate.predicates.some((predicates) => - someObjectInMultiplePredicate(predicates, checker) - ); + return predicate.predicates + .some((predicates) => someObjectInMultiplePredicate(predicates, checker)); } else { throw new UnreachableError(); } diff --git a/src/dictionary.ts b/src/dictionary.ts index 922ed3a..79fe4d9 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1104,11 +1104,7 @@ export type Definition = pluralObject: string; pluralPossessive: string; } - | { - type: "adjective"; - adjective: string; - kind: AdjectiveType; - } + | { type: "adjective"; adjective: string; kind: AdjectiveType } | { type: "compound adjective"; adjectives: Array; @@ -1118,18 +1114,9 @@ export type Definition = adverbs: Array; adjective: Definition & { type: "adjective" }; } - | { - type: "quantifier"; - quantifier: string; - } - | { - type: "numeral"; - number: number; - } - | { - type: "adverb"; - adverb: string; - } + | { type: "quantifier"; quantifier: string } + | { type: "numeral"; number: number } + | { type: "adverb"; adverb: string } | { type: "verb"; present: string; @@ -1144,14 +1131,8 @@ export type Definition = verb: Definition & { type: "verb" }; object: Definition & { type: "noun" | "adjective noun phrase" }; } - | { - type: "gerund"; - gerund: string; - } - | { - type: "interjection"; - interjection: string; - }; + | { type: "gerund"; gerund: string } + | { type: "interjection"; interjection: string }; export const CONTENT_WORD = new Set([ ...SPECIAL_CONTENT_WORD, ...Object.keys(CONTENT_WORD_DEFINITION), @@ -1172,60 +1153,36 @@ function noun(word: string): Definition & { type: "noun" } { } else { throw new Error(`${word} must either contain parenthesis or slash`); } - return { - type: "noun", - singular, - plural, - condensed: word, - }; + return { type: "noun", singular, plural, condensed: word }; } function singularNoun(word: string): Definition & { type: "noun" } { - return { - type: "noun", - singular: word, - plural: null, - condensed: word, - }; + return { type: "noun", singular: word, plural: null, condensed: word }; } function pluralNoun(word: string): Definition & { type: "noun" } { - return { - type: "noun", - singular: null, - plural: word, - condensed: word, - }; + return { type: "noun", singular: null, plural: word, condensed: word }; } function adjectiveNounPhrase( adjectives: Array, noun: Definition & { type: "noun" }, ): Definition & { type: "adjective noun phrase" } { - return { - type: "adjective noun phrase", - adjectives, - noun, - }; + return { type: "adjective noun phrase", adjectives, noun }; } function adverbAdjectivePhrase( adverbs: Array, adjective: Definition & { type: "adjective" }, ): Definition & { type: "adverb adjective phrase" } { - return { - type: "adverb adjective phrase", - adverbs, - adjective, - }; + return { type: "adverb adjective phrase", adverbs, adjective }; } function compoundAdjective( adjectives: Array, ): Definition & { type: "compound adjective" } { - return { - type: "compound adjective", - adjectives, - }; + return { type: "compound adjective", adjectives }; } -function parseVerb( - word: string, -): { past: string; present: string; condensed: string } { +function parseVerb(word: string): { + past: string; + present: string; + condensed: string; +} { const paren = word.match(/([a-z]*)\(([a-z]*)\)(| [a-z]*)/); let present: string; let past: string; @@ -1289,11 +1246,7 @@ function verbObjectPhrase( verb: Definition & { type: "verb" }, object: Definition & { type: "noun" | "adjective noun phrase" }, ): Definition & { type: "verb object phrase" } { - return { - type: "verb object phrase", - verb, - object, - }; + return { type: "verb object phrase", verb, object }; } type IntransitiveVerbOption = { presentPast: string; @@ -1342,33 +1295,17 @@ function adjective( word: string, kind: AdjectiveType, ): Definition & { type: "adjective" } { - return { - type: "adjective", - adjective: word, - kind, - }; + return { type: "adjective", adjective: word, kind }; } function numeral(number: number): Definition & { type: "numeral" } { - return { - type: "numeral", - number, - }; + return { type: "numeral", number }; } function adverb(word: string): Definition & { type: "adverb" } { - return { - type: "adverb", - adverb: word, - }; + return { type: "adverb", adverb: word }; } function quantifier(word: string): Definition & { type: "quantifier" } { - return { - type: "quantifier", - quantifier: word, - }; + return { type: "quantifier", quantifier: word }; } function interjection(word: string): Definition & { type: "interjection" } { - return { - type: "interjection", - interjection: word, - }; + return { type: "interjection", interjection: word }; } diff --git a/src/lexer.ts b/src/lexer.ts index 776561c..c3a5a65 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -8,6 +8,7 @@ import { Output } from "./output.ts"; import { + CoveredError, UnexpectedError, UnreachableError, UnrecognizedError, @@ -17,12 +18,12 @@ import { allAtLeastOnce, choiceOnlyOne, error, + nothing, optionalAll, Parser, sequence as rawSequence, } from "./parser-lib.ts"; import { TokenTree } from "./token-tree.ts"; -import { CoveredError } from "./error.ts"; import { settings } from "./settings.ts"; import { END_OF_CARTOUCHE, @@ -35,7 +36,6 @@ import { START_OF_REVERSE_LONG_GLYPH, UCSUR_TO_LATIN, } from "./ucsur.ts"; -import { nothing } from "./parser-lib.ts"; export type Lexer = Parser; @@ -109,9 +109,8 @@ function latinWord(): Lexer { } /** Parses variation selector. */ function variationSelector(): Lexer { - return match(/[\uFE00-\uFE0F]/, "variation selector").map(([character]) => - character - ); + return match(/[\uFE00-\uFE0F]/, "variation selector") + .map(([character]) => character); } /** Parses an UCSUR character with optional variation selector and space. */ function ucsur( @@ -186,7 +185,8 @@ function combinedGlyphs(): Lexer> { allAtLeastOnce( joiner().with(ucsurWord({ allowVariation: false, allowSpace: false })), ), - ).map(([first, rest]) => [first, ...rest]); + ) + .map(([first, rest]) => [first, ...rest]); } /** Parses a word, either UCSUR or latin. */ function word(): Lexer { @@ -196,9 +196,8 @@ function word(): Lexer { function properWords(): Lexer { return allAtLeastOnce( match(/([A-Z][a-zA-Z]*)\s*/, "proper word").map(([_, word]) => word), - ).map( - (array) => array.join(" "), - ); + ) + .map((array) => array.join(" ")); } /** Parses a specific word, either UCSUR or latin. */ function specificWord(thatWord: string): Lexer { @@ -209,9 +208,8 @@ function specificWord(thatWord: string): Lexer { } /** Parses multiple a. */ function multipleA(): Lexer { - return sequence(specificWord("a"), allAtLeastOnce(specificWord("a"))).map(( - [a, as], - ) => [a, ...as].length); + return sequence(specificWord("a"), allAtLeastOnce(specificWord("a"))) + .map(([a, as]) => [a, ...as].length); } function longA(): Lexer { return match(/(a+)\s*/, "long a").map(([_, a]) => { @@ -244,18 +242,19 @@ function comma(): Lexer { /** Parses a punctuation. */ function punctuation(): Lexer { return choiceOnlyOne( - match(/([.,:;?!])\s*/, "punctuation").map(([_, punctuation]) => - punctuation - ), + match(/([.,:;?!])\s*/, "punctuation") + .map(([_, punctuation]) => punctuation), // NOTE: maybe these are unnecessary specificUcsurCharacter("󱦜", "middle dot", { allowVariation: true, allowSpace: true, - }).map(() => "."), + }) + .map(() => "."), specificUcsurCharacter("󱦝", "middle dot", { allowVariation: true, allowSpace: true, - }).map(() => ":"), + }) + .map(() => ":"), ); } /** Parses cartouche element and returns the phonemes or letters it represents. */ @@ -280,26 +279,25 @@ function cartoucheElement(): Lexer { allowSpace: true, }), ), - ).map( - (dots) => dots.length, - ), - ).map(([word, dots]) => { - const VOWEL = /[aeiou]/; - const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; - let count = dots; - if (VOWEL.test(word[0])) { - count++; - } - const morae = word.match(MORAE)!; - if (morae.length < count) { - throw new UnrecognizedError("Excess dots"); - } - return morae.slice(0, count).join(""); - }), + ) + .map((dots) => dots.length), + ) + .map(([word, dots]) => { + const VOWEL = /[aeiou]/; + const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; + let count = dots; + if (VOWEL.test(word[0])) { + count++; + } + const morae = word.match(MORAE)!; + if (morae.length < count) { + throw new UnrecognizedError("Excess dots"); + } + return morae.slice(0, count).join(""); + }), singleUcsurWord().map((word) => word[0]), - match(/([a-zA-Z]+)\s*/, "Latin letter").map(( - [_, letter], - ) => letter.toLowerCase()), + match(/([a-zA-Z]+)\s*/, "Latin letter") + .map(([_, letter]) => letter.toLowerCase()), ); } /** Parses a single cartouche. */ @@ -314,12 +312,11 @@ function cartouche(): Lexer { allowVariation: false, allowSpace: true, }), - ).map( - ([_, words, _1]) => { + ) + .map(([_, words, _1]) => { const word = words.join(""); return word[0].toUpperCase() + word.slice(1); - }, - ); + }); } /** Parses multiple cartouches. */ function cartouches(): Lexer { @@ -333,27 +330,23 @@ function quotation( openQuotationMark(), tokenTrees({ allowQuotation: false, allowLongGlyph }), closeQuotationMark(), - ).map(([leftMark, tokenTree, rightMark]) => { - if (leftMark === '"' || leftMark === "“") { - if (rightMark !== '"' && rightMark !== "”") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else if (leftMark === "«") { - if (rightMark !== "»") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else if (leftMark === "「") { - if (rightMark !== "」") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else throw new UnreachableError(); - return { - type: "quotation", - tokenTree, - leftMark, - rightMark, - }; - }); + ) + .map(([leftMark, tokenTree, rightMark]) => { + if (leftMark === '"' || leftMark === "“") { + if (rightMark !== '"' && rightMark !== "”") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + } else if (leftMark === "«") { + if (rightMark !== "»") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + } else if (leftMark === "「") { + if (rightMark !== "」") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + } else throw new UnreachableError(); + return { type: "quotation", tokenTree, leftMark, rightMark }; + }); } // spaces after the first glyph and the last glyph aren't parsed and so must be // manually added by the caller if needed @@ -378,7 +371,8 @@ function longContainer( allowSpace: false, allowVariation: false, }), - ).map(([_, inside, _1]) => inside); + ) + .map(([_, inside, _1]) => inside); } function longCharacterContainer( allowQuotation: boolean, @@ -398,16 +392,16 @@ function longSpaceContainer(): Lexer { START_OF_LONG_GLYPH, END_OF_LONG_GLYPH, match(/\s+/, "space").map(([space]) => space.length), - ).skip(spaces()); + ) + .skip(spaces()); } // This doesn't parses space on the right and so must be manually added by the // caller if needed function longGlyphHead(): Lexer> { return choiceOnlyOne( combinedGlyphs(), - ucsurWord({ allowSpace: false, allowVariation: false }).map(( - word, - ) => [word]), + ucsurWord({ allowSpace: false, allowVariation: false }) + .map((word) => [word]), ); } function characterLongGlyph( @@ -429,7 +423,8 @@ function characterLongGlyph( END_OF_LONG_GLYPH, ), ), - ).skip(spaces()) + ) + .skip(spaces()) .map(([beforeNull, words, afterNull]) => { const before = beforeNull ?? []; const after = afterNull ?? []; @@ -448,11 +443,12 @@ function longSpaceGlyph(): Lexer { return sequence( longGlyphHead(), longSpaceContainer().skip(spaces()), - ).map(([words, spaceLength]) => ({ - type: "long glyph space", - words, - spaceLength, - })); + ) + .map(([words, spaceLength]) => ({ + type: "long glyph space", + words, + spaceLength, + })); } function longLon( allowQuotation: boolean, @@ -461,7 +457,8 @@ function longLon( allowQuotation, START_OF_REVERSE_LONG_GLYPH, END_OF_LONG_GLYPH, - ).skip(spaces()); + ) + .skip(spaces()); } function longGlyph(allowQuotation: boolean): Lexer { return choiceOnlyOne( @@ -490,9 +487,8 @@ function tokenTree( if (settings.xAlaXPartialParsing) { xAlaXParser = error(new CoveredError()); } else { - xAlaXParser = xAlaX().map((word) => - ({ type: "x ala x", word }) as TokenTree - ); + xAlaXParser = xAlaX() + .map((word) => ({ type: "x ala x", word }) as TokenTree); } return choiceOnlyOne( punctuation().map((punctuation) => @@ -501,12 +497,11 @@ function tokenTree( comma().map(() => ({ type: "comma" }) as TokenTree), quotationParser, longGlyphParser, - choiceOnlyOne(cartouches(), properWords()).map((words) => - ({ type: "proper word", words }) as TokenTree - ), - combinedGlyphs().skip(spaces()).map((words) => - ({ type: "combined glyphs", words }) as TokenTree - ), + choiceOnlyOne(cartouches(), properWords()) + .map((words) => ({ type: "proper word", words }) as TokenTree), + combinedGlyphs() + .skip(spaces()) + .map((words) => ({ type: "combined glyphs", words }) as TokenTree), longA().map((length) => ({ type: "long a", length }) as TokenTree), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), xAlaXParser, diff --git a/src/main.ts b/src/main.ts index c333328..ce1a754 100644 --- a/src/main.ts +++ b/src/main.ts @@ -128,9 +128,9 @@ function updateOutput(): void { if (error.length === 0) { error = [ ...new Set( - translations.errors.filter((x) => !(x instanceof CoveredError)).map( - (x) => x.message, - ), + translations.errors + .filter((x) => !(x instanceof CoveredError)) + .map((x) => x.message), ), ]; } diff --git a/src/parser-lib.ts b/src/parser-lib.ts index d66dbc8..f9970a4 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -20,18 +20,19 @@ export class Parser { */ map(mapper: (value: U) => V): Parser { return new Parser((src) => - this.parser(src).map(({ value, rest }) => ({ - value: mapper(value), - rest, - })) + this + .parser(src) + .map(({ value, rest }) => ({ value: mapper(value), rest })) ); } /** TODO better comment. */ flatMapValue(mapper: (value: U) => Output): Parser { return new Parser((src) => - this.parser(src).flatMap(({ value, rest }) => - mapper(value).map((value) => ({ value, rest })) - ) + this + .parser(src) + .flatMap(({ value, rest }) => + mapper(value).map((value) => ({ value, rest })) + ) ); } /** @@ -133,15 +134,13 @@ export function sequence>( ): Parser { // We resorted to using `any` types here, make sure it works properly // deno-lint-ignore no-explicit-any - return (sequence as Array).reduceRight( - (newParser, parser) => + return (sequence as Array) + .reduceRight((newParser, parser) => // deno-lint-ignore no-explicit-any parser.then((value: any) => // deno-lint-ignore no-explicit-any newParser.map((newValue: any) => [value, ...newValue]) - ), - nothing().map(() => []), - ); + ), nothing().map(() => [])); } /** * Parses `parser` multiple times and returns an `Array`. The resulting @@ -154,9 +153,8 @@ export function sequence>( */ export function many(parser: Parser): Parser> { return choice>( - sequence]>(parser, lazy(() => many(parser))).map(( - [first, rest], - ) => [first, ...rest]), + sequence]>(parser, lazy(() => many(parser))) + .map(([first, rest]) => [first, ...rest]), nothing().map(() => []), ); } @@ -170,9 +168,8 @@ export function many(parser: Parser): Parser> { export function manyAtLeastOnce( parser: Parser, ): Parser> { - return sequence]>(parser, many(parser)).map(( - [first, rest], - ) => [first, ...rest]); + return sequence]>(parser, many(parser)) + .map(([first, rest]) => [first, ...rest]); } /** * Parses `parser` multiple times and returns an `Array`. This function is @@ -184,9 +181,8 @@ export function manyAtLeastOnce( */ export function all(parser: Parser): Parser> { return choiceOnlyOne>( - sequence]>(parser, lazy(() => all(parser))).map(( - [first, rest], - ) => [first, ...rest]), + sequence]>(parser, lazy(() => all(parser))) + .map(([first, rest]) => [first, ...rest]), nothing().map(() => []), ); } @@ -200,7 +196,6 @@ export function all(parser: Parser): Parser> { export function allAtLeastOnce( parser: Parser, ): Parser> { - return sequence]>(parser, all(parser)).map(( - [first, rest], - ) => [first, ...rest]); + return sequence]>(parser, all(parser)) + .map(([first, rest]) => [first, ...rest]); } diff --git a/src/translator.ts b/src/translator.ts index 12184f4..26426a2 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,7 +1,7 @@ // This module is bound to be replaced with a translator that uses English AST -import { Clause } from "./ast.ts"; import { + Clause, FullClause, Modifier, MultiplePhrases, @@ -66,9 +66,8 @@ function wordUnitAs( } else if (word.type === "numbers") { return new Output([number(word.numbers).toString()]); } else if (word.type === "reduplication") { - return definition(kind, word.word).map((noun) => - new Array(word.count).fill(noun).join(" ") - ); + return definition(kind, word.word) + .map((noun) => new Array(word.count).fill(noun).join(" ")); } else if (word.type === "x ala x") { return new Output(new TodoError("translation for X ala X")); } else { @@ -105,23 +104,20 @@ function modifierAsSuffix( construction = "in X way"; } if (suffix.type === "default") { - return wordUnitAs(kind, suffix.word).map((translation) => - construction.replace("X", translation) - ); + return wordUnitAs(kind, suffix.word) + .map((translation) => construction.replace("X", translation)); } else if (suffix.type === "nanpa") { return phraseAs(kind, suffix.phrase, { named: kind === "noun", suffix: false, - }).map( - (translation) => `in position ${translation}`, - ); + }) + .map((translation) => `in position ${translation}`); } else if (suffix.type === "pi") { return phraseAs(kind, suffix.phrase, { named: kind === "noun", suffix: false, - }).map(( - translation, - ) => construction.replace("X", translation)); + }) + .map((translation) => construction.replace("X", translation)); } else if (suffix.type === "proper words") { return new Output([`named ${suffix.words}`]); } else { @@ -133,18 +129,17 @@ function modifierAsSuffix( function defaultPhraseAs( kind: "noun" | "adjective", phrase: Phrase & { type: "default" }, - options?: { - named?: boolean; - suffix?: boolean; - }, + options?: { named?: boolean; suffix?: boolean }, ): TranslationOutput { const named = options?.named ?? true; const suffix = options?.suffix ?? true; const name = ( - phrase.modifiers.filter( - (modifier) => modifier.type === "proper words", - )[0] as undefined | (Modifier & { type: "proper words" }) - )?.words; + phrase.modifiers + .filter((modifier) => modifier.type === "proper words")[0] as + | undefined + | (Modifier & { type: "proper words" }) + ) + ?.words; if (name && !named) { return new Output(); } @@ -155,60 +150,57 @@ function defaultPhraseAs( modifierKind = "adverb"; } const headWord = wordUnitAs(kind, phrase.headWord); - const modifierNoName = phrase.modifiers.filter(( - modifier, - ) => modifier.type !== "proper words"); - const modifierTranslation: Array = modifierNoName.map( - (modifier) => modifierAs(modifierKind, modifier), - ); + const modifierNoName = phrase.modifiers + .filter((modifier) => modifier.type !== "proper words"); + const modifierTranslation: Array = modifierNoName + .map((modifier) => modifierAs(modifierKind, modifier)); const translations = rotate([headWord, rotate(modifierTranslation)] as const) - .map( - ([headWord, modifiers]) => - [...modifiers.slice().reverse(), headWord].join(" "), - ).map( - (translation) => { - if (name != null) { - return `${translation} named ${name}`; - } else { - return translation; - } - }, - ); + .map(([headWord, modifiers]) => + [...modifiers.slice().reverse(), headWord].join(" ") + ) + .map((translation) => { + if (name != null) { + return `${translation} named ${name}`; + } else { + return translation; + } + }); if (suffix) { const extraTranslations: Array = [ ...modifierNoName.keys(), - ].map( - (i) => { + ] + .map((i) => { const suffixTranslation = modifierAsSuffix(kind, modifierNoName[i]); const modifierTranslation = [ ...modifierNoName.slice(0, i), ...modifierNoName.slice(i + 1), - ].map((modifier) => modifierAs(modifierKind, modifier)); - return rotate([headWord, rotate(modifierTranslation)] as const).map( - ([headWord, modifiers]) => - [...modifiers.slice().reverse(), headWord].join(" "), - ).map( - (translation) => { + ] + .map((modifier) => modifierAs(modifierKind, modifier)); + return rotate([headWord, rotate(modifierTranslation)] as const) + .map(([headWord, modifiers]) => + [...modifiers.slice().reverse(), headWord].join(" ") + ) + .map((translation) => { if (name != null) { return `${translation} named ${name}`; } else { return translation; } - }, - ).flatMap((left) => - suffixTranslation.map((right) => [left, right].join(" ")) - ); - }, - ); + }) + .flatMap((left) => + suffixTranslation.map((right) => [left, right].join(" ")) + ); + }); return Output.concat(translations, ...extraTranslations); } else { return translations; } } -function phraseAs(kind: "noun" | "adjective", phrase: Phrase, options?: { - named?: boolean; - suffix?: boolean; -}): TranslationOutput { +function phraseAs( + kind: "noun" | "adjective", + phrase: Phrase, + options?: { named?: boolean; suffix?: boolean }, +): TranslationOutput { if (phrase.type === "default") { return defaultPhraseAs(kind, phrase, options); } else { @@ -246,13 +238,13 @@ function translateMultiplePhrases( conjunction, " ", last, - ].join(""); + ] + .join(""); } }); } else if (level === 1) { - return translations.map((phrases) => - phrases.join([" ", conjunction, " "].join("")) - ); + return translations + .map((phrases) => phrases.join([" ", conjunction, " "].join(""))); } else { throw new UnreachableError(); } @@ -335,14 +327,14 @@ function translateSentence(sentence: Sentence): TranslationOutput { ...contexts.map((context) => `given ${context}, `), final, sentence.punctuation, - ].join(""); + ] + .join(""); }); } /** Translates multiple sentences. */ function translateSentences(sentences: Array): TranslationOutput { - return rotate(sentences.map(translateSentence)).map((sentences) => - sentences.join(" ") - ); + return rotate(sentences.map(translateSentence)) + .map((sentences) => sentences.join(" ")); } /** Full Toki Pona translator. */ export function translate(src: string): TranslationOutput { diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts index 3c39e69..bcc7072 100644 --- a/telo-misikeke/build.ts +++ b/telo-misikeke/build.ts @@ -31,8 +31,7 @@ async function buildFile( } export async function build(): Promise { await Promise.all( - SOURCE.map((file) => - buildFile(file.source, file.destination, file.exportItem) - ), + SOURCE + .map((file) => buildFile(file.source, file.destination, file.exportItem)), ); } From 2fce2468ca19e3a40fff26cc93ab2d1a286dd1c0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 09:03:45 +0800 Subject: [PATCH 236/738] make main.ts runtime agnostic --- src/main.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main.ts b/src/main.ts index ce1a754..33c8f71 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -/** Module for main execution. */ +/** Module for main execution in the browser. */ import { CoveredError } from "./error.ts"; import { translate } from "./translator.ts"; @@ -236,23 +236,25 @@ function resetSettings(): void { setRedundancy("number", elements!.number); setRedundancy("tense", elements!.tense); } -document.addEventListener("DOMContentLoaded", () => { - loadElements(); - setVersion(); - loadSettings(); - elements!.settingsButton.addEventListener("click", () => { - elements!.dialogBox.showModal(); - }); - elements!.confirmButton.addEventListener("click", () => { - confirmSettings(); - elements!.dialogBox.close(); - }); - elements!.resetButton.addEventListener("click", resetSettings); - elements!.translateButton.addEventListener("click", updateOutput); - elements!.input.addEventListener("keydown", (event) => { - if (event.code === "Enter") { - event.preventDefault(); - updateOutput(); - } +if (typeof document !== "undefined") { + document.addEventListener("DOMContentLoaded", () => { + loadElements(); + setVersion(); + loadSettings(); + elements!.settingsButton.addEventListener("click", () => { + elements!.dialogBox.showModal(); + }); + elements!.confirmButton.addEventListener("click", () => { + confirmSettings(); + elements!.dialogBox.close(); + }); + elements!.resetButton.addEventListener("click", resetSettings); + elements!.translateButton.addEventListener("click", updateOutput); + elements!.input.addEventListener("keydown", (event) => { + if (event.code === "Enter") { + event.preventDefault(); + updateOutput(); + } + }); }); -}); +} From 82caea0958d55ba2c416b06fe25641f8a356d403 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 09:12:09 +0800 Subject: [PATCH 237/738] simplify code for loading elements --- src/main.ts | 57 +++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/src/main.ts b/src/main.ts index 33c8f71..c4711a8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,40 +33,29 @@ type Elements = { let elements: undefined | Elements; function loadElements(): void { - elements = { - input: document.getElementById("input") as HTMLTextAreaElement, - output: document.getElementById("output") as HTMLUListElement, - error: document.getElementById("error") as HTMLParagraphElement, - errorList: document.getElementById( - "error-list", - ) as HTMLParagraphElement, - translateButton: document.getElementById( - "translate-button", - ) as HTMLButtonElement, - settingsButton: document.getElementById( - "settings-button", - ) as HTMLButtonElement, - dialogBox: document.getElementById( - "dialog-box", - ) as HTMLDialogElement, - confirmButton: document.getElementById( - "confirm-button", - ) as HTMLButtonElement, - resetButton: document.getElementById("reset-button") as HTMLButtonElement, - version: document.getElementById("version") as HTMLAnchorElement, - useTeloMisikeke: document.getElementById( - "use-telo-misikeke", - ) as HTMLInputElement, - randomize: document.getElementById("randomize") as HTMLInputElement, - number: document.getElementById("number") as HTMLSelectElement, - tense: document.getElementById("tense") as HTMLSelectElement, - xAlaXPartialParsing: document.getElementById( - "x-ala-x-parsing", - ) as HTMLInputElement, - anuAsContentWord: document.getElementById( - "anu-as-content-word", - ) as HTMLInputElement, - }; + const elementNames = { + input: "input", + output: "output", + error: "error", + errorList: "error-list", + translateButton: "translate-button", + settingsButton: "settings-button", + dialogBox: "dialog-box", + confirmButton: "confirm-button", + resetButton: "reset-button", + version: "version", + useTeloMisikeke: "use-telo-misikeke", + randomize: "randomize", + number: "number", + tense: "tense", + xAlaXPartialParsing: "x-ala-x-parsing", + anuAsContentWord: "anu-as-content-word", + // deno-lint-ignore no-explicit-any + } as any; + for (const name of Object.keys(elementNames)) { + elementNames[name] = document.getElementById(name); + } + elements = elementNames; } function setVersion(): void { if (DEVELOPMENT) { From d4cfd99fee61ab88586d99fb3a7cc328b4be8593 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:32:26 +0800 Subject: [PATCH 238/738] refactor settings --- src/filter.ts | 2 +- src/lexer.ts | 2 +- src/main.ts | 114 +++--------------------------------- src/settings.ts | 149 +++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 137 insertions(+), 130 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 41bb6f0..e7d0f4f 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -37,7 +37,7 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ }, // disallow "anu" as content word only when turned off in settings (wordUnit) => { - if (settings.anuAsContentWord) { + if (settings.get("anu-as-content-word")) { return true; } const isAnu = ( diff --git a/src/lexer.ts b/src/lexer.ts index c3a5a65..ee4e2fe 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -484,7 +484,7 @@ function tokenTree( longGlyphParser = error(new CoveredError()); } let xAlaXParser: Lexer; - if (settings.xAlaXPartialParsing) { + if (settings.get("x-ala-x-parsing")) { xAlaXParser = error(new CoveredError()); } else { xAlaXParser = xAlaX() diff --git a/src/main.ts b/src/main.ts index c4711a8..dc943b4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { CoveredError } from "./error.ts"; import { translate } from "./translator.ts"; -import { defaultSettings, RedundancySettings, settings } from "./settings.ts"; +import { settings } from "./settings.ts"; import { teloMisikeke } from "../deps.ts"; // Set to false when releasing, set to true when developing @@ -22,12 +22,6 @@ type Elements = { confirmButton: HTMLButtonElement; resetButton: HTMLButtonElement; version: HTMLAnchorElement; - useTeloMisikeke: HTMLInputElement; - randomize: HTMLInputElement; - number: HTMLSelectElement; - tense: HTMLSelectElement; - xAlaXPartialParsing: HTMLInputElement; - anuAsContentWord: HTMLInputElement; }; /** A map of all HTML elements that are used here. */ let elements: undefined | Elements; @@ -44,12 +38,6 @@ function loadElements(): void { confirmButton: "confirm-button", resetButton: "reset-button", version: "version", - useTeloMisikeke: "use-telo-misikeke", - randomize: "randomize", - number: "number", - tense: "tense", - xAlaXPartialParsing: "x-ala-x-parsing", - anuAsContentWord: "anu-as-content-word", // deno-lint-ignore no-explicit-any } as any; for (const name of Object.keys(elementNames)) { @@ -105,13 +93,13 @@ function updateOutput(): void { const translations = translate(source); if (!translations.isError()) { const output = [...new Set(translations.output)]; - if (settings.randomize) { + if (settings.get("randomize")) { output.sort(() => Math.random() - Math.random()); } outputTranslations(output); } else { let error: Array = []; - if (settings.useTeloMisikeke) { + if (settings.get("use-telo-misikeke")) { error = teloMisikeke.errors(source); } if (error.length === 0) { @@ -137,107 +125,21 @@ function updateOutput(): void { throw unreachableError; } } -function loadSettings(): void { - function setBool< - T extends - | "useTeloMisikeke" - | "randomize" - | "xAlaXPartialParsing" - | "anuAsContentWord", - >(name: T, element: HTMLInputElement): void { - const x = localStorage.getItem(name); - let value: boolean; - if (x == null) { - value = defaultSettings[name]; - } else { - value = x === "true"; - } - settings[name] = value; - element.checked = value; - } - function setRedundancy( - name: T, - element: HTMLSelectElement, - ): void { - const x = localStorage.getItem(name) ?? defaultSettings[name]; - if (["both", "condensed", "default only"].includes(x)) { - settings[name] = x as RedundancySettings; - element.value = x; - } else { - settings[name] = defaultSettings[name]; - element.value = defaultSettings[name]; - } - } - setBool("useTeloMisikeke", elements!.useTeloMisikeke); - setBool("randomize", elements!.randomize); - setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); - setBool("anuAsContentWord", elements!.anuAsContentWord); - setRedundancy("number", elements!.number); - setRedundancy("tense", elements!.tense); -} -function confirmSettings(): void { - function setBool< - T extends - | "useTeloMisikeke" - | "randomize" - | "xAlaXPartialParsing" - | "anuAsContentWord", - >(name: T, element: HTMLInputElement) { - const value = element.checked; - localStorage.setItem(name, value.toString()); - settings[name] = value; - } - function setRedundancy( - name: T, - element: HTMLSelectElement, - ): void { - const value = element.value as RedundancySettings; - localStorage.setItem(name, value.toString()); - settings[name] = value; - } - setBool("useTeloMisikeke", elements!.useTeloMisikeke); - setBool("randomize", elements!.randomize); - setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); - setBool("anuAsContentWord", elements!.anuAsContentWord); - setRedundancy("number", elements!.number); - setRedundancy("tense", elements!.tense); -} -function resetSettings(): void { - function setBool< - T extends - | "useTeloMisikeke" - | "randomize" - | "xAlaXPartialParsing" - | "anuAsContentWord", - >(name: T, element: HTMLInputElement) { - element.checked = defaultSettings[name]; - } - function setRedundancy( - name: T, - element: HTMLSelectElement, - ): void { - element.value = defaultSettings[name]; - } - setBool("useTeloMisikeke", elements!.useTeloMisikeke); - setBool("randomize", elements!.randomize); - setBool("xAlaXPartialParsing", elements!.xAlaXPartialParsing); - setBool("anuAsContentWord", elements!.anuAsContentWord); - setRedundancy("number", elements!.number); - setRedundancy("tense", elements!.tense); -} if (typeof document !== "undefined") { document.addEventListener("DOMContentLoaded", () => { loadElements(); setVersion(); - loadSettings(); + settings.load(); elements!.settingsButton.addEventListener("click", () => { elements!.dialogBox.showModal(); }); elements!.confirmButton.addEventListener("click", () => { - confirmSettings(); + settings.confirm(); elements!.dialogBox.close(); }); - elements!.resetButton.addEventListener("click", resetSettings); + elements!.resetButton.addEventListener("click", () => { + settings.reset(); + }); elements!.translateButton.addEventListener("click", updateOutput); elements!.input.addEventListener("keydown", (event) => { if (event.code === "Enter") { diff --git a/src/settings.ts b/src/settings.ts index ffb7d4d..1c19604 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,28 +1,133 @@ /** Module for translation settings stored as a global state */ /** */ -export type RedundancySettings = "both" | "condensed" | "default only"; -export type Settings = { - useTeloMisikeke: boolean; - randomize: boolean; - xAlaXPartialParsing: boolean; - anuAsContentWord: boolean; - number: RedundancySettings; - tense: RedundancySettings; +type RedundancySettings = "both" | "condensed" | "default only"; +type Settings = { + "use-telo-misikeke": boolean; + "randomize": boolean; + "x-ala-x-parsing": boolean; + "anu-as-content-word": boolean; + "number": RedundancySettings; + "tense": RedundancySettings; }; -export const defaultSettings: Settings = { - useTeloMisikeke: false, - randomize: false, - xAlaXPartialParsing: false, - anuAsContentWord: false, - number: "both", - tense: "both", +interface Option { + default: T; + updater: Updater; +} +interface SettingsItem extends Option { + value: T; +} +type Updater = { + parse: (value: string) => T | null; + stringify: (value: T) => string; + load: (input: HTMLInputElement) => T; + set: (input: HTMLInputElement, value: T) => void; }; -export const settings: Settings = { - useTeloMisikeke: false, - randomize: false, - xAlaXPartialParsing: false, - anuAsContentWord: false, - number: "both", - tense: "both", +class Setter { + private settings: { [S in keyof T]: SettingsItem }; + constructor(option: { [S in keyof T]: Option }) { + // deno-lint-ignore no-explicit-any + const settings: any = {}; + for (const name of Object.keys(option)) { + const item = option[name]; + settings[name] = { + value: item.default, + default: item.default, + updater: item.updater, + }; + } + this.settings = settings; + } + get(name: S): T[S] { + return this.settings[name].value as T[S]; + } + load(): void { + for (const name of Object.keys(this.settings)) { + const settings = this.settings[name]; + const src = localStorage.getItem(name); + if (typeof src === "string") { + settings.value = settings.updater.parse(src) ?? settings.default; + } else { + settings.value = settings.default; + } + settings.updater.set( + document.getElementById(name) as HTMLInputElement, + settings.value, + ); + } + } + confirm(): void { + for (const name of Object.keys(this.settings)) { + const settings = this.settings[name]; + settings.value = settings.updater.load( + document.getElementById(name) as HTMLInputElement, + ); + localStorage.setItem(name, settings.updater.stringify(settings.value)); + } + } + reset(): void { + for (const name of Object.keys(this.settings)) { + const settings = this.settings[name]; + settings.updater.set( + document.getElementById(name) as HTMLInputElement, + settings.default, + ); + } + } +} +const boolUpdater: Updater = { + parse: (value) => { + if (value === "true") { + return true; + } else if (value === "false") { + return false; + } else { + return null; + } + }, + stringify: (value) => value.toString(), + load: (input) => input.checked, + set: (input, value) => { + input.checked = value; + }, }; +const redundancyUpdater: Updater = { + parse: (value) => { + if (["both", "condensed", "default only"].includes(value)) { + return value as RedundancySettings; + } else { + return null; + } + }, + stringify: (value) => value, + load: (input) => input.value as RedundancySettings, + set: (input, value) => { + input.value = value; + }, +}; +export const settings = new Setter({ + "use-telo-misikeke": { + default: false, + updater: boolUpdater, + }, + "randomize": { + default: false, + updater: boolUpdater, + }, + "x-ala-x-parsing": { + default: false, + updater: boolUpdater, + }, + "anu-as-content-word": { + default: false, + updater: boolUpdater, + }, + "number": { + default: "both", + updater: redundancyUpdater, + }, + "tense": { + default: "both", + updater: redundancyUpdater, + }, +}); From 8eab15a18e32389232dcd708f3abcbdb2fa52a13 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:34:24 +0800 Subject: [PATCH 239/738] rename ids --- index.html | 8 ++++---- src/settings.ts | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 5586eb4..212aade 100644 --- a/index.html +++ b/index.html @@ -98,7 +98,7 @@

        title="This allows X ala X constructions to be parsed as series of modifiers along with the usual translation" > Allow partial + parsing of X ala X constructions (?)

        diff --git a/src/settings.ts b/src/settings.ts index 1c19604..9f70f5a 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -5,10 +5,10 @@ type RedundancySettings = "both" | "condensed" | "default only"; type Settings = { "use-telo-misikeke": boolean; "randomize": boolean; - "x-ala-x-parsing": boolean; + "x-ala-x-partial-parsing": boolean; "anu-as-content-word": boolean; - "number": RedundancySettings; - "tense": RedundancySettings; + "number-settings": RedundancySettings; + "tense-settings": RedundancySettings; }; interface Option { default: T; @@ -114,7 +114,7 @@ export const settings = new Setter({ default: false, updater: boolUpdater, }, - "x-ala-x-parsing": { + "x-ala-x-partial-parsing": { default: false, updater: boolUpdater, }, @@ -122,11 +122,11 @@ export const settings = new Setter({ default: false, updater: boolUpdater, }, - "number": { + "number-settings": { default: "both", updater: redundancyUpdater, }, - "tense": { + "tense-settings": { default: "both", updater: redundancyUpdater, }, From 0887672e696e4ebb6ec2db40bc68bf2bd44a8eb2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:39:42 +0800 Subject: [PATCH 240/738] update settings --- src/main.ts | 6 +++--- src/settings.ts | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.ts b/src/main.ts index dc943b4..7e64d38 100644 --- a/src/main.ts +++ b/src/main.ts @@ -128,17 +128,17 @@ function updateOutput(): void { if (typeof document !== "undefined") { document.addEventListener("DOMContentLoaded", () => { loadElements(); + settings.loadFromLocalStorage(); setVersion(); - settings.load(); elements!.settingsButton.addEventListener("click", () => { elements!.dialogBox.showModal(); }); elements!.confirmButton.addEventListener("click", () => { - settings.confirm(); + settings.loadFromElements(); elements!.dialogBox.close(); }); elements!.resetButton.addEventListener("click", () => { - settings.reset(); + settings.resetElements(); }); elements!.translateButton.addEventListener("click", updateOutput); elements!.input.addEventListener("keydown", (event) => { diff --git a/src/settings.ts b/src/settings.ts index 9f70f5a..40f2531 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -20,8 +20,8 @@ interface SettingsItem extends Option { type Updater = { parse: (value: string) => T | null; stringify: (value: T) => string; - load: (input: HTMLInputElement) => T; - set: (input: HTMLInputElement, value: T) => void; + load: (input: HTMLInputElement | HTMLSelectElement) => T; + set: (input: HTMLInputElement | HTMLSelectElement, value: T) => void; }; class Setter { private settings: { [S in keyof T]: SettingsItem }; @@ -41,7 +41,7 @@ class Setter { get(name: S): T[S] { return this.settings[name].value as T[S]; } - load(): void { + loadFromLocalStorage(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; const src = localStorage.getItem(name); @@ -56,7 +56,7 @@ class Setter { ); } } - confirm(): void { + loadFromElements(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; settings.value = settings.updater.load( @@ -65,7 +65,7 @@ class Setter { localStorage.setItem(name, settings.updater.stringify(settings.value)); } } - reset(): void { + resetElements(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; settings.updater.set( @@ -86,9 +86,9 @@ const boolUpdater: Updater = { } }, stringify: (value) => value.toString(), - load: (input) => input.checked, + load: (input) => (input as HTMLInputElement).checked, set: (input, value) => { - input.checked = value; + (input as HTMLInputElement).checked = value; }, }; const redundancyUpdater: Updater = { From 10c0b54d62705b1d531574ad503e522db3d3fae4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:44:42 +0800 Subject: [PATCH 241/738] fix bug --- src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.ts b/src/lexer.ts index ee4e2fe..8c80887 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -484,7 +484,7 @@ function tokenTree( longGlyphParser = error(new CoveredError()); } let xAlaXParser: Lexer; - if (settings.get("x-ala-x-parsing")) { + if (settings.get("x-ala-x-partial-parsing")) { xAlaXParser = error(new CoveredError()); } else { xAlaXParser = xAlaX() From 9c30bcff915d1540396de55a4ecd13f837a2d563 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:46:54 +0800 Subject: [PATCH 242/738] fix another bug --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 7e64d38..0eb0053 100644 --- a/src/main.ts +++ b/src/main.ts @@ -41,7 +41,7 @@ function loadElements(): void { // deno-lint-ignore no-explicit-any } as any; for (const name of Object.keys(elementNames)) { - elementNames[name] = document.getElementById(name); + elementNames[name] = document.getElementById(elementNames[name]); } elements = elementNames; } From 7540906ed6824a0db3cc2c088c10f5261de0301f Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:53:13 +0800 Subject: [PATCH 243/738] rename headword to content word --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index d7f9e6d..410c9a8 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -369,7 +369,7 @@ function phrase(): AstParser { lazy(preposition) .map((preposition) => ({ type: "preposition", preposition }) as Phrase), sequence( - optionalCombined(CONTENT_WORD, "headword"), + optionalCombined(CONTENT_WORD, "content word"), lazy(modifiers), ) .map(([[headWord, modifier], modifiers]) => From ab6886541082e0c7780bb8ed318c0c5fc7b53ddf Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:56:08 +0800 Subject: [PATCH 244/738] small update --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 410c9a8..28b9da5 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -397,7 +397,7 @@ function nestedPhrasesOnly( } else { const [first, ...rest] = nestingRule; let type: "and conjunction" | "anu"; - if (["en", "li", "o", "e"].indexOf(first) !== -1) { + if (["en", "li", "o", "e"].includes(first)) { type = "and conjunction"; } else { type = "anu"; From 654d94436777e0228550e62d8d9afb7b65a35662 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 10:59:38 +0800 Subject: [PATCH 245/738] remove debugging statements --- src/ast-parser.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 28b9da5..40c14df 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -732,6 +732,3 @@ const FULL_PARSER = allAtLeastOnce(sentence()) export function parser(src: string): Output> { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } -console.log( - wordUnit(CONTENT_WORD, "content word").parse([{ type: "word", word: "ijo" }]), -); From 5730ade65c06a8dedfa8b972e8aa27a2525ead22 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 12:22:38 +0800 Subject: [PATCH 246/738] revamp dictionary --- src/dictionary.ts | 117 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 79fe4d9..da0fbab 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -2,34 +2,57 @@ import { UnreachableError } from "./error.ts"; -export const PARTICLES = new Set([ - "a", - "ala", - "anu", - "e", - "en", - "kin", - "la", - "li", - "n", - "nanpa", - "o", - "pi", - "taso", -]); -export const SPECIAL_CONTENT_WORD = new Set([ - "ala", // not - "jasima", // opposite of - "kijetesantakalu", - "kokosila", - "ku", - "lili", // piece of - "mu", - "ni", - "pu", - "seme", - "su", -]); +export const PARTICLE_DEFINITION: { [word: string]: Array } = { + a: ["[placed after something for emphasis or emotion]"], + ala: ["not", "[negates a word or phrase]", "[forms a yes-no question]"], + anu: ["or", "[separates multiple possibilities, replacing another particle]"], + e: ["[marks the start of a direct object]"], + en: ["[separates multiple subjects]"], + kin: [ + "too", + "also", + "as well", + "additionally", + "[after phrase or at sentence start]", + ], + la: ["[mark the previous statement as context to a following statement]"], + li: ["[marks the start of an indicative verb (statement)]"], + n: ["[indicate thinking or pause]"], + nanpa: ["-th", "[ordinal number]"], + o: [ + "should", + "[marks the end of a vocative (who is being spoken to)]", + "[marks the start of an imperative (command, wish, instruction)]", + ], + pi: ["[modify the next word with one or more following words]"], + seme: [ + "what", + "which", + "who", + "[indicate a question by marking missing info in a sentence]", + ], + taso: ["but", "however", "[marks a sentence as qualifying or contradictory]"], +}; +export const SPECIAL_CONTENT_WORD_DEFINITION: { + [word: string]: Array; +} = { + jasima: ["opposite of"], + kokosila: [ + "to speak a non-Toki Pona language in an environment where Toki Pona is \ + more appropriate", + ], + ku: ["interacting with the Toki Pona Dictionary by Sonja Lang"], + lili: ["piece of"], + mu: ["(animal noise or communication, onomatopoeia)"], + ni: ["this", "that", "these", "those"], + pu: [ + "to interact with the book Toki Pona: The Language of Good by Sonja Lang", + ], + su: [ + "interacting with a book from the illustrated story book series that \ + began with The Wonderful Wizard of Oz, produced by Sonja Lang", + ], +}; export const PREPOSITION_DEFINITION: { [word: string]: Array } = { kepeken: ["using"], lon: ["at"], @@ -38,7 +61,7 @@ export const PREPOSITION_DEFINITION: { [word: string]: Array } = { tawa: ["towards", "in perspective of"], }; export const PREVERB_DEFINITION: { [word: string]: Array } = { - alasa: [], + alasa: [], // Will be duplicated with lukin awen: [], kama: [], ken: [], @@ -48,7 +71,16 @@ export const PREVERB_DEFINITION: { [word: string]: Array } = { sona: [], wile: [], }; +PREVERB_DEFINITION.alasa = PREVERB_DEFINITION.lukin; export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { + a: [ + interjection("ah"), + interjection("oh"), + interjection("ha"), + interjection("eh"), + interjection("um"), + interjection("oy"), + ], akesi: [ noun("reptile(s)"), noun("amphibian(s)"), @@ -221,9 +253,20 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("possibility/possibilities"), ], kepeken: [ - verb("use(d)", "using "), + verb("use(d)", "using"), + ], + kijetesantakalu: [ + noun("raccoon(s)"), + noun("coati(s)"), + noun("kinkajou(s)"), + noun("olingo(s)"), + noun("ringtail(s)"), + noun("cacomistle(s)"), + noun("weasel(s)"), + noun("otter(s)"), + noun("skunk(s)"), + noun("red panda(s)"), ], - kijetesantakalu: [], // Special case kili: [ noun("fruit(s)"), noun("vegetable(s)"), @@ -589,6 +632,13 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("silly", "opinion"), verbObjectPhrase(verb("have/had", "having"), singularNoun("fun")), ], + n: [ + interjection("hm"), + interjection("uh"), + interjection("mm"), + interjection("er"), + interjection("umm"), + ], mute: [ numeral(20), quantifier("many"), @@ -1133,10 +1183,7 @@ export type Definition = } | { type: "gerund"; gerund: string } | { type: "interjection"; interjection: string }; -export const CONTENT_WORD = new Set([ - ...SPECIAL_CONTENT_WORD, - ...Object.keys(CONTENT_WORD_DEFINITION), -]); +export const CONTENT_WORD = new Set(Object.keys(CONTENT_WORD_DEFINITION)); export const PREVERB = new Set(Object.keys(PREVERB_DEFINITION)); export const PREPOSITION = new Set(Object.keys(PREPOSITION_DEFINITION)); From e5dd1b7c70438be01747227d37de9758fd20d68b Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 12:42:44 +0800 Subject: [PATCH 247/738] formatting --- src/dictionary.ts | 73 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index da0fbab..eac0cba 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -3,11 +3,24 @@ import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { - a: ["[placed after something for emphasis or emotion]"], - ala: ["not", "[negates a word or phrase]", "[forms a yes-no question]"], - anu: ["or", "[separates multiple possibilities, replacing another particle]"], - e: ["[marks the start of a direct object]"], - en: ["[separates multiple subjects]"], + a: [ + "[placed after something for emphasis or emotion]", + ], + ala: [ + "not", + "[negates a word or phrase]", + "[forms a yes-no question]", + ], + anu: [ + "or", + "[separates multiple possibilities, replacing another particle]", + ], + e: [ + "[marks the start of a direct object]", + ], + en: [ + "[separates multiple subjects]", + ], kin: [ "too", "also", @@ -15,36 +28,64 @@ export const PARTICLE_DEFINITION: { [word: string]: Array } = { "additionally", "[after phrase or at sentence start]", ], - la: ["[mark the previous statement as context to a following statement]"], - li: ["[marks the start of an indicative verb (statement)]"], - n: ["[indicate thinking or pause]"], - nanpa: ["-th", "[ordinal number]"], + la: [ + "[mark the previous statement as context to a following statement]", + ], + li: [ + "[marks the start of an indicative verb (statement)]", + ], + n: [ + "[indicate thinking or pause]", + ], + nanpa: [ + "-th", + "[ordinal number]", + ], o: [ "should", "[marks the end of a vocative (who is being spoken to)]", "[marks the start of an imperative (command, wish, instruction)]", ], - pi: ["[modify the next word with one or more following words]"], + pi: [ + "[modify the next word with one or more following words]", + ], seme: [ "what", "which", "who", "[indicate a question by marking missing info in a sentence]", ], - taso: ["but", "however", "[marks a sentence as qualifying or contradictory]"], + taso: [ + "but", + "however", + "[marks a sentence as qualifying or contradictory]", + ], }; export const SPECIAL_CONTENT_WORD_DEFINITION: { [word: string]: Array; } = { - jasima: ["opposite of"], + jasima: [ + "opposite of", + ], kokosila: [ "to speak a non-Toki Pona language in an environment where Toki Pona is \ more appropriate", ], - ku: ["interacting with the Toki Pona Dictionary by Sonja Lang"], - lili: ["piece of"], - mu: ["(animal noise or communication, onomatopoeia)"], - ni: ["this", "that", "these", "those"], + ku: [ + "interacting with the Toki Pona Dictionary by Sonja Lang", + ], + lili: [ + "piece of", + ], + mu: [ + "(animal noise or communication, onomatopoeia)", + ], + ni: [ + "this", + "that", + "these", + "those", + ], pu: [ "to interact with the book Toki Pona: The Language of Good by Sonja Lang", ], From 707b65dc7b2b7ffc2d9832d68243e285777a2d76 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 13:19:57 +0800 Subject: [PATCH 248/738] add special case for a single word --- src/ast-parser.ts | 23 ++++++++++++++++++----- src/ast.ts | 9 +++++++++ src/dictionary.ts | 3 +++ src/translator.ts | 11 ++++++++--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 40c14df..e6627b9 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -13,11 +13,17 @@ import { Preposition, Quotation, Sentence, + Sentences, WordUnit, } from "./ast.ts"; import { CoveredError, UnexpectedError, UnrecognizedError } from "./error.ts"; import { Output } from "./output.ts"; -import { CONTENT_WORD, PREPOSITION, PREVERB } from "./dictionary.ts"; +import { + CONTENT_WORD, + PREPOSITION, + PREVERB, + TOKI_PONA_WORD, +} from "./dictionary.ts"; import { CLAUSE_RULE, filter, @@ -33,6 +39,7 @@ import { all, allAtLeastOnce, choice, + choiceOnlyOne, lazy, many, manyAtLeastOnce, @@ -725,10 +732,16 @@ export function quotation(): AstParser { ) ); } -const FULL_PARSER = allAtLeastOnce(sentence()) - .skip(eol("end of sentence")) - .filter(filter(SENTENCES_RULE)); +const FULL_PARSER = choiceOnlyOne( + wordFrom(TOKI_PONA_WORD, "Toki Pona word") + .skip(eol("end of sentence")) + .map((word) => ({ type: "single word", word }) as Sentences), + allAtLeastOnce(sentence()) + .skip(eol("end of sentence")) + .filter(filter(SENTENCES_RULE)) + .map((sentences) => ({ type: "sentences", sentences }) as Sentences), +); /** A multiple Toki Pona sentence parser. */ -export function parser(src: string): Output> { +export function parser(src: string): Output { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/ast.ts b/src/ast.ts index 564df5d..c015950 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -133,6 +133,15 @@ export type Quotation = { leftMark: string; rightMark: string; }; +export type Sentences = + | { + type: "single word"; + word: string; + } + | { + type: "sentences"; + sentences: Array; + }; /** * Helper function for checking whether some modifier passes the test * function. diff --git a/src/dictionary.ts b/src/dictionary.ts index eac0cba..a6afe35 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,6 +1,7 @@ /** Module for describing word to word translations. */ import { UnreachableError } from "./error.ts"; +import { SENTENCES_RULE } from "./filter.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { a: [ @@ -1224,9 +1225,11 @@ export type Definition = } | { type: "gerund"; gerund: string } | { type: "interjection"; interjection: string }; +export const PARTICLE = new Set(Object.keys(PARTICLE_DEFINITION)); export const CONTENT_WORD = new Set(Object.keys(CONTENT_WORD_DEFINITION)); export const PREVERB = new Set(Object.keys(PREVERB_DEFINITION)); export const PREPOSITION = new Set(Object.keys(PREPOSITION_DEFINITION)); +export const TOKI_PONA_WORD = new Set([...PARTICLE, ...CONTENT_WORD]); function noun(word: string): Definition & { type: "noun" } { const paren = word.match(/([a-z]*)\(([a-z]*)\)/); diff --git a/src/translator.ts b/src/translator.ts index 26426a2..0f6816c 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -7,6 +7,7 @@ import { MultiplePhrases, Phrase, Sentence, + Sentences, WordUnit, } from "./ast.ts"; import { Output } from "./output.ts"; @@ -332,9 +333,13 @@ function translateSentence(sentence: Sentence): TranslationOutput { }); } /** Translates multiple sentences. */ -function translateSentences(sentences: Array): TranslationOutput { - return rotate(sentences.map(translateSentence)) - .map((sentences) => sentences.join(" ")); +function translateSentences(sentences: Sentences): TranslationOutput { + if (sentences.type === "sentences") { + return rotate(sentences.sentences.map(translateSentence)) + .map((sentences) => sentences.join(" ")); + } else { + return new Output(new TodoError("translation of a single word")); + } } /** Full Toki Pona translator. */ export function translate(src: string): TranslationOutput { From 4b62bf92cdffa602e5ca0312356fd9966ee04292 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 13:21:29 +0800 Subject: [PATCH 249/738] I hate this --- src/dictionary.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index a6afe35..8b4d9c5 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,7 +1,6 @@ /** Module for describing word to word translations. */ import { UnreachableError } from "./error.ts"; -import { SENTENCES_RULE } from "./filter.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { a: [ From ebf7164427c174906b78434bbaa4215bcc15ffd2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 14:02:31 +0800 Subject: [PATCH 250/738] initial implementation of english ast --- src/english-ast.ts | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/english-ast.ts b/src/english-ast.ts index 0b6ffe4..eb9ab6f 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -1 +1,50 @@ /** Module for describing English AST. */ + +import { AdjectiveType } from "./dictionary.ts"; + +// TODO: preposition + +export type NounPhrase = + | { + type: "simple"; + determiners: Array; + adjectives: Array; + noun: string; + } + | { type: "compound"; conjunction: string; subjects: NounPhrase }; +export type Determiner = + | { type: "quantifier"; quantifier: string } + | { type: "numeral"; number: number }; +export type AdjectivePhrase = { + type: AdjectiveType; + adverbs: Array; + adjective: string; +}; +export type PredicateAdjective = + | { type: "simple"; adjective: AdjectivePhrase } + | { type: "compound"; conjunction: string; adjectives: PredicateAdjective }; +export type Verb = + | { + type: "default"; + adverbs: Array; + verb: string; + } + | { + type: "linking noun"; + noun: NounPhrase; + } + | { + type: "linking adjective"; + adjective: PredicateAdjective; + }; +export type Sentence = + | { type: "free form"; text: string } + | { type: "default"; subject: NounPhrase; verb: Verb; object: NounPhrase } + | { type: "subject phrase"; subject: NounPhrase } + | { type: "implied it's noun"; noun: NounPhrase } + | { type: "implied it's adjective"; noun: PredicateAdjective } + | { type: "interjection"; interjection: string }; +export type SentencePunctuation = { + sentence: Sentence; + punctuation: string; +}; From f368c6df7b168c18a883ad13056bd116fa21dfa4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 14:54:00 +0800 Subject: [PATCH 251/738] format --- src/ast.ts | 71 +++++++++++------------------------------------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index c015950..3e42c27 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -24,32 +24,19 @@ export type Modifier = * prepositional phrases intended for predicate. */ export type Phrase = - | { - type: "default"; - headWord: WordUnit; - modifiers: Array; - } + | { type: "default"; headWord: WordUnit; modifiers: Array } | { type: "preverb"; preverb: WordUnit; modifiers: Array; phrase: Phrase; } - | { - type: "preposition"; - preposition: Preposition; - } - | { - type: "quotation"; - quotation: Quotation; - }; + | { type: "preposition"; preposition: Preposition } + | { type: "quotation"; quotation: Quotation }; /** Represents multiple phrases separated by repeated particle or _anu_. */ export type MultiplePhrases = | { type: "single"; phrase: Phrase } - | { - type: "and conjunction"; - phrases: Array; - } + | { type: "and conjunction"; phrases: Array } | { type: "anu"; phrases: Array }; /** Represents a single prepositional phrase. */ export type Preposition = { @@ -72,10 +59,7 @@ export type MultiplePredicates = /** Represents a simple clause. */ export type Clause = | { type: "phrases"; phrases: MultiplePhrases } - | { - type: "o vocative"; - phrases: MultiplePhrases; - } + | { type: "o vocative"; phrases: MultiplePhrases } | { type: "li clause"; subjects: MultiplePhrases; @@ -87,32 +71,14 @@ export type Clause = subjects: null | MultiplePhrases; predicates: MultiplePredicates; } - | { - type: "prepositions"; - prepositions: Array; - } - | { - type: "quotation"; - quotation: Quotation; - }; + | { type: "prepositions"; prepositions: Array } + | { type: "quotation"; quotation: Quotation }; export type Preclause = - | { - type: "taso"; - taso: WordUnit; - } - | { - type: "marker"; - marker: Marker; - }; + | { type: "taso"; taso: WordUnit } + | { type: "marker"; marker: Marker }; export type Postclause = - | { - type: "anu seme"; - seme: WordUnit; - } - | { - type: "marker"; - marker: Marker; - }; + | { type: "anu seme"; seme: WordUnit } + | { type: "marker"; marker: Marker }; /** Represents a clause including preclause and postclause. */ export type FullClause = | { @@ -121,10 +87,7 @@ export type FullClause = postclause: null | Postclause; clause: Clause; } - | { - type: "marker"; - marker: Marker; - }; + | { type: "marker"; marker: Marker }; /** Represents a single full sentence. */ export type Sentence = { laClauses: Array; punctuation: string }; /** Represents quotation. */ @@ -134,14 +97,8 @@ export type Quotation = { rightMark: string; }; export type Sentences = - | { - type: "single word"; - word: string; - } - | { - type: "sentences"; - sentences: Array; - }; + | { type: "single word"; word: string } + | { type: "sentences"; sentences: Array }; /** * Helper function for checking whether some modifier passes the test * function. From 38ca2b33f90853b08387414fe11b149698efed47 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 15:31:39 +0800 Subject: [PATCH 252/738] rename --- src/main.ts | 2 +- src/{translator.ts => old-translator.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{translator.ts => old-translator.ts} (100%) diff --git a/src/main.ts b/src/main.ts index 0eb0053..3d00b6d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ /** Module for main execution in the browser. */ import { CoveredError } from "./error.ts"; -import { translate } from "./translator.ts"; +import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; import { teloMisikeke } from "../deps.ts"; diff --git a/src/translator.ts b/src/old-translator.ts similarity index 100% rename from src/translator.ts rename to src/old-translator.ts From d91659668def53f5216a18bebea837d4cc9ab7cf Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 16:00:34 +0800 Subject: [PATCH 253/738] dependent clause --- src/english-ast.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/english-ast.ts b/src/english-ast.ts index eb9ab6f..14dff92 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -37,14 +37,19 @@ export type Verb = type: "linking adjective"; adjective: PredicateAdjective; }; -export type Sentence = +export type Clause = | { type: "free form"; text: string } | { type: "default"; subject: NounPhrase; verb: Verb; object: NounPhrase } | { type: "subject phrase"; subject: NounPhrase } | { type: "implied it's noun"; noun: NounPhrase } | { type: "implied it's adjective"; noun: PredicateAdjective } | { type: "interjection"; interjection: string }; -export type SentencePunctuation = { - sentence: Sentence; +export type DependentClause = { + conjunction: string; + clause: Clause; +}; +export type Sentence = { + dependentClauses: Array; + independentClause: Clause; punctuation: string; }; From e82e825fa7ada74ea17842fcded831319ad1cd26 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 16:01:42 +0800 Subject: [PATCH 254/738] compound sentence --- src/english-ast.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/english-ast.ts b/src/english-ast.ts index 14dff92..eee6b08 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -43,7 +43,8 @@ export type Clause = | { type: "subject phrase"; subject: NounPhrase } | { type: "implied it's noun"; noun: NounPhrase } | { type: "implied it's adjective"; noun: PredicateAdjective } - | { type: "interjection"; interjection: string }; + | { type: "interjection"; interjection: string } + | { type: "compound"; clauses: Array }; export type DependentClause = { conjunction: string; clause: Clause; From 05900bb183216697fe13a83d2d032d98a866916c Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:26:43 +0800 Subject: [PATCH 255/738] small improvement --- src/lexer.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 8c80887..c1c1b07 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -212,14 +212,7 @@ function multipleA(): Lexer { .map(([a, as]) => [a, ...as].length); } function longA(): Lexer { - return match(/(a+)\s*/, "long a").map(([_, a]) => { - const length = a.length; - if (length > 1) { - return length; - } else { - throw new CoveredError(); - } - }); + return match(/(a{2,})\s*/, "long a").map(([_, a]) => a.length); } /** Parses X ala X constructions. */ function xAlaX(): Lexer { @@ -244,7 +237,7 @@ function punctuation(): Lexer { return choiceOnlyOne( match(/([.,:;?!])\s*/, "punctuation") .map(([_, punctuation]) => punctuation), - // NOTE: maybe these are unnecessary + // NOTE: maybe these mapping are unnecessary specificUcsurCharacter("󱦜", "middle dot", { allowVariation: true, allowSpace: true, From e114949132857309d30eba750c7f37cac3f5c725 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:32:20 +0800 Subject: [PATCH 256/738] update updater for boolean settings --- src/settings.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/settings.ts b/src/settings.ts index 40f2531..77f9b16 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -77,15 +77,21 @@ class Setter { } const boolUpdater: Updater = { parse: (value) => { - if (value === "true") { + if (value === "T") { return true; - } else if (value === "false") { + } else if (value === "F") { return false; } else { return null; } }, - stringify: (value) => value.toString(), + stringify: (value) => { + if (value) { + return "T"; + } else { + return "F"; + } + }, load: (input) => (input as HTMLInputElement).checked, set: (input, value) => { (input as HTMLInputElement).checked = value; From f8633319938d5f19850fb0ad235ee288b79d15b8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:35:42 +0800 Subject: [PATCH 257/738] add notes for browser only functions --- src/settings.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/settings.ts b/src/settings.ts index 77f9b16..70fef3c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -41,6 +41,7 @@ class Setter { get(name: S): T[S] { return this.settings[name].value as T[S]; } + /** This function is for browser only. */ loadFromLocalStorage(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; @@ -56,6 +57,7 @@ class Setter { ); } } + /** This function is for browser only. */ loadFromElements(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; @@ -65,6 +67,7 @@ class Setter { localStorage.setItem(name, settings.updater.stringify(settings.value)); } } + /** This function is for browser only. */ resetElements(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; From 51744f5d991191a094afe73d41db94ef80ad6cea Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:41:47 +0800 Subject: [PATCH 258/738] detect local storage availability --- src/settings.ts | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/settings.ts b/src/settings.ts index 70fef3c..b986708 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -10,6 +10,34 @@ type Settings = { "number-settings": RedundancySettings; "tense-settings": RedundancySettings; }; +const LOCAL_STORAGE_AVAILABLE = (() => { + if (typeof localStorage === "undefined") { + return false; + } + // https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API + try { + const x = "__storage_test__"; + localStorage.setItem(x, x); + localStorage.removeItem(x); + return true; + } catch (e) { + return ( + e instanceof DOMException && + // everything except Firefox + (e.code === 22 || + // Firefox + e.code === 1014 || + // test name field too, because code might not be present + // everything except Firefox + e.name === "QuotaExceededError" || + // Firefox + e.name === "NS_ERROR_DOM_QUOTA_REACHED") && + // acknowledge QuotaExceededError only if there's something already stored + localStorage && + localStorage.length !== 0 + ); + } +})(); interface Option { default: T; updater: Updater; @@ -43,6 +71,9 @@ class Setter { } /** This function is for browser only. */ loadFromLocalStorage(): void { + if (!LOCAL_STORAGE_AVAILABLE) { + return; + } for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; const src = localStorage.getItem(name); @@ -64,7 +95,9 @@ class Setter { settings.value = settings.updater.load( document.getElementById(name) as HTMLInputElement, ); - localStorage.setItem(name, settings.updater.stringify(settings.value)); + if (LOCAL_STORAGE_AVAILABLE) { + localStorage.setItem(name, settings.updater.stringify(settings.value)); + } } } /** This function is for browser only. */ From c891e246b417cc5b1847a408891eaa76c932d210 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:51:08 +0800 Subject: [PATCH 259/738] update readme --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f782f89..19e5062 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,17 @@ This could be mitigated by making use of local server but I didn't do that, ther ### Runtime agnostic -With exception to `./src/main.ts`, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. +With some exception, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. + +- `main.ts` needs to access the web page DOM. It detects if `document` is available, otherwise it will do nothing. +- `settings.ts` will access DOM and local storage unless you don't use methods marked as browser-only. + +If adding `Deno.test`, please use `if (typeof Deno !== "undefined")` so the code can be run on browser. ### UCSUR included Some parts of the code make use of sitelen pona UCSUR characters. To display properly, install an UCSUR font and change the font settings on your editor. [UCSUR Installation guides](https://github.com/neroist/sitelen-pona-ucsur-guide/). +Oftentimes, you don't need to be able to type UCSUR in the source codes. We reduce UCSUR used in code and prefer to use latin letters instead. + Also, take note that UCSUR characters are two characters wide in JavaScript string. Be careful with string and regex manipulation. From 206a7d9a21bc8a6ea99d108b40d9c7fcf0011946 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 17:58:29 +0800 Subject: [PATCH 260/738] update readme --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 19e5062..c84134c 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,7 @@ To stop this command, simply press Ctrl + C. ## Running locally -After building or watching, you can directly run `./index.html` using your favorite browser with some caveat however: - -- Settings won't be saved. -- UCSUR characters will display as tofu. +After building or watching, you can directly run `./index.html` using your favorite browser with some caveat however: UCSUR characters will display as tofu. This could be mitigated by making use of local server but I didn't do that, there's little need for that. From d82b0ce9c84fbb50bdac1784c383a93c60dad08b Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 18:23:31 +0800 Subject: [PATCH 261/738] implement parser for n, long nnn, and kin --- src/ast-parser.ts | 10 +++++++--- src/ast.ts | 4 ++-- src/lexer.ts | 23 ++++++++++++++++++++--- src/token-tree.ts | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e6627b9..f1ed74b 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -132,9 +132,13 @@ function marker(): AstParser { return choice( specificTokenTree("multiple a") .map(({ count }) => ({ type: "multiple a", count }) as Marker), - specificTokenTree("long a") - .map(({ length }) => ({ type: "long a", length }) as Marker), - specificWord("a").map(() => ({ type: "a" }) as Marker), + specificTokenTree("long word") + .map(({ word, length }) => + ({ type: "long word", word, length }) as Marker + ), + wordFrom(new Set(["a", "n", "kin"]), "a/n/kin").map((word) => + ({ type: "word", word }) as Marker + ), ); } function xAlaX( diff --git a/src/ast.ts b/src/ast.ts index 3e42c27..8ce3e07 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -3,8 +3,8 @@ import { UnreachableError } from "./error.ts"; export type Marker = - | { type: "a" } - | { type: "long a"; length: number } + | { type: "word"; word: string } + | { type: "long word"; word: string; length: number } | { type: "multiple a"; count: number }; /** Represents a word unit. */ export type WordUnit = diff --git a/src/lexer.ts b/src/lexer.ts index c1c1b07..7a42be7 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -90,6 +90,15 @@ function slice(length: number, description: string): Lexer { } }); } +function matchString(match: string): Lexer { + return slice(match.length, `"${match}"`).map((slice) => { + if (slice === match) { + return match; + } else { + throw new UnexpectedError(`"${slice}"`, `"${match}"`); + } + }); +} /** Parses the end of line (or the end of sentence in context of Toki Pona) */ function eol(): Lexer { return new Parser((src) => { @@ -211,8 +220,16 @@ function multipleA(): Lexer { return sequence(specificWord("a"), allAtLeastOnce(specificWord("a"))) .map(([a, as]) => [a, ...as].length); } -function longA(): Lexer { - return match(/(a{2,})\s*/, "long a").map(([_, a]) => a.length); +function longWord(): Lexer { + return match(/[an]/, 'long "a" or "n"').then(([word, _]) => + allAtLeastOnce(matchString(word)) + .skip(spaces()) + .map((array) => ({ + type: "long word", + word, + length: 1 + array.length, + })) + ); } /** Parses X ala X constructions. */ function xAlaX(): Lexer { @@ -495,7 +512,7 @@ function tokenTree( combinedGlyphs() .skip(spaces()) .map((words) => ({ type: "combined glyphs", words }) as TokenTree), - longA().map((length) => ({ type: "long a", length }) as TokenTree), + longWord(), multipleA().map((count) => ({ type: "multiple a", count }) as TokenTree), xAlaXParser, word().map((word) => ({ type: "word", word })), diff --git a/src/token-tree.ts b/src/token-tree.ts index ba70c68..59659db 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -30,7 +30,7 @@ export type TokenTree = words: Array; } | { type: "multiple a"; count: number } - | { type: "long a"; length: number } + | { type: "long word"; word: string; length: number } | { type: "x ala x"; word: string } | { type: "proper word"; words: string } | { From 6ec150bedad50e5b2aa71e3c47288c3c74767ee3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 18:37:03 +0800 Subject: [PATCH 262/738] remove unused style --- style.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/style.css b/style.css index 07c26e5..3da6e40 100644 --- a/style.css +++ b/style.css @@ -9,9 +9,6 @@ body { h1 { vertical-align: middle; } -h1 > img { - height: 1em; -} a { color: #0057af; } From b9d7dc59a7c0976afa26096a6032bdb64f0a96bb Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 18:37:17 +0800 Subject: [PATCH 263/738] this too --- style.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/style.css b/style.css index 3da6e40..62fb765 100644 --- a/style.css +++ b/style.css @@ -6,9 +6,6 @@ body { margin: 10px; font-family: sans-serif; } -h1 { - vertical-align: middle; -} a { color: #0057af; } From c4407572838fc40aac0730de4dff9664bcd450f4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 18:54:03 +0800 Subject: [PATCH 264/738] implement simple parser for "a" emphasizing whole phrases --- src/ast-parser.ts | 23 ++++++++++++++++++----- src/ast.ts | 9 ++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index f1ed74b..a691550 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -344,12 +344,13 @@ const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); /** Parses phrases. */ function phrase(): AstParser { return choice( - sequence(number(), lazy(modifiers)) - .map(([numbers, modifiers]) => + sequence(number(), lazy(modifiers), optional(marker())) + .map(([numbers, modifiers, marker]) => ({ type: "default", headWord: { type: "numbers", numbers }, modifiers, + marker, }) as Phrase ), binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => @@ -361,20 +362,24 @@ function phrase(): AstParser { type: "default", headWord: { type: "default", word: phrase, marker: null }, modifiers: [], + marker: null, }, + marker: null, }) as Phrase ), sequence( optionalCombined(PREVERB, "preverb"), lazy(modifiers), lazy(phrase), + optional(marker()), ) - .map(([[preverb, modifier], modifiers, phrase]) => + .map(([[preverb, modifier], modifiers, phrase, marker]) => ({ type: "preverb", preverb, modifiers: [...modifier, ...modifiers], phrase, + marker, }) as Phrase ), lazy(preposition) @@ -382,12 +387,14 @@ function phrase(): AstParser { sequence( optionalCombined(CONTENT_WORD, "content word"), lazy(modifiers), + optional(marker()), ) - .map(([[headWord, modifier], modifiers]) => + .map(([[headWord, modifier], modifiers, marker]) => ({ type: "default", headWord, modifiers: [...modifier, ...modifiers], + marker, }) as Phrase ), quotation() @@ -459,6 +466,7 @@ function preposition(): AstParser { preposition: { type: "default", word: "lon", marker: null }, modifiers: [], phrases: { type: "single", phrase }, + marker: null, }) as Preposition ), specificTokenTree("long glyph").flatMapValue((tokenTrees) => { @@ -505,20 +513,24 @@ function preposition(): AstParser { type: "default", headWord: { type: "default", word: phrase, marker: null }, modifiers: [], + marker: null, }, }, + marker: null, }) as Preposition ), sequence( optionalCombined(PREPOSITION, "preposition"), modifiers(), nestedPhrases(["anu"]), + optional(marker()), ) - .map(([[preposition, modifier], modifiers, phrases]) => + .map(([[preposition, modifier], modifiers, phrases, marker]) => ({ preposition, modifiers: [...modifier, ...modifiers], phrases, + marker, }) as Preposition ), ) @@ -606,6 +618,7 @@ function clause(): AstParser { headWord: { type: "default", word: subject, marker: null }, alaQuestion: false, modifiers: [], + marker: null, }, }, predicates, diff --git a/src/ast.ts b/src/ast.ts index 8ce3e07..24ceb88 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -24,12 +24,18 @@ export type Modifier = * prepositional phrases intended for predicate. */ export type Phrase = - | { type: "default"; headWord: WordUnit; modifiers: Array } + | { + type: "default"; + headWord: WordUnit; + modifiers: Array; + marker: null | Marker; + } | { type: "preverb"; preverb: WordUnit; modifiers: Array; phrase: Phrase; + marker: null | Marker; } | { type: "preposition"; preposition: Preposition } | { type: "quotation"; quotation: Quotation }; @@ -44,6 +50,7 @@ export type Preposition = { modifiers: Array; /** This cannot be an "and conjunction": only "anu" or "single". */ phrases: MultiplePhrases; + marker: null | Marker; }; /** Represents multiple predicates. */ export type MultiplePredicates = From a089517932fc83f89d5d57d40a619246e9c827ca Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 18 Apr 2024 19:12:56 +0800 Subject: [PATCH 265/738] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6fdb93..b4ecf6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ The latest on-development version can be accessed by building the source code. Inside update (intended for developers): -- Implement lexer. +- Implement lexer and english AST. - Overhaul dictionary. From 77785b3e14c445090fa967a3ad142330fea8d318 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 07:37:03 +0800 Subject: [PATCH 266/738] small update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c84134c..e80a2d2 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ This could be mitigated by making use of local server but I didn't do that, ther ### Runtime agnostic -With some exception, every source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. +With some exception, most source codes in `./src/` are runtime agnostic. Meaning it can be run on Deno as well. This makes it convenient to directly test codes by using `deno run` or `deno test`. - `main.ts` needs to access the web page DOM. It detects if `document` is available, otherwise it will do nothing. - `settings.ts` will access DOM and local storage unless you don't use methods marked as browser-only. From 33515089e29eaa6ba3cf9d7c9b4725889c3520ec Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 07:58:03 +0800 Subject: [PATCH 267/738] small update --- src/settings.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/settings.ts b/src/settings.ts index b986708..bcdbbc4 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -38,13 +38,13 @@ const LOCAL_STORAGE_AVAILABLE = (() => { ); } })(); -interface Option { +type Option = { default: T; updater: Updater; -} -interface SettingsItem extends Option { +}; +type SettingsItem = Option & { value: T; -} +}; type Updater = { parse: (value: string) => T | null; stringify: (value: T) => string; From 92dd72bb7a3963114863e4a28e9e7609a6345ee8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:18:39 +0800 Subject: [PATCH 268/738] rename --- src/ast-parser.ts | 10 ++++++---- src/ast.ts | 2 +- src/old-translator.ts | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index a691550..4dd6df0 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -7,13 +7,13 @@ import { Modifier, MultiplePhrases, MultiplePredicates, + MultipleSentences, Phrase, Postclause, Preclause, Preposition, Quotation, Sentence, - Sentences, WordUnit, } from "./ast.ts"; import { CoveredError, UnexpectedError, UnrecognizedError } from "./error.ts"; @@ -752,13 +752,15 @@ export function quotation(): AstParser { const FULL_PARSER = choiceOnlyOne( wordFrom(TOKI_PONA_WORD, "Toki Pona word") .skip(eol("end of sentence")) - .map((word) => ({ type: "single word", word }) as Sentences), + .map((word) => ({ type: "single word", word }) as MultipleSentences), allAtLeastOnce(sentence()) .skip(eol("end of sentence")) .filter(filter(SENTENCES_RULE)) - .map((sentences) => ({ type: "sentences", sentences }) as Sentences), + .map((sentences) => + ({ type: "sentences", sentences }) as MultipleSentences + ), ); /** A multiple Toki Pona sentence parser. */ -export function parser(src: string): Output { +export function parser(src: string): Output { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/ast.ts b/src/ast.ts index 24ceb88..6a69cd4 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -103,7 +103,7 @@ export type Quotation = { leftMark: string; rightMark: string; }; -export type Sentences = +export type MultipleSentences = | { type: "single word"; word: string } | { type: "sentences"; sentences: Array }; /** diff --git a/src/old-translator.ts b/src/old-translator.ts index 0f6816c..ccfc547 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -5,9 +5,9 @@ import { FullClause, Modifier, MultiplePhrases, + MultipleSentences, Phrase, Sentence, - Sentences, WordUnit, } from "./ast.ts"; import { Output } from "./output.ts"; @@ -333,7 +333,7 @@ function translateSentence(sentence: Sentence): TranslationOutput { }); } /** Translates multiple sentences. */ -function translateSentences(sentences: Sentences): TranslationOutput { +function translateSentences(sentences: MultipleSentences): TranslationOutput { if (sentences.type === "sentences") { return rotate(sentences.sentences.map(translateSentence)) .map((sentences) => sentences.join(" ")); From c6151a31880840557460302850feb938e44403be Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:32:05 +0800 Subject: [PATCH 269/738] small improvement for error messages --- src/ast-parser.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 4dd6df0..24a2731 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -64,10 +64,13 @@ function sequence>( /** Parses the end of line (or the end of sentence in context of Toki Pona) */ function eol(description: string): AstParser { return new Parser((src) => { - if (src.length === 0) return new Output([{ value: null, rest: [] }]); - else {return new Output( - new UnexpectedError(src[0].type, description), - );} + if (src.length === 0) { + return new Output([{ value: null, rest: [] }]); + } else { + return new Output( + new UnexpectedError(describe(src[0]), description), + ); + } }); } /** Parses a single token tree. */ From 0a447d4a04cdcbfd03aa41b77d192bb5d5f2eaa5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:41:15 +0800 Subject: [PATCH 270/738] add cancel button --- index.html | 5 +++-- src/main.ts | 8 +++++++- src/settings.ts | 12 +++++++++++- style.css | 3 +++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 212aade..85fe770 100644 --- a/index.html +++ b/index.html @@ -129,9 +129,10 @@

        content word

        -

        - +

        + +

        diff --git a/src/main.ts b/src/main.ts index 3d00b6d..7896faf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,6 +20,7 @@ type Elements = { settingsButton: HTMLButtonElement; dialogBox: HTMLDialogElement; confirmButton: HTMLButtonElement; + cancelButton: HTMLButtonElement; resetButton: HTMLButtonElement; version: HTMLAnchorElement; }; @@ -36,6 +37,7 @@ function loadElements(): void { settingsButton: "settings-button", dialogBox: "dialog-box", confirmButton: "confirm-button", + cancelButton: "cancel-button", resetButton: "reset-button", version: "version", // deno-lint-ignore no-explicit-any @@ -137,8 +139,12 @@ if (typeof document !== "undefined") { settings.loadFromElements(); elements!.dialogBox.close(); }); + elements!.cancelButton.addEventListener("click", () => { + settings.resetElementsToCurrent(); + elements!.dialogBox.close(); + }); elements!.resetButton.addEventListener("click", () => { - settings.resetElements(); + settings.resetElementsToDefault(); }); elements!.translateButton.addEventListener("click", updateOutput); elements!.input.addEventListener("keydown", (event) => { diff --git a/src/settings.ts b/src/settings.ts index bcdbbc4..6be32bd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -101,7 +101,17 @@ class Setter { } } /** This function is for browser only. */ - resetElements(): void { + resetElementsToCurrent(): void { + for (const name of Object.keys(this.settings)) { + const settings = this.settings[name]; + settings.updater.set( + document.getElementById(name) as HTMLInputElement, + settings.value, + ); + } + } + /** This function is for browser only. */ + resetElementsToDefault(): void { for (const name of Object.keys(this.settings)) { const settings = this.settings[name]; settings.updater.set( diff --git a/style.css b/style.css index 62fb765..67ba2e9 100644 --- a/style.css +++ b/style.css @@ -22,6 +22,9 @@ a:visited { #error-list { color: #b60000; } +.button-row { + text-align: right; +} summary { cursor: pointer; } From a3404955c940ecc07b0aab63ffa3a8a46854000d Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:43:19 +0800 Subject: [PATCH 271/738] update settings layout --- index.html | 4 +++- style.css | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 85fe770..420b288 100644 --- a/index.html +++ b/index.html @@ -129,8 +129,10 @@

        content word

        -

        +

        +

        +

        diff --git a/style.css b/style.css index 67ba2e9..0074a9d 100644 --- a/style.css +++ b/style.css @@ -22,7 +22,7 @@ a:visited { #error-list { color: #b60000; } -.button-row { +.align-right { text-align: right; } summary { From 3f8e3926731699394a9aa037d40c36ca09ace1b6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:49:00 +0800 Subject: [PATCH 272/738] improve dark mode for settings dialog --- style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/style.css b/style.css index 0074a9d..ee25c8e 100644 --- a/style.css +++ b/style.css @@ -28,6 +28,10 @@ a:visited { summary { cursor: pointer; } +dialog { + background-color: inherit; + color: inherit; +} @media (min-width: 800px) { body { margin: 50px; From cbb10b9a8bb87eb345c83a9caf71f0904b81f6bb Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:50:06 +0800 Subject: [PATCH 273/738] update css --- style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style.css b/style.css index ee25c8e..bb56fc9 100644 --- a/style.css +++ b/style.css @@ -1,5 +1,5 @@ @font-face { - font-family: "nasin-nanpa"; + font-family: nasin-nanpa; src: url("https://github.com/ETBCOR/nasin-nanpa/raw/main/versions/nasin-nanpa.otf"); } body { @@ -13,7 +13,7 @@ a:visited { color: #551a8b; } #input { - font-family: "nasin-nanpa", sans-serif; + font-family: sans-serif, nasin-nanpa; box-sizing: border-box; resize: vertical; width: 100%; From fb81d8701e04a8ec753b478d4b7c9f5667fa8a88 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:51:16 +0800 Subject: [PATCH 274/738] update css --- style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/style.css b/style.css index bb56fc9..8c46887 100644 --- a/style.css +++ b/style.css @@ -28,10 +28,6 @@ a:visited { summary { cursor: pointer; } -dialog { - background-color: inherit; - color: inherit; -} @media (min-width: 800px) { body { margin: 50px; @@ -42,6 +38,10 @@ dialog { background-color: black; color: white; } + dialog { + background-color: black; + color: white; + } a { color: #3197ff; } From ba75391356609392b22f03d21f1143e373d9d61a Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 19 Apr 2024 12:54:21 +0800 Subject: [PATCH 275/738] update settings dialog layout --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 420b288..44eab75 100644 --- a/index.html +++ b/index.html @@ -97,7 +97,7 @@

        >(Help)

        -

        - -

        -

        - -

        -

        - -

        -

        - -

        -

        + Use telo misikeke error messages + + + + + + + +

        -

        - -

        -

        + Allow partial parsing of X ala X + + + +

        -

        -

        +

        -

        +
        diff --git a/style.css b/style.css index c58b8db..c19e06c 100644 --- a/style.css +++ b/style.css @@ -27,20 +27,50 @@ a:visited { summary { cursor: pointer; } -button { +button, +select { padding: 10px; } -select { +dialog:modal { + padding: 0px; + padding-top: 20px; + padding-bottom: 20px; + display: flex; + flex-direction: column; + resize: both; +} +dialog > label, +dialog > h1 { padding: 10px; + margin: 0; +} +dialog > label[for] { + padding-bottom: 5px; +} +dialog > select { + margin: 10px; + margin-top: 0px; +} +dialog > div { + margin: 10px; + display: flex; } -input[type="checkbox"] { - width: 20px; - height: 20px; +dialog > div > div { + flex-grow: 1; +} +dialog input[type="checkbox"] { + margin-left: 10px; + width: 1em; + height: 1em; + float: right; } @media (min-width: 800px) { body { margin: 50px; } + dialog:modal { + padding: 20px; + } } @media (prefers-color-scheme: dark) { body { From 433b8ec33dd29ed63675c380dbc9a0ab912023b3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 17:54:44 +0800 Subject: [PATCH 280/738] textarea adapts to darkmode --- style.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/style.css b/style.css index c19e06c..ecce0d7 100644 --- a/style.css +++ b/style.css @@ -16,6 +16,14 @@ a:visited { box-sizing: border-box; resize: vertical; width: 100%; + + background-color: inherit; + color: inherit; + padding: 5px; + + border-color: gray; + border-width: 2px; + border-radius: 2px; } #error, #error-list { From 72d85ef62e40913a13e0cc6f751695ea1350932a Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 18:23:27 +0800 Subject: [PATCH 281/738] auto resize textarea --- src/main.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.ts b/src/main.ts index 7896faf..956d98f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -132,6 +132,10 @@ if (typeof document !== "undefined") { loadElements(); settings.loadFromLocalStorage(); setVersion(); + elements!.input.style.height = elements!.input.scrollHeight + "px"; + elements!.input.addEventListener("input", () => { + elements!.input.style.height = elements!.input.scrollHeight + "px"; + }); elements!.settingsButton.addEventListener("click", () => { elements!.dialogBox.showModal(); }); From a14f391552bc815222f450ae5ceb58306cf703da Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 18:23:51 +0800 Subject: [PATCH 282/738] add comments --- src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.ts b/src/main.ts index 956d98f..a8f6647 100644 --- a/src/main.ts +++ b/src/main.ts @@ -132,6 +132,7 @@ if (typeof document !== "undefined") { loadElements(); settings.loadFromLocalStorage(); setVersion(); + // Auto resize elements!.input.style.height = elements!.input.scrollHeight + "px"; elements!.input.addEventListener("input", () => { elements!.input.style.height = elements!.input.scrollHeight + "px"; From d74236a9f419e0aaa8c6fed7951b8fd118c6214e Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 18:30:48 +0800 Subject: [PATCH 283/738] set min for auto resize --- src/main.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index a8f6647..2602eb2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -133,10 +133,12 @@ if (typeof document !== "undefined") { settings.loadFromLocalStorage(); setVersion(); // Auto resize - elements!.input.style.height = elements!.input.scrollHeight + "px"; - elements!.input.addEventListener("input", () => { - elements!.input.style.height = elements!.input.scrollHeight + "px"; - }); + function resizeTextarea() { + elements!.input.style.height = + Math.max(50, elements!.input.scrollHeight) + "px"; + } + resizeTextarea(); + elements!.input.addEventListener("input", resizeTextarea); elements!.settingsButton.addEventListener("click", () => { elements!.dialogBox.showModal(); }); From 77bd11ce8b98955a07e575215f7959ec2f27b4ac Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 18:37:28 +0800 Subject: [PATCH 284/738] improve textarea auto resizing --- src/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 2602eb2..1e73887 100644 --- a/src/main.ts +++ b/src/main.ts @@ -134,8 +134,9 @@ if (typeof document !== "undefined") { setVersion(); // Auto resize function resizeTextarea() { + elements!.input.style.height = "auto"; elements!.input.style.height = - Math.max(50, elements!.input.scrollHeight) + "px"; + Math.max(50, elements!.input.scrollHeight + 20) + "px"; } resizeTextarea(); elements!.input.addEventListener("input", resizeTextarea); From 5013deadf3d9a1eb404ae4b401ade0f7ed09ef94 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 18:50:41 +0800 Subject: [PATCH 285/738] remove resize --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index ecce0d7..162797d 100644 --- a/style.css +++ b/style.css @@ -14,7 +14,7 @@ a:visited { } #input { box-sizing: border-box; - resize: vertical; + resize: none; width: 100%; background-color: inherit; From e5ed02a8c9bb30b57cc35a0d46a44caeec3afbe6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 19:52:05 +0800 Subject: [PATCH 286/738] remove underline for hyperlink --- style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/style.css b/style.css index 162797d..e0ee637 100644 --- a/style.css +++ b/style.css @@ -8,6 +8,10 @@ body { } a { color: #0057af; + text-decoration: none; +} +a:hover { + text-decoration: underline; } a:visited { color: #551a8b; From 26b5d0d86efcf22e5f970bde19c5e6460a336fbc Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 19:57:15 +0800 Subject: [PATCH 287/738] small change --- style.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/style.css b/style.css index e0ee637..507d21c 100644 --- a/style.css +++ b/style.css @@ -43,14 +43,16 @@ button, select { padding: 10px; } -dialog:modal { +dialog { padding: 0px; padding-top: 20px; padding-bottom: 20px; - display: flex; flex-direction: column; resize: both; } +dialog:modal { + display: flex; +} dialog > label, dialog > h1 { padding: 10px; From 42287404b258541b79c73c3a91522a64aa4e0ddb Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 23 Apr 2024 19:58:26 +0800 Subject: [PATCH 288/738] small update --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 507d21c..ff15542 100644 --- a/style.css +++ b/style.css @@ -82,7 +82,7 @@ dialog input[type="checkbox"] { body { margin: 50px; } - dialog:modal { + dialog { padding: 20px; } } From 44a781c2e377ff07162bd97b8ba9e92f7c5e5912 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 26 Apr 2024 14:43:53 +0800 Subject: [PATCH 289/738] add translator.ts --- src/ast-parser.ts | 2 +- src/old-translator.ts | 4 ++-- src/translator.ts | 13 +++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 src/translator.ts diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 24a2731..848e2f8 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -764,6 +764,6 @@ const FULL_PARSER = choiceOnlyOne( ), ); /** A multiple Toki Pona sentence parser. */ -export function parser(src: string): Output { +export function parse(src: string): Output { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/old-translator.ts b/src/old-translator.ts index ccfc547..c42d59a 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -11,7 +11,7 @@ import { WordUnit, } from "./ast.ts"; import { Output } from "./output.ts"; -import { parser } from "./ast-parser.ts"; +import { parse } from "./ast-parser.ts"; import { OutputError, TodoError, UnreachableError } from "./error.ts"; import { DEFINITION } from "./old-definition.ts"; @@ -343,5 +343,5 @@ function translateSentences(sentences: MultipleSentences): TranslationOutput { } /** Full Toki Pona translator. */ export function translate(src: string): TranslationOutput { - return parser(src).flatMap(translateSentences); + return parse(src).flatMap(translateSentences); } diff --git a/src/translator.ts b/src/translator.ts new file mode 100644 index 0000000..8567c9f --- /dev/null +++ b/src/translator.ts @@ -0,0 +1,13 @@ +import { parse } from "./ast-parser.ts"; +import * as TokiPona from "./ast.ts"; +import * as English from "./english-ast.ts"; +import { Output } from "./output.ts"; + +function multipleSentences( + sentences: TokiPona.MultipleSentences, +): Output { + throw new Error("todo"); +} +export function translate(src: string): Output { + return parse(src).flatMap(multipleSentences); +} From d0d566666756775b78dd6bf82dc3bd5ca9a3d4c4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 26 Apr 2024 14:51:49 +0800 Subject: [PATCH 290/738] partial implementation of single word translator --- src/translator.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/translator.ts b/src/translator.ts index 8567c9f..2be0d05 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,12 +1,34 @@ import { parse } from "./ast-parser.ts"; import * as TokiPona from "./ast.ts"; +import { SPECIAL_CONTENT_WORD_DEFINITION } from "./dictionary.ts"; +import { CONTENT_WORD_DEFINITION } from "./dictionary.ts"; +import { PREPOSITION_DEFINITION } from "./dictionary.ts"; +import { PARTICLE_DEFINITION } from "./dictionary.ts"; import * as English from "./english-ast.ts"; +import { TodoError, UnreachableError } from "./error.ts"; import { Output } from "./output.ts"; function multipleSentences( sentences: TokiPona.MultipleSentences, ): Output { - throw new Error("todo"); + if (sentences.type === "single word") { + const word = sentences.word; + return new Output([ + ...PARTICLE_DEFINITION[word] ?? [], + ...SPECIAL_CONTENT_WORD_DEFINITION[word] ?? [], + ...PREPOSITION_DEFINITION[word] ?? [], + // TODO: Preverb + // TODO: Content word definition + ]).map((definition) => ({ + dependentClauses: [], + independentClause: { type: "free form", text: definition }, + punctuation: "", + })); + } else if (sentences.type === "sentences") { + return new Output(new TodoError("translation of sentences")); + } else { + throw new UnreachableError(); + } } export function translate(src: string): Output { return parse(src).flatMap(multipleSentences); From c80a1be3a141622bb1f2b034e741e01cd6d61082 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 26 Apr 2024 14:52:17 +0800 Subject: [PATCH 291/738] remove unused import --- src/translator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/translator.ts b/src/translator.ts index 2be0d05..a41ff40 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,7 +1,6 @@ import { parse } from "./ast-parser.ts"; import * as TokiPona from "./ast.ts"; import { SPECIAL_CONTENT_WORD_DEFINITION } from "./dictionary.ts"; -import { CONTENT_WORD_DEFINITION } from "./dictionary.ts"; import { PREPOSITION_DEFINITION } from "./dictionary.ts"; import { PARTICLE_DEFINITION } from "./dictionary.ts"; import * as English from "./english-ast.ts"; From 2919442d1723ac9233e8b6115cd0719508fd7610 Mon Sep 17 00:00:00 2001 From: neverRare Date: Fri, 26 Apr 2024 14:53:53 +0800 Subject: [PATCH 292/738] format imports --- src/translator.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/translator.ts b/src/translator.ts index a41ff40..ab003b2 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,8 +1,10 @@ import { parse } from "./ast-parser.ts"; import * as TokiPona from "./ast.ts"; -import { SPECIAL_CONTENT_WORD_DEFINITION } from "./dictionary.ts"; -import { PREPOSITION_DEFINITION } from "./dictionary.ts"; -import { PARTICLE_DEFINITION } from "./dictionary.ts"; +import { + PARTICLE_DEFINITION, + PREPOSITION_DEFINITION, + SPECIAL_CONTENT_WORD_DEFINITION, +} from "./dictionary.ts"; import * as English from "./english-ast.ts"; import { TodoError, UnreachableError } from "./error.ts"; import { Output } from "./output.ts"; From e2d2e30a5a53fc5cb05b06c5fb288081748674ab Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 15:48:17 +0800 Subject: [PATCH 293/738] add preposition --- src/english-ast.ts | 57 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/english-ast.ts b/src/english-ast.ts index eee6b08..d6dc7d9 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -2,16 +2,20 @@ import { AdjectiveType } from "./dictionary.ts"; -// TODO: preposition - export type NounPhrase = | { type: "simple"; determiners: Array; adjectives: Array; noun: string; + preposition: Array; } - | { type: "compound"; conjunction: string; subjects: NounPhrase }; + | { + type: "compound"; + conjunction: string; + subjects: NounPhrase; + preposition: Array; + }; export type Determiner = | { type: "quantifier"; quantifier: string } | { type: "numeral"; number: number }; @@ -21,34 +25,69 @@ export type AdjectivePhrase = { adjective: string; }; export type PredicateAdjective = - | { type: "simple"; adjective: AdjectivePhrase } - | { type: "compound"; conjunction: string; adjectives: PredicateAdjective }; + | { + type: "simple"; + adjective: AdjectivePhrase; + preposition: Array; + } + | { + type: "compound"; + conjunction: string; + adjectives: PredicateAdjective; + preposition: Array; + }; export type Verb = | { type: "default"; adverbs: Array; verb: string; + preposition: Array; } | { type: "linking noun"; noun: NounPhrase; + preposition: Array; } | { type: "linking adjective"; adjective: PredicateAdjective; + preposition: Array; }; export type Clause = | { type: "free form"; text: string } - | { type: "default"; subject: NounPhrase; verb: Verb; object: NounPhrase } + | { + type: "default"; + subject: NounPhrase; + verb: Verb; + object: NounPhrase; + preposition: Array; + } | { type: "subject phrase"; subject: NounPhrase } - | { type: "implied it's noun"; noun: NounPhrase } - | { type: "implied it's adjective"; noun: PredicateAdjective } + | { + type: "implied it's noun"; + noun: NounPhrase; + preposition: Array; + } + | { + type: "implied it's adjective"; + noun: PredicateAdjective; + preposition: Array; + } | { type: "interjection"; interjection: string } - | { type: "compound"; clauses: Array }; + | { + type: "compound"; + conjunction: string; + clauses: Array; + preposition: Array; + }; export type DependentClause = { conjunction: string; clause: Clause; }; +export type Preposition = { + preposition: string; + object: NounPhrase; +}; export type Sentence = { dependentClauses: Array; independentClause: Clause; From 1029e3bb4071ba2ae1aeb7561bde39e534dc800e Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 16:44:12 +0800 Subject: [PATCH 294/738] implement determiner type and move adjective type --- src/dictionary.ts | 10 +--------- src/english-ast.ts | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 8b4d9c5..8deab19 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,5 +1,6 @@ /** Module for describing word to word translations. */ +import { AdjectiveType } from "./english-ast.ts"; import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { @@ -1165,15 +1166,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { CONTENT_WORD_DEFINITION.ali = CONTENT_WORD_DEFINITION.ale; CONTENT_WORD_DEFINITION.oko = CONTENT_WORD_DEFINITION.lukin; -export type AdjectiveType = - | "opinion" - | "size" - | "physical quality" - | "age" - | "color" - | "origin" - | "material" - | "qualifier"; export type Definition = | { type: "noun"; diff --git a/src/english-ast.ts b/src/english-ast.ts index d6dc7d9..67c6b4c 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -1,7 +1,5 @@ /** Module for describing English AST. */ -import { AdjectiveType } from "./dictionary.ts"; - export type NounPhrase = | { type: "simple"; @@ -16,9 +14,26 @@ export type NounPhrase = subjects: NounPhrase; preposition: Array; }; +export type DeterminerType = + | "article" + | "demonstrative" + | "distributive" + | "interrogative" + | "possessive" + | "quantifier" + | "relative"; export type Determiner = - | { type: "quantifier"; quantifier: string } + | { type: "default"; kind: DeterminerType; word: string } | { type: "numeral"; number: number }; +export type AdjectiveType = + | "opinion" + | "size" + | "physical quality" + | "age" + | "color" + | "origin" + | "material" + | "qualifier"; export type AdjectivePhrase = { type: AdjectiveType; adverbs: Array; From 89f3624faafeb629345f805b354e458ce156343d Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 16:46:16 +0800 Subject: [PATCH 295/738] remove "another" from "sin" --- src/dictionary.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 8deab19..5b0ced8 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -924,7 +924,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("fresh", "opinion"), adjective("additional", "origin"), adjective("extra", "origin"), - quantifier("another"), // It is a determiner. But is it a quantifier? adverb("newly"), ], sina: [ From a660116ae1a8aed9e47cd78d23fb04df26570087 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 16:56:10 +0800 Subject: [PATCH 296/738] change quantifiers into determiners --- src/dictionary.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 5b0ced8..e3234ab 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,6 +1,6 @@ /** Module for describing word to word translations. */ -import { AdjectiveType } from "./english-ast.ts"; +import { AdjectiveType, DeterminerType } from "./english-ast.ts"; import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { @@ -131,7 +131,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], ala: [ numeral(0), - quantifier("no"), + determiner("no", "quantifier"), singularNoun("nothing"), // This is technically a pronoun ], alasa: [ @@ -143,8 +143,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("everything"), singularNoun("anything"), singularNoun("entirety"), - quantifier("all"), - quantifier("every"), + determiner("all", "distributive"), + determiner("every", "distributive"), adverb("completely"), ], ali: [], // Will be duplicated with "ale" @@ -464,7 +464,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("small", "size"), adjective("short", "size"), adjective("young", "age"), - quantifier("few"), + determiner("few", "quantifier"), ], linja: [ adjectiveNounPhrase([ @@ -683,8 +683,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], mute: [ numeral(20), - quantifier("many"), - quantifier("several"), + determiner("many", "quantifier"), + determiner("several", "quantifier"), adverb("very"), ], nanpa: [ @@ -1005,7 +1005,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("cause(s)"), ], taso: [ - quantifier("only"), + determiner("only", "distributive"), // Question: is this really a distributive determiner? ], tawa: [ noun("motion(s)"), @@ -1196,7 +1196,7 @@ export type Definition = adverbs: Array; adjective: Definition & { type: "adjective" }; } - | { type: "quantifier"; quantifier: string } + | { type: "determiner"; determiner: string; kind: DeterminerType } | { type: "numeral"; number: number } | { type: "adverb"; adverb: string } | { @@ -1381,12 +1381,15 @@ function adjective( function numeral(number: number): Definition & { type: "numeral" } { return { type: "numeral", number }; } +function determiner( + determiner: string, + kind: DeterminerType, +): Definition & { type: "determiner" } { + return { type: "determiner", determiner, kind }; +} function adverb(word: string): Definition & { type: "adverb" } { return { type: "adverb", adverb: word }; } -function quantifier(word: string): Definition & { type: "quantifier" } { - return { type: "quantifier", quantifier: word }; -} function interjection(word: string): Definition & { type: "interjection" } { return { type: "interjection", interjection: word }; } From cc7d94c5f90bb5f7dfd59fcd74553572f9c868b4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 16:58:37 +0800 Subject: [PATCH 297/738] include common definition for "ni" --- src/dictionary.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index e3234ab..6e194e0 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -715,7 +715,12 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("nose(s)"), noun("protuberance(s)"), ], - ni: [], // Special case + ni: [ + determiner("this", "demonstrative"), + determiner("that", "demonstrative"), + determiner("these", "demonstrative"), + determiner("those", "demonstrative"), + ], nimi: [ noun("name(s)"), noun("word(s)"), From 573ac9585ddcd19985941870413f8100b25d1262 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 17:05:16 +0800 Subject: [PATCH 298/738] something --- src/english-ast.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/english-ast.ts b/src/english-ast.ts index 67c6b4c..928e2f8 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -1,5 +1,6 @@ /** Module for describing English AST. */ +/** */ export type NounPhrase = | { type: "simple"; From 646fff0811afbca81752886e055bb807e870c9f4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sat, 27 Apr 2024 17:10:52 +0800 Subject: [PATCH 299/738] remove unused definition type --- src/dictionary.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index 6e194e0..b2e0ad6 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1218,7 +1218,6 @@ export type Definition = verb: Definition & { type: "verb" }; object: Definition & { type: "noun" | "adjective noun phrase" }; } - | { type: "gerund"; gerund: string } | { type: "interjection"; interjection: string }; export const PARTICLE = new Set(Object.keys(PARTICLE_DEFINITION)); export const CONTENT_WORD = new Set(Object.keys(CONTENT_WORD_DEFINITION)); From 7ef4bdcafb1837409991d8eb00d40f8201f81602 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 28 Apr 2024 09:51:39 +0800 Subject: [PATCH 300/738] implement personal and indefinite pronouns --- src/dictionary.ts | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index b2e0ad6..d6d4f52 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -132,7 +132,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ala: [ numeral(0), determiner("no", "quantifier"), - singularNoun("nothing"), // This is technically a pronoun + indefinitePronoun("nothing"), ], alasa: [ verb("hunt(ed)", "hunting"), @@ -140,8 +140,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], ale: [ numeral(100), - singularNoun("everything"), - singularNoun("anything"), + indefinitePronoun("everything"), + indefinitePronoun("anything"), singularNoun("entirety"), determiner("all", "distributive"), determiner("every", "distributive"), @@ -239,7 +239,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { jan: [ singularNoun("human being"), noun("person/people"), - singularNoun("somebody"), // This is technically a pronoun + indefinitePronoun("somebody"), ], jasima: [ noun("reflection(s)"), @@ -594,7 +594,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], mi: [ { - type: "pronoun", + type: "personal pronoun", singularSubject: "I", singularObject: "me", singularPossessive: "my", @@ -758,7 +758,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], ona: [ { - type: "pronoun", + type: "personal pronoun", singularSubject: null, singularObject: null, singularPossessive: null, @@ -900,7 +900,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { singularNoun("skin"), noun("boundary/boundaries"), ], - seme: [], // Special case + seme: [ + determiner("what", "interrogative"), + determiner("which", "interrogative"), + ], sewi: [ // TODO: area above, something elevated adjectiveNounPhrase([adjective("highest", "origin")], noun("part(s)")), @@ -933,7 +936,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], sina: [ { - type: "pronoun", + type: "personal pronoun", singularSubject: null, singularObject: null, singularPossessive: null, @@ -1183,7 +1186,7 @@ export type Definition = noun: Definition & { type: "noun" }; } | { - type: "pronoun"; + type: "personal pronoun"; singularSubject: null | string; singularObject: null | string; singularPossessive: null | string; @@ -1191,6 +1194,10 @@ export type Definition = pluralObject: string; pluralPossessive: string; } + | { + type: "indefinite pronoun"; + pronoun: string; + } | { type: "adjective"; adjective: string; kind: AdjectiveType } | { type: "compound adjective"; @@ -1246,6 +1253,11 @@ function singularNoun(word: string): Definition & { type: "noun" } { function pluralNoun(word: string): Definition & { type: "noun" } { return { type: "noun", singular: null, plural: word, condensed: word }; } +function indefinitePronoun( + pronoun: string, +): Definition & { type: "indefinite pronoun" } { + return { type: "indefinite pronoun", pronoun }; +} function adjectiveNounPhrase( adjectives: Array, noun: Definition & { type: "noun" }, From 785bc30ac87b46afc03250eda88a05fed5ff6076 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 28 Apr 2024 10:43:32 +0800 Subject: [PATCH 301/738] implement determiner quantity --- src/dictionary.ts | 42 ++++++++++++++++++++++++++---------------- src/english-ast.ts | 8 +++++++- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index d6d4f52..f0576e8 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -1,6 +1,10 @@ /** Module for describing word to word translations. */ -import { AdjectiveType, DeterminerType } from "./english-ast.ts"; +import { + AdjectiveType, + DeterminerQuantity, + DeterminerType, +} from "./english-ast.ts"; import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { @@ -131,7 +135,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], ala: [ numeral(0), - determiner("no", "quantifier"), + determiner("no", "quantifier", "zero"), indefinitePronoun("nothing"), ], alasa: [ @@ -143,8 +147,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { indefinitePronoun("everything"), indefinitePronoun("anything"), singularNoun("entirety"), - determiner("all", "distributive"), - determiner("every", "distributive"), + determiner("all", "distributive", "plural"), + determiner("every", "distributive", "plural"), adverb("completely"), ], ali: [], // Will be duplicated with "ale" @@ -464,7 +468,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("small", "size"), adjective("short", "size"), adjective("young", "age"), - determiner("few", "quantifier"), + determiner("few", "quantifier", "plural"), ], linja: [ adjectiveNounPhrase([ @@ -683,8 +687,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { ], mute: [ numeral(20), - determiner("many", "quantifier"), - determiner("several", "quantifier"), + determiner("many", "quantifier", "plural"), + determiner("several", "quantifier", "plural"), adverb("very"), ], nanpa: [ @@ -716,10 +720,10 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("protuberance(s)"), ], ni: [ - determiner("this", "demonstrative"), - determiner("that", "demonstrative"), - determiner("these", "demonstrative"), - determiner("those", "demonstrative"), + determiner("this", "demonstrative", "both"), + determiner("that", "demonstrative", "both"), + determiner("these", "demonstrative", "both"), + determiner("those", "demonstrative", "both"), ], nimi: [ noun("name(s)"), @@ -901,8 +905,8 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("boundary/boundaries"), ], seme: [ - determiner("what", "interrogative"), - determiner("which", "interrogative"), + determiner("what", "interrogative", "both"), + determiner("which", "interrogative", "both"), ], sewi: [ // TODO: area above, something elevated @@ -1013,7 +1017,7 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { noun("cause(s)"), ], taso: [ - determiner("only", "distributive"), // Question: is this really a distributive determiner? + determiner("only", "distributive", "both"), // Question: is this really a distributive determiner? ], tawa: [ noun("motion(s)"), @@ -1208,7 +1212,12 @@ export type Definition = adverbs: Array; adjective: Definition & { type: "adjective" }; } - | { type: "determiner"; determiner: string; kind: DeterminerType } + | { + type: "determiner"; + determiner: string; + kind: DeterminerType; + quantity: DeterminerQuantity; + } | { type: "numeral"; number: number } | { type: "adverb"; adverb: string } | { @@ -1400,8 +1409,9 @@ function numeral(number: number): Definition & { type: "numeral" } { function determiner( determiner: string, kind: DeterminerType, + quantity: DeterminerQuantity, ): Definition & { type: "determiner" } { - return { type: "determiner", determiner, kind }; + return { type: "determiner", determiner, kind, quantity }; } function adverb(word: string): Definition & { type: "adverb" } { return { type: "adverb", adverb: word }; diff --git a/src/english-ast.ts b/src/english-ast.ts index 928e2f8..5538812 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -23,8 +23,14 @@ export type DeterminerType = | "possessive" | "quantifier" | "relative"; +export type DeterminerQuantity = "zero" | "singular" | "plural" | "both"; export type Determiner = - | { type: "default"; kind: DeterminerType; word: string } + | { + type: "default"; + kind: DeterminerType; + word: string; + quantity: DeterminerQuantity; + } | { type: "numeral"; number: number }; export type AdjectiveType = | "opinion" From 39e122cebac20b3e8e187c8056a40bec7d0d5a94 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 28 Apr 2024 16:07:14 +0800 Subject: [PATCH 302/738] Remove discord from provide feedback --- index.html | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index 0569746..a9f66d2 100644 --- a/index.html +++ b/index.html @@ -43,25 +43,13 @@

        ilo Token

        Provide feedback
        • - Open an issue on Github: + Github: neverRare/ilo-token.
        • - Join - ma pona pi toki pona Discord Server - and ping me (never_rare) at dedicated forum channel: - ilo Token - Rule-based Toki Pona to English - translator. -
        • -
        • - Email me: + Email: contactneverrare@gmail.com. From 707b581d8694d0a16e24d1caecd756c4721a66e9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 28 Apr 2024 19:36:51 +0800 Subject: [PATCH 303/738] update sequence combinator --- src/parser-lib.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parser-lib.ts b/src/parser-lib.ts index f9970a4..6dc207f 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -134,13 +134,13 @@ export function sequence>( ): Parser { // We resorted to using `any` types here, make sure it works properly // deno-lint-ignore no-explicit-any - return (sequence as Array) - .reduceRight((newParser, parser) => + return sequence.reduceRight((newParser: any, parser) => + // deno-lint-ignore no-explicit-any + parser.then((value: any) => // deno-lint-ignore no-explicit-any - parser.then((value: any) => - // deno-lint-ignore no-explicit-any - newParser.map((newValue: any) => [value, ...newValue]) - ), nothing().map(() => [])); + newParser.map((newValue: any) => [value, ...newValue] as any) + // deno-lint-ignore no-explicit-any + ), nothing().map(() => [] as any)); } /** * Parses `parser` multiple times and returns an `Array`. The resulting From eef7bb163409ed7df843aa6237fd573b054139e5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 11:08:11 +0800 Subject: [PATCH 304/738] update type signature of multiple sentence parser --- src/translator.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/translator.ts b/src/translator.ts index ab003b2..5b00457 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -11,7 +11,7 @@ import { Output } from "./output.ts"; function multipleSentences( sentences: TokiPona.MultipleSentences, -): Output { +): Output> { if (sentences.type === "single word") { const word = sentences.word; return new Output([ @@ -20,17 +20,22 @@ function multipleSentences( ...PREPOSITION_DEFINITION[word] ?? [], // TODO: Preverb // TODO: Content word definition - ]).map((definition) => ({ - dependentClauses: [], - independentClause: { type: "free form", text: definition }, - punctuation: "", - })); + ]) + .map((definition) => ({ + dependentClauses: [], + independentClause: { + type: "free form", + text: definition, + } as English.Clause, + punctuation: "", + })) + .map((definition) => [definition]); } else if (sentences.type === "sentences") { return new Output(new TodoError("translation of sentences")); } else { throw new UnreachableError(); } } -export function translate(src: string): Output { +export function translate(src: string): Output> { return parse(src).flatMap(multipleSentences); } From 1c4855ec7643ba4ed0292b627f8aa2902766ebb1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 11:13:18 +0800 Subject: [PATCH 305/738] move and rename rotate in Output.combine --- src/old-translator.ts | 52 +++++++++++++++++-------------------------- src/output.ts | 16 +++++++++++++ 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/old-translator.ts b/src/old-translator.ts index c42d59a..1d23d38 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -29,23 +29,6 @@ const WORD_TO_NUMBER: { [word: string]: number } = { // TODO: -like and -related suffixes for nouns as adjectives // TODO: "and" in "of" and "in X way" -/** - * Helper function for turning array or tuple of Output into Output of array or - * tuple. Make use of `as const` to infer array as tuple. - */ -// TODO: maybe there's a better name -function rotate>( - array: { [I in keyof T]: Output } & { length: T["length"] }, -): Output { - // We resorted to using `any` types here, make sure it works properly - return array.reduce( - // deno-lint-ignore no-explicit-any - (result: Output, output) => - result.flatMap((left) => output.map((right) => [...left, right])), - // deno-lint-ignore no-explicit-any - new Output([[]]), - ) as Output; -} function definition( kind: "noun" | "adjective" | "adverb", word: string, @@ -155,7 +138,10 @@ function defaultPhraseAs( .filter((modifier) => modifier.type !== "proper words"); const modifierTranslation: Array = modifierNoName .map((modifier) => modifierAs(modifierKind, modifier)); - const translations = rotate([headWord, rotate(modifierTranslation)] as const) + const translations = Output.combine( + headWord, + Output.combine(...modifierTranslation), + ) .map(([headWord, modifiers]) => [...modifiers.slice().reverse(), headWord].join(" ") ) @@ -177,7 +163,7 @@ function defaultPhraseAs( ...modifierNoName.slice(i + 1), ] .map((modifier) => modifierAs(modifierKind, modifier)); - return rotate([headWord, rotate(modifierTranslation)] as const) + return Output.combine(headWord, Output.combine(...modifierTranslation)) .map(([headWord, modifiers]) => [...modifiers.slice().reverse(), headWord].join(" ") ) @@ -222,8 +208,8 @@ function translateMultiplePhrases( } else { conjunction = "or"; } - const translations = rotate( - phrases.phrases.map((phrases) => + const translations = Output.combine( + ...phrases.phrases.map((phrases) => translateMultiplePhrases(phrases, translator, level - 1) ), ); @@ -321,21 +307,23 @@ function translateFullClause(fullClause: FullClause): TranslationOutput { } /** Translates a single sentence. */ function translateSentence(sentence: Sentence): TranslationOutput { - return rotate(sentence.laClauses.map(translateFullClause)).map((clauses) => { - const contexts = clauses.slice(0, clauses.length - 1); - const final = clauses[clauses.length - 1]; - return [ - ...contexts.map((context) => `given ${context}, `), - final, - sentence.punctuation, - ] - .join(""); - }); + return Output.combine(...sentence.laClauses.map(translateFullClause)).map( + (clauses) => { + const contexts = clauses.slice(0, clauses.length - 1); + const final = clauses[clauses.length - 1]; + return [ + ...contexts.map((context) => `given ${context}, `), + final, + sentence.punctuation, + ] + .join(""); + }, + ); } /** Translates multiple sentences. */ function translateSentences(sentences: MultipleSentences): TranslationOutput { if (sentences.type === "sentences") { - return rotate(sentences.sentences.map(translateSentence)) + return Output.combine(...sentences.sentences.map(translateSentence)) .map((sentences) => sentences.join(" ")); } else { return new Output(new TodoError("translation of a single word")); diff --git a/src/output.ts b/src/output.ts index 4ef163e..22a3da8 100644 --- a/src/output.ts +++ b/src/output.ts @@ -102,4 +102,20 @@ export class Output { } return wholeOutput; } + /** + * Combines all permutations of all Outputs into an Output of a single tuple + * or array. + */ + static combine>( + ...outputs: { [I in keyof T]: Output } & { length: T["length"] } + ): Output { + // We resorted to using `any` types here, make sure it works properly + return outputs.reduce( + // deno-lint-ignore no-explicit-any + (result: Output, output) => + result.flatMap((left) => output.map((right) => [...left, right])), + // deno-lint-ignore no-explicit-any + new Output([[]]), + ) as Output; + } } From 823655eb2743a0b5a11b02bf2656497d606f2897 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 11:19:48 +0800 Subject: [PATCH 306/738] initial implementation of sentence translator --- src/translator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/translator.ts b/src/translator.ts index 5b00457..f1cd2ad 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -9,6 +9,9 @@ import * as English from "./english-ast.ts"; import { TodoError, UnreachableError } from "./error.ts"; import { Output } from "./output.ts"; +function sentence(sentence: TokiPona.Sentence): Output { + return new Output(new TodoError("translation of sentence")); +} function multipleSentences( sentences: TokiPona.MultipleSentences, ): Output> { @@ -31,7 +34,7 @@ function multipleSentences( })) .map((definition) => [definition]); } else if (sentences.type === "sentences") { - return new Output(new TodoError("translation of sentences")); + return Output.combine(...sentences.sentences.map(sentence)); } else { throw new UnreachableError(); } From 2e869408666cfefbeb20b9e2a0ceb2624f49db20 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 11:24:25 +0800 Subject: [PATCH 307/738] update type definition of sentence --- src/ast-parser.ts | 7 ++++--- src/ast.ts | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 848e2f8..e91f0a5 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -723,15 +723,16 @@ function la(): AstParser { /** Parses a single full sentence with optional punctuations. */ function sentence(): AstParser { return sequence( + many(fullClause().skip(la())), fullClause(), - many(la().with(fullClause())), choice( eol("end of sentence").map(() => ""), punctuation(), ), ) - .map(([clause, moreClauses, punctuation]) => ({ - laClauses: [clause, ...moreClauses], + .map(([laClauses, finalClause, punctuation]) => ({ + laClauses, + finalClause, punctuation, })); } diff --git a/src/ast.ts b/src/ast.ts index 6a69cd4..8bedba9 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -96,7 +96,11 @@ export type FullClause = } | { type: "marker"; marker: Marker }; /** Represents a single full sentence. */ -export type Sentence = { laClauses: Array; punctuation: string }; +export type Sentence = { + laClauses: Array; + finalClause: FullClause; + punctuation: string; +}; /** Represents quotation. */ export type Quotation = { sentences: Array; From 78f142b3e9db44e14b357198d5125e8b93f5451d Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 13:02:48 +0800 Subject: [PATCH 308/738] formatting --- bundle.ts | 10 +++++----- deps.ts | 2 +- dev-deps.ts | 6 +++--- src/main.ts | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bundle.ts b/bundle.ts index 43949fd..57da875 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,21 +1,21 @@ -import { debounce, emit, teloMisikeke } from "./dev-deps.ts"; +import { Debounce, Emit, TeloMisikeke } from "./dev-deps.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./main.js", import.meta.url); -async function build(options: emit.BundleOptions): Promise { - const result = await emit.bundle(SOURCE, options); +async function build(options: Emit.BundleOptions): Promise { + const result = await Emit.bundle(SOURCE, options); const { code } = result; await Deno.writeTextFile(DESTINATION, code); } if (Deno.args[0] === "build") { console.log("Building telo misikeke..."); - await teloMisikeke.build(); + await TeloMisikeke.build(); console.log("Building main.js..."); await build({ minify: true, type: "classic" }); console.log("Building done!"); } else if (Deno.args[0] === "watch") { - const builder = debounce.debounce(async () => { + const builder = Debounce.debounce(async () => { console.log("Starting to build..."); try { await build({ diff --git a/deps.ts b/deps.ts index 6f1850e..45d71f3 100644 --- a/deps.ts +++ b/deps.ts @@ -1 +1 @@ -export * as teloMisikeke from "./telo-misikeke/telo-misikeke.ts"; +export * as TeloMisikeke from "./telo-misikeke/telo-misikeke.ts"; diff --git a/dev-deps.ts b/dev-deps.ts index 514173a..8351cd2 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,3 +1,3 @@ -export * as emit from "https://deno.land/x/emit@0.38.2/mod.ts"; -export * as debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; -export * as teloMisikeke from "./telo-misikeke/build.ts"; +export * as Emit from "https://deno.land/x/emit@0.38.2/mod.ts"; +export * as Debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; +export * as TeloMisikeke from "./telo-misikeke/build.ts"; diff --git a/src/main.ts b/src/main.ts index 1e73887..4f1acfd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import { CoveredError } from "./error.ts"; import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; -import { teloMisikeke } from "../deps.ts"; +import { TeloMisikeke } from "../deps.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; @@ -102,7 +102,7 @@ function updateOutput(): void { } else { let error: Array = []; if (settings.get("use-telo-misikeke")) { - error = teloMisikeke.errors(source); + error = TeloMisikeke.errors(source); } if (error.length === 0) { error = [ From a1e6e4d0cf9f10907d67ce4dbfb33d9830f7809f Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 13:48:53 +0800 Subject: [PATCH 309/738] add vocative in english AST --- src/english-ast.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/english-ast.ts b/src/english-ast.ts index 5538812..9a67520 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -96,6 +96,7 @@ export type Clause = preposition: Array; } | { type: "interjection"; interjection: string } + | { type: "vocative"; call: string; addressee: NounPhrase } | { type: "compound"; conjunction: string; From d2ce6ca6ad8ec3cc2e620dd4007ed766dff3f00c Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 15:20:48 +0800 Subject: [PATCH 310/738] fix naming --- src/english-ast.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/english-ast.ts b/src/english-ast.ts index 9a67520..68987a2 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -92,7 +92,7 @@ export type Clause = } | { type: "implied it's adjective"; - noun: PredicateAdjective; + adjective: PredicateAdjective; preposition: Array; } | { type: "interjection"; interjection: string } From 294bded77fd82692f1d12e8d56693047412b6ba7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 15:31:53 +0800 Subject: [PATCH 311/738] fix old translator --- src/old-translator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/old-translator.ts b/src/old-translator.ts index 1d23d38..e1334bb 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -307,7 +307,9 @@ function translateFullClause(fullClause: FullClause): TranslationOutput { } /** Translates a single sentence. */ function translateSentence(sentence: Sentence): TranslationOutput { - return Output.combine(...sentence.laClauses.map(translateFullClause)).map( + return Output.combine( + ...[...sentence.laClauses, sentence.finalClause].map(translateFullClause), + ).map( (clauses) => { const contexts = clauses.slice(0, clauses.length - 1); const final = clauses[clauses.length - 1]; From e7e0c595381d7850d95da2662c181978a12f020a Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 17:23:42 +0800 Subject: [PATCH 312/738] revert treating "a" and "n" as content word --- src/dictionary.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index f0576e8..c21f65c 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -9,6 +9,12 @@ import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { a: [ + "ah", + "oh", + "ha", + "eh", + "um", + "oy", "[placed after something for emphasis or emotion]", ], ala: [ @@ -40,6 +46,11 @@ export const PARTICLE_DEFINITION: { [word: string]: Array } = { "[marks the start of an indicative verb (statement)]", ], n: [ + "hm", + "uh", + "mm", + "er", + "umm", "[indicate thinking or pause]", ], nanpa: [ @@ -119,14 +130,6 @@ export const PREVERB_DEFINITION: { [word: string]: Array } = { }; PREVERB_DEFINITION.alasa = PREVERB_DEFINITION.lukin; export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { - a: [ - interjection("ah"), - interjection("oh"), - interjection("ha"), - interjection("eh"), - interjection("um"), - interjection("oy"), - ], akesi: [ noun("reptile(s)"), noun("amphibian(s)"), @@ -678,13 +681,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("silly", "opinion"), verbObjectPhrase(verb("have/had", "having"), singularNoun("fun")), ], - n: [ - interjection("hm"), - interjection("uh"), - interjection("mm"), - interjection("er"), - interjection("umm"), - ], mute: [ numeral(20), determiner("many", "quantifier", "plural"), From 7257d652d3812e0c7b04094b7994ef630d193412 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 17:33:19 +0800 Subject: [PATCH 313/738] add filters for markers --- src/filter.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/filter.ts b/src/filter.ts index e7d0f4f..a70bdc5 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -13,7 +13,7 @@ import { somePhraseInMultiplePhrases, WordUnit, } from "./ast.ts"; -import { UnreachableError, UnrecognizedError } from "./error.ts"; +import { CoveredError, UnreachableError, UnrecognizedError } from "./error.ts"; import { settings } from "./settings.ts"; /** Array of filter rules for a word unit. */ @@ -219,6 +219,16 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ } return true; }, + // If the phrase has no modifiers, avoid marker + (phrase) => { + if ( + phrase.type === "default" && phrase.marker !== null && + phrase.modifiers.length === 0 + ) { + throw new CoveredError(); + } + return true; + }, ]; /** Array of filter rules for preposition. */ export const PREPOSITION_RULE: Array<(phrase: Preposition) => boolean> = [ @@ -284,6 +294,17 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ } return true; }, + // If the clause is just a single phrase, avoid post markers + (fullClause) => { + if ( + fullClause.type === "default" && fullClause.postclause !== null && + fullClause.clause.type === "phrases" && + fullClause.clause.phrases.type === "single" + ) { + throw new CoveredError(); + } + return true; + }, ]; /** Array of filter rules for multiple sentences. */ export const SENTENCES_RULE: Array<(sentences: Array) => boolean> = [ From 557b130b5996838bc6bef9e45a8d5813bf3753f2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 17:40:55 +0800 Subject: [PATCH 314/738] refactor nested ifs --- src/filter.ts | 53 ++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index a70bdc5..6c97041 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -91,23 +91,23 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ }, // nanpa construction cannot contain pi (modifier) => { - if (modifier.type === "nanpa" && modifier.phrase.type === "default") { - if ( - modifier.phrase.modifiers.some((modifier) => modifier.type === "pi") - ) { - throw new UnrecognizedError("pi inside nanpa"); - } + if ( + modifier.type === "nanpa" && + modifier.phrase.type === "default" && + modifier.phrase.modifiers.some((modifier) => modifier.type === "pi") + ) { + throw new UnrecognizedError("pi inside nanpa"); } return true; }, // nanpa construction cannot contain nanpa (modifier) => { - if (modifier.type === "nanpa" && modifier.phrase.type === "default") { - if ( - modifier.phrase.modifiers.some((modifier) => modifier.type === "nanpa") - ) { - throw new UnrecognizedError("nanpa inside nanpa"); - } + if ( + modifier.type === "nanpa" && + modifier.phrase.type === "default" && + modifier.phrase.modifiers.some((modifier) => modifier.type === "nanpa") + ) { + throw new UnrecognizedError("nanpa inside nanpa"); } return true; }, @@ -197,24 +197,22 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ }, // Disallow preverb modifiers other than _ala_ (phrase) => { - if (phrase.type === "preverb") { - if (!modifiersIsAlaOrNone(phrase.modifiers)) { - throw new UnrecognizedError('preverb with modifiers other than "ala"'); - } + if (phrase.type === "preverb" && !modifiersIsAlaOrNone(phrase.modifiers)) { + throw new UnrecognizedError('preverb with modifiers other than "ala"'); } return true; }, // No multiple number words (phrase) => { if (phrase.type === "default") { + const headWord = phrase.headWord; if ( - phrase.headWord.type === "numbers" || - (phrase.headWord.type === "default" && - (phrase.headWord.word === "wan" || phrase.headWord.word === "tu")) + (headWord.type === "numbers" || + (headWord.type === "default" && + (headWord.word === "wan" || headWord.word === "tu"))) && + phrase.modifiers.some(modifierIsNumeric) ) { - if (phrase.modifiers.some(modifierIsNumeric)) { - throw new UnrecognizedError("Multiple number words"); - } + throw new UnrecognizedError("Multiple number words"); } } return true; @@ -272,12 +270,11 @@ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ }, // disallow preposition in object (clause) => { - if (clause.type === "li clause" || clause.type === "o clause") { - if ( - someObjectInMultiplePredicate(clause.predicates, hasPrepositionInPhrase) - ) { - throw new UnrecognizedError("Preposition in object"); - } + if ( + (clause.type === "li clause" || clause.type === "o clause") && + someObjectInMultiplePredicate(clause.predicates, hasPrepositionInPhrase) + ) { + throw new UnrecognizedError("Preposition in object"); } return true; }, From 691ded77050f98b0200fd45363b0ad90b7c6c6b6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 18:05:27 +0800 Subject: [PATCH 315/738] rename marker into modifying particle --- src/ast-parser.ts | 98 ++++++++++++++++++++++++++++++----------------- src/ast.ts | 20 ++++++---- src/filter.ts | 6 +-- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e91f0a5..2924e26 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -3,8 +3,8 @@ import { Clause, FullClause, - Marker, Modifier, + ModifyingParticle, MultiplePhrases, MultiplePredicates, MultipleSentences, @@ -131,17 +131,16 @@ function specificWord(thatWord: string): AstParser { else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); } -function marker(): AstParser { +function modifyingParticle(): AstParser { return choice( specificTokenTree("multiple a") - .map(({ count }) => ({ type: "multiple a", count }) as Marker), + .map(({ count }) => ({ type: "multiple a", count }) as ModifyingParticle), specificTokenTree("long word") .map(({ word, length }) => - ({ type: "long word", word, length }) as Marker + ({ type: "long word", word, length }) as ModifyingParticle ), - wordFrom(new Set(["a", "n", "kin"]), "a/n/kin").map((word) => - ({ type: "word", word }) as Marker - ), + wordFrom(new Set(["a", "n", "kin"]), "a/n/kin") + .map((word) => ({ type: "word", word }) as ModifyingParticle), ); } function xAlaX( @@ -218,8 +217,10 @@ function wordUnit(word: Set, description: string): AstParser { ) ), xAlaX(word, description), - sequence(wordFrom(word, description), optional(marker())) - .map(([word, marker]) => ({ type: "default", word, marker }) as WordUnit), + sequence(wordFrom(word, description), optional(modifyingParticle())) + .map(([word, modifyingParticle]) => + ({ type: "default", word, modifyingParticle }) as WordUnit + ), ) .filter(filter(WORD_UNIT_RULES)); } @@ -347,42 +348,42 @@ const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); /** Parses phrases. */ function phrase(): AstParser { return choice( - sequence(number(), lazy(modifiers), optional(marker())) - .map(([numbers, modifiers, marker]) => + sequence(number(), lazy(modifiers), optional(modifyingParticle())) + .map(([numbers, modifiers, modifyingParticle]) => ({ type: "default", headWord: { type: "numbers", numbers }, modifiers, - marker, + modifyingParticle, }) as Phrase ), binaryWords(PREVERB, "preveb").map(([preverb, phrase]) => ({ type: "preverb", - preverb: { type: "default", word: preverb, marker: null }, + preverb: { type: "default", word: preverb, modifyingParticle: null }, modifiers: [], phrase: { type: "default", - headWord: { type: "default", word: phrase, marker: null }, + headWord: { type: "default", word: phrase, modifyingParticle: null }, modifiers: [], - marker: null, + modifyingParticle: null, }, - marker: null, + modifyingParticle: null, }) as Phrase ), sequence( optionalCombined(PREVERB, "preverb"), lazy(modifiers), lazy(phrase), - optional(marker()), + optional(modifyingParticle()), ) - .map(([[preverb, modifier], modifiers, phrase, marker]) => + .map(([[preverb, modifier], modifiers, phrase, modifyingParticle]) => ({ type: "preverb", preverb, modifiers: [...modifier, ...modifiers], phrase, - marker, + modifyingParticle, }) as Phrase ), lazy(preposition) @@ -390,14 +391,14 @@ function phrase(): AstParser { sequence( optionalCombined(CONTENT_WORD, "content word"), lazy(modifiers), - optional(marker()), + optional(modifyingParticle()), ) - .map(([[headWord, modifier], modifiers, marker]) => + .map(([[headWord, modifier], modifiers, modifyingParticle]) => ({ type: "default", headWord, modifiers: [...modifier, ...modifiers], - marker, + modifyingParticle, }) as Phrase ), quotation() @@ -466,10 +467,14 @@ function preposition(): AstParser { .flatMapValue((tokenTrees) => INNER_PHRASE_PARSER.parse(tokenTrees.words)) .map((phrase) => ({ - preposition: { type: "default", word: "lon", marker: null }, + preposition: { + type: "default", + word: "lon", + modifyingParticle: null, + }, modifiers: [], phrases: { type: "single", phrase }, - marker: null, + modifyingParticle: null, }) as Preposition ), specificTokenTree("long glyph").flatMapValue((tokenTrees) => { @@ -508,32 +513,40 @@ function preposition(): AstParser { }), binaryWords(PREPOSITION, "preposition").map(([preposition, phrase]) => ({ - preposition: { type: "default", word: preposition, marker: null }, + preposition: { + type: "default", + word: preposition, + modifyingParticle: null, + }, modifiers: [], phrases: { type: "single", phrase: { type: "default", - headWord: { type: "default", word: phrase, marker: null }, + headWord: { + type: "default", + word: phrase, + modifyingParticle: null, + }, modifiers: [], - marker: null, + modifyingParticle: null, }, }, - marker: null, + modifyingParticle: null, }) as Preposition ), sequence( optionalCombined(PREPOSITION, "preposition"), modifiers(), nestedPhrases(["anu"]), - optional(marker()), + optional(modifyingParticle()), ) - .map(([[preposition, modifier], modifiers, phrases, marker]) => + .map(([[preposition, modifier], modifiers, phrases, modifyingParticle]) => ({ preposition, modifiers: [...modifier, ...modifiers], phrases, - marker, + modifyingParticle, }) as Preposition ), ) @@ -618,10 +631,14 @@ function clause(): AstParser { type: "single", phrase: { type: "default", - headWord: { type: "default", word: subject, marker: null }, + headWord: { + type: "default", + word: subject, + modifyingParticle: null, + }, alaQuestion: false, modifiers: [], - marker: null, + modifyingParticle: null, }, }, predicates, @@ -684,14 +701,20 @@ function clause(): AstParser { } function preclause(): AstParser { return choice( - marker().map((marker) => ({ type: "marker", marker }) as Preclause), + modifyingParticle() + .map((modifyingParticle) => + ({ type: "modifying particle", modifyingParticle }) as Preclause + ), wordUnit(new Set(["taso"]), '"taso"') .map((taso) => ({ type: "taso", taso }) as Preclause), ); } function postclause(): AstParser { return choice( - marker().map((marker) => ({ type: "marker", marker }) as Postclause), + modifyingParticle() + .map((modifyingParticle) => + ({ type: "modifying particle", modifyingParticle }) as Postclause + ), specificWord("anu") .with(wordUnit(new Set(["seme"]), '"seme"')) .map((seme) => ({ type: "anu seme", seme }) as Postclause), @@ -708,7 +731,10 @@ function fullClause(): AstParser { .map(([preclause, clause, postclause]) => ({ type: "default", preclause, clause, postclause }) as FullClause ), - marker().map((marker) => ({ type: "marker", marker }) as FullClause), + modifyingParticle() + .map((modifyingParticle) => + ({ type: "modifying particle", modifyingParticle }) as FullClause + ), ) .filter(filter(FULL_CLAUSE_RULE)); } diff --git a/src/ast.ts b/src/ast.ts index 8bedba9..b4a763c 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -2,13 +2,17 @@ import { UnreachableError } from "./error.ts"; -export type Marker = +export type ModifyingParticle = | { type: "word"; word: string } | { type: "long word"; word: string; length: number } | { type: "multiple a"; count: number }; /** Represents a word unit. */ export type WordUnit = - | { type: "default"; word: string; marker: null | Marker } + | { + type: "default"; + word: string; + modifyingParticle: null | ModifyingParticle; + } | { type: "x ala x"; word: string } | { type: "reduplication"; word: string; count: number } | { type: "numbers"; numbers: Array }; @@ -28,14 +32,14 @@ export type Phrase = type: "default"; headWord: WordUnit; modifiers: Array; - marker: null | Marker; + modifyingParticle: null | ModifyingParticle; } | { type: "preverb"; preverb: WordUnit; modifiers: Array; phrase: Phrase; - marker: null | Marker; + modifyingParticle: null | ModifyingParticle; } | { type: "preposition"; preposition: Preposition } | { type: "quotation"; quotation: Quotation }; @@ -50,7 +54,7 @@ export type Preposition = { modifiers: Array; /** This cannot be an "and conjunction": only "anu" or "single". */ phrases: MultiplePhrases; - marker: null | Marker; + modifyingParticle: null | ModifyingParticle; }; /** Represents multiple predicates. */ export type MultiplePredicates = @@ -82,10 +86,10 @@ export type Clause = | { type: "quotation"; quotation: Quotation }; export type Preclause = | { type: "taso"; taso: WordUnit } - | { type: "marker"; marker: Marker }; + | { type: "modifying particle"; modifyingParticle: ModifyingParticle }; export type Postclause = | { type: "anu seme"; seme: WordUnit } - | { type: "marker"; marker: Marker }; + | { type: "modifying particle"; modifyingParticle: ModifyingParticle }; /** Represents a clause including preclause and postclause. */ export type FullClause = | { @@ -94,7 +98,7 @@ export type FullClause = postclause: null | Postclause; clause: Clause; } - | { type: "marker"; marker: Marker }; + | { type: "modifying particle"; modifyingParticle: ModifyingParticle }; /** Represents a single full sentence. */ export type Sentence = { laClauses: Array; diff --git a/src/filter.ts b/src/filter.ts index 6c97041..9628400 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -217,10 +217,10 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ } return true; }, - // If the phrase has no modifiers, avoid marker + // If the phrase has no modifiers, avoid modifying particle (phrase) => { if ( - phrase.type === "default" && phrase.marker !== null && + phrase.type === "default" && phrase.modifyingParticle !== null && phrase.modifiers.length === 0 ) { throw new CoveredError(); @@ -291,7 +291,7 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ } return true; }, - // If the clause is just a single phrase, avoid post markers + // If the clause is just a single phrase, avoid post modifying particles (fullClause) => { if ( fullClause.type === "default" && fullClause.postclause !== null && From 6be4d39657107338e898c79bcc5ad61a940a3e15 Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 18:14:06 +0800 Subject: [PATCH 316/738] filter "n" used as modifier --- src/filter.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/filter.ts b/src/filter.ts index 9628400..71666fb 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -51,6 +51,19 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ return true; } }, + // "n" cannot modify a word + (wordUnit) => { + if ( + wordUnit.type === "default" && wordUnit.modifyingParticle && + ((wordUnit.modifyingParticle.type === "word" && + wordUnit.modifyingParticle.word === "n") || + (wordUnit.modifyingParticle.type === "long word" && + wordUnit.modifyingParticle.word === "n")) + ) { + throw new UnrecognizedError('"n" modifying a word'); + } + return true; + }, ]; /** Array of filter rules for a single modifier. */ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ @@ -227,6 +240,19 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ } return true; }, + // "n" cannot modify a phrase + (phrase) => { + if ( + phrase.type === "default" && phrase.modifyingParticle && + ((phrase.modifyingParticle.type === "word" && + phrase.modifyingParticle.word === "n") || + (phrase.modifyingParticle.type === "long word" && + phrase.modifyingParticle.word === "n")) + ) { + throw new UnrecognizedError('"n" modifying a phrase'); + } + return true; + }, ]; /** Array of filter rules for preposition. */ export const PREPOSITION_RULE: Array<(phrase: Preposition) => boolean> = [ From 0e600e2487843c0114db9f44df293934b1e63ddf Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 18:17:12 +0800 Subject: [PATCH 317/738] format comments --- src/ast-parser.ts | 6 +++--- src/ast.ts | 2 +- src/filter.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 2924e26..c9e314f 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -452,7 +452,7 @@ function nestedPhrases( ); } } -/** Parses phrases separated by _en_ or _anu_. */ +/** Parses phrases separated by "en" or "anu". */ function subjectPhrases(): AstParser { return choice( nestedPhrasesOnly(["en", "anu"]), @@ -575,7 +575,7 @@ function associatedPredicates( } }); } -/** Parses multiple predicates without _li_ nor _o_ at the beginning. */ +/** Parses multiple predicates without "li" nor "o" at the beginning. */ function multiplePredicates( nestingRule: Array<"li" | "o" | "anu">, ): AstParser { @@ -738,7 +738,7 @@ function fullClause(): AstParser { ) .filter(filter(FULL_CLAUSE_RULE)); } -/** parses _la_ with optional comma around. */ +/** parses "la" with optional comma around. */ function la(): AstParser { return choice( comma().with(specificWord("la")), diff --git a/src/ast.ts b/src/ast.ts index b4a763c..1453ffc 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -43,7 +43,7 @@ export type Phrase = } | { type: "preposition"; preposition: Preposition } | { type: "quotation"; quotation: Quotation }; -/** Represents multiple phrases separated by repeated particle or _anu_. */ +/** Represents multiple phrases separated by repeated particle or "anu". */ export type MultiplePhrases = | { type: "single"; phrase: Phrase } | { type: "and conjunction"; phrases: Array } diff --git a/src/filter.ts b/src/filter.ts index 71666fb..b83b81f 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -208,7 +208,7 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ } return true; }, - // Disallow preverb modifiers other than _ala_ + // Disallow preverb modifiers other than "ala" (phrase) => { if (phrase.type === "preverb" && !modifiersIsAlaOrNone(phrase.modifiers)) { throw new UnrecognizedError('preverb with modifiers other than "ala"'); @@ -256,7 +256,7 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ ]; /** Array of filter rules for preposition. */ export const PREPOSITION_RULE: Array<(phrase: Preposition) => boolean> = [ - // Disallow preverb modifiers other than _ala_ + // Disallow preverb modifiers other than "ala" (preposition) => { if (!modifiersIsAlaOrNone(preposition.modifiers)) { throw new UnrecognizedError('preverb with modifiers other than "ala"'); @@ -356,7 +356,7 @@ function modifierIsNumeric(modifier: Modifier): boolean { return false; } /** - * Helper function for checking if the modifiers is exactly just _ala_ or nothing. + * Helper function for checking if the modifiers is exactly just "ala" or nothing. */ function modifiersIsAlaOrNone(modifiers: Array): boolean { if (modifiers.length > 1) { From 8c876364299f5afd59603fa4f8303e392d40783c Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 19:31:49 +0800 Subject: [PATCH 318/738] small change --- src/filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index b83b81f..9549793 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -233,7 +233,7 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ // If the phrase has no modifiers, avoid modifying particle (phrase) => { if ( - phrase.type === "default" && phrase.modifyingParticle !== null && + phrase.type === "default" && phrase.modifyingParticle != null && phrase.modifiers.length === 0 ) { throw new CoveredError(); @@ -320,7 +320,7 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // If the clause is just a single phrase, avoid post modifying particles (fullClause) => { if ( - fullClause.type === "default" && fullClause.postclause !== null && + fullClause.type === "default" && fullClause.postclause != null && fullClause.clause.type === "phrases" && fullClause.clause.phrases.type === "single" ) { From c0b6791711d0111d3124cef63374197abd0ceceb Mon Sep 17 00:00:00 2001 From: neverRare Date: Mon, 29 Apr 2024 19:33:28 +0800 Subject: [PATCH 319/738] small update --- src/filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 9549793..a1f9206 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -54,7 +54,7 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ // "n" cannot modify a word (wordUnit) => { if ( - wordUnit.type === "default" && wordUnit.modifyingParticle && + wordUnit.type === "default" && wordUnit.modifyingParticle != null && ((wordUnit.modifyingParticle.type === "word" && wordUnit.modifyingParticle.word === "n") || (wordUnit.modifyingParticle.type === "long word" && @@ -243,7 +243,7 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ // "n" cannot modify a phrase (phrase) => { if ( - phrase.type === "default" && phrase.modifyingParticle && + phrase.type === "default" && phrase.modifyingParticle != null && ((phrase.modifyingParticle.type === "word" && phrase.modifyingParticle.word === "n") || (phrase.modifyingParticle.type === "long word" && From c471bc6e0ccca6151e7ee3fb3d3b828f2676c089 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 07:49:00 +0800 Subject: [PATCH 320/738] update filter --- src/filter.ts | 66 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index a1f9206..5a28f5e 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -53,14 +53,16 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ }, // "n" cannot modify a word (wordUnit) => { - if ( - wordUnit.type === "default" && wordUnit.modifyingParticle != null && - ((wordUnit.modifyingParticle.type === "word" && - wordUnit.modifyingParticle.word === "n") || - (wordUnit.modifyingParticle.type === "long word" && - wordUnit.modifyingParticle.word === "n")) - ) { - throw new UnrecognizedError('"n" modifying a word'); + if (wordUnit.type === "default") { + const modifyingParticle = wordUnit.modifyingParticle; + const hasN = modifyingParticle != null && + ((modifyingParticle.type === "word" && + modifyingParticle.word === "n") || + (modifyingParticle.type === "long word" && + modifyingParticle.word === "n")); + if (hasN) { + throw new UnrecognizedError('"n" modifying a word'); + } } return true; }, @@ -242,14 +244,17 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ }, // "n" cannot modify a phrase (phrase) => { - if ( - phrase.type === "default" && phrase.modifyingParticle != null && - ((phrase.modifyingParticle.type === "word" && - phrase.modifyingParticle.word === "n") || - (phrase.modifyingParticle.type === "long word" && - phrase.modifyingParticle.word === "n")) - ) { - throw new UnrecognizedError('"n" modifying a phrase'); + if (phrase.type === "default") { + const modifyingParticle = phrase.modifyingParticle; + if ( + modifyingParticle != null && + ((modifyingParticle.type === "word" && + modifyingParticle.word === "n") || + (modifyingParticle.type === "long word" && + modifyingParticle.word === "n")) + ) { + throw new UnrecognizedError('"n" modifying a phrase'); + } } return true; }, @@ -308,22 +313,37 @@ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // Prevent "taso ala taso" (fullClause) => { - if ( - fullClause.type === "default" && fullClause.preclause && - fullClause.preclause.type === "taso" && - fullClause.preclause.taso.type === "x ala x" - ) { - throw new UnrecognizedError('"taso ala taso"'); + if (fullClause.type === "default") { + const preclause = fullClause.preclause; + if ( + preclause != null && + preclause.type === "taso" && + preclause.taso.type === "x ala x" + ) { + throw new UnrecognizedError('"taso ala taso"'); + } } return true; }, // If the clause is just a single phrase, avoid post modifying particles + // unless it is "n" (fullClause) => { + // TODO: clean this mess if ( - fullClause.type === "default" && fullClause.postclause != null && + fullClause.type === "default" && + fullClause.postclause != null && + fullClause.postclause.type === "modifying particle" && fullClause.clause.type === "phrases" && fullClause.clause.phrases.type === "single" ) { + const modifyingParticle = fullClause.postclause.modifyingParticle; + if ( + (modifyingParticle.type === "word" || + modifyingParticle.type === "long word") && + modifyingParticle.word === "n" + ) { + return true; + } throw new CoveredError(); } return true; From 0c59d02b551ec04ba02beea7aeddbdfb9c5e20d9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 08:32:35 +0800 Subject: [PATCH 321/738] use switch statement --- bundle.ts | 52 +++++---- src/ast.ts | 95 ++++++++-------- src/filter.ts | 65 +++++------ src/lexer.ts | 33 +++--- src/old-translator.ts | 250 ++++++++++++++++++++++-------------------- src/settings.ts | 13 ++- src/token-tree.ts | 47 ++++---- src/translator.ts | 46 ++++---- 8 files changed, 315 insertions(+), 286 deletions(-) diff --git a/bundle.ts b/bundle.ts index 57da875..327a1ee 100644 --- a/bundle.ts +++ b/bundle.ts @@ -8,30 +8,34 @@ async function build(options: Emit.BundleOptions): Promise { const { code } = result; await Deno.writeTextFile(DESTINATION, code); } -if (Deno.args[0] === "build") { - console.log("Building telo misikeke..."); - await TeloMisikeke.build(); - console.log("Building main.js..."); - await build({ minify: true, type: "classic" }); - console.log("Building done!"); -} else if (Deno.args[0] === "watch") { - const builder = Debounce.debounce(async () => { - console.log("Starting to build..."); - try { - await build({ - compilerOptions: { inlineSourceMap: true }, - type: "classic", - }); - console.log("Building done!"); - } catch (error) { - console.error(error); - } - }, 500); - const watcher = Deno.watchFs(["./src/", "./telo-misikeke/"]); - builder(); - for await (const _ of watcher) { +switch (Deno.args[0]) { + case "build": + console.log("Building telo misikeke..."); + await TeloMisikeke.build(); + console.log("Building main.js..."); + await build({ minify: true, type: "classic" }); + console.log("Building done!"); + break; + case "watch": { + const builder = Debounce.debounce(async () => { + console.log("Starting to build..."); + try { + await build({ + compilerOptions: { inlineSourceMap: true }, + type: "classic", + }); + console.log("Building done!"); + } catch (error) { + console.error(error); + } + }, 500); + const watcher = Deno.watchFs(["./src/", "./telo-misikeke/"]); builder(); + for await (const _ of watcher) { + builder(); + } + throw new Error("unreachable"); } -} else { - throw new Error(`Unrecognized build option, ${Deno.args[0]}`); + default: + throw new Error(`Unrecognized build option, ${Deno.args[0]}`); } diff --git a/src/ast.ts b/src/ast.ts index 1453ffc..d094d09 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,7 +1,6 @@ /** Module for describing Toki Pona AST. */ -import { UnreachableError } from "./error.ts"; - +/** */ export type ModifyingParticle = | { type: "word"; word: string } | { type: "long word"; word: string; length: number } @@ -123,23 +122,23 @@ export function someModifierInPhrase( whenQuotation: boolean, checker: (modifier: Modifier) => boolean, ): boolean { - if (phrase.type === "default") { - return phrase.modifiers.some(checker); - } else if (phrase.type === "preverb") { - return phrase.modifiers.some(checker) || - someModifierInPhrase(phrase.phrase, whenQuotation, checker); - } else if (phrase.type === "preposition") { - const preposition = phrase.preposition; - return preposition.modifiers.some(checker) || - someModifierInMultiplePhrases( - preposition.phrases, - whenQuotation, - checker, - ); - } else if (phrase.type === "quotation") { - return whenQuotation; - } else { - throw new UnreachableError(); + switch (phrase.type) { + case "default": + return phrase.modifiers.some(checker); + case "preverb": + return phrase.modifiers.some(checker) || + someModifierInPhrase(phrase.phrase, whenQuotation, checker); + case "preposition": { + const preposition = phrase.preposition; + return preposition.modifiers.some(checker) || + someModifierInMultiplePhrases( + preposition.phrases, + whenQuotation, + checker, + ); + } + case "quotation": + return whenQuotation; } } /** @@ -151,15 +150,15 @@ export function someModifierInMultiplePhrases( whenQuotation: boolean, checker: (modifier: Modifier) => boolean, ): boolean { - if (phrases.type === "single") { - return someModifierInPhrase(phrases.phrase, whenQuotation, checker); - } else if (phrases.type === "and conjunction" || phrases.type === "anu") { - return phrases.phrases - .some((phrases) => - someModifierInMultiplePhrases(phrases, whenQuotation, checker) - ); - } else { - throw new UnreachableError(); + switch (phrases.type) { + case "single": + return someModifierInPhrase(phrases.phrase, whenQuotation, checker); + case "and conjunction": + case "anu": + return phrases.phrases + .some((phrases) => + someModifierInMultiplePhrases(phrases, whenQuotation, checker) + ); } } /** @@ -170,13 +169,13 @@ export function somePhraseInMultiplePhrases( phrases: MultiplePhrases, checker: (modifier: Phrase) => boolean, ): boolean { - if (phrases.type === "single") { - return checker(phrases.phrase); - } else if (phrases.type === "and conjunction" || phrases.type === "anu") { - return phrases.phrases - .some((phrases) => somePhraseInMultiplePhrases(phrases, checker)); - } else { - throw new UnreachableError(); + switch (phrases.type) { + case "single": + return checker(phrases.phrase); + case "and conjunction": + case "anu": + return phrases.phrases + .some((phrases) => somePhraseInMultiplePhrases(phrases, checker)); } } /** @@ -187,18 +186,20 @@ export function someObjectInMultiplePredicate( predicate: MultiplePredicates, checker: (object: Phrase) => boolean, ): boolean { - if (predicate.type === "single") { - return false; - } else if (predicate.type === "associated") { - if (predicate.objects) { - return somePhraseInMultiplePhrases(predicate.objects, checker); - } else { + switch (predicate.type) { + case "single": return false; - } - } else if (predicate.type === "and conjunction" || predicate.type === "anu") { - return predicate.predicates - .some((predicates) => someObjectInMultiplePredicate(predicates, checker)); - } else { - throw new UnreachableError(); + case "associated": + if (predicate.objects) { + return somePhraseInMultiplePhrases(predicate.objects, checker); + } else { + return false; + } + case "and conjunction": + case "anu": + return predicate.predicates + .some((predicates) => + someObjectInMultiplePredicate(predicates, checker) + ); } } diff --git a/src/filter.ts b/src/filter.ts index 5a28f5e..9d09577 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -13,7 +13,7 @@ import { somePhraseInMultiplePhrases, WordUnit, } from "./ast.ts"; -import { CoveredError, UnreachableError, UnrecognizedError } from "./error.ts"; +import { CoveredError, UnrecognizedError } from "./error.ts"; import { settings } from "./settings.ts"; /** Array of filter rules for a word unit. */ @@ -146,17 +146,15 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ // pi cannot be nested (modifier) => { const checker = (modifier: Modifier) => { - if ( - modifier.type === "default" || modifier.type === "proper words" || - modifier.type === "quotation" - ) { - return false; - } else if (modifier.type === "nanpa") { - return someModifierInPhrase(modifier.phrase, false, checker); - } else if (modifier.type === "pi") { - return true; - } else { - throw new UnreachableError(); + switch (modifier.type) { + case "default": + case "proper words": + case "quotation": + return false; + case "nanpa": + return someModifierInPhrase(modifier.phrase, false, checker); + case "pi": + return true; } }; if (modifier.type === "pi") { @@ -283,16 +281,22 @@ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ // disallow preposition in subject (clause) => { let phrases: MultiplePhrases; - if (clause.type === "phrases" || clause.type === "o vocative") { - phrases = clause.phrases; - } else if (clause.type === "li clause" || clause.type === "o clause") { - if (clause.subjects) { - phrases = clause.subjects; - } else { + switch (clause.type) { + case "phrases": + case "o vocative": + phrases = clause.phrases; + break; + case "li clause": + case "o clause": + if (clause.subjects) { + phrases = clause.subjects; + } else { + return true; + } + break; + case "prepositions": + case "quotation": return true; - } - } else { - return true; } if (somePhraseInMultiplePhrases(phrases, hasPrepositionInPhrase)) { throw new UnrecognizedError("Preposition in subject"); @@ -392,15 +396,14 @@ function modifiersIsAlaOrNone(modifiers: Array): boolean { * Helper function for determining whether the phrase has a preposition inside. */ function hasPrepositionInPhrase(phrase: Phrase): boolean { - if (phrase.type === "default") { - return false; - } else if (phrase.type === "preposition") { - return true; - } else if (phrase.type === "preverb") { - return hasPrepositionInPhrase(phrase.phrase); - } else if (phrase.type === "quotation") { - return false; - } else { - throw new UnreachableError(); + switch (phrase.type) { + case "default": + return false; + case "preposition": + return true; + case "preverb": + return hasPrepositionInPhrase(phrase.phrase); + case "quotation": + return false; } } diff --git a/src/lexer.ts b/src/lexer.ts index 7a42be7..ffdc3b6 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -342,19 +342,26 @@ function quotation( closeQuotationMark(), ) .map(([leftMark, tokenTree, rightMark]) => { - if (leftMark === '"' || leftMark === "“") { - if (rightMark !== '"' && rightMark !== "”") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else if (leftMark === "«") { - if (rightMark !== "»") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else if (leftMark === "「") { - if (rightMark !== "」") { - throw new UnrecognizedError("Mismatched quotation marks"); - } - } else throw new UnreachableError(); + switch (leftMark) { + case '"': + case "“": + if (rightMark !== '"' && rightMark !== "”") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + break; + case "«": + if (rightMark !== "»") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + break; + case "「": + if (rightMark !== "」") { + throw new UnrecognizedError("Mismatched quotation marks"); + } + break; + default: + throw new UnreachableError(); + } return { type: "quotation", tokenTree, leftMark, rightMark }; }); } diff --git a/src/old-translator.ts b/src/old-translator.ts index e1334bb..a0855b9 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -45,36 +45,38 @@ function wordUnitAs( kind: "noun" | "adjective" | "adverb", word: WordUnit, ): TranslationOutput { - if (word.type === "default") { - return definition(kind, word.word); - } else if (word.type === "numbers") { - return new Output([number(word.numbers).toString()]); - } else if (word.type === "reduplication") { - return definition(kind, word.word) - .map((noun) => new Array(word.count).fill(noun).join(" ")); - } else if (word.type === "x ala x") { - return new Output(new TodoError("translation for X ala X")); - } else { - throw new UnreachableError(); + switch (word.type) { + case "default": + return definition(kind, word.word); + case "numbers": + return new Output([number(word.numbers).toString()]); + case "reduplication": + return definition(kind, word.word) + .map((noun) => new Array(word.count).fill(noun).join(" ")); + case "x ala x": + return new Output(new TodoError("translation for X ala X")); } } function modifierAs( kind: "noun" | "adjective" | "adverb", modifier: Modifier, ): TranslationOutput { - if (modifier.type === "default") { - return wordUnitAs(kind, modifier.word); - } else if (modifier.type === "nanpa" || modifier.type === "proper words") { - return new Output(); - } else if (modifier.type === "pi") { - if (kind === "adverb") { + switch (modifier.type) { + case "default": + return wordUnitAs(kind, modifier.word); + case "nanpa": + case "proper words": return new Output(); - } - return phraseAs(kind, modifier.phrase, { named: false, suffix: false }); - } else { - return new Output( - new TodoError(`translating ${modifier.type} as adjective`), - ); + case "pi": + if (kind === "adverb") { + return new Output(); + } + return phraseAs(kind, modifier.phrase, { named: false, suffix: false }); + // case "quotation": + default: + return new Output( + new TodoError(`translating ${modifier.type} as adjective`), + ); } } function modifierAsSuffix( @@ -87,27 +89,29 @@ function modifierAsSuffix( } else { construction = "in X way"; } - if (suffix.type === "default") { - return wordUnitAs(kind, suffix.word) - .map((translation) => construction.replace("X", translation)); - } else if (suffix.type === "nanpa") { - return phraseAs(kind, suffix.phrase, { - named: kind === "noun", - suffix: false, - }) - .map((translation) => `in position ${translation}`); - } else if (suffix.type === "pi") { - return phraseAs(kind, suffix.phrase, { - named: kind === "noun", - suffix: false, - }) - .map((translation) => construction.replace("X", translation)); - } else if (suffix.type === "proper words") { - return new Output([`named ${suffix.words}`]); - } else { - return new Output( - new TodoError(`translation of ${suffix.type} as noun`), - ); + switch (suffix.type) { + case "default": + return wordUnitAs(kind, suffix.word) + .map((translation) => construction.replace("X", translation)); + case "nanpa": + return phraseAs(kind, suffix.phrase, { + named: kind === "noun", + suffix: false, + }) + .map((translation) => `in position ${translation}`); + case "pi": + return phraseAs(kind, suffix.phrase, { + named: kind === "noun", + suffix: false, + }) + .map((translation) => construction.replace("X", translation)); + case "proper words": + return new Output([`named ${suffix.words}`]); + // case "quotation": + default: + return new Output( + new TodoError(`translation of ${suffix.type} as noun`), + ); } } function defaultPhraseAs( @@ -128,10 +132,13 @@ function defaultPhraseAs( return new Output(); } let modifierKind: "adjective" | "adverb"; - if (kind === "noun") { - modifierKind = "adjective"; - } else if (kind === "adjective") { - modifierKind = "adverb"; + switch (kind) { + case "noun": + modifierKind = "adjective"; + break; + case "adjective": + modifierKind = "adverb"; + break; } const headWord = wordUnitAs(kind, phrase.headWord); const modifierNoName = phrase.modifiers @@ -199,83 +206,92 @@ function translateMultiplePhrases( translator: (phrase: Phrase) => TranslationOutput, level = 2, ): TranslationOutput { - if (phrases.type === "single") { - return translator(phrases.phrase); - } else if (phrases.type === "and conjunction" || phrases.type === "anu") { - let conjunction: string; - if (phrases.type === "and conjunction") { - conjunction = "and"; - } else { - conjunction = "or"; - } - const translations = Output.combine( - ...phrases.phrases.map((phrases) => - translateMultiplePhrases(phrases, translator, level - 1) - ), - ); - if (level === 2) { - return translations.map((phrases) => { - if (phrases.length === 2) { - return [phrases[0], conjunction, phrases[1]].join(" "); - } else { - const comma = phrases.slice(0, phrases.length - 1); - const last = phrases[phrases.length - 1]; - return [ - comma.map((translation) => [translation, ", "].join("")).join(""), - conjunction, - " ", - last, - ] - .join(""); - } - }); - } else if (level === 1) { - return translations - .map((phrases) => phrases.join([" ", conjunction, " "].join(""))); - } else { - throw new UnreachableError(); + switch (phrases.type) { + case "single": + return translator(phrases.phrase); + case "and conjunction": + case "anu": { + let conjunction: string; + if (phrases.type === "and conjunction") { + conjunction = "and"; + } else { + conjunction = "or"; + } + const translations = Output.combine( + ...phrases.phrases.map((phrases) => + translateMultiplePhrases(phrases, translator, level - 1) + ), + ); + switch (level) { + case 2: + return translations.map((phrases) => { + if (phrases.length === 2) { + return [phrases[0], conjunction, phrases[1]].join(" "); + } else { + const comma = phrases.slice(0, phrases.length - 1); + const last = phrases[phrases.length - 1]; + return [ + comma.map((translation) => [translation, ", "].join("")).join( + "", + ), + conjunction, + " ", + last, + ] + .join(""); + } + }); + case 1: + return translations + .map((phrases) => phrases.join([" ", conjunction, " "].join(""))); + default: + throw new UnreachableError(); + } } - } else { - throw new UnreachableError(); } } /** Translates a clause. */ function translateClause(clause: Clause): TranslationOutput { - if (clause.type === "phrases") { - const hasEn = (phrases: MultiplePhrases): boolean => { - if (phrases.type === "single") { - return false; - } else if (phrases.type === "and conjunction") { - return true; - } else if (phrases.type === "anu") { - return phrases.phrases.some(hasEn); + switch (clause.type) { + case "phrases": { + const hasEn = (phrases: MultiplePhrases): boolean => { + switch (phrases.type) { + case "single": + return false; + case "and conjunction": + return true; + case "anu": + return phrases.phrases.some(hasEn); + } + }; + const phrases = clause.phrases; + const translations = translateMultiplePhrases( + phrases, + (phrase) => phraseAs("noun", phrase), + ); + if (hasEn(phrases)) { + return translations; } else { - throw new UnreachableError(); + return Output.concat( + translateMultiplePhrases( + phrases, + (phrase) => phraseAs("adjective", phrase), + ), + translations, + ); } - }; - const phrases = clause.phrases; - const translations = translateMultiplePhrases( - phrases, - (phrase) => phraseAs("noun", phrase), - ); - if (hasEn(phrases)) { - return translations; - } else { - return Output.concat( - translateMultiplePhrases( - phrases, - (phrase) => phraseAs("adjective", phrase), - ), - translations, - ); } - } else if (clause.type === "o vocative") { - return translateMultiplePhrases( - clause.phrases, - (phrase) => phraseAs("noun", phrase).map((phrase) => `hey ${phrase}`), - ); - } else { - return new Output(new TodoError(`translation for ${clause.type}`)); + case "o vocative": + return translateMultiplePhrases( + clause.phrases, + (phrase) => phraseAs("noun", phrase).map((phrase) => `hey ${phrase}`), + ); + // case "li clause": + // case "o clause": + // case "prepositions": + // case "quotation": + default: + return new Output(new TodoError(`translation for ${clause.type}`)); } } /** Translates a full clause. */ diff --git a/src/settings.ts b/src/settings.ts index 7a71658..add0624 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -123,12 +123,13 @@ class Setter { } const boolUpdater: Updater = { parse: (value) => { - if (value === "T") { - return true; - } else if (value === "F") { - return false; - } else { - return null; + switch (value) { + case "T": + return true; + case "F": + return false; + default: + return null; } }, stringify: (value) => { diff --git a/src/token-tree.ts b/src/token-tree.ts index 59659db..6a3ed2a 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -3,8 +3,6 @@ * stores array of token trees hence the name token tree. */ -import { UnreachableError } from "./error.ts"; - /** * Represents token tree. */ @@ -43,28 +41,27 @@ export type TokenTree = | { type: "punctuation"; punctuation: string }; export function describe(tokenTree: TokenTree): string { - if (tokenTree.type === "word") { - return `"${tokenTree.word}"`; - } else if (tokenTree.type === "combined glyphs") { - return `combined glyphs "${tokenTree.words.join(" ")}"`; - } else if ( - tokenTree.type === "long glyph" || - tokenTree.type === "long glyph space" - ) { - return "long glyph"; - } else if (tokenTree.type === "multiple a") { - return new Array(tokenTree.count).fill("a").join(" "); - } else if (tokenTree.type === "x ala x") { - return `"${tokenTree.word} ala ${tokenTree.word}"`; - } else if (tokenTree.type === "proper word") { - return "proper word or cartouche"; - } else if (tokenTree.type === "quotation") { - return "quotation"; - } else if (tokenTree.type === "comma") { - return "comma"; - } else if (tokenTree.type === "punctuation") { - return "punctuation mark"; - } else { - throw new UnreachableError(); + switch (tokenTree.type) { + case "word": + return `"${tokenTree.word}"`; + case "combined glyphs": + return `combined glyphs "${tokenTree.words.join(" ")}"`; + case "long glyph": + case "long glyph space": + case "underline lon": + return "long glyph"; + case "multiple a": + return new Array(tokenTree.count).fill("a").join(" "); + case "long word": + case "x ala x": + return `"${tokenTree.word} ala ${tokenTree.word}"`; + case "proper word": + return "proper word or cartouche"; + case "quotation": + return "quotation"; + case "comma": + return "comma"; + case "punctuation": + return "punctuation mark"; } } diff --git a/src/translator.ts b/src/translator.ts index f1cd2ad..7e5594d 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -6,7 +6,7 @@ import { SPECIAL_CONTENT_WORD_DEFINITION, } from "./dictionary.ts"; import * as English from "./english-ast.ts"; -import { TodoError, UnreachableError } from "./error.ts"; +import { TodoError } from "./error.ts"; import { Output } from "./output.ts"; function sentence(sentence: TokiPona.Sentence): Output { @@ -15,28 +15,28 @@ function sentence(sentence: TokiPona.Sentence): Output { function multipleSentences( sentences: TokiPona.MultipleSentences, ): Output> { - if (sentences.type === "single word") { - const word = sentences.word; - return new Output([ - ...PARTICLE_DEFINITION[word] ?? [], - ...SPECIAL_CONTENT_WORD_DEFINITION[word] ?? [], - ...PREPOSITION_DEFINITION[word] ?? [], - // TODO: Preverb - // TODO: Content word definition - ]) - .map((definition) => ({ - dependentClauses: [], - independentClause: { - type: "free form", - text: definition, - } as English.Clause, - punctuation: "", - })) - .map((definition) => [definition]); - } else if (sentences.type === "sentences") { - return Output.combine(...sentences.sentences.map(sentence)); - } else { - throw new UnreachableError(); + switch (sentences.type) { + case "single word": { + const word = sentences.word; + return new Output([ + ...PARTICLE_DEFINITION[word] ?? [], + ...SPECIAL_CONTENT_WORD_DEFINITION[word] ?? [], + ...PREPOSITION_DEFINITION[word] ?? [], + // TODO: Preverb + // TODO: Content word definition + ]) + .map((definition) => ({ + dependentClauses: [], + independentClause: { + type: "free form", + text: definition, + } as English.Clause, + punctuation: "", + })) + .map((definition) => [definition]); + } + case "sentences": + return Output.combine(...sentences.sentences.map(sentence)); } } export function translate(src: string): Output> { From b5f39781d1003844e404cbed5f4a9617bfb33645 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 08:50:28 +0800 Subject: [PATCH 322/738] fix duplicate code and few more --- src/ast-parser.ts | 49 ++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index c9e314f..5747242 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -143,53 +143,42 @@ function modifyingParticle(): AstParser { .map((word) => ({ type: "word", word }) as ModifyingParticle), ); } +function parseXAlaXSide(tokenTrees: Array, name: string): TokenTree { + if (tokenTrees.length !== 1) { + if (tokenTrees.length === 0) { + throw new UnexpectedError(name, "long glyph on both sides"); + } else { + throw new UnexpectedError( + describe(tokenTrees[0]), + "end of long glyph", + ); + } + } + return tokenTrees[0]; +} function xAlaX( word: Set, description: string, ): AstParser { return choice( specificTokenTree("long glyph").map((longGlyph) => { - // TODO: reduce code duplication - if (longGlyph.words.length !== 1 || longGlyph.words[0] !== "ala") { + if (longGlyph.words.length !== 1) { throw new UnexpectedError( describe({ type: "combined glyphs", words: longGlyph.words }), '"ala"', ); } - if (longGlyph.before.length !== 1) { - if (longGlyph.before.length === 0) { - throw new UnexpectedError( - "forward long glyph", - "long glyph on both sides", - ); - } else { - throw new UnexpectedError( - describe(longGlyph.before[0]), - "end of long glyph", - ); - } + if (longGlyph.words[0] !== "ala") { + throw new UnexpectedError(`"${longGlyph.words[0]}"`, '"ala"'); } - const leftGlyph = longGlyph.before[0]; + const leftGlyph = parseXAlaXSide(longGlyph.before, "backward long glyph"); if (leftGlyph.type !== "word") { throw new UnexpectedError(describe(leftGlyph), "word"); } const word = leftGlyph.word; - if (longGlyph.after.length !== 1) { - if (longGlyph.after.length === 0) { - throw new UnexpectedError( - "backwards long glyph", - "long glyph on both sides", - ); - } else { - throw new UnexpectedError( - describe(longGlyph.after[0]), - "end of long glyph", - ); - } - } - const rightGlyph = longGlyph.after[0]; + const rightGlyph = parseXAlaXSide(longGlyph.after, "forward long glyph"); if (rightGlyph.type !== "word" || rightGlyph.word !== word) { - throw new UnexpectedError(describe(leftGlyph), `"${word}"`); + throw new UnexpectedError(describe(rightGlyph), `"${word}"`); } return { type: "x ala x", word } as WordUnit & { type: "x ala x" }; }), From 77fc077199be48c176e76a8f5be5a80f3e190926 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:05:08 +0800 Subject: [PATCH 323/738] small refactoring --- src/filter.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 9d09577..2a52b76 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -332,13 +332,10 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // If the clause is just a single phrase, avoid post modifying particles // unless it is "n" (fullClause) => { - // TODO: clean this mess if ( fullClause.type === "default" && fullClause.postclause != null && - fullClause.postclause.type === "modifying particle" && - fullClause.clause.type === "phrases" && - fullClause.clause.phrases.type === "single" + fullClause.postclause.type === "modifying particle" ) { const modifyingParticle = fullClause.postclause.modifyingParticle; if ( @@ -348,7 +345,12 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ ) { return true; } - throw new CoveredError(); + if ( + fullClause.clause.type === "phrases" && + fullClause.clause.phrases.type === "single" + ) { + throw new CoveredError(); + } } return true; }, From da4b540d12518a2bfcaf57820d82e7d12b29961d Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:07:24 +0800 Subject: [PATCH 324/738] small update --- src/ast-parser.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 5747242..e11b045 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -268,14 +268,14 @@ function number(): AstParser> { } function pi(): AstParser { return choice( - specificTokenTree("long glyph").flatMapValue( + specificTokenTree("long glyph").flatMapValue( (longGlyph) => { if (longGlyph.before.length !== 0) { return new Output( new UnexpectedError("reverse long glyph", "long pi"), ); } - if (longGlyph.words.length !== 1 || longGlyph.words[0] !== "pi") { + if (longGlyph.words.length !== 1) { return new Output( new UnexpectedError( describe({ type: "combined glyphs", words: longGlyph.words }), @@ -283,6 +283,11 @@ function pi(): AstParser { ), ); } + if (longGlyph.words[0] !== "pi") { + return new Output( + new UnexpectedError(`"${longGlyph.words[0]}"`, "pi"), + ); + } return INNER_PHRASE_PARSER.parse(longGlyph.after); }, ) From 701179c3bd622ad9488fd2e51bbe48b799c86740 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:30:20 +0800 Subject: [PATCH 325/738] format --- src/ast-parser.ts | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e11b045..19fc6fb 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -268,29 +268,30 @@ function number(): AstParser> { } function pi(): AstParser { return choice( - specificTokenTree("long glyph").flatMapValue( - (longGlyph) => { - if (longGlyph.before.length !== 0) { - return new Output( - new UnexpectedError("reverse long glyph", "long pi"), - ); - } - if (longGlyph.words.length !== 1) { - return new Output( - new UnexpectedError( - describe({ type: "combined glyphs", words: longGlyph.words }), - "pi", - ), - ); - } - if (longGlyph.words[0] !== "pi") { - return new Output( - new UnexpectedError(`"${longGlyph.words[0]}"`, "pi"), - ); - } - return INNER_PHRASE_PARSER.parse(longGlyph.after); - }, - ) + specificTokenTree("long glyph") + .flatMapValue( + (longGlyph) => { + if (longGlyph.before.length !== 0) { + return new Output( + new UnexpectedError("reverse long glyph", "long pi"), + ); + } + if (longGlyph.words.length !== 1) { + return new Output( + new UnexpectedError( + describe({ type: "combined glyphs", words: longGlyph.words }), + "pi", + ), + ); + } + if (longGlyph.words[0] !== "pi") { + return new Output( + new UnexpectedError(`"${longGlyph.words[0]}"`, "pi"), + ); + } + return INNER_PHRASE_PARSER.parse(longGlyph.after); + }, + ) .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }), specificWord("pi") .with(phrase()) From 8fa5b16fdb1885403e3978906726b3f659523f75 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:31:48 +0800 Subject: [PATCH 326/738] reduce duplicate codes --- src/ast-parser.ts | 52 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 19fc6fb..47e20b0 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -268,35 +268,33 @@ function number(): AstParser> { } function pi(): AstParser { return choice( - specificTokenTree("long glyph") - .flatMapValue( - (longGlyph) => { - if (longGlyph.before.length !== 0) { - return new Output( - new UnexpectedError("reverse long glyph", "long pi"), - ); - } - if (longGlyph.words.length !== 1) { - return new Output( - new UnexpectedError( - describe({ type: "combined glyphs", words: longGlyph.words }), - "pi", - ), - ); - } - if (longGlyph.words[0] !== "pi") { - return new Output( - new UnexpectedError(`"${longGlyph.words[0]}"`, "pi"), - ); - } - return INNER_PHRASE_PARSER.parse(longGlyph.after); - }, - ) - .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }), + specificTokenTree("long glyph").flatMapValue( + (longGlyph) => { + if (longGlyph.before.length !== 0) { + return new Output( + new UnexpectedError("reverse long glyph", "long pi"), + ); + } + if (longGlyph.words.length !== 1) { + return new Output( + new UnexpectedError( + describe({ type: "combined glyphs", words: longGlyph.words }), + "pi", + ), + ); + } + if (longGlyph.words[0] !== "pi") { + return new Output( + new UnexpectedError(`"${longGlyph.words[0]}"`, "pi"), + ); + } + return INNER_PHRASE_PARSER.parse(longGlyph.after); + }, + ), specificWord("pi") - .with(phrase()) - .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }), + .with(phrase()), ) + .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }) .filter(filter(MODIFIER_RULES)); } /** Parses multiple modifiers. */ From 64b369d5c2142f2027cf814829d1155f85cd5895 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:33:53 +0800 Subject: [PATCH 327/738] small formatting --- src/ast-parser.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 47e20b0..d74f2c6 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -291,8 +291,7 @@ function pi(): AstParser { return INNER_PHRASE_PARSER.parse(longGlyph.after); }, ), - specificWord("pi") - .with(phrase()), + specificWord("pi").with(phrase()), ) .map((phrase) => ({ type: "pi", phrase }) as Modifier & { type: "pi" }) .filter(filter(MODIFIER_RULES)); From cca590743e4bb6fe9f39d1ab96e1c89b50353ab0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:37:35 +0800 Subject: [PATCH 328/738] small formatting --- src/translator.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/translator.ts b/src/translator.ts index 7e5594d..87b9891 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -25,14 +25,13 @@ function multipleSentences( // TODO: Preverb // TODO: Content word definition ]) - .map((definition) => ({ - dependentClauses: [], - independentClause: { - type: "free form", - text: definition, - } as English.Clause, - punctuation: "", - })) + .map((definition) => + ({ + dependentClauses: [], + independentClause: { type: "free form", text: definition }, + punctuation: "", + }) as English.Sentence + ) .map((definition) => [definition]); } case "sentences": From 2364fff8505164ccc52ed7144c324cbb8d3e403d Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:48:43 +0800 Subject: [PATCH 329/738] implement long "a" and long "n" parser --- src/ast-parser.ts | 17 +++++++++++++++++ src/lexer.ts | 5 +---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index d74f2c6..501608e 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -133,6 +133,23 @@ function specificWord(thatWord: string): AstParser { } function modifyingParticle(): AstParser { return choice( + specificTokenTree("long glyph space").map((longGlyph) => { + if (longGlyph.words.length !== 1) { + throw new UnexpectedError( + describe({ type: "combined glyphs", words: longGlyph.words }), + '"ala"', + ); + } + const word = longGlyph.words[0]; + if (word !== "n" && word !== "a") { + throw new UnexpectedError(`"${word}"`, '"a" or "n"'); + } + return { + type: "long word", + word, + length: longGlyph.spaceLength, + } as ModifyingParticle; + }), specificTokenTree("multiple a") .map(({ count }) => ({ type: "multiple a", count }) as ModifyingParticle), specificTokenTree("long word") diff --git a/src/lexer.ts b/src/lexer.ts index ffdc3b6..09ba00f 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -457,10 +457,7 @@ function characterLongGlyph( }); } function longSpaceGlyph(): Lexer { - return sequence( - longGlyphHead(), - longSpaceContainer().skip(spaces()), - ) + return sequence(longGlyphHead(), longSpaceContainer()) .map(([words, spaceLength]) => ({ type: "long glyph space", words, From 493dc62cbda4fa7efbe945af762af3519355719b Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 10:58:32 +0800 Subject: [PATCH 330/738] update changelog --- CHANGELOG.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ecf6a..b76e5f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,18 @@ You may need to force restart the page in order to use the latest version: shift The latest on-development version can be accessed by building the source code. +This is a huge update, now with configurable settings, UCSUR support, and expanded vocabulary! + - Implement settings dialog. [More info](https://github.com/neverRare/ilo-token/wiki/Settings-Help). -- All possible errors will now be listed. -- ilo Token can now use telo misikeke for error messages. You can turn this on from the settings dialog. +- Changes in error messages: + - All possible errors will now be listed. + - ilo Token can now use telo misikeke for error messages. You can turn this on from the settings dialog. - Implement UCSUR support! It supports: - Cartouche with nasin sitelen kalama - Combined glyphs - Long glyphs - (Deprecated characters and combiners are not supported) -- The vocabulary has been expanded to nimi ku suli plus nimi su! (Some are still unimplemented) +- The vocabulary has been expanded to _nimi ku suli_ plus _nimi su!_ (Some are still unimplemented) - Multiline text will no longer be recognized. - Add icons. From 5dcfeb5b310f18ddf224e8c0de2a964a76fedde1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 11:02:11 +0800 Subject: [PATCH 331/738] fix typos --- src/ast-parser.ts | 2 +- src/lexer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 501608e..ae8d957 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -1,4 +1,4 @@ -/** Module for AST Parser. It is responsible for turning an array of token tress into AST. */ +/** Module for AST Parser. It is responsible for turning an array of token trees into AST. */ import { Clause, diff --git a/src/lexer.ts b/src/lexer.ts index 09ba00f..ffa380f 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -1,6 +1,6 @@ /** * Module for lexer. It is responsible for turning string into array of token - * tress. It also latinizes UCSUR characters. + * trees. It also latinizes UCSUR characters. * * Note: the words lexer and parser are used interchangeably since they both * have the same capabilities. From 56df691c5b30fa41f39fde4df0f8ede9cf8ca96a Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 11:03:41 +0800 Subject: [PATCH 332/738] update contributing --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aaa5b62..febf281 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,9 @@ You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. -Unless you want to edit the dictionary in `./src/dictionary.ts`, please claim an issue first, or open a new issue and claim it, before working on the code. This is to avoid duplicate efforts. +Unless you want to edit the dictionary in `./src/dictionary.ts` or it is just a small edit e.g. a type fix, please claim an issue first or open a new issue and claim it before working on the code. This is to avoid duplicate efforts. -If you to edit the dictionary, just go ahead and contribute! We provide useful guidelines in the wiki! +If you want to edit the dictionary, just go ahead and contribute! We provide useful guidelines in the wiki! ## [The wiki](https://github.com/neverRare/ilo-token/wiki) From cb1ad9e058f969e2a35f376ae4817b3e245b3dd5 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 11:25:44 +0800 Subject: [PATCH 333/738] more documentation comments --- src/ast-parser.ts | 16 ++++++++++++++-- src/ast.ts | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index ae8d957..89f1ec8 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -83,6 +83,7 @@ function tokenTree(description: string): AstParser { } }); } +/** Parses a specific type of token tree. */ function specificTokenTree( type: T, ): AstParser { @@ -131,6 +132,7 @@ function specificWord(thatWord: string): AstParser { else throw new UnexpectedError(`"${thisWord}"`, `"${thatWord}"`); }); } +/** Parses a modifying particle. */ function modifyingParticle(): AstParser { return choice( specificTokenTree("long glyph space").map((longGlyph) => { @@ -160,6 +162,7 @@ function modifyingParticle(): AstParser { .map((word) => ({ type: "word", word }) as ModifyingParticle), ); } +/** Parses a side of long X ala X construction. */ function parseXAlaXSide(tokenTrees: Array, name: string): TokenTree { if (tokenTrees.length !== 1) { if (tokenTrees.length === 0) { @@ -173,6 +176,7 @@ function parseXAlaXSide(tokenTrees: Array, name: string): TokenTree { } return tokenTrees[0]; } +/** Parses an X ala X construction. */ function xAlaX( word: Set, description: string, @@ -210,7 +214,7 @@ function xAlaX( ), ); } -/** Parses word unit without numbers. */ +/** Parses word unit except numbers. */ function wordUnit(word: Set, description: string): AstParser { return choice( wordFrom(word, description).then((word) => @@ -230,6 +234,7 @@ function wordUnit(word: Set, description: string): AstParser { ) .filter(filter(WORD_UNIT_RULES)); } +/** Parses a binary combined glyphs. */ function binaryWords( word: Set, description: string, @@ -246,6 +251,7 @@ function binaryWords( } }); } +/** Parses a word unit or a combined glyphs. */ function optionalCombined( word: Set, description: string, @@ -283,6 +289,7 @@ function number(): AstParser> { } }); } +/** Parses a "pi" construction. */ function pi(): AstParser { return choice( specificTokenTree("long glyph").flatMapValue( @@ -353,6 +360,7 @@ function modifiers(): AstParser> { ]) .filter(filter(MODIFIERS_RULES)); } +/** Phrase parser intended for phrases inside long glyphs. */ const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); /** Parses phrases. */ function phrase(): AstParser { @@ -708,6 +716,7 @@ function clause(): AstParser { ) .filter(filter(CLAUSE_RULE)); } +/** Parses a preclause. */ function preclause(): AstParser { return choice( modifyingParticle() @@ -718,6 +727,7 @@ function preclause(): AstParser { .map((taso) => ({ type: "taso", taso }) as Preclause), ); } +/** Parses a postclause. */ function postclause(): AstParser { return choice( modifyingParticle() @@ -771,6 +781,7 @@ function sentence(): AstParser { punctuation, })); } +/** Parses a sentence inside quotation. */ const INNER_QUOTATION_PARSER = all(sentence()) .skip(eol("end of sentence")) .filter(filter(SENTENCES_RULE)); @@ -788,6 +799,7 @@ export function quotation(): AstParser { ) ); } +/** A multiple sentence parser for final parser. */ const FULL_PARSER = choiceOnlyOne( wordFrom(TOKI_PONA_WORD, "Toki Pona word") .skip(eol("end of sentence")) @@ -799,7 +811,7 @@ const FULL_PARSER = choiceOnlyOne( ({ type: "sentences", sentences }) as MultipleSentences ), ); -/** A multiple Toki Pona sentence parser. */ +/** A Toki Pona parser. */ export function parse(src: string): Output { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/ast.ts b/src/ast.ts index d094d09..f1b6d47 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,6 +1,6 @@ /** Module for describing Toki Pona AST. */ -/** */ +/** Represents a modifying particle. */ export type ModifyingParticle = | { type: "word"; word: string } | { type: "long word"; word: string; length: number } @@ -83,9 +83,11 @@ export type Clause = } | { type: "prepositions"; prepositions: Array } | { type: "quotation"; quotation: Quotation }; +/** Represents constructions found in the start of the clause. */ export type Preclause = | { type: "taso"; taso: WordUnit } | { type: "modifying particle"; modifyingParticle: ModifyingParticle }; +/** Represents constructions found in the end of the clause. */ export type Postclause = | { type: "anu seme"; seme: WordUnit } | { type: "modifying particle"; modifyingParticle: ModifyingParticle }; @@ -110,6 +112,7 @@ export type Quotation = { leftMark: string; rightMark: string; }; +/** The final representation of whole Toki Pona input text. */ export type MultipleSentences = | { type: "single word"; word: string } | { type: "sentences"; sentences: Array }; From d5b66065c4c118c4bee1d7e41ecfb2d5fbad22b9 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 11:30:38 +0800 Subject: [PATCH 334/738] disable filter --- src/filter.ts | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 2a52b76..d3c102a 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -329,31 +329,31 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ } return true; }, - // If the clause is just a single phrase, avoid post modifying particles - // unless it is "n" - (fullClause) => { - if ( - fullClause.type === "default" && - fullClause.postclause != null && - fullClause.postclause.type === "modifying particle" - ) { - const modifyingParticle = fullClause.postclause.modifyingParticle; - if ( - (modifyingParticle.type === "word" || - modifyingParticle.type === "long word") && - modifyingParticle.word === "n" - ) { - return true; - } - if ( - fullClause.clause.type === "phrases" && - fullClause.clause.phrases.type === "single" - ) { - throw new CoveredError(); - } - } - return true; - }, + // // If the clause is just a single phrase, avoid post modifying particles + // // unless it is "n" + // (fullClause) => { + // if ( + // fullClause.type === "default" && + // fullClause.postclause != null && + // fullClause.postclause.type === "modifying particle" + // ) { + // const modifyingParticle = fullClause.postclause.modifyingParticle; + // if ( + // (modifyingParticle.type === "word" || + // modifyingParticle.type === "long word") && + // modifyingParticle.word === "n" + // ) { + // return true; + // } + // if ( + // fullClause.clause.type === "phrases" && + // fullClause.clause.phrases.type === "single" + // ) { + // throw new CoveredError(); + // } + // } + // return true; + // }, ]; /** Array of filter rules for multiple sentences. */ export const SENTENCES_RULE: Array<(sentences: Array) => boolean> = [ From 7d512c39f4eae6ae1b71761a2fdb19a29240e128 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 11:59:59 +0800 Subject: [PATCH 335/738] more documentation comments --- src/ast-parser.ts | 2 +- src/lexer.ts | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 89f1ec8..203d232 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -811,7 +811,7 @@ const FULL_PARSER = choiceOnlyOne( ({ type: "sentences", sentences }) as MultipleSentences ), ); -/** A Toki Pona parser. */ +/** Turns string into Toki Pona AST. */ export function parse(src: string): Output { return lex(src).flatMap((src) => FULL_PARSER.parse(src)); } diff --git a/src/lexer.ts b/src/lexer.ts index ffa380f..0a2a4d5 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -90,6 +90,7 @@ function slice(length: number, description: string): Lexer { } }); } +/** Parses a string that exactly matches the given string. */ function matchString(match: string): Lexer { return slice(match.length, `"${match}"`).map((slice) => { if (slice === match) { @@ -220,6 +221,7 @@ function multipleA(): Lexer { return sequence(specificWord("a"), allAtLeastOnce(specificWord("a"))) .map(([a, as]) => [a, ...as].length); } +/** Parses lengthened words. */ function longWord(): Lexer { return match(/[an]/, 'long "a" or "n"').then(([word, _]) => allAtLeastOnce(matchString(word)) @@ -365,8 +367,12 @@ function quotation( return { type: "quotation", tokenTree, leftMark, rightMark }; }); } -// spaces after the first glyph and the last glyph aren't parsed and so must be -// manually added by the caller if needed +/** + * Parses long glyph container. + * + * spaces after the first glyph and the last glyph aren't parsed and so must be + * manually added by the caller if needed. + */ function longContainer( left: string, right: string, @@ -391,6 +397,7 @@ function longContainer( ) .map(([_, inside, _1]) => inside); } +/** Parses long glyph container containing words. */ function longCharacterContainer( allowQuotation: boolean, left: string, @@ -404,6 +411,7 @@ function longCharacterContainer( ), ); } +/** Parses long glyph container containing just spaces. */ function longSpaceContainer(): Lexer { return longContainer( START_OF_LONG_GLYPH, @@ -412,8 +420,12 @@ function longSpaceContainer(): Lexer { ) .skip(spaces()); } -// This doesn't parses space on the right and so must be manually added by the -// caller if needed +/** + * Parses long glyph head. + * + * This doesn't parses space on the right and so must be manually added by the + * caller if needed. + */ function longGlyphHead(): Lexer> { return choiceOnlyOne( combinedGlyphs(), @@ -421,6 +433,7 @@ function longGlyphHead(): Lexer> { .map((word) => [word]), ); } +/** Parses long glyph that contains characters. */ function characterLongGlyph( allowQuotation: boolean, ): Lexer { @@ -456,6 +469,7 @@ function characterLongGlyph( }; }); } +/** Parses long glyph that only contains spaces. */ function longSpaceGlyph(): Lexer { return sequence(longGlyphHead(), longSpaceContainer()) .map(([words, spaceLength]) => ({ @@ -464,6 +478,7 @@ function longSpaceGlyph(): Lexer { spaceLength, })); } +/** Parses underline lon. */ function longLon( allowQuotation: boolean, ): Lexer> { @@ -474,6 +489,7 @@ function longLon( ) .skip(spaces()); } +/** Parses all kinds of long glyphs. */ function longGlyph(allowQuotation: boolean): Lexer { return choiceOnlyOne( characterLongGlyph(allowQuotation) as Lexer, @@ -528,10 +544,11 @@ function tokenTrees( ): Lexer> { return all(tokenTree(nestingSettings)); } +/** The final lexer. */ const FULL_PARSER = spaces() .with(tokenTrees({ allowQuotation: true, allowLongGlyph: true })) .skip(eol()); -/** Parses multiple token trees. */ +/** Turns string into token trees. */ export function lex(src: string): Output> { if (/\n/.test(src.trim())) { return new Output(new UnrecognizedError("multiline text")); From 716a241f63db18c090f054ba0ba6e4e81a21a4a2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 12:01:52 +0800 Subject: [PATCH 336/738] more documentation comments --- src/token-tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token-tree.ts b/src/token-tree.ts index 6a3ed2a..5c8c0cc 100644 --- a/src/token-tree.ts +++ b/src/token-tree.ts @@ -39,7 +39,7 @@ export type TokenTree = } | { type: "comma" } | { type: "punctuation"; punctuation: string }; - +/** Describes a token tree. Useful for error messages. */ export function describe(tokenTree: TokenTree): string { switch (tokenTree.type) { case "word": From d2242fdbb78bf2c6c9a0b1f6a967f8c217b60331 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 12:15:26 +0800 Subject: [PATCH 337/738] small edit --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index febf281..ce79419 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ You can do the usual github stuff: Open issue if there's an issue or you have a suggestion; Open pull request if you want to propose changes. -Unless you want to edit the dictionary in `./src/dictionary.ts` or it is just a small edit e.g. a type fix, please claim an issue first or open a new issue and claim it before working on the code. This is to avoid duplicate efforts. +Unless you want to edit the dictionary in `./src/dictionary.ts` or it is just a small edit e.g. a typo fix, please claim an issue first or open a new issue and claim it before working on the code. This is to avoid duplicate efforts. If you want to edit the dictionary, just go ahead and contribute! We provide useful guidelines in the wiki! From 5a3aa21a1347ed254b35688b05cda919cf7a27ca Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 17:11:10 +0800 Subject: [PATCH 338/738] rename filter arrays --- src/ast-parser.ts | 10 +++++----- src/filter.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 203d232..ab8144b 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -29,10 +29,10 @@ import { filter, FULL_CLAUSE_RULE, MODIFIER_RULES, - MODIFIERS_RULES, + MULTIPLE_MODIFIERS_RULES, + MULTIPLE_SENTENCES_RULE, PHRASE_RULE, PREPOSITION_RULE, - SENTENCES_RULE, WORD_UNIT_RULES, } from "./filter.ts"; import { @@ -358,7 +358,7 @@ function modifiers(): AstParser> { ...nanpaModifiers, ...piModifiers, ]) - .filter(filter(MODIFIERS_RULES)); + .filter(filter(MULTIPLE_MODIFIERS_RULES)); } /** Phrase parser intended for phrases inside long glyphs. */ const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); @@ -784,7 +784,7 @@ function sentence(): AstParser { /** Parses a sentence inside quotation. */ const INNER_QUOTATION_PARSER = all(sentence()) .skip(eol("end of sentence")) - .filter(filter(SENTENCES_RULE)); + .filter(filter(MULTIPLE_SENTENCES_RULE)); /** Parses a quotation. */ export function quotation(): AstParser { return specificTokenTree("quotation").flatMapValue((tokenTree) => @@ -806,7 +806,7 @@ const FULL_PARSER = choiceOnlyOne( .map((word) => ({ type: "single word", word }) as MultipleSentences), allAtLeastOnce(sentence()) .skip(eol("end of sentence")) - .filter(filter(SENTENCES_RULE)) + .filter(filter(MULTIPLE_SENTENCES_RULE)) .map((sentences) => ({ type: "sentences", sentences }) as MultipleSentences ), diff --git a/src/filter.ts b/src/filter.ts index d3c102a..388ff3c 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -166,7 +166,9 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ }, ]; /** Array of filter rules for multiple modifiers. */ -export const MODIFIERS_RULES: Array<(modifier: Array) => boolean> = [ +export const MULTIPLE_MODIFIERS_RULES: Array< + (modifier: Array) => boolean +> = [ // no multiple pi (modifiers) => { if (modifiers.filter((modifier) => modifier.type === "pi").length > 1) { @@ -356,7 +358,9 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // }, ]; /** Array of filter rules for multiple sentences. */ -export const SENTENCES_RULE: Array<(sentences: Array) => boolean> = [ +export const MULTIPLE_SENTENCES_RULE: Array< + (sentences: Array) => boolean +> = [ // Only allow at most 2 sentences (sentences) => { if (sentences.length > 2) { From 4f8ebba81ce93fc102a160ada593b897dbc68598 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 19:13:15 +0800 Subject: [PATCH 339/738] better error aggregation --- src/output.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/output.ts b/src/output.ts index 22a3da8..d08c8a5 100644 --- a/src/output.ts +++ b/src/output.ts @@ -110,12 +110,21 @@ export class Output { ...outputs: { [I in keyof T]: Output } & { length: T["length"] } ): Output { // We resorted to using `any` types here, make sure it works properly - return outputs.reduce( - // deno-lint-ignore no-explicit-any - (result: Output, output) => - result.flatMap((left) => output.map((right) => [...left, right])), - // deno-lint-ignore no-explicit-any - new Output([[]]), - ) as Output; + if (outputs.some((output) => output.isError())) { + return Output.concat(...[ + // deno-lint-ignore no-explicit-any + new Output(), + // deno-lint-ignore no-explicit-any + ...outputs as Array>, + ]); + } else { + return outputs.reduce( + // deno-lint-ignore no-explicit-any + (result: Output, output) => + result.flatMap((left) => output.map((right) => [...left, right])), + // deno-lint-ignore no-explicit-any + new Output([[]]), + ) as Output; + } } } From 3d1915334c94ee556c5b778a85c8a63d9de83b4f Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 30 Apr 2024 19:16:19 +0800 Subject: [PATCH 340/738] more efficient error aggregation --- src/output.ts | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/output.ts b/src/output.ts index d08c8a5..f173680 100644 --- a/src/output.ts +++ b/src/output.ts @@ -110,21 +110,18 @@ export class Output { ...outputs: { [I in keyof T]: Output } & { length: T["length"] } ): Output { // We resorted to using `any` types here, make sure it works properly - if (outputs.some((output) => output.isError())) { - return Output.concat(...[ - // deno-lint-ignore no-explicit-any - new Output(), - // deno-lint-ignore no-explicit-any - ...outputs as Array>, - ]); - } else { - return outputs.reduce( - // deno-lint-ignore no-explicit-any - (result: Output, output) => - result.flatMap((left) => output.map((right) => [...left, right])), - // deno-lint-ignore no-explicit-any - new Output([[]]), - ) as Output; - } + return outputs.reduce( + // deno-lint-ignore no-explicit-any + (output: Output, newOutput) => { + if (output.isError() || newOutput.isError()) { + return Output.concat(output, newOutput); + } else { + return output + .flatMap((left) => newOutput.map((right) => [...left, right])); + } + }, + // deno-lint-ignore no-explicit-any + new Output([[]]), + ) as Output; } } From f6ac5be86ecba48d64062bc972be284a5e52a3b4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 09:23:56 +0800 Subject: [PATCH 341/738] update english AST --- src/english-ast.ts | 50 ++++++++++++++++++++-------------------------- src/translator.ts | 10 ++++++---- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/english-ast.ts b/src/english-ast.ts index 68987a2..46b59bc 100644 --- a/src/english-ast.ts +++ b/src/english-ast.ts @@ -41,24 +41,19 @@ export type AdjectiveType = | "origin" | "material" | "qualifier"; -export type AdjectivePhrase = { - type: AdjectiveType; - adverbs: Array; - adjective: string; -}; -export type PredicateAdjective = +export type AdjectivePhrase = | { type: "simple"; - adjective: AdjectivePhrase; - preposition: Array; + kind: AdjectiveType; + adverbs: Array; + adjective: string; } | { type: "compound"; conjunction: string; - adjectives: PredicateAdjective; - preposition: Array; + adjectives: AdjectivePhrase; }; -export type Verb = +export type VerbPhrase = | { type: "default"; adverbs: Array; @@ -67,12 +62,20 @@ export type Verb = } | { type: "linking noun"; + linkingVerb: string; noun: NounPhrase; preposition: Array; } | { type: "linking adjective"; - adjective: PredicateAdjective; + linkingVerb: string; + adjective: AdjectivePhrase; + preposition: Array; + } + | { + type: "compound"; + conjunction: string; + verbs: VerbPhrase; preposition: Array; }; export type Clause = @@ -80,19 +83,14 @@ export type Clause = | { type: "default"; subject: NounPhrase; - verb: Verb; - object: NounPhrase; + verb: VerbPhrase; + object: null | NounPhrase; preposition: Array; } | { type: "subject phrase"; subject: NounPhrase } | { - type: "implied it's noun"; - noun: NounPhrase; - preposition: Array; - } - | { - type: "implied it's adjective"; - adjective: PredicateAdjective; + type: "implied it's"; + verb: VerbPhrase; preposition: Array; } | { type: "interjection"; interjection: string } @@ -102,17 +100,13 @@ export type Clause = conjunction: string; clauses: Array; preposition: Array; - }; -export type DependentClause = { - conjunction: string; - clause: Clause; -}; + } + | { type: "dependent"; conjunction: string; clause: Clause }; export type Preposition = { preposition: string; object: NounPhrase; }; export type Sentence = { - dependentClauses: Array; - independentClause: Clause; + clause: Clause; punctuation: string; }; diff --git a/src/translator.ts b/src/translator.ts index 87b9891..be81263 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -9,7 +9,9 @@ import * as English from "./english-ast.ts"; import { TodoError } from "./error.ts"; import { Output } from "./output.ts"; -function sentence(sentence: TokiPona.Sentence): Output { +function sentence( + sentence: TokiPona.Sentence, +): Output> { return new Output(new TodoError("translation of sentence")); } function multipleSentences( @@ -27,15 +29,15 @@ function multipleSentences( ]) .map((definition) => ({ - dependentClauses: [], - independentClause: { type: "free form", text: definition }, + clause: { type: "free form", text: definition }, punctuation: "", }) as English.Sentence ) .map((definition) => [definition]); } case "sentences": - return Output.combine(...sentences.sentences.map(sentence)); + return Output.combine(...sentences.sentences.map(sentence)) + .map((array) => array.flat()); } } export function translate(src: string): Output> { From 3a4350eee54e3f31facf143c68d8ecd5a05ebed1 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 09:57:05 +0800 Subject: [PATCH 342/738] implement nasin nanpa and update handling of number --- src/ast-parser.ts | 48 ++++++++++++++++++++++++++++++++++------------- src/ast.ts | 2 +- src/dictionary.ts | 19 +++++++++---------- src/filter.ts | 13 ++----------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index ab8144b..f6b413f 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -20,6 +20,7 @@ import { CoveredError, UnexpectedError, UnrecognizedError } from "./error.ts"; import { Output } from "./output.ts"; import { CONTENT_WORD, + NUMERAL, PREPOSITION, PREVERB, TOKI_PONA_WORD, @@ -271,24 +272,45 @@ function optionalCombined( ), ); } -/** Parses number words in order. */ -function number(): AstParser> { +/** + * Parses number words other than "ale" and "ala". This can parse nothing and + * return 0. + */ +function subAleNumber(): AstParser { return sequence( - many(choice(specificWord("ale"), specificWord("ali"))), many(specificWord("mute")), many(specificWord("luka")), many(specificWord("tu")), many(specificWord("wan")), ) - .map((array) => { - const output = array.flat(); - if (output.length >= 2) { - return output; - } else { - throw new CoveredError(); + .map((array) => array.flat()) + .map((array) => array.reduce((number, word) => number + NUMERAL[word], 0)); +} +/** + * Parses "ale" with optional sub-ale numbers before it which is part of "nasin + * nanpa pona". + */ +function ale(): AstParser { + return sequence( + subAleNumber(), + manyAtLeastOnce(choice(specificWord("ale"), specificWord("ali"))), + ) + .map(([sub, ale]) => { + let number = sub; + if (sub === 0) { + number = 1; } + return number * Math.pow(100, ale.length); }); } +/** Parses number words including "nasin nanpa pona". */ +function number(): AstParser { + return choice( + specificWord("ala").map(() => 0), + sequence(many(ale()), subAleNumber()) + .map(([supers, sub]) => [...supers, sub].reduce((a, b) => a + b)), + ); +} /** Parses a "pi" construction. */ function pi(): AstParser { return choice( @@ -332,10 +354,10 @@ function modifiers(): AstParser> { .map((words) => ({ type: "proper words", words }) as Modifier) .filter(filter(MODIFIER_RULES)), number() - .map((numbers) => + .map((number) => ({ type: "default", - word: { type: "numbers", numbers }, + word: { type: "number", number }, }) as Modifier ) .filter(filter(MODIFIER_RULES)), @@ -366,10 +388,10 @@ const INNER_PHRASE_PARSER = phrase().skip(eol("end of long glyph")); function phrase(): AstParser { return choice( sequence(number(), lazy(modifiers), optional(modifyingParticle())) - .map(([numbers, modifiers, modifyingParticle]) => + .map(([number, modifiers, modifyingParticle]) => ({ type: "default", - headWord: { type: "numbers", numbers }, + headWord: { type: "number", number }, modifiers, modifyingParticle, }) as Phrase diff --git a/src/ast.ts b/src/ast.ts index f1b6d47..c659579 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -14,7 +14,7 @@ export type WordUnit = } | { type: "x ala x"; word: string } | { type: "reduplication"; word: string; count: number } - | { type: "numbers"; numbers: Array }; + | { type: "number"; number: number }; /** Represents a single modifier. */ export type Modifier = | { type: "default"; word: WordUnit } diff --git a/src/dictionary.ts b/src/dictionary.ts index c21f65c..e265369 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -129,6 +129,15 @@ export const PREVERB_DEFINITION: { [word: string]: Array } = { wile: [], }; PREVERB_DEFINITION.alasa = PREVERB_DEFINITION.lukin; +export const NUMERAL: { [word: string]: number } = { + ala: 0, + wan: 1, + tu: 2, + luka: 5, + mute: 20, + ale: 100, + ali: 100, +}; export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { akesi: [ noun("reptile(s)"), @@ -137,7 +146,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("amphibian", "qualifier"), ], ala: [ - numeral(0), determiner("no", "quantifier", "zero"), indefinitePronoun("nothing"), ], @@ -146,7 +154,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("search(ed)", "searching"), ], ale: [ - numeral(100), indefinitePronoun("everything"), indefinitePronoun("anything"), singularNoun("entirety"), @@ -525,7 +532,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verb("exist(ed)", "existing"), ], luka: [ - numeral(5), noun("hand(s)"), noun("arm(s)"), adjectiveNounPhrase([adjective("tactile", "qualifier")], noun("organ(s)")), @@ -682,7 +688,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { verbObjectPhrase(verb("have/had", "having"), singularNoun("fun")), ], mute: [ - numeral(20), determiner("many", "quantifier", "plural"), determiner("several", "quantifier", "plural"), adverb("very"), @@ -1076,7 +1081,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjective("transgender", "qualifier"), ], tu: [ - numeral(2), verb("separate(d)", "separating"), verb("divide(d)", "dividing"), verb("split", "splitting"), @@ -1124,7 +1128,6 @@ export const CONTENT_WORD_DEFINITION: { [word: string]: Array } = { adjectiveNounPhrase([adjective("light", "color")], singularNoun("gray")), ], wan: [ - numeral(1), adjective("singular", "opinion"), verb("combine(d)", "combining"), verb("mix(ed)", "mixing"), @@ -1214,7 +1217,6 @@ export type Definition = kind: DeterminerType; quantity: DeterminerQuantity; } - | { type: "numeral"; number: number } | { type: "adverb"; adverb: string } | { type: "verb"; @@ -1399,9 +1401,6 @@ function adjective( ): Definition & { type: "adjective" } { return { type: "adjective", adjective: word, kind }; } -function numeral(number: number): Definition & { type: "numeral" } { - return { type: "numeral", number }; -} function determiner( determiner: string, kind: DeterminerType, diff --git a/src/filter.ts b/src/filter.ts index 388ff3c..6a3d2d1 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -222,10 +222,7 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ if (phrase.type === "default") { const headWord = phrase.headWord; if ( - (headWord.type === "numbers" || - (headWord.type === "default" && - (headWord.word === "wan" || headWord.word === "tu"))) && - phrase.modifiers.some(modifierIsNumeric) + headWord.type === "number" && phrase.modifiers.some(modifierIsNumeric) ) { throw new UnrecognizedError("Multiple number words"); } @@ -377,13 +374,7 @@ export function filter( } /** Helper function for checking whether a modifier is numeric. */ function modifierIsNumeric(modifier: Modifier): boolean { - if (modifier.type === "default") { - const word = modifier.word; - return word.type === "numbers" || - (word.type === "default" && - (word.word === "wan" || word.word === "tu")); - } - return false; + return modifier.type === "default" && modifier.word.type === "number"; } /** * Helper function for checking if the modifiers is exactly just "ala" or nothing. From cded124ff54c8a478963aa62c123cd604736d7b8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 10:00:00 +0800 Subject: [PATCH 343/738] fix type error in old translator --- src/old-translator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/old-translator.ts b/src/old-translator.ts index a0855b9..7a96a47 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -48,8 +48,8 @@ function wordUnitAs( switch (word.type) { case "default": return definition(kind, word.word); - case "numbers": - return new Output([number(word.numbers).toString()]); + case "number": + return new Output([word.number.toString()]); case "reduplication": return definition(kind, word.word) .map((noun) => new Array(word.count).fill(noun).join(" ")); From 5ba33d033b8da87b7286ba43c963fa4765beb5a6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 10:13:37 +0800 Subject: [PATCH 344/738] add todo notes --- src/translator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/translator.ts b/src/translator.ts index be81263..f8267d2 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,6 +1,7 @@ import { parse } from "./ast-parser.ts"; import * as TokiPona from "./ast.ts"; import { + NUMERAL, PARTICLE_DEFINITION, PREPOSITION_DEFINITION, SPECIAL_CONTENT_WORD_DEFINITION, @@ -24,6 +25,7 @@ function multipleSentences( ...PARTICLE_DEFINITION[word] ?? [], ...SPECIAL_CONTENT_WORD_DEFINITION[word] ?? [], ...PREPOSITION_DEFINITION[word] ?? [], + // TODO: Numeral // TODO: Preverb // TODO: Content word definition ]) From b9ff0df57fbd549e08bc1cec71bfa687950d63ec Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 11:07:30 +0800 Subject: [PATCH 345/738] update nasin nanpa pona parser --- src/ast-parser.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index f6b413f..b421316 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -287,28 +287,33 @@ function subAleNumber(): AstParser { .map((array) => array.reduce((number, word) => number + NUMERAL[word], 0)); } /** - * Parses "ale" with optional sub-ale numbers before it which is part of "nasin - * nanpa pona". + * Parses multiple "ale" and returns the count. This can parse nothing and + * return 0. */ function ale(): AstParser { - return sequence( - subAleNumber(), - manyAtLeastOnce(choice(specificWord("ale"), specificWord("ali"))), - ) - .map(([sub, ale]) => { - let number = sub; - if (sub === 0) { - number = 1; - } - return number * Math.pow(100, ale.length); - }); + return many(choice(specificWord("ale"), specificWord("ali"))) + .map((array) => array.length); } /** Parses number words including "nasin nanpa pona". */ function number(): AstParser { return choice( specificWord("ala").map(() => 0), - sequence(many(ale()), subAleNumber()) - .map(([supers, sub]) => [...supers, sub].reduce((a, b) => a + b)), + sequence( + ale(), + many( + sequence(subAleNumber(), ale()).filter(([sub, ale]) => { + if (ale !== 0 && sub === 0) { + throw new CoveredError(); + } + return true; + }), + ), + ).map(([first, rest]) => + [[1, first], ...rest].reduce( + (result, [sub, ale]) => result + sub * Math.pow(100, ale), + 0, + ) + ), ); } /** Parses a "pi" construction. */ From ab2c3a9afaccfcb54eb04c70e8d44234fc933265 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 11:21:41 +0800 Subject: [PATCH 346/738] early filtering out of covered errors --- src/ast-parser.ts | 9 +++------ src/lexer.ts | 8 ++++---- src/main.ts | 5 +---- src/output.ts | 38 +++++++++++++++++++++++++++----------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index b421316..cf08bb4 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -301,12 +301,9 @@ function number(): AstParser { sequence( ale(), many( - sequence(subAleNumber(), ale()).filter(([sub, ale]) => { - if (ale !== 0 && sub === 0) { - throw new CoveredError(); - } - return true; - }), + sequence(subAleNumber(), ale()).filter( + ([sub, ale]) => ale === 0 || sub !== 0, + ), ), ).map(([first, rest]) => [[1, first], ...rest].reduce( diff --git a/src/lexer.ts b/src/lexer.ts index 0a2a4d5..6a84888 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -17,7 +17,7 @@ import { all, allAtLeastOnce, choiceOnlyOne, - error, + empty, nothing, optionalAll, Parser, @@ -505,17 +505,17 @@ function tokenTree( if (nestingSettings.allowQuotation) { quotationParser = quotation(nestingSettings.allowLongGlyph); } else { - quotationParser = error(new CoveredError()); + quotationParser = empty(); } let longGlyphParser: Lexer; if (nestingSettings.allowLongGlyph) { longGlyphParser = longGlyph(nestingSettings.allowQuotation); } else { - longGlyphParser = error(new CoveredError()); + longGlyphParser = empty(); } let xAlaXParser: Lexer; if (settings.get("x-ala-x-partial-parsing")) { - xAlaXParser = error(new CoveredError()); + xAlaXParser = empty(); } else { xAlaXParser = xAlaX() .map((word) => ({ type: "x ala x", word }) as TokenTree); diff --git a/src/main.ts b/src/main.ts index 4f1acfd..3b55109 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,5 @@ /** Module for main execution in the browser. */ -import { CoveredError } from "./error.ts"; import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; import { TeloMisikeke } from "../deps.ts"; @@ -107,9 +106,7 @@ function updateOutput(): void { if (error.length === 0) { error = [ ...new Set( - translations.errors - .filter((x) => !(x instanceof CoveredError)) - .map((x) => x.message), + translations.errors.map((x) => x.message), ), ]; } diff --git a/src/output.ts b/src/output.ts index f173680..a173413 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,6 +1,6 @@ /** Module containing the Output data type. */ -import { OutputError } from "./error.ts"; +import { CoveredError, OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { /** Represents possibilities, considered error when the array is empty. */ @@ -10,6 +10,8 @@ export class Output { constructor(output?: undefined | null | Array | OutputError) { if (Array.isArray(output)) { this.output = output; + } else if (output instanceof CoveredError) { + this.output = []; } else if (output instanceof OutputError) { this.output = []; this.errors.push(output); @@ -47,18 +49,30 @@ export class Output { isError(): boolean { return this.output.length === 0; } - /** - * Filters outputs. Instead of returning false, OutputError must be thrown - * instead. + /** Filters outputs. For convenience, the mapper function can throw + * OutputError; Other kinds of errors will be ignored. */ filter(mapper: (value: T) => boolean): Output { - return this.map((value) => { - if (mapper(value)) { - return value; - } else { - throw new Error("no error provided"); + if (this.isError()) { + return Output.newErrors(this.errors); + } + const wholeOutput = new Output(); + for (const value of this.output) { + try { + if (mapper(value)) { + wholeOutput.push(value); + } + } catch (error) { + if (error instanceof CoveredError) { + // Do nothing + } else if (error instanceof OutputError) { + wholeOutput.pushError(error); + } else { + throw error; + } } - }); + } + return wholeOutput; } /** * Maps all values and returns new Output. For convenience, the mapper @@ -73,7 +87,9 @@ export class Output { try { wholeOutput.push(mapper(value)); } catch (error) { - if (error instanceof OutputError) { + if (error instanceof CoveredError) { + // Do nothing + } else if (error instanceof OutputError) { wholeOutput.pushError(error); } else { throw error; From 1e5906f03acfda4a5b83e24b607c9e67f5f08895 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 11:36:19 +0800 Subject: [PATCH 347/738] refactor: CoveredError isn't actually needed --- src/ast-parser.ts | 30 +++++++++++++++--------------- src/error.ts | 2 -- src/filter.ts | 15 +++++---------- src/lexer.ts | 29 ++++++++++++----------------- src/output.ts | 12 +++--------- 5 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index cf08bb4..84bc156 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -16,7 +16,7 @@ import { Sentence, WordUnit, } from "./ast.ts"; -import { CoveredError, UnexpectedError, UnrecognizedError } from "./error.ts"; +import { UnexpectedError, UnrecognizedError } from "./error.ts"; import { Output } from "./output.ts"; import { CONTENT_WORD, @@ -608,13 +608,15 @@ function associatedPredicates( ), many(optionalComma().with(preposition())), ) - .map(([predicates, objects, prepositions]) => { - if (!objects && prepositions.length === 0) { - throw new CoveredError(); - } else { - return { type: "associated", predicates, objects, prepositions }; - } - }); + .filter(([_, objects, prepositions]) => + objects != null || prepositions.length !== 0 + ) + .map(([predicates, objects, prepositions]) => ({ + type: "associated", + predicates, + objects, + prepositions, + })); } /** Parses multiple predicates without "li" nor "o" at the beginning. */ function multiplePredicates( @@ -696,13 +698,11 @@ function clause(): AstParser { prepositions: [preposition, ...morePreposition], }) as Clause ), - subjectPhrases().map((phrases) => { - if (phrases.type === "single" && phrases.phrase.type === "quotation") { - throw new CoveredError(); - } else { - return { type: "phrases", phrases } as Clause; - } - }), + subjectPhrases() + .filter((phrases) => + phrases.type !== "single" || phrases.phrase.type !== "quotation" + ) + .map((phrases) => ({ type: "phrases", phrases }) as Clause), subjectPhrases() .skip(specificWord("o")) .map((phrases) => ({ type: "o vocative", phrases }) as Clause), diff --git a/src/error.ts b/src/error.ts index bd5fee8..6afd644 100644 --- a/src/error.ts +++ b/src/error.ts @@ -8,8 +8,6 @@ export class UnreachableError extends Error { super("Reached unreachable error."); } } -/** Represents errors expected to be covered by other errors. */ -export class CoveredError extends OutputError {} /** Represents Error with unexpected and expected elements. */ export class UnexpectedError extends OutputError { constructor(unexpected: string, expected: string) { diff --git a/src/filter.ts b/src/filter.ts index 6a3d2d1..5f3d5a2 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -13,7 +13,7 @@ import { somePhraseInMultiplePhrases, WordUnit, } from "./ast.ts"; -import { CoveredError, UnrecognizedError } from "./error.ts"; +import { UnrecognizedError } from "./error.ts"; import { settings } from "./settings.ts"; /** Array of filter rules for a word unit. */ @@ -230,15 +230,10 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ return true; }, // If the phrase has no modifiers, avoid modifying particle - (phrase) => { - if ( - phrase.type === "default" && phrase.modifyingParticle != null && - phrase.modifiers.length === 0 - ) { - throw new CoveredError(); - } - return true; - }, + (phrase) => + phrase.type !== "default" || + phrase.modifyingParticle == null || + phrase.modifiers.length !== 0, // "n" cannot modify a phrase (phrase) => { if (phrase.type === "default") { diff --git a/src/lexer.ts b/src/lexer.ts index 6a84888..075c602 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -8,7 +8,6 @@ import { Output } from "./output.ts"; import { - CoveredError, UnexpectedError, UnreachableError, UnrecognizedError, @@ -161,7 +160,7 @@ function ucsurWord( return ucsur(trailingSettings).map((word) => { const latin = UCSUR_TO_LATIN[word]; if (latin == null) { - throw new CoveredError(); + throw new UnexpectedError(word, "UCSUR glyph"); } else { return latin; } @@ -444,7 +443,8 @@ function characterLongGlyph( START_OF_REVERSE_LONG_GLYPH, END_OF_REVERSE_LONG_GLYPH, ), - ), + ) + .map((before) => before ?? []), longGlyphHead(), optionalAll( longCharacterContainer( @@ -452,22 +452,17 @@ function characterLongGlyph( START_OF_LONG_GLYPH, END_OF_LONG_GLYPH, ), - ), + ) + .map((before) => before ?? []), ) .skip(spaces()) - .map(([beforeNull, words, afterNull]) => { - const before = beforeNull ?? []; - const after = afterNull ?? []; - if (before.length === 0 && after.length === 0) { - throw new CoveredError(); - } - return { - type: "long glyph", - before: before ?? [], - words, - after: after ?? [], - }; - }); + .filter(([before, _, after]) => before.length !== 0 || after.length !== 0) + .map(([before, words, after]) => ({ + type: "long glyph", + before: before ?? [], + words, + after: after ?? [], + })); } /** Parses long glyph that only contains spaces. */ function longSpaceGlyph(): Lexer { diff --git a/src/output.ts b/src/output.ts index a173413..413295f 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,6 +1,6 @@ /** Module containing the Output data type. */ -import { CoveredError, OutputError } from "./error.ts"; +import { OutputError } from "./error.ts"; /** Represents possibilities and error. */ export class Output { /** Represents possibilities, considered error when the array is empty. */ @@ -10,8 +10,6 @@ export class Output { constructor(output?: undefined | null | Array | OutputError) { if (Array.isArray(output)) { this.output = output; - } else if (output instanceof CoveredError) { - this.output = []; } else if (output instanceof OutputError) { this.output = []; this.errors.push(output); @@ -63,9 +61,7 @@ export class Output { wholeOutput.push(value); } } catch (error) { - if (error instanceof CoveredError) { - // Do nothing - } else if (error instanceof OutputError) { + if (error instanceof OutputError) { wholeOutput.pushError(error); } else { throw error; @@ -87,9 +83,7 @@ export class Output { try { wholeOutput.push(mapper(value)); } catch (error) { - if (error instanceof CoveredError) { - // Do nothing - } else if (error instanceof OutputError) { + if (error instanceof OutputError) { wholeOutput.pushError(error); } else { throw error; From 3609f9d980329f3f3646a15da7c9c69414059c4f Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 11:49:52 +0800 Subject: [PATCH 348/738] prevent infinite recursion on parsing number phrase --- src/ast-parser.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 84bc156..fff452c 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -300,17 +300,14 @@ function number(): AstParser { specificWord("ala").map(() => 0), sequence( ale(), - many( - sequence(subAleNumber(), ale()).filter( - ([sub, ale]) => ale === 0 || sub !== 0, - ), + many(sequence(subAleNumber(), ale()).filter(([sub, _]) => sub !== 0)), + ) + .map(([first, rest]) => + [[1, first], ...rest].reduce( + (result, [sub, ale]) => result + sub * Math.pow(100, ale), + 0, + ) ), - ).map(([first, rest]) => - [[1, first], ...rest].reduce( - (result, [sub, ale]) => result + sub * Math.pow(100, ale), - 0, - ) - ), ); } /** Parses a "pi" construction. */ From bfb97f98f51fa6d66610900ebfdfd1104f4a0567 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:10:32 +0800 Subject: [PATCH 349/738] improve number parser --- src/ast-parser.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index fff452c..3e5159e 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -272,8 +272,7 @@ function optionalCombined( ), ); } -/** - * Parses number words other than "ale" and "ala". This can parse nothing and +/** Parses number words other than "ale" and "ala". This can parse nothing and * return 0. */ function subAleNumber(): AstParser { @@ -286,28 +285,31 @@ function subAleNumber(): AstParser { .map((array) => array.flat()) .map((array) => array.reduce((number, word) => number + NUMERAL[word], 0)); } -/** - * Parses multiple "ale" and returns the count. This can parse nothing and - * return 0. - */ -function ale(): AstParser { - return many(choice(specificWord("ale"), specificWord("ali"))) - .map((array) => array.length); +/** Parses "ale" or "ali". */ +function ale(): AstParser { + return choice(specificWord("ale"), specificWord("ali")); } /** Parses number words including "nasin nanpa pona". */ function number(): AstParser { return choice( specificWord("ala").map(() => 0), - sequence( - ale(), - many(sequence(subAleNumber(), ale()).filter(([sub, _]) => sub !== 0)), + many( + sequence( + subAleNumber().filter((number) => number !== 0), + many(ale()).map((array) => array.length), + ), ) - .map(([first, rest]) => - [[1, first], ...rest].reduce( + .map((array) => + array.reduce( (result, [sub, ale]) => result + sub * Math.pow(100, ale), 0, ) ), + sequence( + manyAtLeastOnce(ale()).map((array) => array.length), + subAleNumber(), + ) + .map(([ale, sub]) => ale * 100 + sub), ); } /** Parses a "pi" construction. */ From e8ed87c45116bc391a40e92ee447e4762eddc325 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:24:58 +0800 Subject: [PATCH 350/738] add count combinator --- src/ast-parser.ts | 12 +++++------- src/lexer.ts | 12 ++++++------ src/parser-lib.ts | 3 +++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 3e5159e..e83c1db 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -41,6 +41,7 @@ import { allAtLeastOnce, choice, choiceOnlyOne, + count, lazy, many, manyAtLeastOnce, @@ -219,11 +220,11 @@ function xAlaX( function wordUnit(word: Set, description: string): AstParser { return choice( wordFrom(word, description).then((word) => - manyAtLeastOnce(specificWord(word)).map((words) => + count(manyAtLeastOnce(specificWord(word))).map((count) => ({ type: "reduplication", word, - count: words.length + 1, + count: count + 1, }) as WordUnit ) ), @@ -296,7 +297,7 @@ function number(): AstParser { many( sequence( subAleNumber().filter((number) => number !== 0), - many(ale()).map((array) => array.length), + count(many(ale())), ), ) .map((array) => @@ -305,10 +306,7 @@ function number(): AstParser { 0, ) ), - sequence( - manyAtLeastOnce(ale()).map((array) => array.length), - subAleNumber(), - ) + sequence(count(manyAtLeastOnce(ale())), subAleNumber()) .map(([ale, sub]) => ale * 100 + sub), ); } diff --git a/src/lexer.ts b/src/lexer.ts index 075c602..2a236cd 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -16,6 +16,7 @@ import { all, allAtLeastOnce, choiceOnlyOne, + count, empty, nothing, optionalAll, @@ -223,12 +224,12 @@ function multipleA(): Lexer { /** Parses lengthened words. */ function longWord(): Lexer { return match(/[an]/, 'long "a" or "n"').then(([word, _]) => - allAtLeastOnce(matchString(word)) + count(allAtLeastOnce(matchString(word))) .skip(spaces()) - .map((array) => ({ + .map((count) => ({ type: "long word", word, - length: 1 + array.length, + length: count + 1, })) ); } @@ -282,7 +283,7 @@ function cartoucheElement(): Lexer { ), sequence( singleUcsurWord(), - allAtLeastOnce( + count(allAtLeastOnce( choiceOnlyOne( match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), specificUcsurCharacter("󱦜", "middle dot", { @@ -290,8 +291,7 @@ function cartoucheElement(): Lexer { allowSpace: true, }), ), - ) - .map((dots) => dots.length), + )), ) .map(([word, dots]) => { const VOWEL = /[aeiou]/; diff --git a/src/parser-lib.ts b/src/parser-lib.ts index 6dc207f..058eb65 100644 --- a/src/parser-lib.ts +++ b/src/parser-lib.ts @@ -199,3 +199,6 @@ export function allAtLeastOnce( return sequence]>(parser, all(parser)) .map(([first, rest]) => [first, ...rest]); } +export function count(parser: Parser>): Parser { + return parser.map((array) => array.length); +} From cebde6c9f80766681dd2000edc25a50cfbc9d29d Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:27:19 +0800 Subject: [PATCH 351/738] improve number parser --- src/ast-parser.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index e83c1db..eef30bd 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -294,19 +294,22 @@ function ale(): AstParser { function number(): AstParser { return choice( specificWord("ala").map(() => 0), - many( - sequence( - subAleNumber().filter((number) => number !== 0), - count(many(ale())), + sequence( + manyAtLeastOnce( + sequence( + subAleNumber().filter((number) => number !== 0), + count(manyAtLeastOnce(ale())), + ), ), + subAleNumber(), ) - .map((array) => - array.reduce( + .map(([rest, last]) => + [...rest, [last, 0]].reduce( (result, [sub, ale]) => result + sub * Math.pow(100, ale), 0, ) ), - sequence(count(manyAtLeastOnce(ale())), subAleNumber()) + sequence(count(many(ale())), subAleNumber()) .map(([ale, sub]) => ale * 100 + sub), ); } From 01fda6dc0ca4221e3d8c26014da322c963c8649d Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:34:52 +0800 Subject: [PATCH 352/738] avoid parsing nothing as number --- src/ast-parser.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index eef30bd..a9b8dd1 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -309,7 +309,10 @@ function number(): AstParser { 0, ) ), - sequence(count(many(ale())), subAleNumber()) + sequence( + count(many(ale())), + subAleNumber().filter((number) => number !== 0), + ) .map(([ale, sub]) => ale * 100 + sub), ); } From 6bf0830c6ee7226e636c28df8a8f8c2f0c5be7f4 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:39:30 +0800 Subject: [PATCH 353/738] update document comments --- src/ast-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index a9b8dd1..a2681a7 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -273,8 +273,8 @@ function optionalCombined( ), ); } -/** Parses number words other than "ale" and "ala". This can parse nothing and - * return 0. +/** Parses number words in order other than "ale" and "ala". This can parse + * nothing and return 0. */ function subAleNumber(): AstParser { return sequence( From 2c0519d2a0f4cab23920ebf51e38e7fe29e86cae Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:42:02 +0800 Subject: [PATCH 354/738] update filtering --- src/ast-parser.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index a2681a7..9fa7bb7 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -311,9 +311,10 @@ function number(): AstParser { ), sequence( count(many(ale())), - subAleNumber().filter((number) => number !== 0), + subAleNumber(), ) - .map(([ale, sub]) => ale * 100 + sub), + .map(([ale, sub]) => ale * 100 + sub) + .filter((number) => number !== 0), ); } /** Parses a "pi" construction. */ From cf812862c8e2f2298278b04f0ee4e25607bab972 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 12:55:28 +0800 Subject: [PATCH 355/738] update filter function --- src/filter.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 5f3d5a2..5b24163 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -219,13 +219,12 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ }, // No multiple number words (phrase) => { - if (phrase.type === "default") { - const headWord = phrase.headWord; - if ( - headWord.type === "number" && phrase.modifiers.some(modifierIsNumeric) - ) { - throw new UnrecognizedError("Multiple number words"); - } + if ( + phrase.type === "default" && + phrase.headWord.type === "number" && + phrase.modifiers.some(modifierIsNumeric) + ) { + throw new UnrecognizedError("Multiple number words"); } return true; }, From 581c153ca0ff71880b71e8a6d6a1b5e05a004b94 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 13:32:43 +0800 Subject: [PATCH 356/738] update duplication filter --- src/filter.ts | 56 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 5b24163..8bc1bdf 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -25,16 +25,16 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ } return true; }, - // avoid reduplication of "wan" and "tu" - (wordUnit) => { - if ( - wordUnit.type === "reduplication" && - (wordUnit.word === "wan" || wordUnit.word === "tu") - ) { - throw new UnrecognizedError(`reduplication of ${wordUnit.word}`); - } - return true; - }, + // // avoid reduplication of "wan" and "tu" + // (wordUnit) => { + // if ( + // wordUnit.type === "reduplication" && + // (wordUnit.word === "wan" || wordUnit.word === "tu") + // ) { + // throw new UnrecognizedError(`reduplication of ${wordUnit.word}`); + // } + // return true; + // }, // disallow "anu" as content word only when turned off in settings (wordUnit) => { if (settings.get("anu-as-content-word")) { @@ -200,6 +200,42 @@ export const MULTIPLE_MODIFIERS_RULES: Array< } return true; }, + // avoid duplicate modifiers + (modifiers) => { + const set = new Set(); + for (const modifier of modifiers) { + let word: string; + switch (modifier.type) { + case "default": + if (modifier.word.type !== "number") { + word = modifier.word.word; + break; + } else { + continue; + } + case "pi": + if ( + modifier.phrase.type === "default" && + modifier.phrase.headWord.type !== "number" + ) { + word = modifier.phrase.headWord.word; + break; + } else { + continue; + } + case "quotation": + case "proper words": + case "nanpa": + continue; + } + if (set.has(word)) { + throw new UnrecognizedError(`duplicate "${word}" in modifier`); + } else { + set.add(word); + } + } + return true; + }, ]; /** Array of filter rules for a single phrase. */ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ From ab3a37ae1584544a8d979679ea84bd7901f72e8a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 14:09:25 +0800 Subject: [PATCH 357/738] update filters --- src/filter.ts | 73 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 8bc1bdf..1d21a8e 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -15,6 +15,7 @@ import { } from "./ast.ts"; import { UnrecognizedError } from "./error.ts"; import { settings } from "./settings.ts"; +import { describe } from "./token-tree.ts"; /** Array of filter rules for a word unit. */ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ @@ -61,7 +62,23 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ (modifyingParticle.type === "long word" && modifyingParticle.word === "n")); if (hasN) { - throw new UnrecognizedError('"n" modifying a word'); + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); + } + } + return true; + }, + // multiple "a" cannot modify a word + (wordUnit) => { + if (wordUnit.type === "default") { + const modifyingParticle = wordUnit.modifyingParticle; + if ( + modifyingParticle != null && modifyingParticle.type === "multiple a" + ) { + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); } } return true; @@ -270,17 +287,32 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ phrase.modifyingParticle == null || phrase.modifiers.length !== 0, // "n" cannot modify a phrase - (phrase) => { - if (phrase.type === "default") { - const modifyingParticle = phrase.modifyingParticle; - if ( - modifyingParticle != null && + (wordUnit) => { + if (wordUnit.type === "default" || wordUnit.type === "preverb") { + const modifyingParticle = wordUnit.modifyingParticle; + const hasN = modifyingParticle != null && ((modifyingParticle.type === "word" && modifyingParticle.word === "n") || (modifyingParticle.type === "long word" && - modifyingParticle.word === "n")) + modifyingParticle.word === "n")); + if (hasN) { + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); + } + } + return true; + }, + // multiple "a" cannot modify a phrase + (wordUnit) => { + if (wordUnit.type === "default" || wordUnit.type === "preverb") { + const modifyingParticle = wordUnit.modifyingParticle; + if ( + modifyingParticle != null && modifyingParticle.type === "multiple a" ) { - throw new UnrecognizedError('"n" modifying a phrase'); + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); } } return true; @@ -304,6 +336,31 @@ export const PREPOSITION_RULE: Array<(phrase: Preposition) => boolean> = [ } return true; }, + // "n" cannot modify a preposition + (wordUnit) => { + const modifyingParticle = wordUnit.modifyingParticle; + const hasN = modifyingParticle != null && + ((modifyingParticle.type === "word" && + modifyingParticle.word === "n") || + (modifyingParticle.type === "long word" && + modifyingParticle.word === "n")); + if (hasN) { + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); + } + return true; + }, + // multiple "a" cannot modify a preposition + (wordUnit) => { + const modifyingParticle = wordUnit.modifyingParticle; + if (modifyingParticle != null && modifyingParticle.type === "multiple a") { + throw new UnrecognizedError( + `${describe(modifyingParticle)} modifying a word`, + ); + } + return true; + }, ]; /** Array of filter rules for clauses. */ export const CLAUSE_RULE: Array<(clause: Clause) => boolean> = [ From 8944e09d4219c0dd291b8d4a6b7cdc6e99470087 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 14:27:51 +0800 Subject: [PATCH 358/738] implement modifying particle for reduplication --- src/ast-parser.ts | 22 ++++++++++++++-------- src/ast.ts | 7 ++++++- src/filter.ts | 4 ++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 9fa7bb7..bcd0b82 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -219,14 +219,20 @@ function xAlaX( /** Parses word unit except numbers. */ function wordUnit(word: Set, description: string): AstParser { return choice( - wordFrom(word, description).then((word) => - count(manyAtLeastOnce(specificWord(word))).map((count) => - ({ - type: "reduplication", - word, - count: count + 1, - }) as WordUnit - ) + sequence( + wordFrom(word, description) + .then((word) => + count(manyAtLeastOnce(specificWord(word))) + .map((count) => [word, count + 1] as [string, number]) + ), + optional(modifyingParticle()), + ).map(([[word, count], modifyingParticle]) => + ({ + type: "reduplication", + word, + count, + modifyingParticle, + }) as WordUnit ), xAlaX(word, description), sequence(wordFrom(word, description), optional(modifyingParticle())) diff --git a/src/ast.ts b/src/ast.ts index c659579..67cd115 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -13,7 +13,12 @@ export type WordUnit = modifyingParticle: null | ModifyingParticle; } | { type: "x ala x"; word: string } - | { type: "reduplication"; word: string; count: number } + | { + type: "reduplication"; + word: string; + count: number; + modifyingParticle: null | ModifyingParticle; + } | { type: "number"; number: number }; /** Represents a single modifier. */ export type Modifier = diff --git a/src/filter.ts b/src/filter.ts index 1d21a8e..8c1cce6 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -54,7 +54,7 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ }, // "n" cannot modify a word (wordUnit) => { - if (wordUnit.type === "default") { + if (wordUnit.type === "default" || wordUnit.type === "reduplication") { const modifyingParticle = wordUnit.modifyingParticle; const hasN = modifyingParticle != null && ((modifyingParticle.type === "word" && @@ -71,7 +71,7 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ }, // multiple "a" cannot modify a word (wordUnit) => { - if (wordUnit.type === "default") { + if (wordUnit.type === "default" || wordUnit.type === "reduplication") { const modifyingParticle = wordUnit.modifyingParticle; if ( modifyingParticle != null && modifyingParticle.type === "multiple a" From 78829e2b7caddedf36d1a0b1604f4aea9e7114b7 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 14:33:41 +0800 Subject: [PATCH 359/738] format --- src/lexer.ts | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 2a236cd..bfb6e0a 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -223,15 +223,18 @@ function multipleA(): Lexer { } /** Parses lengthened words. */ function longWord(): Lexer { - return match(/[an]/, 'long "a" or "n"').then(([word, _]) => - count(allAtLeastOnce(matchString(word))) - .skip(spaces()) - .map((count) => ({ - type: "long word", - word, - length: count + 1, - })) - ); + return match(/[an]/, 'long "a" or "n"') + .then(([word, _]) => + count(allAtLeastOnce(matchString(word))) + .map((count) => + ({ + type: "long word", + word, + length: count + 1, + }) as TokenTree & { type: "long word" } + ) + ) + .skip(spaces()); } /** Parses X ala X constructions. */ function xAlaX(): Lexer { @@ -283,15 +286,17 @@ function cartoucheElement(): Lexer { ), sequence( singleUcsurWord(), - count(allAtLeastOnce( - choiceOnlyOne( - match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), - specificUcsurCharacter("󱦜", "middle dot", { - allowVariation: true, - allowSpace: true, - }), + count( + allAtLeastOnce( + choiceOnlyOne( + match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), + specificUcsurCharacter("󱦜", "middle dot", { + allowVariation: true, + allowSpace: true, + }), + ), ), - )), + ), ) .map(([word, dots]) => { const VOWEL = /[aeiou]/; From 5298568a728b2dc481640855e924ee3b37bc4304 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 14:38:39 +0800 Subject: [PATCH 360/738] something --- src/lexer.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index bfb6e0a..98bc861 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -299,13 +299,11 @@ function cartoucheElement(): Lexer { ), ) .map(([word, dots]) => { - const VOWEL = /[aeiou]/; - const MORAE = /[aeiou]|[jklmnpstw][aeiou]|n/g; let count = dots; - if (VOWEL.test(word[0])) { + if (/[aeiou]/.test(word[0])) { count++; } - const morae = word.match(MORAE)!; + const morae = word.match(/[aeiou]|[jklmnpstw][aeiou]|n/g)!; if (morae.length < count) { throw new UnrecognizedError("Excess dots"); } From 721493776f122f99b8d43cd2871befbb25d7a48e Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 14:43:19 +0800 Subject: [PATCH 361/738] use exponentiation operator --- src/ast-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index bcd0b82..128cd8f 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -311,7 +311,7 @@ function number(): AstParser { ) .map(([rest, last]) => [...rest, [last, 0]].reduce( - (result, [sub, ale]) => result + sub * Math.pow(100, ale), + (result, [sub, ale]) => result + sub * 100 ** ale, 0, ) ), From 8d05394ad0f1e27f6290477e0b53c27fbaf2fb9a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 15:26:37 +0800 Subject: [PATCH 362/738] simplify filter functions --- src/filter.ts | 100 ++++++++++++++------------------------------------ 1 file changed, 27 insertions(+), 73 deletions(-) diff --git a/src/filter.ts b/src/filter.ts index 8c1cce6..39a0296 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -4,6 +4,7 @@ import { Clause, FullClause, Modifier, + ModifyingParticle, MultiplePhrases, Phrase, Preposition, @@ -52,34 +53,15 @@ export const WORD_UNIT_RULES: Array<(wordUnit: WordUnit) => boolean> = [ return true; } }, - // "n" cannot modify a word + // "n" and multiple "a" cannot modify a word (wordUnit) => { - if (wordUnit.type === "default" || wordUnit.type === "reduplication") { - const modifyingParticle = wordUnit.modifyingParticle; - const hasN = modifyingParticle != null && - ((modifyingParticle.type === "word" && - modifyingParticle.word === "n") || - (modifyingParticle.type === "long word" && - modifyingParticle.word === "n")); - if (hasN) { - throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, - ); - } - } - return true; - }, - // multiple "a" cannot modify a word - (wordUnit) => { - if (wordUnit.type === "default" || wordUnit.type === "reduplication") { - const modifyingParticle = wordUnit.modifyingParticle; - if ( - modifyingParticle != null && modifyingParticle.type === "multiple a" - ) { - throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, - ); - } + if ( + (wordUnit.type === "default" || wordUnit.type === "reduplication") && + isMultipleAOrN(wordUnit.modifyingParticle) + ) { + throw new UnrecognizedError( + `${describe(wordUnit.modifyingParticle!)} modifying a word`, + ); } return true; }, @@ -286,34 +268,15 @@ export const PHRASE_RULE: Array<(phrase: Phrase) => boolean> = [ phrase.type !== "default" || phrase.modifyingParticle == null || phrase.modifiers.length !== 0, - // "n" cannot modify a phrase + // "n" and multiple "a" cannot modify a phrase (wordUnit) => { - if (wordUnit.type === "default" || wordUnit.type === "preverb") { - const modifyingParticle = wordUnit.modifyingParticle; - const hasN = modifyingParticle != null && - ((modifyingParticle.type === "word" && - modifyingParticle.word === "n") || - (modifyingParticle.type === "long word" && - modifyingParticle.word === "n")); - if (hasN) { - throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, - ); - } - } - return true; - }, - // multiple "a" cannot modify a phrase - (wordUnit) => { - if (wordUnit.type === "default" || wordUnit.type === "preverb") { - const modifyingParticle = wordUnit.modifyingParticle; - if ( - modifyingParticle != null && modifyingParticle.type === "multiple a" - ) { - throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, - ); - } + if ( + (wordUnit.type === "default" || wordUnit.type === "preverb") && + isMultipleAOrN(wordUnit.modifyingParticle) + ) { + throw new UnrecognizedError( + `${describe(wordUnit.modifyingParticle!)} modifying a word`, + ); } return true; }, @@ -336,27 +299,11 @@ export const PREPOSITION_RULE: Array<(phrase: Preposition) => boolean> = [ } return true; }, - // "n" cannot modify a preposition + // "n" and multiple "a" cannot modify a preposition (wordUnit) => { - const modifyingParticle = wordUnit.modifyingParticle; - const hasN = modifyingParticle != null && - ((modifyingParticle.type === "word" && - modifyingParticle.word === "n") || - (modifyingParticle.type === "long word" && - modifyingParticle.word === "n")); - if (hasN) { + if (isMultipleAOrN(wordUnit.modifyingParticle)) { throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, - ); - } - return true; - }, - // multiple "a" cannot modify a preposition - (wordUnit) => { - const modifyingParticle = wordUnit.modifyingParticle; - if (modifyingParticle != null && modifyingParticle.type === "multiple a") { - throw new UnrecognizedError( - `${describe(modifyingParticle)} modifying a word`, + `${describe(wordUnit.modifyingParticle!)} modifying a word`, ); } return true; @@ -491,3 +438,10 @@ function hasPrepositionInPhrase(phrase: Phrase): boolean { return false; } } +function isMultipleAOrN(modifyingParticle: null | ModifyingParticle): boolean { + return modifyingParticle != null && + (modifyingParticle.type === "multiple a" || + ((modifyingParticle.type === "word" || + modifyingParticle.type === "long word") && + modifyingParticle.word === "n")); +} From adcf8a93aca271aee9e59df31ecc8933d5f28441 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 15:45:03 +0800 Subject: [PATCH 363/738] add link to contributing instruction --- index.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index a9f66d2..ca96ce0 100644 --- a/index.html +++ b/index.html @@ -46,7 +46,11 @@

          ilo Token

          Github: neverRare/ilo-token. + > + (Contributing Instruction).
        • Email: From 5271cc2cf5cb1a0f0ca587023ba3224ec695322d Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 16:35:56 +0800 Subject: [PATCH 364/738] update dependencies --- dev-deps.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-deps.ts b/dev-deps.ts index 8351cd2..54854e8 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,3 +1,3 @@ -export * as Emit from "https://deno.land/x/emit@0.38.2/mod.ts"; -export * as Debounce from "https://deno.land/std@0.218.2/async/debounce.ts"; +export * as Emit from "https://deno.land/x/emit@0.40.0/mod.ts"; +export * as Debounce from "https://deno.land/std@0.224.0/async/debounce.ts"; export * as TeloMisikeke from "./telo-misikeke/build.ts"; From 18ce300d466ace8b4e228a84f1572e1272a8b9fe Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 16:58:01 +0800 Subject: [PATCH 365/738] add try finally --- bundle.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bundle.ts b/bundle.ts index 327a1ee..d48620d 100644 --- a/bundle.ts +++ b/bundle.ts @@ -30,9 +30,13 @@ switch (Deno.args[0]) { } }, 500); const watcher = Deno.watchFs(["./src/", "./telo-misikeke/"]); - builder(); - for await (const _ of watcher) { + try { builder(); + for await (const _ of watcher) { + builder(); + } + } finally { + watcher.close(); } throw new Error("unreachable"); } From 21d92968e92b27b46e36d8216b9afbf88a2b3b73 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 17:13:13 +0800 Subject: [PATCH 366/738] improve to string coercion --- src/lexer.ts | 2 +- src/main.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 98bc861..a10e813 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -56,7 +56,7 @@ function match( regex: RegExp, description: string, ): Lexer { - const newRegex = new RegExp("^" + regex.source, regex.flags); + const newRegex = new RegExp(`^${regex.source}`, regex.flags); return new Parser((src) => { const match = src.match(newRegex); if (match != null) { diff --git a/src/main.ts b/src/main.ts index 3b55109..ff3a31d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -117,7 +117,7 @@ function updateOutput(): void { if (unreachableError instanceof Error) { error = unreachableError.message; } else { - error = unreachableError?.toString() + ""; + error = `${unreachableError?.toString()}`; } error += " (please report this)"; outputErrors([error]); @@ -132,8 +132,9 @@ if (typeof document !== "undefined") { // Auto resize function resizeTextarea() { elements!.input.style.height = "auto"; - elements!.input.style.height = - Math.max(50, elements!.input.scrollHeight + 20) + "px"; + elements!.input.style.height = `${ + Math.max(50, elements!.input.scrollHeight + 20) + }px`; } resizeTextarea(); elements!.input.addEventListener("input", resizeTextarea); From 0eaee2a6c8adeacfc6698a658441a9da9f733730 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 17:58:26 +0800 Subject: [PATCH 367/738] remove need for unreachable error --- src/dictionary.ts | 11 ++--------- src/error.ts | 6 ------ src/lexer.ts | 18 +++++++----------- src/old-translator.ts | 8 +++----- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/src/dictionary.ts b/src/dictionary.ts index e265369..f8a4675 100644 --- a/src/dictionary.ts +++ b/src/dictionary.ts @@ -5,7 +5,6 @@ import { DeterminerQuantity, DeterminerType, } from "./english-ast.ts"; -import { UnreachableError } from "./error.ts"; export const PARTICLE_DEFINITION: { [word: string]: Array } = { a: [ @@ -1320,9 +1319,6 @@ function verb( gerund?: string, ): Definition & { type: "verb" } { if (typeof word === "string") { - if (typeof gerund !== "string") { - throw new UnreachableError(); - } const { past, present, condensed } = parseVerb(word); return { type: "verb", @@ -1330,7 +1326,7 @@ function verb( past, pastParticiple: past, condensed, - gerund, + gerund: gerund as string, object: true, }; } else { @@ -1369,9 +1365,6 @@ function intransitiveVerb( gerund?: string, ): Definition & { type: "verb" } { if (typeof word === "string") { - if (typeof gerund !== "string") { - throw new UnreachableError(); - } const { past, present, condensed } = parseVerb(word); return { type: "verb", @@ -1379,7 +1372,7 @@ function intransitiveVerb( past, pastParticiple: past, condensed, - gerund, + gerund: gerund as string, object: false, }; } else { diff --git a/src/error.ts b/src/error.ts index 6afd644..cb1648b 100644 --- a/src/error.ts +++ b/src/error.ts @@ -2,12 +2,6 @@ /** Represents Error used by `Output`. */ export class OutputError extends Error {} -/** Represents errors that cannot be reached. */ -export class UnreachableError extends Error { - constructor() { - super("Reached unreachable error."); - } -} /** Represents Error with unexpected and expected elements. */ export class UnexpectedError extends OutputError { constructor(unexpected: string, expected: string) { diff --git a/src/lexer.ts b/src/lexer.ts index a10e813..a402d32 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -7,11 +7,7 @@ */ import { Output } from "./output.ts"; -import { - UnexpectedError, - UnreachableError, - UnrecognizedError, -} from "./error.ts"; +import { UnexpectedError, UnrecognizedError } from "./error.ts"; import { all, allAtLeastOnce, @@ -64,12 +60,14 @@ function match( } else if (src === "") { return new Output(new UnexpectedError("end of sentence", description)); } else { - const token = src.match(/[^\s]*/)?.[0]; - if (token != null) { - return new Output(new UnexpectedError(`"${token}"`, description)); + const token = src.match(/[^\s]*/)![0]; + let tokenDescription: string; + if (token === "") { + tokenDescription = "space"; } else { - throw new UnreachableError(); + tokenDescription = `"${token}"`; } + return new Output(new UnexpectedError(tokenDescription, description)); } }); } @@ -363,8 +361,6 @@ function quotation( throw new UnrecognizedError("Mismatched quotation marks"); } break; - default: - throw new UnreachableError(); } return { type: "quotation", tokenTree, leftMark, rightMark }; }); diff --git a/src/old-translator.ts b/src/old-translator.ts index 7a96a47..b56b2e3 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -12,7 +12,7 @@ import { } from "./ast.ts"; import { Output } from "./output.ts"; import { parse } from "./ast-parser.ts"; -import { OutputError, TodoError, UnreachableError } from "./error.ts"; +import { OutputError, TodoError } from "./error.ts"; import { DEFINITION } from "./old-definition.ts"; /** A special kind of Output that translators returns. */ @@ -204,7 +204,7 @@ function phraseAs( function translateMultiplePhrases( phrases: MultiplePhrases, translator: (phrase: Phrase) => TranslationOutput, - level = 2, + level: 1 | 2 = 2, ): TranslationOutput { switch (phrases.type) { case "single": @@ -219,7 +219,7 @@ function translateMultiplePhrases( } const translations = Output.combine( ...phrases.phrases.map((phrases) => - translateMultiplePhrases(phrases, translator, level - 1) + translateMultiplePhrases(phrases, translator, 1) ), ); switch (level) { @@ -244,8 +244,6 @@ function translateMultiplePhrases( case 1: return translations .map((phrases) => phrases.join([" ", conjunction, " "].join(""))); - default: - throw new UnreachableError(); } } } From 3dca433bba1a0fa06d9ed3561e78323cfb0d73f2 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 18:15:51 +0800 Subject: [PATCH 368/738] remove unused import --- src/translator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/translator.ts b/src/translator.ts index f8267d2..6eabd8d 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -1,7 +1,6 @@ import { parse } from "./ast-parser.ts"; import * as TokiPona from "./ast.ts"; import { - NUMERAL, PARTICLE_DEFINITION, PREPOSITION_DEFINITION, SPECIAL_CONTENT_WORD_DEFINITION, From 0fcdfb362873abc3239290ed9b64c7a89c22c1e3 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 18:48:28 +0800 Subject: [PATCH 369/738] custom debounce function --- bundle.ts | 22 ++++++++++++++++++++-- dev-deps.ts | 1 - 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/bundle.ts b/bundle.ts index d48620d..f868c54 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,4 +1,4 @@ -import { Debounce, Emit, TeloMisikeke } from "./dev-deps.ts"; +import { Emit, TeloMisikeke } from "./dev-deps.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./main.js", import.meta.url); @@ -17,7 +17,7 @@ switch (Deno.args[0]) { console.log("Building done!"); break; case "watch": { - const builder = Debounce.debounce(async () => { + const builder = debounce(async () => { console.log("Starting to build..."); try { await build({ @@ -43,3 +43,21 @@ switch (Deno.args[0]) { default: throw new Error(`Unrecognized build option, ${Deno.args[0]}`); } +function debounce(callback: () => Promise, delay: number): () => void { + let previous = { aborted: true }; + let current = Promise.resolve(); + return () => { + previous.aborted = true; + const newPrevious = { aborted: false }; + setTimeout(() => { + if (!newPrevious.aborted) { + current = current + .then(() => callback()) + .catch((error) => { + throw error; + }); + } + }, delay); + previous = newPrevious; + }; +} diff --git a/dev-deps.ts b/dev-deps.ts index 54854e8..2a94a30 100644 --- a/dev-deps.ts +++ b/dev-deps.ts @@ -1,3 +1,2 @@ export * as Emit from "https://deno.land/x/emit@0.40.0/mod.ts"; -export * as Debounce from "https://deno.land/std@0.224.0/async/debounce.ts"; export * as TeloMisikeke from "./telo-misikeke/build.ts"; From e99859dc5f26e8c97518dc413bf6457c046b2c49 Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 19:06:16 +0800 Subject: [PATCH 370/738] migrate to jsr --- bundle.ts | 13 +++++++------ deno.json | 3 +++ deps.ts | 1 - dev-deps.ts | 2 -- src/main.ts | 4 ++-- telo-misikeke/build.ts | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 deps.ts delete mode 100644 dev-deps.ts diff --git a/bundle.ts b/bundle.ts index f868c54..e44ae55 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,26 +1,27 @@ -import { Emit, TeloMisikeke } from "./dev-deps.ts"; +import { bundle, BundleOptions } from "@deno/emit"; +import { buildTeloMisikeke } from "./telo-misikeke/build.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./main.js", import.meta.url); -async function build(options: Emit.BundleOptions): Promise { - const result = await Emit.bundle(SOURCE, options); +async function buildCode(options: BundleOptions): Promise { + const result = await bundle(SOURCE, options); const { code } = result; await Deno.writeTextFile(DESTINATION, code); } switch (Deno.args[0]) { case "build": console.log("Building telo misikeke..."); - await TeloMisikeke.build(); + await buildTeloMisikeke(); console.log("Building main.js..."); - await build({ minify: true, type: "classic" }); + await buildCode({ minify: true, type: "classic" }); console.log("Building done!"); break; case "watch": { const builder = debounce(async () => { console.log("Starting to build..."); try { - await build({ + await buildCode({ compilerOptions: { inlineSourceMap: true }, type: "classic", }); diff --git a/deno.json b/deno.json index 05afe4b..d50de30 100644 --- a/deno.json +++ b/deno.json @@ -25,5 +25,8 @@ "./dev-deps.ts", "./deps.ts" ] + }, + "imports": { + "@deno/emit": "jsr:@deno/emit@^0.40.2" } } diff --git a/deps.ts b/deps.ts deleted file mode 100644 index 45d71f3..0000000 --- a/deps.ts +++ /dev/null @@ -1 +0,0 @@ -export * as TeloMisikeke from "./telo-misikeke/telo-misikeke.ts"; diff --git a/dev-deps.ts b/dev-deps.ts deleted file mode 100644 index 2a94a30..0000000 --- a/dev-deps.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * as Emit from "https://deno.land/x/emit@0.40.0/mod.ts"; -export * as TeloMisikeke from "./telo-misikeke/build.ts"; diff --git a/src/main.ts b/src/main.ts index ff3a31d..f8eb0e4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; -import { TeloMisikeke } from "../deps.ts"; +import { errors } from "../telo-misikeke/telo-misikeke.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; @@ -101,7 +101,7 @@ function updateOutput(): void { } else { let error: Array = []; if (settings.get("use-telo-misikeke")) { - error = TeloMisikeke.errors(source); + error = errors(source); } if (error.length === 0) { error = [ diff --git a/telo-misikeke/build.ts b/telo-misikeke/build.ts index bcc7072..73e33f4 100644 --- a/telo-misikeke/build.ts +++ b/telo-misikeke/build.ts @@ -29,7 +29,7 @@ async function buildFile( //write the code await Deno.writeTextFile(destination, file); } -export async function build(): Promise { +export async function buildTeloMisikeke(): Promise { await Promise.all( SOURCE .map((file) => buildFile(file.source, file.destination, file.exportItem)), From a7a3430c6f548f22a47b6862ae371cd7cbe1ef8e Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 19:38:46 +0800 Subject: [PATCH 371/738] simplify deno.json --- deno.json | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/deno.json b/deno.json index d50de30..2f589cd 100644 --- a/deno.json +++ b/deno.json @@ -9,22 +9,10 @@ "watch": "deno run --allow-read --allow-write --allow-env --allow-net ./bundle.ts watch" }, "fmt": { - "include": [ - "./src/**/*.ts", - "./telo-misikeke/**/*", - "./bundle.ts", - "./dev-deps.ts", - "./deps.ts" - ] + "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "lint": { - "include": [ - "./src/**/*.ts", - "./telo-misikeke/**/*", - "./bundle.ts", - "./dev-deps.ts", - "./deps.ts" - ] + "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "imports": { "@deno/emit": "jsr:@deno/emit@^0.40.2" From 94fff2d67ac2ac47986d56fd83e2b7c35c5e3f7a Mon Sep 17 00:00:00 2001 From: neverRare Date: Wed, 1 May 2024 19:54:53 +0800 Subject: [PATCH 372/738] dist --- .github/workflows/deno.yml | 2 +- .gitignore | 2 +- README.md | 6 +++--- index.html => dist/index.html | 0 style.css => dist/style.css | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename index.html => dist/index.html (100%) rename style.css => dist/style.css (100%) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index e26630f..5862004 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -35,7 +35,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: "." + path: "/dist/" - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 4e37a0b..8d2a340 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -main.js +dist/main.js telo-misikeke/rules.js telo-misikeke/Parser.js diff --git a/README.md b/README.md index e80a2d2..487c930 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ You'll need the following in order to run commands: ## Building -This fetches more dependencies needed and builds `./main.js` as a minified file ready for production use. +This fetches more dependencies needed and builds `./dist/main.js` as a minified file ready for production use. ``` deno task build @@ -24,7 +24,7 @@ deno task build Before running this command, you'll need to run `deno task build` first. This is because `deno task watch` doesn't fetch dependencies. You'll only need to run this command once. -This builds `./main.js` as a non-minified file with source mapping, intended for testing and debugging. This command also watches the source codes in `./src/` path and rebuilds `./main.js` whenever there are changes. +This builds `./dist/main.js` as a non-minified file with source mapping, intended for testing and debugging. This command also watches the source codes in `./src/` path and rebuilds `./dist/main.js` whenever there are changes. ``` deno task watch @@ -34,7 +34,7 @@ To stop this command, simply press Ctrl + C. ## Running locally -After building or watching, you can directly run `./index.html` using your favorite browser with some caveat however: UCSUR characters will display as tofu. +After building or watching, you can directly run `./dist/index.js` using your favorite browser with some caveat however: UCSUR characters will display as tofu. This could be mitigated by making use of local server but I didn't do that, there's little need for that. diff --git a/index.html b/dist/index.html similarity index 100% rename from index.html rename to dist/index.html diff --git a/style.css b/dist/style.css similarity index 100% rename from style.css rename to dist/style.css From 1125a903b415d01b0e695bbfed8b9bd30c555947 Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 2 May 2024 07:28:04 +0800 Subject: [PATCH 373/738] move icon to dist --- {icon => dist/icon}/16.png | Bin {icon => dist/icon}/180.png | Bin {icon => dist/icon}/192.png | Bin {icon => dist/icon}/32.png | Bin {icon => dist/icon}/48.png | Bin {icon => dist/icon}/ico.ico | Bin 6 files changed, 0 insertions(+), 0 deletions(-) rename {icon => dist/icon}/16.png (100%) rename {icon => dist/icon}/180.png (100%) rename {icon => dist/icon}/192.png (100%) rename {icon => dist/icon}/32.png (100%) rename {icon => dist/icon}/48.png (100%) rename {icon => dist/icon}/ico.ico (100%) diff --git a/icon/16.png b/dist/icon/16.png similarity index 100% rename from icon/16.png rename to dist/icon/16.png diff --git a/icon/180.png b/dist/icon/180.png similarity index 100% rename from icon/180.png rename to dist/icon/180.png diff --git a/icon/192.png b/dist/icon/192.png similarity index 100% rename from icon/192.png rename to dist/icon/192.png diff --git a/icon/32.png b/dist/icon/32.png similarity index 100% rename from icon/32.png rename to dist/icon/32.png diff --git a/icon/48.png b/dist/icon/48.png similarity index 100% rename from icon/48.png rename to dist/icon/48.png diff --git a/icon/ico.ico b/dist/icon/ico.ico similarity index 100% rename from icon/ico.ico rename to dist/icon/ico.ico From 9502f9d4163cb39de77d8894ec1c69415cd4b4fc Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 2 May 2024 14:07:37 +0800 Subject: [PATCH 374/738] update path --- bundle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle.ts b/bundle.ts index e44ae55..3c67490 100644 --- a/bundle.ts +++ b/bundle.ts @@ -2,7 +2,7 @@ import { bundle, BundleOptions } from "@deno/emit"; import { buildTeloMisikeke } from "./telo-misikeke/build.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); -const DESTINATION = new URL("./main.js", import.meta.url); +const DESTINATION = new URL("./dist/main.js", import.meta.url); async function buildCode(options: BundleOptions): Promise { const result = await bundle(SOURCE, options); From e468221b7bb343d36ef63971d87c1d3a81f0bfbe Mon Sep 17 00:00:00 2001 From: neverRare Date: Thu, 2 May 2024 15:38:49 +0800 Subject: [PATCH 375/738] deno stuffs --- deno.json | 3 +-- deno.lock | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 deno.lock diff --git a/deno.json b/deno.json index 2f589cd..13fc738 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,4 @@ { - "lock": false, "compilerOptions": { "target": "esnext", "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] @@ -15,6 +14,6 @@ "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "imports": { - "@deno/emit": "jsr:@deno/emit@^0.40.2" + "@deno/emit": "jsr:@deno/emit@0.40.2" } } diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..ab9fbc4 --- /dev/null +++ b/deno.lock @@ -0,0 +1,75 @@ +{ + "version": "3", + "packages": { + "specifiers": { + "jsr:@deno/cache-dir@0.8": "jsr:@deno/cache-dir@0.8.0", + "jsr:@deno/emit@0.40.2": "jsr:@deno/emit@0.40.2", + "jsr:@std/assert@^0.218.2": "jsr:@std/assert@0.218.2", + "jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0", + "jsr:@std/bytes@^0.218.2": "jsr:@std/bytes@0.218.2", + "jsr:@std/fmt@^0.218.2": "jsr:@std/fmt@0.218.2", + "jsr:@std/fs@^0.218.2": "jsr:@std/fs@0.218.2", + "jsr:@std/io@^0.218.2": "jsr:@std/io@0.218.2", + "jsr:@std/path@^0.218.2": "jsr:@std/path@0.218.2", + "jsr:@std/path@^0.223.0": "jsr:@std/path@0.223.0" + }, + "jsr": { + "@deno/cache-dir@0.8.0": { + "integrity": "e87e80a404958f6350d903e6238b72afb92468378b0b32111f7a1e4916ac7fe7", + "dependencies": [ + "jsr:@std/fmt@^0.218.2", + "jsr:@std/fs@^0.218.2", + "jsr:@std/io@^0.218.2", + "jsr:@std/path@^0.218.2" + ] + }, + "@deno/emit@0.40.2": { + "integrity": "892d50a17eeb7c3f597cbfc2e0463c065836ddcbfecdfcb4e2f4d430b49d2305", + "dependencies": [ + "jsr:@deno/cache-dir@0.8", + "jsr:@std/path@^0.223.0" + ] + }, + "@std/assert@0.218.2": { + "integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf" + }, + "@std/assert@0.223.0": { + "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" + }, + "@std/bytes@0.218.2": { + "integrity": "91fe54b232dcca73856b79a817247f4a651dbb60d51baafafb6408c137241670" + }, + "@std/fmt@0.218.2": { + "integrity": "99526449d2505aa758b6cbef81e7dd471d8b28ec0dcb1491d122b284c548788a" + }, + "@std/fs@0.218.2": { + "integrity": "dd9431453f7282e8c577cc22c9e6d036055a9a980b5549f887d6012969fabcca" + }, + "@std/io@0.218.2": { + "integrity": "c64fbfa087b7c9d4d386c5672f291f607d88cb7d44fc299c20c713e345f2785f", + "dependencies": [ + "jsr:@std/assert@^0.218.2", + "jsr:@std/bytes@^0.218.2" + ] + }, + "@std/path@0.218.2": { + "integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662", + "dependencies": [ + "jsr:@std/assert@^0.218.2" + ] + }, + "@std/path@0.223.0": { + "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", + "dependencies": [ + "jsr:@std/assert@^0.223.0" + ] + } + } + }, + "remote": {}, + "workspace": { + "dependencies": [ + "jsr:@deno/emit@0.40.2" + ] + } +} From e6cb27ab7425831ee14fc413b51717e4ef853f22 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 15:22:16 +0800 Subject: [PATCH 376/738] this is unnecessary --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index f8eb0e4..e82c0f2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -117,7 +117,7 @@ function updateOutput(): void { if (unreachableError instanceof Error) { error = unreachableError.message; } else { - error = `${unreachableError?.toString()}`; + error = `${unreachableError}`; } error += " (please report this)"; outputErrors([error]); From ad5436178c2004f20d9ada193e66d77f6e63f1f6 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 15:50:11 +0800 Subject: [PATCH 377/738] format --- src/ast-parser.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index 128cd8f..fb52475 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -478,9 +478,9 @@ function nestedPhrasesOnly( return sequence( nestedPhrases(rest), manyAtLeastOnce( - optionalComma().with(specificWord(first)).with( - nestedPhrases(rest), - ), + optionalComma() + .with(specificWord(first)) + .with(nestedPhrases(rest)), ), ) .map(([group, moreGroups]) => ({ @@ -612,9 +612,9 @@ function associatedPredicates( return sequence( nestedPhrasesOnly(nestingRule), optional( - optionalComma().with(specificWord("e")).with( - nestedPhrases(["e", "anu"]), - ), + optionalComma() + .with(specificWord("e")) + .with(nestedPhrases(["e", "anu"])), ), many(optionalComma().with(preposition())), ) @@ -655,12 +655,14 @@ function multiplePredicates( multiplePredicates(rest), ), manyAtLeastOnce( - optionalComma().with(specificWord(first)).with( - choice( - associatedPredicates(nestingRule), - multiplePredicates(rest), + optionalComma() + .with(specificWord(first)) + .with( + choice( + associatedPredicates(nestingRule), + multiplePredicates(rest), + ), ), - ), ), ) .map(([group, moreGroups]) => @@ -718,9 +720,9 @@ function clause(): AstParser { .map((phrases) => ({ type: "o vocative", phrases }) as Clause), sequence( subjectPhrases(), - optionalComma().with(specificWord("li")).with( - multiplePredicates(["li", "anu"]), - ), + optionalComma() + .with(specificWord("li")) + .with(multiplePredicates(["li", "anu"])), ) .map(([subjects, predicates]) => ({ @@ -737,9 +739,9 @@ function clause(): AstParser { ), sequence( subjectPhrases(), - optionalComma().with(specificWord("o")).with( - multiplePredicates(["o", "anu"]), - ), + optionalComma() + .with(specificWord("o")) + .with(multiplePredicates(["o", "anu"])), ) .map(([subjects, predicates]) => ({ type: "o clause", subjects, predicates }) as Clause From 17222409ff097fb688e1d4e9e767aef30a41edf0 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 15:56:39 +0800 Subject: [PATCH 378/738] remove unneeded operators --- src/lexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index a402d32..153d96a 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -458,9 +458,9 @@ function characterLongGlyph( .filter(([before, _, after]) => before.length !== 0 || after.length !== 0) .map(([before, words, after]) => ({ type: "long glyph", - before: before ?? [], + before, words, - after: after ?? [], + after, })); } /** Parses long glyph that only contains spaces. */ From f592854aea82521db4aa300ca2810c934e659890 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 15:58:08 +0800 Subject: [PATCH 379/738] rename --- src/lexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 153d96a..20ba126 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -443,7 +443,7 @@ function characterLongGlyph( END_OF_REVERSE_LONG_GLYPH, ), ) - .map((before) => before ?? []), + .map((array) => array ?? []), longGlyphHead(), optionalAll( longCharacterContainer( @@ -452,7 +452,7 @@ function characterLongGlyph( END_OF_LONG_GLYPH, ), ) - .map((before) => before ?? []), + .map((array) => array ?? []), ) .skip(spaces()) .filter(([before, _, after]) => before.length !== 0 || after.length !== 0) From 69b18275d7a5fd1f0f5a5dd9db63e6c2eddfa293 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 16:11:39 +0800 Subject: [PATCH 380/738] simplify field access --- src/ast-parser.ts | 2 +- src/ast.ts | 2 +- src/filter.ts | 4 ++-- src/old-translator.ts | 6 +++--- src/translator.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ast-parser.ts b/src/ast-parser.ts index fb52475..b39c66b 100644 --- a/src/ast-parser.ts +++ b/src/ast-parser.ts @@ -198,7 +198,7 @@ function xAlaX( if (leftGlyph.type !== "word") { throw new UnexpectedError(describe(leftGlyph), "word"); } - const word = leftGlyph.word; + const { word } = leftGlyph; const rightGlyph = parseXAlaXSide(longGlyph.after, "forward long glyph"); if (rightGlyph.type !== "word" || rightGlyph.word !== word) { throw new UnexpectedError(describe(rightGlyph), `"${word}"`); diff --git a/src/ast.ts b/src/ast.ts index 67cd115..e83b1ee 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -137,7 +137,7 @@ export function someModifierInPhrase( return phrase.modifiers.some(checker) || someModifierInPhrase(phrase.phrase, whenQuotation, checker); case "preposition": { - const preposition = phrase.preposition; + const { preposition } = phrase; return preposition.modifiers.some(checker) || someModifierInMultiplePhrases( preposition.phrases, diff --git a/src/filter.ts b/src/filter.ts index 39a0296..787a8df 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -135,7 +135,7 @@ export const MODIFIER_RULES: Array<(modifier: Modifier) => boolean> = [ // pi must follow phrases with modifier (modifier) => { if (modifier.type === "pi") { - const phrase = modifier.phrase; + const { phrase } = modifier; if (phrase.type === "default" && phrase.modifiers.length === 0) { throw new UnrecognizedError("pi followed by one word"); } @@ -351,7 +351,7 @@ export const FULL_CLAUSE_RULE: Array<(fullClase: FullClause) => boolean> = [ // Prevent "taso ala taso" (fullClause) => { if (fullClause.type === "default") { - const preclause = fullClause.preclause; + const { preclause } = fullClause; if ( preclause != null && preclause.type === "taso" && diff --git a/src/old-translator.ts b/src/old-translator.ts index b56b2e3..33adc41 100644 --- a/src/old-translator.ts +++ b/src/old-translator.ts @@ -262,7 +262,7 @@ function translateClause(clause: Clause): TranslationOutput { return phrases.phrases.some(hasEn); } }; - const phrases = clause.phrases; + const { phrases } = clause; const translations = translateMultiplePhrases( phrases, (phrase) => phraseAs("noun", phrase), @@ -295,7 +295,7 @@ function translateClause(clause: Clause): TranslationOutput { /** Translates a full clause. */ function translateFullClause(fullClause: FullClause): TranslationOutput { // let but = ""; - // const taso = fullClause.taso; + // const {taso} = fullClause // if (taso) { // if (taso.type === "default") { // but = "but "; @@ -304,7 +304,7 @@ function translateFullClause(fullClause: FullClause): TranslationOutput { // } // } // let isntIt = ""; - // const anuSeme = fullClause.anuSeme; + // const {anuSeme} = fullClause // if (anuSeme) { // if (anuSeme.type === "default") { // isntIt = ", isn't it"; diff --git a/src/translator.ts b/src/translator.ts index 6eabd8d..0734a81 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -19,7 +19,7 @@ function multipleSentences( ): Output> { switch (sentences.type) { case "single word": { - const word = sentences.word; + const { word } = sentences; return new Output([ ...PARTICLE_DEFINITION[word] ?? [], ...SPECIAL_CONTENT_WORD_DEFINITION[word] ?? [], From 8ed09518bd30650f8217b93160ceb2f0c2e8f8e8 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 16:26:02 +0800 Subject: [PATCH 381/738] use unicode flag --- README.md | 2 +- src/lexer.ts | 36 ++++++------------------------------ 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 487c930..7a93201 100644 --- a/README.md +++ b/README.md @@ -55,4 +55,4 @@ Some parts of the code make use of sitelen pona UCSUR characters. To display pro Oftentimes, you don't need to be able to type UCSUR in the source codes. We reduce UCSUR used in code and prefer to use latin letters instead. -Also, take note that UCSUR characters are two characters wide in JavaScript string. Be careful with string and regex manipulation. +Also, take note that UCSUR characters are two characters wide in JavaScript string. Be careful with string and regex manipulation. If you're using regex, use the [`u` flag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode). diff --git a/src/lexer.ts b/src/lexer.ts index 20ba126..a4cd5c4 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -254,45 +254,21 @@ function comma(): Lexer { } /** Parses a punctuation. */ function punctuation(): Lexer { - return choiceOnlyOne( - match(/([.,:;?!])\s*/, "punctuation") - .map(([_, punctuation]) => punctuation), - // NOTE: maybe these mapping are unnecessary - specificUcsurCharacter("󱦜", "middle dot", { - allowVariation: true, - allowSpace: true, - }) - .map(() => "."), - specificUcsurCharacter("󱦝", "middle dot", { - allowVariation: true, - allowSpace: true, - }) - .map(() => ":"), - ); + return match(/([.,:;?!󱦜󱦝])\s*/u, "punctuation") + .map(([_, punctuation]) => punctuation); } /** Parses cartouche element and returns the phonemes or letters it represents. */ function cartoucheElement(): Lexer { return choiceOnlyOne( - singleUcsurWord().skip( - choiceOnlyOne( - match(/(\uff1a)\s*/, "full width colon").map(([_, dot]) => dot), - specificUcsurCharacter("󱦝", "colon", { - allowVariation: true, - allowSpace: true, - }), + singleUcsurWord() + .skip( + match(/([\uff1a󱦝])\s*/u, "full width colon").map(([_, dot]) => dot), ), - ), sequence( singleUcsurWord(), count( allAtLeastOnce( - choiceOnlyOne( - match(/([・。/])\s*/, "full width dot").map(([_, dot]) => dot), - specificUcsurCharacter("󱦜", "middle dot", { - allowVariation: true, - allowSpace: true, - }), - ), + match(/([・。/󱦜])\s*/u, "full width dot").map(([_, dot]) => dot), ), ), ) From c638595e29ea7da05198a0c8b2dc5a183dda517c Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 16:53:10 +0800 Subject: [PATCH 382/738] simplify imports to telo misikeke --- bundle.ts | 2 +- deno.json | 3 ++- src/main.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bundle.ts b/bundle.ts index 3c67490..6224884 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,5 +1,5 @@ import { bundle, BundleOptions } from "@deno/emit"; -import { buildTeloMisikeke } from "./telo-misikeke/build.ts"; +import { buildTeloMisikeke } from "telo-misikeke/build.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./dist/main.js", import.meta.url); diff --git a/deno.json b/deno.json index 13fc738..726a055 100644 --- a/deno.json +++ b/deno.json @@ -14,6 +14,7 @@ "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "imports": { - "@deno/emit": "jsr:@deno/emit@0.40.2" + "@deno/emit": "jsr:@deno/emit@0.40.2", + "telo-misikeke/": "./telo-misikeke/" } } diff --git a/src/main.ts b/src/main.ts index e82c0f2..3fdb800 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; -import { errors } from "../telo-misikeke/telo-misikeke.ts"; +import { errors } from "telo-misikeke/telo-misikeke.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; From 545ee6fcd0cde933d4e7b6e8b653ea9c030f3f83 Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 17:20:39 +0800 Subject: [PATCH 383/738] revert --- bundle.ts | 2 +- deno.json | 3 +-- src/main.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bundle.ts b/bundle.ts index 6224884..3c67490 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,5 +1,5 @@ import { bundle, BundleOptions } from "@deno/emit"; -import { buildTeloMisikeke } from "telo-misikeke/build.ts"; +import { buildTeloMisikeke } from "./telo-misikeke/build.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./dist/main.js", import.meta.url); diff --git a/deno.json b/deno.json index 726a055..13fc738 100644 --- a/deno.json +++ b/deno.json @@ -14,7 +14,6 @@ "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "imports": { - "@deno/emit": "jsr:@deno/emit@0.40.2", - "telo-misikeke/": "./telo-misikeke/" + "@deno/emit": "jsr:@deno/emit@0.40.2" } } diff --git a/src/main.ts b/src/main.ts index 3fdb800..e82c0f2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { translate } from "./old-translator.ts"; import { settings } from "./settings.ts"; -import { errors } from "telo-misikeke/telo-misikeke.ts"; +import { errors } from "../telo-misikeke/telo-misikeke.ts"; // Set to false when releasing, set to true when developing const DEVELOPMENT = true; From 062cb28b794f7ac18716c9255038f21d49c1d5db Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 17:55:05 +0800 Subject: [PATCH 384/738] use esbuild --- bundle.ts | 19 ++++++++++--------- deno.json | 4 ++-- deno.lock | 4 +++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/bundle.ts b/bundle.ts index 3c67490..7e36458 100644 --- a/bundle.ts +++ b/bundle.ts @@ -1,30 +1,31 @@ -import { bundle, BundleOptions } from "@deno/emit"; +import { bundle } from "@deno/emit"; import { buildTeloMisikeke } from "./telo-misikeke/build.ts"; const SOURCE = new URL("./src/main.ts", import.meta.url); const DESTINATION = new URL("./dist/main.js", import.meta.url); -async function buildCode(options: BundleOptions): Promise { - const result = await bundle(SOURCE, options); - const { code } = result; - await Deno.writeTextFile(DESTINATION, code); -} switch (Deno.args[0]) { - case "build": + case "build": { console.log("Building telo misikeke..."); await buildTeloMisikeke(); console.log("Building main.js..."); - await buildCode({ minify: true, type: "classic" }); + const bundled = await bundle(SOURCE, { type: "classic" }); + const { stop, transform } = await import("esbuild"); + const minified = await transform(bundled.code, { minify: true }); + await stop(); + await Deno.writeTextFile(DESTINATION, minified.code); console.log("Building done!"); break; + } case "watch": { const builder = debounce(async () => { console.log("Starting to build..."); try { - await buildCode({ + const { code } = await bundle(SOURCE, { compilerOptions: { inlineSourceMap: true }, type: "classic", }); + await Deno.writeTextFile(DESTINATION, code); console.log("Building done!"); } catch (error) { console.error(error); diff --git a/deno.json b/deno.json index 13fc738..91db618 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "target": "esnext", "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] }, "tasks": { @@ -14,6 +13,7 @@ "include": ["./src/**/*.ts", "./telo-misikeke/**/*", "./bundle.ts"] }, "imports": { - "@deno/emit": "jsr:@deno/emit@0.40.2" + "@deno/emit": "jsr:@deno/emit@0.40.2", + "esbuild": "https://deno.land/x/esbuild@v0.20.2/wasm.js" } } diff --git a/deno.lock b/deno.lock index ab9fbc4..ff7530b 100644 --- a/deno.lock +++ b/deno.lock @@ -66,7 +66,9 @@ } } }, - "remote": {}, + "remote": { + "https://deno.land/x/esbuild@v0.20.2/wasm.js": "5a887c1e38ad1056af11c58d45b6084d33bd33a62afa480d805801739370eed0" + }, "workspace": { "dependencies": [ "jsr:@deno/emit@0.40.2" From c5ecebfde67435e1969d0197e0c12595051d25ea Mon Sep 17 00:00:00 2001 From: neverRare Date: Sun, 5 May 2024 18:20:42 +0800 Subject: [PATCH 385/738] update description --- dist/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/index.html b/dist/index.html index ca96ce0..37bbe2d 100644 --- a/dist/index.html +++ b/dist/index.html @@ -26,7 +26,8 @@

          ilo Token

          properly.

          - A rule-based Toki Pona to English translator. + An open source rule-based Toki Pona to English translator. No data are + collected. Limitations. From 34086df5885a6e1b6d1ee0b3f646d945759d8846 Mon Sep 17 00:00:00 2001 From: neverRare Date: Tue, 7 May 2024 10:24:57 +0800 Subject: [PATCH 386/738] hide x ala x partial parsing option --- dist/index.html | 1 + dist/style.css | 3 +++ 2 files changed, 4 insertions(+) diff --git a/dist/index.html b/dist/index.html index 37bbe2d..19d7ee3 100644 --- a/dist/index.html +++ b/dist/index.html @@ -97,6 +97,7 @@

        • Email (private): - neverrare@proton.me + neverrare@proton.me
        @@ -125,7 +123,7 @@

        Allow partial parsing of X ala X -