Skip to content

Commit

Permalink
Cloning (#373)
Browse files Browse the repository at this point in the history
* implement cloning

* add cloning to cover

* improve janet cloning
  • Loading branch information
MichalMarsalek authored Mar 12, 2024
1 parent 538073f commit 8f687e4
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 7 deletions.
8 changes: 8 additions & 0 deletions src/cover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
isSubtype,
type OpCode,
forRangeCommon,
block,
} from "../IR";
import languages from "../languages/languages";
import { isCompilable } from "../common/compile";
Expand Down Expand Up @@ -153,6 +154,13 @@ const features: CoverTableRecipe = {
assignment: (lang) => assignment(id("x"), lang.expr()),
builtin: (lang) => lang.stmt(nextBuiltin(integerType(0, 0))),
discard: (lang) => lang.expr(),
"list clone": (lang) =>
block([assignment("x", list([lang.expr()])), assignment("y", id("x"))]),
"list of list clone": (lang) =>
block([
assignment("x", list([list([lang.expr()])])),
assignment("y", id("x")),
]),
bigint: (lang) => lang.stmt(int(10n ** 40n)),
if: (lang) => ifStatement(lang.expr(booleanType), lang.stmt(), lang.stmt()),
for: (lang) =>
Expand Down
7 changes: 6 additions & 1 deletion src/languages/clojure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
pickAnyInt,
} from "../../plugins/arithmetic";
import { forArgvToForEach } from "../../plugins/loops";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import { assertInt64 } from "../../plugins/types";
import { implicitlyConvertConcatArg } from "./plugins";

