Skip to content

Commit

Permalink
Add coco function call syntaxes (#377)
Browse files Browse the repository at this point in the history
* add two coco function call syntaxes

* add ↦ op alias & add test

* only consider |> in coco for chars scoring

* add infix calls to coco

* add implicit multiplication

* remove temp test

* reuse py detokenize in coco

* fix ambiguous py emit
  • Loading branch information
MichalMarsalek authored Mar 25, 2024
1 parent 9b09c6f commit f6610f3
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 31 deletions.
7 changes: 4 additions & 3 deletions src/common/Language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 !== "");
}
}

Expand Down Expand Up @@ -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--;
Expand Down
48 changes: 47 additions & 1 deletion src/languages/coconut/coconut.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ for_argv $x 100 {
};
```

```coconut
```coconut nogolf
for x in os.sys.argv[1:]:print(x)
```

Expand All @@ -33,3 +33,49 @@ x=0
y=0
x≤y
```

## Implicit calls

```polygolf
max (builtin "a") (builtin "b") (builtin "c");
```

```coco skipTypecheck
max a b c
```

```polygolf
42 * (builtin "x"):Int;
```

```coco
42x
```

```polygolf
print (42 * (builtin "x"):Int);
```

```coco
print(42x)
```

## Pipes

```polygolf
ord "x";
```

```coco chars
"x"↦ord
```

## Infix calls

```polygolf
function_call (builtin "f") (builtin "x") "x";
```

```coco skipTypecheck
x`f`"x"
```
6 changes: 4 additions & 2 deletions src/languages/coconut/emit.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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()(
super.detokenize(
context.options.objective === "chars"
? tokens.map(
(token) =>
(
({
"|>": "↦",
"=>": "⇒",
"->": "→",
"**": "↑",
Expand All @@ -23,5 +24,6 @@ export class CoconutEmitter extends PythonEmitter {
)[token] ?? token,
)
: tokens,
context,
);
}
46 changes: 27 additions & 19 deletions src/languages/coconut/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
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 {
useImplicitFunctionCalls,
useInfixFunctionCalls,
usePipesWhenChars,
} 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(useImplicitFunctionCalls, usePipesWhenChars, useInfixFunctionCalls),
],
};

export default coconutLanguage;
53 changes: 53 additions & 0 deletions src/languages/coconut/plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { CompilationContext } from "@/common/compile";
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.args[0]) &&
isInt()(node.args[1]))
);
}

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[]]));
}
}
if (
node.kind === "Infix" &&
node.name === "*" &&
node.args.slice(1).every(isAllowedAsImplicitArg)
) {
return infix("", ...node.args);
}
}

export function useInfixFunctionCalls(node: Node) {
if (
node.kind === "FunctionCall" &&
isIdent()(node.func) &&
node.args.length === 2
) {
return infix("`", node.args[0], node.func, node.args[1]);
}
}

export function usePipesWhenChars(
node: Node,
_: unknown,
context: CompilationContext,
) {
if (context.options.objective !== "chars") {
context.skipChildren();
return;
}
if (node.kind === "FunctionCall" && node.args.length === 1) {
return infix("|>", node.args[0], node.func);
}
}
17 changes: 15 additions & 2 deletions src/languages/python/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function binaryPrecedence(opname: string): number {
switch (opname) {
case "**":
return 12;
case "":
return 11.5; // Used in Coconut
case "*":
case "//":
case "%":
Expand All @@ -50,6 +52,10 @@ 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 "<":
case "<=":
case "==":
Expand Down Expand Up @@ -79,8 +85,15 @@ function unaryPrecedence(opname: string): number {
}

export class PythonEmitter extends PrecedenceVisitorEmitter {
detokenize = (x: Token[], context: CompilationContext) =>
defaultDetokenizer()(x);
detokenize(x: Token[], context: CompilationContext) {
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);
}

prec(expr: Node): number {
switch (expr.kind) {
Expand Down
14 changes: 13 additions & 1 deletion src/languages/python/python.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ conditional true 3 4;
```

```python nogolf
3 if 1 else 4
3if 1else 4
```

```python
Expand Down Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion src/plugins/arithmetic.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ $b:Int <- 0;

```python
a=b=0
a!=5 and b==6
a!=5and b==6
a=b=0
```

Expand Down
4 changes: 2 additions & 2 deletions src/programs/code.golf-default.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Expand Down

0 comments on commit f6610f3

Please sign in to comment.