Skip to content

Commit

Permalink
Add new operators and accents
Browse files Browse the repository at this point in the history
**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
  • Loading branch information
runarberg committed Dec 2, 2023
1 parent 0d2480b commit 9e189d6
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 11 deletions.
18 changes: 17 additions & 1 deletion demo/test-cases.html
Expand Up @@ -70,6 +70,11 @@ <h2>Operators</h2>
<test-case>\`foo bar`</test-case>
<test-case>ı.^\^</test-case>
<test-case>x.^ \(n)</test-case>
<test-case>&lt;&lt;&lt; [] &gt;&gt;&gt;</test-case>

<test-case display="block">
\`lim sup`._(n -> oo)
</test-case>

<test-case display="block">
sum_(n=0)^k a_n = a_0 + a_1 + cdots + a_k
Expand Down Expand Up @@ -407,6 +412,10 @@ <h3>Forced identifiers</h3>
<test-case>it sf`AaBbCc`</test-case>
<test-case>tt`1234`</test-case>
<test-case>i rm i</test-case>

<test-case display="block">
obrace(a\`↑↑`b = ubrace(a^a^⋰^a)._(b "times")).^"" "up-arrow" notation ""
</test-case>
</section>
</section>

Expand Down Expand Up @@ -468,8 +477,15 @@ <h2>Accents</h2>
<test-case>ddot x</test-case>
<test-case>tilde x</test-case>
<test-case>ddot i , bar i , hat j , ul j</test-case>
<test-case>3hat(xyz)</test-case>
<test-case>vec x = a hat i + b hat j + c hat k</test-case>

<test-case display="block">
3oparen(a + bx)
</test-case>

<test-case display="block">
oparen a+b uparen c+d oshell e+f ushell g+h obracket i+j ubracket k+l
</test-case>
</section>
</main>
</body>
Expand Down
127 changes: 124 additions & 3 deletions docs/index.html
Expand Up @@ -546,6 +546,11 @@ <h3>Operators</h3>
<math><mo>±</mo></math>
</dd>

<dt><code>-+</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>*</code></dt>
<dd>
<math><mo>·</mo></math>
Expand Down Expand Up @@ -626,6 +631,11 @@ <h3>Operators</h3>
<math><mo></mo></math>
</dd>

<dt><code>!==</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>o+</code></dt>
<dd>
<math><mo></mo></math>
Expand Down Expand Up @@ -722,7 +732,7 @@ <h3>Operators</h3>
<math><mo></mo></math>
</dd>

<dt><code>prop</code></dt>
<dt><code>oc</code>, <code>prop</code></dt>
<dd>
<math><mo></mo></math>
</dd>
Expand Down Expand Up @@ -767,15 +777,25 @@ <h3>Operators</h3>
<math><mo></mo></math>
</dd>

<dt><code>diamond</code></dt>
<dt><code>&lt;&gt;</code>, <code>diamond</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>square</code></dt>
<dt><code>[]</code>, <code>square</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code><|</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>|></code></dt>
<dd>
<math><mo></mo></math>
</dd>
</dl>
</details>

Expand Down Expand Up @@ -830,6 +850,16 @@ <h3>Operators</h3>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>&lt;&lt;&lt;</code></dt>
<dd>
<math><mo></mo></math>
</dd>

<dt><code>&gt;&gt;&gt;</code></dt>
<dd>
<math><mo></mo></math>
</dd>
</dl>
</details>

Expand Down Expand Up @@ -1001,6 +1031,10 @@ <h3>Operators</h3>

<aside class="examples">
<ul>
<li>
<use-example>\`lim sup`._(n -&gt; oo) x_n</use-example>
</li>

<li>
<!-- prettier-ignore -->
<use-example>
Expand Down Expand Up @@ -1977,6 +2011,86 @@ <h3>Accents</h3>
</math>
</dd>

<dt><code>oparen a+b</code></dt>
<dd>
<math>
<mover accent="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</mover>
</math>
</dd>

<dt><code>uparen a+b</code></dt>
<dd>
<math>
<munder accentunder="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</munder>
</math>
</dd>

<dt><code>obrace a+b</code></dt>
<dd>
<math>
<mover accent="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</mover>
</math>
</dd>

<dt><code>ubrace a+b</code></dt>
<dd>
<math>
<munder accentunder="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</munder>
</math>
</dd>

<dt><code>obracket a+b</code></dt>
<dd>
<math>
<mover accent="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</mover>
</math>
</dd>

<dt><code>ubracket a+b</code></dt>
<dd>
<math>
<munder accentunder="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</munder>
</math>
</dd>

<dt><code>oshell a+b</code></dt>
<dd>
<math>
<mover accent="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</mover>
</math>
</dd>

<dt><code>ushell a+b</code></dt>
<dd>
<math>
<munder accentunder="true">
<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>
<mo></mo>
</munder>
</math>
</dd>

<td><code>cancel a+b</code></td>
<dd>
<math>
Expand Down Expand Up @@ -2015,6 +2129,13 @@ <h3>Accents</h3>
</li>

<li><use-example>X -&gt; x + tilde x</use-example></li>

<li>
<!-- prettier-ignore -->
<use-example>
oparen a+b uparen c+d oshell e+f ushell g+h obracket i+j ubracket k+l
</use-example>
</li>
</ul>
</aside>
</section>
Expand Down
18 changes: 17 additions & 1 deletion src/compiler/tokenizer/lexemes.js
Expand Up @@ -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" }],
Expand All @@ -156,6 +157,7 @@ export const KNOWN_OPS = new Map([
["**", { value: "∗" }],
["***", { value: "⋆" }],
["+-", { value: "±" }],
["-+", { value: "∓" }],
["-:", { value: "÷" }],
["-<", { value: "≺" }],
["-<=", { value: "⪯" }],
Expand All @@ -167,8 +169,11 @@ export const KNOWN_OPS = new Map([
["/_", { value: "∠" }],
[":.", { value: "∴" }],
["<-", { value: "←" }],
["<<<", { value: "≪" }],
["<=", { value: "≤" }],
["<=>", { value: "⇔" }],
["<>", { value: "⋄" }],
["<|", { value: "⊲" }],
["==", { value: "≡" }],
["=>", { value: "⇒" }],
[">-", { value: "≻" }],
Expand All @@ -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: "⊥" }],
Expand Down Expand Up @@ -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" }],
Expand All @@ -233,6 +241,7 @@ export const KNOWN_OPS = new Map([
["|--", { value: "⊢" }],
["|->", { value: "↦" }],
["|==", { value: "⊨" }],
["|>", { value: "⊳" }],
["|><", { value: "⋉" }],
["|><|", { value: "⋈" }],
["~=", { value: "≅" }],
Expand Down Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion 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
Expand Down Expand Up @@ -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) {
Expand Down
11 changes: 10 additions & 1 deletion 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
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/tokenizer/scanners/paren-open.test.js
Expand Up @@ -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);
});
12 changes: 10 additions & 2 deletions test/accents.js
Expand Up @@ -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) => {
Expand Down
4 changes: 4 additions & 0 deletions test/operators.js
Expand Up @@ -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("ı.^\\^"));
});
Expand Down

0 comments on commit 9e189d6

Please sign in to comment.