From e87c15cd9c229bdd402661eb1bfd2f77ecfa4168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Thu, 14 Mar 2024 22:26:11 +0100 Subject: [PATCH] convert infix node to take any 2+ number of args --- src/IR/exprs.ts | 10 ++++------ src/common/Language.ts | 16 +++++++++++++++- src/common/fragments.ts | 2 -- src/frontend/parse.ts | 7 +++++-- src/languages/javascript/emit.ts | 6 ++---- src/languages/lua/emit.ts | 8 +++----- src/languages/nim/emit.ts | 13 +++++++++---- src/languages/nim/plugins.ts | 32 ++++++++++++++++---------------- src/languages/polygolf/emit.ts | 2 +- src/languages/python/emit.ts | 6 ++---- src/languages/swift/emit.ts | 10 ++++++---- src/plugins/ops.ts | 2 +- 12 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/IR/exprs.ts b/src/IR/exprs.ts index 5f579d5f..b5578fe3 100644 --- a/src/IR/exprs.ts +++ b/src/IR/exprs.ts @@ -113,8 +113,7 @@ export interface RangeIndexCall extends BaseNode { export interface Infix extends BaseNode { readonly kind: "Infix"; readonly name: string; - readonly left: Node; - readonly right: Node; + readonly args: [Node, Node, ...Node[]]; } export interface Prefix extends BaseNode { @@ -495,12 +494,11 @@ export function rangeIndexCall( }; } -export function infix(name: string, left: Node, right: Node): Infix { +export function infix(name: string, ...args: [Node, Node, ...Node[]]): Infix { return { kind: "Infix", - left, - right, name, + args, }; } @@ -567,7 +565,7 @@ export function getArgs( ): readonly Node[] { switch (node.kind) { case "Infix": - return [node.left, node.right]; + return node.args; case "Prefix": return [node.arg]; case "FunctionCall": diff --git a/src/common/Language.ts b/src/common/Language.ts index 5dfe2c9f..4d91cb32 100644 --- a/src/common/Language.ts +++ b/src/common/Language.ts @@ -1,4 +1,4 @@ -import { type Node } from "IR"; +import type { Infix, Node } from "IR"; import { Spine, type PluginVisitor, programToSpine } from "./Spine"; import { type CompilationContext } from "./compile"; import type { @@ -93,6 +93,20 @@ export abstract class PrecedenceVisitorEmitter extends VisitorEmitter { spine: Spine, context: CompilationContext, ): EmitterVisitResult; + infixChildPrecForNoParens( + parent: Infix, + fragment: PathFragment, + ...rightAssociative: string[] + ) { + const indexThatDoesntNeedHigherPrec = rightAssociative.includes(parent.name) + ? parent.args.length - 1 + : 0; + return ( + this.prec(parent) + + Number(indexThatDoesntNeedHigherPrec !== fragment.index) + ); + } + visit(node: Node, spine: Spine, context: CompilationContext) { const res = this.visitNoParens(node, spine, context); const minPrec = diff --git a/src/common/fragments.ts b/src/common/fragments.ts index dbb9446d..fcda50b2 100644 --- a/src/common/fragments.ts +++ b/src/common/fragments.ts @@ -127,10 +127,8 @@ const childProps = [ "index", "init", "key", - "left", "low", "object", - "right", "step", "value", "variable", diff --git a/src/frontend/parse.ts b/src/frontend/parse.ts index 533a71db..263a196b 100644 --- a/src/frontend/parse.ts +++ b/src/frontend/parse.ts @@ -336,8 +336,11 @@ export function sexpr( expectArity(2, Infinity); return methodCall(args[0], asString(args[1]), ...args.slice(2)); case "infix": - expectArity(3); - return infix(asString(args[0]), args[1], args[2]); + expectArity(3, Infinity); + return infix( + asString(args[0]), + ...(args.slice(1) as [Node, Node, ...Node[]]), + ); case "prefix": expectArity(2); return prefix(asString(args[0]), args[1]); diff --git a/src/languages/javascript/emit.ts b/src/languages/javascript/emit.ts index 9b985572..b5c6d4e2 100644 --- a/src/languages/javascript/emit.ts +++ b/src/languages/javascript/emit.ts @@ -138,9 +138,7 @@ export class JavascriptEmitter extends PrecedenceVisitorEmitter { ? -Infinity : this.prec(parent) : kind === "Infix" - ? prop === "left" - ? this.prec(parent) + (parent.name === "**" ? 1 : 0) - : this.prec(parent) + (parent.name === "**" ? 0 : 1) + ? this.infixChildPrecForNoParens(parent, fragment, "**") : kind === "Prefix" || kind === "Postfix" ? this.prec(parent) : -Infinity; @@ -221,7 +219,7 @@ export class JavascriptEmitter extends PrecedenceVisitorEmitter { case "PropertyCall": return [$.object, ".", $.ident]; case "Infix": - return [$.left, n.name, $.right]; + return $.args.join(n.name); case "Prefix": return [n.name, $.arg]; case "Postfix": diff --git a/src/languages/lua/emit.ts b/src/languages/lua/emit.ts index 8f5777ff..60737c70 100644 --- a/src/languages/lua/emit.ts +++ b/src/languages/lua/emit.ts @@ -73,10 +73,8 @@ export class LuaEmitter extends PrecedenceVisitorEmitter { : kind === "IndexCall" && prop === "collection" ? Infinity : kind === "Infix" - ? prop === "left" - ? this.prec(parent) + (parent.name === "^" ? 1 : 0) - : this.prec(parent) + (parent.name === "^" ? 0 : 1) - : kind === "Prefix" + ? this.infixChildPrecForNoParens(parent, fragment, "^") + : kind === "Prefix" || kind === "Postfix" ? this.prec(parent) : -Infinity; } @@ -148,7 +146,7 @@ export class LuaEmitter extends PrecedenceVisitorEmitter { case "MethodCall": return [$.object, ":", $.ident, "(", $.args.join(","), ")"]; case "Infix": - return [$.left, e.name, $.right]; + return $.args.join(e.name); case "Prefix": return [e.name, $.arg]; case "IndexCall": diff --git a/src/languages/nim/emit.ts b/src/languages/nim/emit.ts index 6607d153..18cf968f 100644 --- a/src/languages/nim/emit.ts +++ b/src/languages/nim/emit.ts @@ -127,7 +127,8 @@ export class NimEmitter extends PrecedenceVisitorEmitter { : kind === "Infix" ? this.prec(parent) + Number( - (parent.name === "^" || parent.name === " ") === (prop === "left"), + (parent.name === "^" || parent.name === " ") === + (fragment.index === 0), ) : kind === "Prefix" || kind === "Postfix" ? this.prec(parent) @@ -222,13 +223,17 @@ export class NimEmitter extends PrecedenceVisitorEmitter { return [$.func, "$GLUE$", "(", $.args.join(","), ")"]; case "Infix": { if (n.name === "") { - return [$.left, "$GLUE$", emitAsRawText((n.right as Text).value)]; + return [ + $.args.at(0), + "$GLUE$", + emitAsRawText((n.args[1] as Text).value), + ]; } return [ - $.left, + $.args.at(0), /[A-Za-z]/.test(n.name[0]) ? [] : "$GLUE$", n.name, - $.right, + $.args.at(1), ]; } case "Prefix": diff --git a/src/languages/nim/plugins.ts b/src/languages/nim/plugins.ts index 86e15bd8..9d5d875b 100644 --- a/src/languages/nim/plugins.ts +++ b/src/languages/nim/plugins.ts @@ -98,8 +98,11 @@ export function useUFCS(node: Node) { ); } } - if (node.kind === "Infix" && node.name === " " && isIdent()(node.left)) { - return infix(".", node.right, node.left); + if (node.kind === "Infix" && node.name === " " && node.args.length === 2) { + const [left, right] = node.args; + if (isIdent()(left)) { + return infix(".", right, left); + } } } @@ -151,20 +154,17 @@ export function useRawStringLiteral( spine: Spine, context: CompilationContext, ) { - if ( - node.kind === "Infix" && - node.name === " " && - isText()(node.right) && - (isIdent()(node.left) || - (node.left.kind === "Infix" && node.left.name === ".")) - ) { - const [low, high] = context.options.codepointRange; - if (low === 1 && high === Infinity) { - if ( - !node.right.value.includes("\n") && - !node.right.value.includes("\r") - ) { - return infix("", node.left, node.right); + if (node.kind === "Infix" && node.name === " " && node.args.length === 2) { + const [left, right] = node.args; + if ( + isText()(right) && + (isIdent()(left) || (left.kind === "Infix" && left.name === ".")) + ) { + const [low, high] = context.options.codepointRange; + if (low === 1 && high === Infinity) { + if (!right.value.includes("\n") && !right.value.includes("\r")) { + return infix("", left, right); + } } } } diff --git a/src/languages/polygolf/emit.ts b/src/languages/polygolf/emit.ts index a911d867..53b5c7b8 100644 --- a/src/languages/polygolf/emit.ts +++ b/src/languages/polygolf/emit.ts @@ -282,7 +282,7 @@ export function emitNodeWithoutAnnotation( case "PropertyCall": return emitSexpr(null, expr.object, text(expr.ident.name)); case "Infix": - return emitSexpr(null, text(expr.name), expr.left, expr.right); + return emitSexpr(null, text(expr.name), ...expr.args); case "Prefix": case "Postfix": return emitSexpr(null, text(expr.name), expr.arg); diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index b1133ad2..1fcdc978 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -109,9 +109,7 @@ export class PythonEmitter extends PrecedenceVisitorEmitter { : kind === "ConditionalOp" ? this.prec(parent) + (prop === "alternate" ? 0 : 1) : kind === "Infix" - ? prop === "left" - ? this.prec(parent) + (parent.name === "**" ? 1 : 0) - : this.prec(parent) + (parent.name === "**" ? 0 : 1) + ? this.infixChildPrecForNoParens(parent, fragment, "**") : kind === "Prefix" || kind === "Postfix" ? this.prec(parent) : -Infinity; @@ -206,7 +204,7 @@ export class PythonEmitter extends PrecedenceVisitorEmitter { case "PropertyCall": return [$.object, ".", $.ident]; case "Infix": - return [$.left, n.name, $.right]; + return $.args.join(n.name); case "Prefix": return [n.name, $.arg]; case "Set": diff --git a/src/languages/swift/emit.ts b/src/languages/swift/emit.ts index 7de1bf5e..739e5bac 100644 --- a/src/languages/swift/emit.ts +++ b/src/languages/swift/emit.ts @@ -131,9 +131,11 @@ export class SwiftEmitter extends PrecedenceVisitorEmitter { : prop === "consequent" ? -Infinity : this.prec(parent) - : kind === "Infix" || kind === "Prefix" || kind === "Postfix" - ? this.prec(parent) + (prop === "right" ? 1 : 0) - : -Infinity; + : kind === "Infix" + ? this.infixChildPrecForNoParens(parent, fragment) + : kind === "Prefix" || kind === "Postfix" + ? this.prec(parent) + : -Infinity; } visitNoParens(n: Node, spine: Spine, context: CompilationContext) { @@ -182,7 +184,7 @@ export class SwiftEmitter extends PrecedenceVisitorEmitter { case "ConditionalOp": return [$.condition, "?", $.consequent, ":", $.alternate]; case "Infix": { - return [$.left, n.name, $.right]; + return $.args.join(n.name); } case "Prefix": return [n.name, $.arg]; diff --git a/src/plugins/ops.ts b/src/plugins/ops.ts index 091a0f92..68bacfe9 100644 --- a/src/plugins/ops.ts +++ b/src/plugins/ops.ts @@ -107,7 +107,7 @@ export const propertyOpMapper: OpMapper = (arg, [first]) => export const prefixOpMapper: OpMapper = (arg, opArgs) => prefix(arg, opArgs[0]); export const infixOpMapper: OpMapper = (arg, opArgs) => - infix(arg, opArgs[0], opArgs[1]); + infix(arg, ...(opArgs as [Node, Node, ...Node[]])); export const postfixOpMapper: OpMapper = (arg, opArgs) => postfix(arg, opArgs[0]); export const indexOpMapper: OpMapper<0 | 1> = (arg, opArgs) => {