From d2bf3e3645fe4c685b565d21a6494d9349bd33f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Wed, 13 Mar 2024 22:46:20 +0100 Subject: [PATCH 1/8] add two coco function call syntaxes --- src/languages/coconut/coconut.test.md | 12 +++++++- src/languages/coconut/index.ts | 42 ++++++++++++++------------ src/languages/coconut/plugins.ts | 32 ++++++++++++++++++++ src/languages/python/emit.ts | 8 +++-- src/programs/code.golf-default.test.md | 4 +-- 5 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 src/languages/coconut/plugins.ts diff --git a/src/languages/coconut/coconut.test.md b/src/languages/coconut/coconut.test.md index ef78829b..f17e08d0 100644 --- a/src/languages/coconut/coconut.test.md +++ b/src/languages/coconut/coconut.test.md @@ -8,7 +8,7 @@ for_argv $x 100 { }; ``` -```coconut +```coconut nogolf for x in os.sys.argv[1:]:print(x) ``` @@ -33,3 +33,13 @@ x=0 y=0 x≤y ``` + +## Implicit application + +```polygolf +max (builtin "a") (builtin "b") (builtin "c"); +``` + +```coco skipTypecheck +max a b c +``` diff --git a/src/languages/coconut/index.ts b/src/languages/coconut/index.ts index 9a3e342e..4c57a2b4 100644 --- a/src/languages/coconut/index.ts +++ b/src/languages/coconut/index.ts @@ -1,32 +1,36 @@ import { builtin, listType, op, succ, textType } from "../../IR"; import { mapOps } from "../../plugins/ops"; -import type { Language } from "../../common/Language"; +import { search, type Language } from "../../common/Language"; import pythonLanguage from "../python"; import { CoconutEmitter } from "./emit"; +import { useImplicitFunctionApplications, usePipes } from "./plugins"; const coconutLanguage: Language = { name: "Coconut", extension: "coco", emitter: new CoconutEmitter(), - phases: pythonLanguage.phases.map((phase) => ({ - mode: phase.mode, - plugins: phase.plugins.map((plugin) => - plugin.name === "useSysArgv" - ? { - ...mapOps({ - argv: () => builtin("os.sys.argv[1:]"), + phases: [ + ...pythonLanguage.phases.map((phase) => ({ + mode: phase.mode, + plugins: phase.plugins.map((plugin) => + plugin.name === "useSysArgv" + ? { + ...mapOps({ + argv: () => builtin("os.sys.argv[1:]"), - "at[argv]": (a) => - op["at[List]"]( - { ...builtin("os.sys.argv"), type: listType(textType()) }, - succ(a), - ), - }), - name: "useOsSysArgv", - } - : plugin, - ), - })), + "at[argv]": (a) => + op["at[List]"]( + { ...builtin("os.sys.argv"), type: listType(textType()) }, + succ(a), + ), + }), + name: "useOsSysArgv", + } + : plugin, + ), + })), + search(useImplicitFunctionApplications, usePipes), + ], }; export default coconutLanguage; diff --git a/src/languages/coconut/plugins.ts b/src/languages/coconut/plugins.ts new file mode 100644 index 00000000..8d65b151 --- /dev/null +++ b/src/languages/coconut/plugins.ts @@ -0,0 +1,32 @@ +import { type Node, infix, isIdent, isInt } from "../../IR"; + +function isAllowedAsImplicitArg(node: Node): boolean { + return ( + isIdent()(node) || + (node.kind === "PropertyCall" && isIdent()(node.object)) || + isInt()(node) || + (node.kind === "Infix" && + node.name === "**" && + isAllowedAsImplicitArg(node.left)) + ); +} + +export function useImplicitFunctionApplications(node: Node) { + if (node.kind === "FunctionCall") { + if (node.args.every(isAllowedAsImplicitArg)) { + return infix( + " ", + node.func, + node.args.reduceRight((b, a) => infix(" ", a, b)), + ); + } + } +} + +export function usePipes(node: Node) { + if (node.kind === "FunctionCall") { + if (node.args.length === 1) { + return infix("|>", node.args[0], node.func); + } + } +} diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index b1133ad2..57b614a4 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -34,6 +34,8 @@ function binaryPrecedence(opname: string): number { switch (opname) { case "**": return 12; + case " ": + return 11.5; // Used in Coconut case "*": case "//": case "%": @@ -50,6 +52,8 @@ function binaryPrecedence(opname: string): number { return 6; case "|": return 5; + case "|>": + return 4.5; // Used in Coconut case "<": case "<=": case "==": @@ -110,8 +114,8 @@ export class PythonEmitter extends PrecedenceVisitorEmitter { ? 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.prec(parent) + (["**", " "].includes(parent.name) ? 1 : 0) + : this.prec(parent) + (["**", " "].includes(parent.name) ? 0 : 1) : kind === "Prefix" || kind === "Postfix" ? this.prec(parent) : -Infinity; diff --git a/src/programs/code.golf-default.test.md b/src/programs/code.golf-default.test.md index 95d44ac4..2c4b584a 100644 --- a/src/programs/code.golf-default.test.md +++ b/src/programs/code.golf-default.test.md @@ -30,8 +30,8 @@ _Coconut_ ```coconut p=print p("Hello, World!") -for i in range(10):p(i) -for a in os.sys.argv[1:]:p(a) +for i in range 10:p i +for a in os.sys.argv[1:]:p a ``` _Golfscript_ From e51f8cfea2a04741fb3822d65ec7e83aceaf633c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Wed, 13 Mar 2024 22:58:06 +0100 Subject: [PATCH 2/8] =?UTF-8?q?add=20=E2=86=A6=20op=20alias=20&=20add=20te?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/languages/coconut/coconut.test.md | 10 ++++++++++ src/languages/coconut/emit.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/src/languages/coconut/coconut.test.md b/src/languages/coconut/coconut.test.md index f17e08d0..4167a9db 100644 --- a/src/languages/coconut/coconut.test.md +++ b/src/languages/coconut/coconut.test.md @@ -43,3 +43,13 @@ max (builtin "a") (builtin "b") (builtin "c"); ```coco skipTypecheck max a b c ``` + +## Pipes + +```polygolf +ord "x"; +``` + +```coco chars +"x"↦ord +``` diff --git a/src/languages/coconut/emit.ts b/src/languages/coconut/emit.ts index 5aa998dd..ef930104 100644 --- a/src/languages/coconut/emit.ts +++ b/src/languages/coconut/emit.ts @@ -10,6 +10,7 @@ export class CoconutEmitter extends PythonEmitter { (token) => ( ({ + "|>": "↦", "=>": "⇒", "->": "→", "**": "↑", From 6bfcecc3bce47142c1bb1be64f42b8d1a21343b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Thu, 14 Mar 2024 20:51:38 +0100 Subject: [PATCH 3/8] only consider |> in coco for chars scoring --- src/languages/coconut/index.ts | 4 ++-- src/languages/coconut/plugins.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/languages/coconut/index.ts b/src/languages/coconut/index.ts index 4c57a2b4..5542ec13 100644 --- a/src/languages/coconut/index.ts +++ b/src/languages/coconut/index.ts @@ -3,7 +3,7 @@ import { mapOps } from "../../plugins/ops"; import { search, type Language } from "../../common/Language"; import pythonLanguage from "../python"; import { CoconutEmitter } from "./emit"; -import { useImplicitFunctionApplications, usePipes } from "./plugins"; +import { useImplicitFunctionApplications, usePipesWhenChars } from "./plugins"; const coconutLanguage: Language = { name: "Coconut", @@ -29,7 +29,7 @@ const coconutLanguage: Language = { : plugin, ), })), - search(useImplicitFunctionApplications, usePipes), + search(useImplicitFunctionApplications, usePipesWhenChars), ], }; diff --git a/src/languages/coconut/plugins.ts b/src/languages/coconut/plugins.ts index 8d65b151..08fedfb1 100644 --- a/src/languages/coconut/plugins.ts +++ b/src/languages/coconut/plugins.ts @@ -1,3 +1,4 @@ +import type { CompilationContext } from "@/common/compile"; import { type Node, infix, isIdent, isInt } from "../../IR"; function isAllowedAsImplicitArg(node: Node): boolean { @@ -23,7 +24,15 @@ export function useImplicitFunctionApplications(node: Node) { } } -export function usePipes(node: Node) { +export function usePipesWhenChars( + node: Node, + _: unknown, + context: CompilationContext, +) { + if (context.options.objective !== "chars") { + context.skipChildren(); + return; + } if (node.kind === "FunctionCall") { if (node.args.length === 1) { return infix("|>", node.args[0], node.func); From dac6dd8c2121ed9ae40d02bb54831c0d15844d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Thu, 14 Mar 2024 21:05:59 +0100 Subject: [PATCH 4/8] add infix calls to coco --- src/languages/coconut/coconut.test.md | 12 +++++++++++- src/languages/coconut/index.ts | 8 ++++++-- src/languages/coconut/plugins.ts | 18 +++++++++++++----- src/languages/python/emit.ts | 2 ++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/languages/coconut/coconut.test.md b/src/languages/coconut/coconut.test.md index 4167a9db..f0d201ee 100644 --- a/src/languages/coconut/coconut.test.md +++ b/src/languages/coconut/coconut.test.md @@ -34,7 +34,7 @@ y=0 x≤y ``` -## Implicit application +## Implicit calls ```polygolf max (builtin "a") (builtin "b") (builtin "c"); @@ -53,3 +53,13 @@ ord "x"; ```coco chars "x"↦ord ``` + +## Infix calls + +```polygolf +function_call (builtin "f") (builtin "x") "x"; +``` + +```coco skipTypecheck +x`f`"x" +``` diff --git a/src/languages/coconut/index.ts b/src/languages/coconut/index.ts index 5542ec13..1fffa142 100644 --- a/src/languages/coconut/index.ts +++ b/src/languages/coconut/index.ts @@ -3,7 +3,11 @@ import { mapOps } from "../../plugins/ops"; import { search, type Language } from "../../common/Language"; import pythonLanguage from "../python"; import { CoconutEmitter } from "./emit"; -import { useImplicitFunctionApplications, usePipesWhenChars } from "./plugins"; +import { + useImplicitFunctionCalls, + useInfixFunctionCalls, + usePipesWhenChars, +} from "./plugins"; const coconutLanguage: Language = { name: "Coconut", @@ -29,7 +33,7 @@ const coconutLanguage: Language = { : plugin, ), })), - search(useImplicitFunctionApplications, usePipesWhenChars), + search(useImplicitFunctionCalls, usePipesWhenChars, useInfixFunctionCalls), ], }; diff --git a/src/languages/coconut/plugins.ts b/src/languages/coconut/plugins.ts index 08fedfb1..c8cd6215 100644 --- a/src/languages/coconut/plugins.ts +++ b/src/languages/coconut/plugins.ts @@ -12,7 +12,7 @@ function isAllowedAsImplicitArg(node: Node): boolean { ); } -export function useImplicitFunctionApplications(node: Node) { +export function useImplicitFunctionCalls(node: Node) { if (node.kind === "FunctionCall") { if (node.args.every(isAllowedAsImplicitArg)) { return infix( @@ -24,6 +24,16 @@ export function useImplicitFunctionApplications(node: Node) { } } +export function useInfixFunctionCalls(node: Node) { + if ( + node.kind === "FunctionCall" && + isIdent()(node.func) && + node.args.length === 2 + ) { + return infix("`", infix("`", node.args[0], node.func), node.args[1]); + } +} + export function usePipesWhenChars( node: Node, _: unknown, @@ -33,9 +43,7 @@ export function usePipesWhenChars( context.skipChildren(); return; } - if (node.kind === "FunctionCall") { - if (node.args.length === 1) { - return infix("|>", node.args[0], node.func); - } + if (node.kind === "FunctionCall" && node.args.length === 1) { + return infix("|>", node.args[0], node.func); } } diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index 57b614a4..f0ee1ef0 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -52,6 +52,8 @@ function binaryPrecedence(opname: string): number { return 6; case "|": return 5; + case "`": + return 4.6; // Used in Coconut case "|>": return 4.5; // Used in Coconut case "<": From 93d0c2a15074773560c5df67141d8d755b19c41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Fri, 15 Mar 2024 00:03:15 +0100 Subject: [PATCH 5/8] add implicit multiplication --- src/common/Language.ts | 7 ++++--- src/languages/coconut/coconut.test.md | 26 ++++++++++++++++++++++++++ src/languages/coconut/emit.ts | 7 ++++++- src/languages/coconut/plugins.ts | 9 ++++++++- src/languages/python/emit.ts | 9 +++++++-- src/languages/python/python.test.md | 2 +- src/plugins/arithmetic.test.md | 2 +- 7 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/common/Language.ts b/src/common/Language.ts index 4d91cb32..aae7656f 100644 --- a/src/common/Language.ts +++ b/src/common/Language.ts @@ -64,8 +64,9 @@ export abstract class VisitorEmitter extends DetokenizingEmitter { prop: item.prop as ChildrenProp, index, }); - if (index > 0 && item.delimiter !== undefined) + if (index > 0 && item.delimiter !== undefined) { tokens.push(item.delimiter); + } collect(this.visit(childSpine.node, childSpine, context), childSpine); }); } else if (item instanceof Spine) { @@ -77,7 +78,7 @@ export abstract class VisitorEmitter extends DetokenizingEmitter { }; const programSpine = programToSpine(program); collect(this.visit(program, programSpine, context), programSpine); - return tokens; + return tokens.filter((x) => x !== ""); } } @@ -217,7 +218,7 @@ export function defaultDetokenizer( ): (x: Token[]) => string { return function (tokens: Token[]): string { let indentLevel = 0; - let result = tokens[0]; + let result = tokens[0] ?? ""; for (let i = 1; i < tokens.length; i++) { if (tokens[i] === "$INDENT$") indentLevel++; else if (tokens[i] === "$DEDENT$") indentLevel--; diff --git a/src/languages/coconut/coconut.test.md b/src/languages/coconut/coconut.test.md index f0d201ee..c3531300 100644 --- a/src/languages/coconut/coconut.test.md +++ b/src/languages/coconut/coconut.test.md @@ -44,6 +44,22 @@ max (builtin "a") (builtin "b") (builtin "c"); max a b c ``` +```polygolf +42 * (builtin "x"):Int; +``` + +```coco +42x +``` + +```polygolf +print (42 * (builtin "x"):Int); +``` + +```coco +print(42x) +``` + ## Pipes ```polygolf @@ -63,3 +79,13 @@ function_call (builtin "f") (builtin "x") "x"; ```coco skipTypecheck x`f`"x" ``` + +## temp + +```polygolf +infix "" 42 (builtin "x"); +``` + +```coco skipTypecheck +42x +``` diff --git a/src/languages/coconut/emit.ts b/src/languages/coconut/emit.ts index ef930104..f7592f86 100644 --- a/src/languages/coconut/emit.ts +++ b/src/languages/coconut/emit.ts @@ -4,7 +4,12 @@ import type { CompilationContext } from "../../common/compile"; export class CoconutEmitter extends PythonEmitter { detokenize = (tokens: Token[], context: CompilationContext) => - defaultDetokenizer()( + defaultDetokenizer((a, b) => { + a = a[a.length - 1]; + b = b[0]; + if (/\d/.test(a) && /[a-zA-Z]/.test(b)) return false; + return /\w/.test(a) && /\w/.test(b); + })( context.options.objective === "chars" ? tokens.map( (token) => diff --git a/src/languages/coconut/plugins.ts b/src/languages/coconut/plugins.ts index 431df733..b03bac6d 100644 --- a/src/languages/coconut/plugins.ts +++ b/src/languages/coconut/plugins.ts @@ -16,9 +16,16 @@ function isAllowedAsImplicitArg(node: Node): boolean { export function useImplicitFunctionCalls(node: Node) { if (node.kind === "FunctionCall") { if (node.args.length >= 1 && node.args.every(isAllowedAsImplicitArg)) { - return infix(" ", node.func, ...(node.args as [Node, ...Node[]])); + return infix("", node.func, ...(node.args as [Node, ...Node[]])); } } + if ( + node.kind === "Infix" && + node.name === "*" && + node.args.slice(1).every(isAllowedAsImplicitArg) + ) { + return infix("", ...node.args); + } } export function useInfixFunctionCalls(node: Node) { diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index 62aed81c..00e17345 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -34,7 +34,7 @@ function binaryPrecedence(opname: string): number { switch (opname) { case "**": return 12; - case " ": + case "": return 11.5; // Used in Coconut case "*": case "//": @@ -90,7 +90,12 @@ function unaryPrecedence(opname: string): number { export class PythonEmitter extends PrecedenceVisitorEmitter { detokenize = (x: Token[], context: CompilationContext) => - defaultDetokenizer()(x); + defaultDetokenizer((a, b) => { + a = a[a.length - 1]; + b = b[0]; + if (/\d/.test(a) && /[a-zA-Z]/.test(b)) return false; + return /\w/.test(a) && /\w/.test(b); + })(x); prec(expr: Node): number { switch (expr.kind) { diff --git a/src/languages/python/python.test.md b/src/languages/python/python.test.md index d7c6c57d..88fbcb35 100644 --- a/src/languages/python/python.test.md +++ b/src/languages/python/python.test.md @@ -317,7 +317,7 @@ conditional true 3 4; ``` ```python nogolf -3 if 1 else 4 +3if 1else 4 ``` ```python diff --git a/src/plugins/arithmetic.test.md b/src/plugins/arithmetic.test.md index 63732cd6..7981c6cd 100644 --- a/src/plugins/arithmetic.test.md +++ b/src/plugins/arithmetic.test.md @@ -94,7 +94,7 @@ $b:Int <- 0; ```python a=b=0 -a!=5 and b==6 +a!=5and b==6 a=b=0 ``` From f013cd3cbfba763df8790dae215e6f8c269535ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Fri, 15 Mar 2024 00:05:27 +0100 Subject: [PATCH 6/8] remove temp test --- src/languages/coconut/coconut.test.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/languages/coconut/coconut.test.md b/src/languages/coconut/coconut.test.md index c3531300..b2435a9d 100644 --- a/src/languages/coconut/coconut.test.md +++ b/src/languages/coconut/coconut.test.md @@ -79,13 +79,3 @@ function_call (builtin "f") (builtin "x") "x"; ```coco skipTypecheck x`f`"x" ``` - -## temp - -```polygolf -infix "" 42 (builtin "x"); -``` - -```coco skipTypecheck -42x -``` From 620984790ec2d0cf8150f576507c951aba700848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Fri, 15 Mar 2024 00:08:28 +0100 Subject: [PATCH 7/8] reuse py detokenize in coco --- src/languages/coconut/emit.ts | 10 +++------- src/languages/python/emit.ts | 5 +++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/languages/coconut/emit.ts b/src/languages/coconut/emit.ts index f7592f86..0e2ed5c6 100644 --- a/src/languages/coconut/emit.ts +++ b/src/languages/coconut/emit.ts @@ -1,15 +1,10 @@ -import { type Token, defaultDetokenizer } from "../../common/Language"; +import type { Token } from "../../common/Language"; import { PythonEmitter } from "../python/emit"; import type { CompilationContext } from "../../common/compile"; export class CoconutEmitter extends PythonEmitter { detokenize = (tokens: Token[], context: CompilationContext) => - defaultDetokenizer((a, b) => { - a = a[a.length - 1]; - b = b[0]; - if (/\d/.test(a) && /[a-zA-Z]/.test(b)) return false; - return /\w/.test(a) && /\w/.test(b); - })( + super.detokenize( context.options.objective === "chars" ? tokens.map( (token) => @@ -29,5 +24,6 @@ export class CoconutEmitter extends PythonEmitter { )[token] ?? token, ) : tokens, + context, ); } diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index 00e17345..2ae075fe 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -89,13 +89,14 @@ function unaryPrecedence(opname: string): number { } export class PythonEmitter extends PrecedenceVisitorEmitter { - detokenize = (x: Token[], context: CompilationContext) => - defaultDetokenizer((a, b) => { + detokenize(x: Token[], context: CompilationContext) { + return defaultDetokenizer((a, b) => { a = a[a.length - 1]; b = b[0]; if (/\d/.test(a) && /[a-zA-Z]/.test(b)) return false; return /\w/.test(a) && /\w/.test(b); })(x); + } prec(expr: Node): number { switch (expr.kind) { From 97218e87efebfe207400b752f7eb132c2191ca34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Fri, 22 Mar 2024 08:01:46 +0100 Subject: [PATCH 8/8] fix ambiguous py emit --- src/languages/python/emit.ts | 1 + src/languages/python/python.test.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/languages/python/emit.ts b/src/languages/python/emit.ts index 2ae075fe..78326cc3 100644 --- a/src/languages/python/emit.ts +++ b/src/languages/python/emit.ts @@ -93,6 +93,7 @@ export class PythonEmitter extends PrecedenceVisitorEmitter { return defaultDetokenizer((a, b) => { a = a[a.length - 1]; b = b[0]; + if (a === "0" && /[box]/.test(b)) return true; if (/\d/.test(a) && /[a-zA-Z]/.test(b)) return false; return /\w/.test(a) && /\w/.test(b); })(x); diff --git a/src/languages/python/python.test.md b/src/languages/python/python.test.md index 88fbcb35..51561b3a 100644 --- a/src/languages/python/python.test.md +++ b/src/languages/python/python.test.md @@ -355,3 +355,15 @@ $r <- (10 ..< 40 2); ```py r=[*range(10,40,2)] ``` + +## Int token followed by a word token + +```polygolf +or 1 (builtin "x"); +or 0 (builtin "x"); +``` + +```py skipTypecheck +1or x +0 or x +```