Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add coco function call syntaxes #377

Merged
merged 9 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
16 changes: 14 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 @@ -83,8 +89,14 @@ 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 (/\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
2 changes: 1 addition & 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
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
Loading