Expand Down Expand Up @@ -181,6 +181,11 @@ const clojureLanguage: Language = {
starts_with: "clojure.string/starts-with?",
ends_with: "clojure.string/ends-with?",
}),
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
}),
),
simplegolf(
alias(
Expand Down
6 changes: 6 additions & 0 deletions src/languages/golfscript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from "../../plugins/ops";
import {
alias,
clone,
defaultIdentGen,
renameIdents,
useBuiltinAliases,
Expand Down Expand Up @@ -232,6 +233,11 @@ const golfscriptLanguage: Language = {
}),
),
required(
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
}),
printToImplicitOutput,
addImports({ a: ["a"] }, (x) =>
x.length > 0 ? assignment(builtin("a"), builtin("")) : undefined,
Expand Down
5 changes: 5 additions & 0 deletions src/languages/janet/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export class JanetEmitter extends VisitorEmitter {
const prop = spine.pathFragment?.prop;

switch (n.kind) {
case "Cast":
if (n.targetType === "array") {
return ["@[;", "$GLUE$", $.expr, "]"];
}
throw new EmitError(n, "unsuported cast target type");
case "Block": {
return prop === "consequent" || prop === "alternate"
? list("do", $.children.join())
Expand Down
15 changes: 14 additions & 1 deletion src/languages/janet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
text,
intToDecOpOrText,
isForEachChar,
cast,
} from "../../IR";
import { golfLastPrint, golfLastPrintInt } from "../../plugins/print";
import {
Expand All @@ -46,7 +47,7 @@ import {
truncatingOpsPlugins,
} from "../../plugins/arithmetic";
import { forArgvToForEach, forEachToForRange } from "../../plugins/loops";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import { assertInt64 } from "../../plugins/types";
import { implicitlyConvertConcatArg } from "./plugins";
import { applyIf } from "../../plugins/helpers";
Expand Down Expand Up @@ -209,6 +210,18 @@ const janetLanguage: Language = {
"sorted[Ascii]": "sorted",
"sorted[Int]": "sorted",
}),
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
if (
type.kind === "List" &&
["boolean", "integer", "text"].includes(type.member.kind)
) {
return cast(node, "array");
}
return func("thaw", node);
}),
),
simplegolf(
alias(
Expand Down
5 changes: 5 additions & 0 deletions src/languages/javascript/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ export class JavascriptEmitter extends PrecedenceVisitorEmitter {

visitNoParens(n: Node, s: Spine, context: CompilationContext) {
switch (n.kind) {
case "Cast":
if (n.targetType === "array") {
return ["[...", $.expr, "]"];
}
throw new EmitError(n, "unsuported cast target type");
case "VarDeclarationWithAssignment":
return ["let", $.assignment];
case "Block":
Expand Down
17 changes: 16 additions & 1 deletion src/languages/javascript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
implicitConversion,
list,
prefix,
cast,
} from "../../IR";
import {
type Language,
Expand All @@ -28,7 +29,7 @@ import {
mapMutationTo,
flipped,
} from "../../plugins/ops";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import {
forArgvToForEach,
forRangeToForCLike,
Expand Down Expand Up @@ -277,6 +278,20 @@ const javascriptLanguage: Language = {
methodsAsFunctions,
),
simplegolf(addOneToManyAssignments()),
required(
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
if (
type.kind === "List" &&
["boolean", "integer", "text"].includes(type.member.kind)
) {
return cast(node, "array");
}
return func("structuredClone", node);
}),
),
search(propertyCallToIndexCall),
simplegolf(
alias({
Expand Down
7 changes: 6 additions & 1 deletion src/languages/lua/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
mapBackwardsIndexToForwards,
mapMutationTo,
} from "../../plugins/ops";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import {
tempVarToMultipleAssignment,
inlineVariables,
Expand Down Expand Up @@ -212,6 +212,11 @@ const luaLanguage: Language = {
bit_not: "~",
}),
mapOpsTo.infix({ mul: "*" }),
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
}),
),
simplegolf(
alias({
Expand Down
13 changes: 12 additions & 1 deletion src/languages/nim/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
useUFCS,
useUnsignedDivision,
} from "./plugins";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import {
forArgvToForEach,
forArgvToForRange,
Expand Down Expand Up @@ -317,6 +317,17 @@ const nimLanguage: Language = {
),
required(
renameIdents(),
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
if (
type.kind === "List" &&
["boolean", "integer", "text"].includes(type.member.kind)
) {
return func("toSeq", node);
}
}),
addVarDeclarations,
addVarDeclarationOneToManyAssignments(),
addVarDeclarationManyToManyAssignments((_, spine) => spine.depth > 1),
Expand Down
3 changes: 3 additions & 0 deletions src/languages/python/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ export class PythonEmitter extends PrecedenceVisitorEmitter {
if (n.targetType === "list") {
return ["[*", $.expr, "]"];
}
if (n.targetType === "set") {
return ["{*", $.expr, "}"];
}
throw new EmitError(n, "unsuported cast target type");
case "Block":
return $.children.join(
Expand Down
19 changes: 17 additions & 2 deletions src/languages/python/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {
flipped,
withDefaults,
} from "../../plugins/ops";
import { alias, renameIdents } from "../../plugins/idents";
import { alias, clone, renameIdents } from "../../plugins/idents";
import {
forArgvToForEach,
forRangeToForEach,
Expand Down Expand Up @@ -363,6 +363,17 @@ const pythonLanguage: Language = {
mapOpsTo.infix({ mul: "*" }),
methodsAsFunctions,
useImplicitForCast,
clone((node, type) => {
if (["boolean", "integer", "text"].includes(type.kind)) {
return node;
}
if (type.kind === "List" || type.kind === "Set") {
if (["boolean", "integer", "text"].includes(type.member.kind)) {
return cast(node, type.kind.toLowerCase());
}
return func("copy.deepcopy", node);
}
}),
),
simplegolf(
addOneToManyAssignments(),
Expand All @@ -384,7 +395,11 @@ const pythonLanguage: Language = {
),
required(
renameIdents(),
addImports({ sys: ["sys.argv[1:]", "sys.argv"], math: ["math.gcd"] }),
addImports({
sys: ["sys.argv[1:]", "sys.argv"],
math: ["math.gcd"],
copy: ["copy.deepcopy"],
}),
removeImplicitConversions,
),
],
Expand Down
37 changes: 37 additions & 0 deletions src/plugins/idents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import {
getNodeFunc,
isText,
builtin,
type Type,
toString,
} from "../IR";
import { getType } from "../common/getType";
import { PolygolfError } from "../common/errors";
import { $ } from "../common/fragments";
import type { CompilationContext } from "@/common/compile";

function getIdentMap(
spine: Spine<IR.Node>,
Expand Down Expand Up @@ -143,3 +149,34 @@ export function useBuiltinAliases(builtins: Record<string, string>) {
}
};
}

export function clone(
mapping: (n: Node, t: Type, s: Spine) => Node | undefined,
) {
function isAliasing(spine: Spine): boolean {
return (
isUserIdent()(spine.node) ||
(spine.node.kind === "ConditionalOp" &&
[$.consequent, $.alternate].some((x) => isAliasing(spine.getChild(x))))
);
}
return function clone(node: Node, spine: Spine, context: CompilationContext) {
if (
!spine.isRoot &&
spine.parent!.node.kind === "Assignment" &&
spine.pathFragment?.prop === "expr" &&
isAliasing(spine)
) {
const type = getType(node, spine);
const res = mapping(node, type, spine);
if (res === undefined) {
throw new PolygolfError(
`Could not clone an identifier of type ${toString(type)}`,
node.source,
);
}
context.skipChildren();
return res;
}
};
}
75 changes: 75 additions & 0 deletions src/programs/control-flow.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,78 @@ _Swift_
var a = -20,n=20,k=3
for x in stride(from:a,to:n,by:k){print(x)}
```
## Cloning a list of ints
```polygolf
$a <- (list 1 2 3);
$b <- $a;
```
_Janet_
```janet nogolf
(var a @[1 2 3])(var b @[;a])
```
_Javascript_
```js nogolf
a=[1,2,3]
b=[...a]
```
_Nim_
```nim nogolf
var
a= @[1,2,3]
b=toSeq(a)
```
_Python_
```py nogolf
a=[1,2,3]
b=[*a]
```
_Swift_
```swift nogolf
var a=[1,2,3],b=a
```
## Cloning a list of lists of ints
```polygolf
$a <- (list (list 1 2 3) (list 4 5 6) (list 7 8 9));
$b <- $a;
```
_Janet_
```janet nogolf
(var a @[@[1 2 3]@[4 5 6]@[7 8 9]])(var b(thaw a))
```
_Javascript_
```js nogolf
a=[[1,2,3],[4,5,6],[7,8,9]]
b=structuredClone(a)
```
_Python_
```py nogolf
import copy
a=[[1,2,3],[4,5,6],[7,8,9]]
b=copy.deepcopy(a)
```
_Swift_
```swift nogolf
var a=[[1,2,3],[4,5,6],[7,8,9]],b=a
```

0 comments on commit 8f687e4

Please sign in to comment.