Skip to content

Commit

Permalink
Only render parentheses where necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
robintown committed Sep 3, 2023
1 parent 340a3c2 commit 891af8a
Showing 1 changed file with 113 additions and 18 deletions.
131 changes: 113 additions & 18 deletions src/semantics/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,31 @@ const italicAlphabets: Alphabets = {
],
};

const infixPrecedence: Record<(Expr & { head: 'infix' })['name'], number> = {
and: 4,
or: 3,
equals: 5,
subinterval: 10,
before: 9,
after: 8,
before_near: 7,
after_near: 6,
roi: 11,
};

const infixAssociativity: Record<(Expr & { head: 'infix' })['name'], boolean> =
{
and: true,
or: true,
equals: false,
subinterval: false,
before: false,
after: false,
before_near: false,
after_near: false,
roi: true,
};

/**
* Specification of a rendering format, such as plain text or LaTeX.
*/
Expand Down Expand Up @@ -355,67 +380,137 @@ function getName(index: number, names: Names, fmt: Format): string {
return fmt.name(names.context[index]);
}

function render(e: Expr, names: Names, fmt: Format, bracket = false): string {
/**
* @param e The (sub)expression to be rendered.
* @param names The naming context for the variables and constants currently in
* scope.
* @param fmt The rendering format specification.
* @param leftPrecedence The precedence of the closest operator to the right of
* this subexpression that could affect its bracketing.
* @param rightPrecedence The precedence of the closest operator to the left of
* this subexpression that could affect its bracketing.
*/
function render(
e: Expr,
names: Names,
fmt: Format,
leftPrecedence: number,
rightPrecedence: number,
): string {
switch (e.head) {
case 'variable': {
return getName(e.index, names, fmt);
}
case 'verb': {
const args = e.args.map(arg => render(arg, names, fmt));
const event = render(e.event, names, fmt);
const world = render(e.world, names, fmt, true);
const args = e.args.map(arg => render(arg, names, fmt, 0, 0));
const event = render(e.event, names, fmt, 0, 0);
const world = render(e.world, names, fmt, 0, 0);
return fmt.verb(e.name, args, event, world);
}
case 'lambda': {
const symbol = fmt.quantifierSymbols.lambda;
const p = 2;
const bracket = rightPrecedence > p;
const innerNames = addName(e.type[0], names);
const name = getName(0, innerNames, fmt);
const body = render(e.body as Expr, innerNames, fmt);
const body = render(
e.body as Expr,
innerNames,
fmt,
p,
bracket ? 0 : rightPrecedence,
);

let content: string;
if (e.restriction === undefined) {
content = fmt.quantifier(symbol, name, body);
} else {
const restriction = render(e.restriction as Expr, innerNames, fmt);
const restriction = render(
e.restriction as Expr,
innerNames,
fmt,
0,
0,
);
content = fmt.restrictedQuantifier(symbol, name, restriction, body);
}

return bracket ? fmt.bracket(content) : content;
}
case 'apply': {
const fn = render(e.fn, names, fmt, true);
const argument = render(e.argument, names, fmt);
return fmt.apply(fn, argument);
const p = 13;
const bracket = leftPrecedence > p;
const fn = render(e.fn, names, fmt, bracket ? 0 : leftPrecedence, p);
const argument = render(e.argument, names, fmt, 0, 0);
const content = fmt.apply(fn, argument);
return bracket ? fmt.bracket(content) : content;
}
case 'presuppose': {
const body = render(e.body, names, fmt, true);
const presupposition = render(e.presupposition, names, fmt, true);
const p = 1;
const bracket = leftPrecedence >= p || rightPrecedence >= p;
const body = render(e.body, names, fmt, bracket ? 0 : leftPrecedence, p);
const presupposition = render(
e.presupposition,
names,
fmt,
p,
bracket ? 0 : rightPrecedence,
);
const content = fmt.presuppose(body, presupposition);
return bracket ? fmt.bracket(content) : content;
}
case 'infix': {
const symbol = fmt.infixSymbols[e.name];
const left = render(e.left, names, fmt, true);
const right = render(e.right, names, fmt, true);
const p = infixPrecedence[e.name];
const associative = infixAssociativity[e.name];
const bracket =
(leftPrecedence > p || rightPrecedence > p) &&
!(associative && (leftPrecedence === p || rightPrecedence === p));

const left = render(e.left, names, fmt, bracket ? 0 : leftPrecedence, p);
const right = render(
e.right,
names,
fmt,
p,
bracket ? 0 : rightPrecedence,
);
const content = fmt.infix(symbol, left, right);
return bracket ? fmt.bracket(content) : content;
}
case 'polarizer': {
const symbol = fmt.polarizerSymbols[e.name];
const body = render(e.body, names, fmt, true);
return fmt.polarizer(symbol, body);
const p = 12;
const bracket = rightPrecedence > p;
const body = render(e.body, names, fmt, p, bracket ? 0 : rightPrecedence);
const content = fmt.polarizer(symbol, body);
return bracket ? fmt.bracket(content) : content;
}
case 'quantifier': {
const symbol = fmt.quantifierSymbols[e.name];
const p = 2;
const bracket = rightPrecedence > p;
const innerNames = addName(e.body.context[0], names);
const name = getName(0, innerNames, fmt);
const body = render(e.body as Expr, innerNames, fmt);
const body = render(
e.body as Expr,
innerNames,
fmt,
p,
bracket ? 0 : rightPrecedence,
);

let content: string;
if (e.restriction === undefined) {
content = fmt.quantifier(symbol, name, body);
} else {
const restriction = render(e.restriction as Expr, innerNames, fmt);
const restriction = render(
e.restriction as Expr,
innerNames,
fmt,
0,
0,
);
content = fmt.restrictedQuantifier(symbol, name, restriction, body);
}

Expand All @@ -437,7 +532,7 @@ function renderFull(e: Expr, fmt: Format): string {
names = addName(e.context[i], names, type !== 's');
}

return render(e, names, fmt);
return render(e, names, fmt, 0, 0);
}

export function toPlainText(e: Expr): string {
Expand Down

0 comments on commit 891af8a

Please sign in to comment.