From 9e189d6c88eb65e5941ff2296297b69cd0f5bc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Berg?= Date: Fri, 1 Dec 2023 11:41:41 -0800 Subject: [PATCH] Add new operators and accents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Language Changes** New operators: * `-+` → ∓ * `!==` → ≢ * `<|` → ⊲ * `|>` → ⊳ * `<<<` → ≪ * `>>>` → ≫ New shortcuts to existing operators: * `oc` → ∝ * `<>` → ⋄ * `[]` → □ New accents: * `oparen` → U+23DC ⏜ * `uparen` → U+23DD ⏝ * `oshell` → U+23E0 ⏠ * `ushell` → U+23E1 ⏡ * `obracket` → U+23B4 ⎴ * `ubracket` → U+23B5 ⎵ Resolves: #110 Resolves: #109 --- demo/test-cases.html | 18 ++- docs/index.html | 127 +++++++++++++++++- src/compiler/tokenizer/lexemes.js | 18 ++- .../tokenizer/scanners/paren-close.js | 11 +- src/compiler/tokenizer/scanners/paren-open.js | 11 +- .../tokenizer/scanners/paren-open.test.js | 8 ++ test/accents.js | 12 +- test/operators.js | 4 + test/snapshots/accents.js.md | 10 +- test/snapshots/accents.js.snap | Bin 455 -> 547 bytes test/snapshots/operators.js.md | 6 + test/snapshots/operators.js.snap | Bin 1061 -> 1103 bytes 12 files changed, 214 insertions(+), 11 deletions(-) diff --git a/demo/test-cases.html b/demo/test-cases.html index 1978e57..244e2a3 100644 --- a/demo/test-cases.html +++ b/demo/test-cases.html @@ -70,6 +70,11 @@

Operators

\`foo bar` ı.^\^ x.^ \(n) + <<< [] >>> + + + \`lim sup`._(n -> oo) + sum_(n=0)^k a_n = a_0 + a_1 + cdots + a_k @@ -407,6 +412,10 @@

Forced identifiers

it sf`AaBbCc` tt`1234` i rm i + + + obrace(a\`↑↑`b = ubrace(a^a^⋰^a)._(b "times")).^"" "up-arrow" notation "" + @@ -468,8 +477,15 @@

Accents

ddot x tilde x ddot i , bar i , hat j , ul j - 3hat(xyz) vec x = a hat i + b hat j + c hat k + + + 3oparen(a + bx) + + + + oparen a+b uparen c+d oshell e+f ushell g+h obracket i+j ubracket k+l + diff --git a/docs/index.html b/docs/index.html index 14edb42..d0ef629 100644 --- a/docs/index.html +++ b/docs/index.html @@ -546,6 +546,11 @@

Operators

± +
-+
+
+ +
+
*
· @@ -626,6 +631,11 @@

Operators

+
!==
+
+ +
+
o+
@@ -722,7 +732,7 @@

Operators

-
prop
+
oc, prop
@@ -767,15 +777,25 @@

Operators

-
diamond
+
<>, diamond
-
square
+
[], square
+ +
<|
+
+ +
+ +
|>
+
+ +
@@ -830,6 +850,16 @@

Operators

+ +
<<<
+
+ +
+ +
>>>
+
+ +
@@ -1001,6 +1031,10 @@

Operators

