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

Virtual opcodes #372

Merged
merged 30 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
76b6326
formalize virtual opcodes
MichalMarsalek Mar 8, 2024
ff2335d
type improvements
MichalMarsalek Mar 8, 2024
43a5a8d
use virtual opcode constructors
MichalMarsalek Mar 8, 2024
1ecfa4c
remove unneccesary conversion plugins
MichalMarsalek Mar 10, 2024
ae01f00
fix forRangeToForEach
MichalMarsalek Mar 10, 2024
31d7795
fix some tests
MichalMarsalek Mar 10, 2024
9ac67b9
use virtual opcodes when emitting to Polygolf
MichalMarsalek Mar 10, 2024
9036259
add virtual opcode consistency test
MichalMarsalek Mar 10, 2024
a51754a
special case inference for some virtual opcodes
MichalMarsalek Mar 10, 2024
20c19b7
fix some tests
MichalMarsalek Mar 10, 2024
f2c48bf
generate docs for virtual opcodes too
MichalMarsalek Mar 10, 2024
f9b21b4
fix some tests
MichalMarsalek Mar 10, 2024
9f002e2
tests
MichalMarsalek Mar 10, 2024
5d2586c
Merge branch 'main' into virtual-opcodes
MichalMarsalek Mar 12, 2024
09dd799
fix some tests
MichalMarsalek Mar 12, 2024
0177cc8
fix more tests
MichalMarsalek Mar 12, 2024
8bf7ed9
fix tests
MichalMarsalek Mar 12, 2024
1d04bb8
fix test
MichalMarsalek Mar 12, 2024
7809ea7
fix tests
MichalMarsalek Mar 12, 2024
5206765
fix test
MichalMarsalek Mar 12, 2024
bd158ea
fix test
MichalMarsalek Mar 12, 2024
0aa4052
Merge branch 'virtual-opcodes' of https://github.com/polygolf-lang/po…
MichalMarsalek Mar 12, 2024
1d339a6
fix circularity
MichalMarsalek Mar 12, 2024
4a2c96d
fix coverage
MichalMarsalek Mar 12, 2024
ba06617
add first & last virtual opcodes
MichalMarsalek Mar 12, 2024
5c50706
turn putc opcodes to virtual
MichalMarsalek Mar 12, 2024
c65b1c1
turn print[Int] & println[Int] into virtual
MichalMarsalek Mar 12, 2024
d31e3a8
fix sub output type in opcodes.generated.md
MichalMarsalek Mar 12, 2024
693add1
support multiple uniqueId sequences
MichalMarsalek Mar 12, 2024
b3b5af4
use uniqueId func in plugins
MichalMarsalek Mar 12, 2024
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
2 changes: 1 addition & 1 deletion docs/opcodes.generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Hover opcode name to see a description.
| succ | [succ](## "Integer successor.") | [Int] | Int |
| pred | [pred](## "Integer predecessor.") | [Int] | Int |
| + | [add](## "Integer addition.")<br>[append](## "Returns a new list with the given item appended at the end.")<br>[concat[List]](## "Returns a new list formed by concatenation of the inputs.")<br>[concat[Text]](## "Returns a new text formed by concatenation of the inputs.") | [Int, Int, ...Int]<br>[(List T1), T1]<br>[(List T1), (List T1), ...(List T1)]<br>[Text, Text, ...Text] | Int<br>(List T1)<br>(List T1)<br>Text |
| - | [sub](## "Integer subtraction.")<br>[neg](## "Integer negation.") | [Int, Int]<br>[Int] | Int<br>Int |
| - | [sub](## "Integer subtraction.")<br>[neg](## "Integer negation.") | [Int, Int, ...Int]<br>[Int] | Int<br>Int |
| * | [mul](## "Integer multiplication.") | [Int, Int, ...Int] | Int |
| div | [div](## "Integer floor division.") | [Int, Int] | Int |
| ^ | [pow](## "Integer exponentiation.") | [Int, 0..oo] | Int |
Expand Down
91 changes: 54 additions & 37 deletions src/IR/exprs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ import {
OpCodes,
defaults,
maxArity,
type PhysicalOpCode,
isPhysicalOpCode,
isVirtualOpCode,
virtualOpCodeDefinitions,
type VirtualOpCode,
} from "./IR";
import { mapObjectValues, useDefaults } from "../common/arrays";

Expand All @@ -51,17 +56,15 @@ export interface Cast extends BaseNode {

* Polygolf ensures that in the IR, there will never be:

* - Op(neg), Op(sub)
* - Op(pred), Op(succ)
* - Op(is_even), Op(is_odd)
* - Op as a direct child of a Op with the same associative OpCode
* - Integer as a nonfirst child of a commutative Op
* - Boolean negation of a boolean negation
* - Boolean negation of an op that has a negated counterpart
*
* This is ensured when using the op contructor function and the Spine API so avoid creating such nodes manually.
*/
export interface Op<Op extends OpCode = OpCode> extends BaseNode {
export interface Op<Op extends PhysicalOpCode = PhysicalOpCode>
extends BaseNode {
readonly kind: "Op";
readonly op: Op;
readonly args: OpCodeArgValues<Op>;
Expand Down Expand Up @@ -192,8 +195,10 @@ export const op = {
), // allow unary opcodes to be used in map
) as {
[O in OpCode]: OpCodeArgValues<O> extends readonly []
? Op<O>
: (...args: OpCodeArgValues<O>) => Op<O>;
? O extends PhysicalOpCode
? Op<O>
: Op
: (...args: OpCodeArgValues<O>) => O extends PhysicalOpCode ? Op<O> : Op;
}),
unsafe(op: OpCode, useDefaults = false) {
return (...args: Node[]) =>
Expand All @@ -205,7 +210,7 @@ export const op = {
* This assumes that the construction will not break the invariants described
* on `Op` interface and hence is made private.
*/
function _op(op: OpCode, ...args: Node[]): Op {
function _op(op: PhysicalOpCode, ...args: Node[]): Op {
return {
kind: "Op",
op,
Expand All @@ -219,14 +224,14 @@ function _op(op: OpCode, ...args: Node[]): Op {
*/
function opUnsafe(opCode: OpCode, ...args: Node[]): Node {
if (!isOpCode(opCode)) return _op(opCode, ...args);
if (opCode === "pred") return op.add(args[0], int(-1n));
if (opCode === "succ") return op.add(args[0], int(1n));
if (opCode === "is_even")
return op["eq[Int]"](int(0), op.mod(args[0], int(2)));
if (opCode === "is_odd")
return op["eq[Int]"](int(1), op.mod(args[0], int(2)));
if (isVirtualOpCode(opCode)) {
return (virtualOpCodeDefinitions[opCode].construct as any).apply(
null,
args,
);
}
if (isUnary(opCode)) {
const value = evalUnary(opCode, args[0]);
const value = evalUnary({ kind: "Op", op: opCode, args: args as any });
if (value !== null) return value;
}
if (opCode === "not" || opCode === "bit_not") {
Expand All @@ -250,15 +255,6 @@ function opUnsafe(opCode: OpCode, ...args: Node[]): Node {
) {
return args[0].args[0];
}
if (opCode === "neg") {
if (isInt()(args[0])) {
return int(-args[0].value);
}
return op.mul(int(-1), args[0]);
}
if (opCode === "sub") {
return op.add(args[0], op.neg(args[1]));
}
if (isAssociative(opCode)) {
args = args.flatMap((x) => (isOp(opCode)(x) ? x.args : [x]));
if (args.length === 1) return args[0];
Expand Down Expand Up @@ -346,7 +342,7 @@ function opUnsafeWithDefaults(opCode: OpCode, ...args: Node[]): Node {
}

function evalBinary(
op: BinaryOpCode | VariadicOpCode,
op: PhysicalOpCode & (BinaryOpCode | VariadicOpCode),
left: Node,
right: Node,
): Integer | Text | null {
Expand All @@ -368,15 +364,19 @@ function evalBinary(
return null;
}

function evalUnary(op: UnaryOpCode, arg: Node): Integer | Text | null {
if (isText()(arg)) {
const value = arg.value;
switch (op) {
case "size[byte]":
case "size[Ascii]":
return int(byteLength(value));
case "size[codepoint]":
return int(charLength(value));
function evalUnary(
op: Op<UnaryOpCode & PhysicalOpCode>,
): Integer | Text | null {
for (const opCode of [
"size[byte]",
"size[Ascii]",
"size[codepoint]",
] as const) {
const args = argsOf[opCode](op);
if (args !== undefined && isText()(args[0])) {
return int(
(opCode === "size[codepoint]" ? charLength : byteLength)(args[0].value),
);
}
}
return null;
Expand Down Expand Up @@ -654,15 +654,32 @@ export function isNegative(expr: Node) {
);
}

function _isOp<O extends OpCode>(...ops: O[]): (x: Node) => x is Op<O> {
function _isOp<O extends PhysicalOpCode>(...ops: O[]): (x: Node) => x is Op<O> {
return ((x: Node) =>
x.kind === "Op" && (ops.length === 0 || ops?.includes(x.op as any))) as any;
}

export const isOp: {
[O in OpCode]: (x: Node) => x is Op<O>;
} & (<O extends OpCode>(...op: O[]) => (x: Node) => x is Op<O>) = _isOp as any;
[O in PhysicalOpCode]: (x: Node) => x is Op<O>;
} & (<O extends PhysicalOpCode>(...op: O[]) => (x: Node) => x is Op<O>) =
_isOp as any;

function _argsOf<O extends VirtualOpCode>(
op: O,
): (x: Op) => OpCodeArgValues<O> | undefined {
return (node: Op) => virtualOpCodeDefinitions[op].getArgs(node) as any;
}

export const argsOf: {
[O in VirtualOpCode]: (x: Node) => OpCodeArgValues<O> | undefined;
} & (<O extends VirtualOpCode>(
op: O,
) => (x: Node) => OpCodeArgValues<O> | undefined) = _argsOf as any;

for (const opCode of OpCodes) {
isOp[opCode] = _isOp(opCode) as any;
if (isPhysicalOpCode(opCode)) {
isOp[opCode] = _isOp(opCode) as any;
} else {
argsOf[opCode] = _argsOf(opCode) as any;
}
}
5 changes: 3 additions & 2 deletions src/IR/loops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
op,
isOp,
type Op,
type OpCode,
type PhysicalOpCode,
} from "./IR";

/**
Expand Down Expand Up @@ -130,7 +130,7 @@ export function forArgv(

export const isRangeOp = isOp("range_excl", "range_incl");

export function isForEach<O extends OpCode>(
export function isForEach<O extends PhysicalOpCode>(
...ops: O[]
): (x: Node) => x is ForEach<Op<O>> {
return function (x: Node): x is ForEach<Op<O>> {
Expand All @@ -139,6 +139,7 @@ export function isForEach<O extends OpCode>(
}

export const isForEachRange = isForEach("range_incl", "range_excl");
export const isForEachExclRange = isForEach("range_excl");
export const isForEachChar = isForEach(
"text_to_list[Ascii]",
"text_to_list[byte]",
Expand Down
Loading
Loading