diff --git a/src/compiler/tokenizer/lexemes.js b/src/compiler/tokenizer/lexemes.js index 015a7a6..17b35ae 100644 --- a/src/compiler/tokenizer/lexemes.js +++ b/src/compiler/tokenizer/lexemes.js @@ -143,6 +143,7 @@ export const KNOWN_IDENTS = new Map([ export const KNOWN_OPS = new Map([ ["-", { value: "−" }], ["!=", { value: "≠" }], + ["!==", { value: "≢" }], ["!in", { value: "∉" }], [".$", { value: "\u2061" }], [".*", { value: "\u2062" }], @@ -156,6 +157,7 @@ export const KNOWN_OPS = new Map([ ["**", { value: "∗" }], ["***", { value: "⋆" }], ["+-", { value: "±" }], + ["-+", { value: "∓" }], ["-:", { value: "÷" }], ["-<", { value: "≺" }], ["-<=", { value: "⪯" }], @@ -167,8 +169,11 @@ export const KNOWN_OPS = new Map([ ["/_", { value: "∠" }], [":.", { value: "∴" }], ["<-", { value: "←" }], + ["<<<", { value: "≪" }], ["<=", { value: "≤" }], ["<=>", { value: "⇔" }], + ["<>", { value: "⋄" }], + ["<|", { value: "⊲" }], ["==", { value: "≡" }], ["=>", { value: "⇒" }], [">-", { value: "≻" }], @@ -177,10 +182,12 @@ export const KNOWN_OPS = new Map([ [">->>", { value: "⤖" }], ["><|", { value: "⋊" }], [">=", { value: "≥" }], + [">>>", { value: "≫" }], ["@", { value: "∘" }], ["AA", { value: "∀" }], ["EE", { value: "∃" }], ["TT", { value: "⊤" }], + ["[]", { value: "□" }], ["^^", { value: "∧" }], ["^^^", { value: "⋀" }], ["_|_", { value: "⊥" }], @@ -208,6 +215,7 @@ export const KNOWN_OPS = new Map([ ["not", { value: "¬" }], ["o+", { value: "⊕" }], ["o.", { value: "⊙" }], + ["oc", { value: "∝" }], ["oint", { value: "∮" }], ["or", { value: "or" }], ["otherwise", { value: "otherwise" }], @@ -233,6 +241,7 @@ export const KNOWN_OPS = new Map([ ["|--", { value: "⊢" }], ["|->", { value: "↦" }], ["|==", { value: "⊨" }], + ["|>", { value: "⊳" }], ["|><", { value: "⋉" }], ["|><|", { value: "⋈" }], ["~=", { value: "≅" }], @@ -262,13 +271,20 @@ export const KNOWN_PARENS_CLOSE = new Map([ export const KNOWN_PREFIX = new Map([ // Accents ["bar", { name: "over", accent: "‾" }], - ["obrace", { name: "over", accent: "⏞" }], ["ddot", { name: "over", accent: "⋅⋅" }], ["dot", { name: "over", accent: "⋅" }], ["hat", { name: "over", accent: "^" }], + ["obrace", { name: "over", accent: "⏞" }], + ["obracket", { name: "over", accent: "⎴" }], + ["oparen", { name: "over", accent: "⏜" }], + ["oshell", { name: "over", accent: "⏠" }], ["tilde", { name: "over", accent: "˜" }], ["ubrace", { name: "under", accent: "⏟" }], + ["ubrace", { name: "under", accent: "⏟" }], + ["ubracket", { name: "under", accent: "⎵" }], ["ul", { name: "under", accent: "_" }], + ["uparen", { name: "under", accent: "⏝" }], + ["ushell", { name: "under", accent: "⏡" }], ["vec", { name: "over", accent: "→" }], // Groups diff --git a/src/compiler/tokenizer/scanners/paren-close.js b/src/compiler/tokenizer/scanners/paren-close.js index 1f40d3c..dd119e6 100644 --- a/src/compiler/tokenizer/scanners/paren-close.js +++ b/src/compiler/tokenizer/scanners/paren-close.js @@ -1,4 +1,4 @@ -import { KNOWN_PARENS_CLOSE, isPunctClose } from "../lexemes.js"; +import { KNOWN_OPS, KNOWN_PARENS_CLOSE, isPunctClose } from "../lexemes.js"; /** * @param {string} partial @@ -33,6 +33,15 @@ export default function parenCloseScanner(char, input, { grouping, start }) { } } + { + const [nextChar] = input.slice(start + value.length); + const nextValue = value + nextChar; + + if (KNOWN_OPS.has(nextValue)) { + return null; + } + } + const known = KNOWN_PARENS_CLOSE.get(value); if (known) { diff --git a/src/compiler/tokenizer/scanners/paren-open.js b/src/compiler/tokenizer/scanners/paren-open.js index 13d0a8c..cca4679 100644 --- a/src/compiler/tokenizer/scanners/paren-open.js +++ b/src/compiler/tokenizer/scanners/paren-open.js @@ -1,4 +1,4 @@ -import { KNOWN_PARENS_OPEN, isPunctOpen } from "../lexemes.js"; +import { KNOWN_OPS, KNOWN_PARENS_OPEN, isPunctOpen } from "../lexemes.js"; /** * @param {string} partial @@ -29,6 +29,15 @@ export default function parenOpenScanner(char, input, { start }) { } } + { + const [nextChar] = input.slice(start + value.length); + const nextValue = value + nextChar; + + if (KNOWN_OPS.has(nextValue)) { + return null; + } + } + const known = KNOWN_PARENS_OPEN.get(value); if (known) { diff --git a/src/compiler/tokenizer/scanners/paren-open.test.js b/src/compiler/tokenizer/scanners/paren-open.test.js index 8e712d3..1b3a36c 100644 --- a/src/compiler/tokenizer/scanners/paren-open.test.js +++ b/src/compiler/tokenizer/scanners/paren-open.test.js @@ -35,3 +35,11 @@ test("combined paren-open", (t) => { end: 2, }); }); + +test("paren open is also an operator", (t) => { + t.is(parenOpen("<", "<<<", { start: 0 }), null); +}); + +test("paren close is also an operator", (t) => { + t.is(parenOpen(">", ">>>", { start: 0 }), null); +}); diff --git a/test/accents.js b/test/accents.js index bb8c3e7..3c7a6e1 100644 --- a/test/accents.js +++ b/test/accents.js @@ -29,8 +29,16 @@ test("Dotless variants", (t) => { t.snapshot(render("ul j")); }); -test("Should put accents over all the following parenthesis", (t) => { - t.snapshot(render("3hat(xyz)")); +test("Should put accents over all in the following parenthesis", (t) => { + t.snapshot(render("3oparen(a + bx)")); +}); + +test("Ties together", (t) => { + t.snapshot( + render( + "oparen a+b uparen c+d oshell e+f ushell g+h obracket i+j ubracket k+l", + ), + ); }); test("Physics vector notation", (t) => { diff --git a/test/operators.js b/test/operators.js index 2d6c399..93b6787 100644 --- a/test/operators.js +++ b/test/operators.js @@ -25,6 +25,10 @@ test("Only force an operator when \\ precedes a character", (t) => { t.snapshot(render("\\")); }); +test("Operators that could be open and close parens", (t) => { + t.snapshot(render("<<< [] >>>")); +}); + test("i hat", (t) => { t.snapshot(render("ı.^\\^")); }); diff --git a/test/snapshots/accents.js.md b/test/snapshots/accents.js.md index d3645e1..f5bb629 100644 --- a/test/snapshots/accents.js.md +++ b/test/snapshots/accents.js.md @@ -60,11 +60,17 @@ Generated by [AVA](https://avajs.dev). 'j_' -## Should put accents over all the following parenthesis +## Should put accents over all in the following parenthesis > Snapshot 1 - '3xyz^' + '3a+bx' + +## Ties together + +> Snapshot 1 + + 'a+bc+de+fg+hi+jk+l' ## Physics vector notation diff --git a/test/snapshots/accents.js.snap b/test/snapshots/accents.js.snap index f9bb841c4c1767a73f0658ff749ef22cc7651717..b4c1ab0e3357d32c29b006a0459ef805cb2c557d 100644 GIT binary patch literal 547 zcmV+;0^I#URzVpbyT5Iu;KG$}BK(QV>Z7#0L zAqyzKjvtE%00000000BEmAg*EFcgNHBE;O4$?Ap}I&-id?u%;#RlYth_sDt@1;_n+bxtA53$-U z7DN==>WNrU)d?cgxjXN9%)JnEJu&m^{7phFhp5{=wMFy*J6gxHPkr(z=dT3!Z~o&L zu6h#cL5RAUhAIc|hQMoy!JoHrP|tkLEh-{opbc#dLk{fEWPSz>2pzBl6o>jW5WrMaX9W-0qpI5*=2Qk1f0o*{KN-c|aFsXqiv_*!2 zgrj~ub>YdpAD5XOWPF`A?TAR!74g$#(lNV9ScQS^Wmk8wQ}K3!HYm7T-p%Q@L8L3Z zjIoLvhZr%yX$^@w>_`>I*`-h!i&?2VCd&>4OZ3S;p-Jh*)}=sk;J&n@HqNi%?6iFOZ8ufW}1`M4(A9v z4iQ>0C9tQc)aqgAx>)1WgXPj{DSWJz(wb#gud$|JWt;f%rdnD10V%^Gv%A#_SP0wa z;7L0)kQu(ieKGUMU~Xq-e&4>QQ0oSDKcNP~9b-rSboNC;9_9R3!Goto9K+*%3iZUG z?&hJUgAWbxR%Y\\' +## Operators that could be open and close parens + +> Snapshot 1 + + '' + ## i hat > Snapshot 1 diff --git a/test/snapshots/operators.js.snap b/test/snapshots/operators.js.snap index ae9ab0bfd1927fca93adf9c8bdc0312c68561bc9..33c6fa2184aec38065d03ec73f9a9e71745e3e0f 100644 GIT binary patch literal 1103 zcmV-V1hD%-RzV`MT7?D~M>#>C`8%bnO>*a zneMS?HpzkqpK&z^nu9M;h({I02Y6TU;tvo!?!mKo@NCbcd!}b*<7|wY9ERR>Ree=o zS5;RpHtH7Do9?YA$itq6W-CW2(-E*JIR~hN81yIuGuSi1RE;_{0Exj@H4yx$)y`m- zK*u$y=U!Pf4CujA``4B>l>EQ)`qIYoj;akkQ>)sH&hRhQ2CEhRt=bq=G`wAy z4k~CPFgW6=)L_V5*v8O*nvp-@G!=C&oX{*EW@z1}M z>)r;MiUEa9?`VtY9;Sq+s_jr0`|IrCeN)K&QWE51?fawK*Cd18&VA7V{EfM2!*w7p z!5M6L=GefV-Fx?;*#W;paLEJ0>0TmOI~kk5IVWRug1zq5N0QOKF{4f>KDV%h_^2YT zkYXQcSv-*eY1#F>p72Rw<6VLclu4!)NxyFAab5y8c+(0LMnZ)gqgzEKMZGvwQLA~& z3FH?V2+sgA$}s>(!PC5lOru1xU5yBMs1Pt;Qttt*H3UAe0;Bv$2LO{xt$XpcLIVX2 zMEpIEwa`F(jl@ks6&OlImAz>G+`~7k-ov-6-NQ@w{+5TLP!Gd4@~>J%8M1ROg|A8o zpUdeGoCtYI2ZgX`s8qWkU9XF}%(ajkL88>70$!Fk^&D?tAKnzjOT=9yxIS&y`#SoZ z?qwch(7=dfCyvu=%gX8hSd$p1I@eQlHu~W{PFNT5QO=)&gy>EgTKkVuqktdbSIMDd zDe@pMOJ^FYGry6ZRirCvjQ_+y;k%Q;w|S<=pMONM&-5zCV%b`=b3R&)xU$q`V>9B9~Z@66*Wk$A6xMh&RPhLbVU~ Vq+B7$=fapD?=NSwUKaTi002kJ8d(4U literal 1061 zcmV+=1ls#SRzVa4}VEE7&~#MBNIsKaBH z9h!7|k{^o*00000000BcSIukGKosAt7SEolAhL}jSdWTTuux@ZH?wBg zWF}6sZ5KRPKkCwY&|dt23iT|CAJDss7ykgk;~qSV2hUDEW|B#=ZMIfz4`H_R=J$K= zm-k-YtktU~tu>qnFOZ8}6U~<?wGE#s0$H;-RtyT|+BdjLz{V)rQL@{w!M+G`5|$hYIRCM6|(R=)hfYCIXt8s-X9$v9vz9Bi*U!z6SD>Ce$0@>A~p~KGr(r3 zhIHfr2xEh8ie&0Y*5HyL7 zSja-PPWw^#Zi8IG8j-vc;SSM`@fDGo!?zoOQAt5_npJ-jh6oa?6mCV3Nz)jGFg@JG z6l*w%DolAe%dh3SJ3vDLP}FoMo22GqNpEILDxl5lK%R)4Phw+yB`Y4NaZ){e+zLUbK%A)1R>SH2ABmN^8EksrQp6NDjhy+~$R}711-AH^mJaL;@s+NU zyq3`^m(j+C^IpHw!+yz;Rot9A?N^CR?$Y