Skip to content

Commit

Permalink
Support destructuring in for; improve ix further
Browse files Browse the repository at this point in the history
  • Loading branch information
phunanon committed Sep 23, 2023
1 parent 0dfeda6 commit 0346977
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 39 deletions.
36 changes: 36 additions & 0 deletions corpus/cobs-encoding.ix
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(function COBS unencoded
(let chunk (take-until [0] unencoded)
length (len chunk)
encoded (append (first 254 chunk) (or %1 []))
more? (< length (len unencoded)))
(if (or (and (= length 254) more?) (> length 254))
(recur (skip 254 unencoded) encoded)
(if more?
(recur (skip (inc length) unencoded) encoded)
(append 0 (flat-map @(.. vec (inc (len %))) encoded)))))

; A more compact but probably wobbly version:
; (function COBS unencoded
; (let chunk (take-until [0] unencoded)
; [c-len u-len] (proj len chunk unencoded)
; encoded (append (first 254 chunk) (or %1 [])))
; (if (<= 253 c-len 255 u-len)
; (recur (skip 254 unencoded) encoded)
; (if (< c-len u-len)
; (recur (skip (inc c-len) unencoded) encoded)
; (append 0 (flat-map @(.. vec (inc (len %))) encoded)))))

(for [a b] [
[[0x00] [0x01 0x01 0x00]]
[[0x00 0x00] [0x01 0x01 0x01 0x00]]
[[0x00 0x11 0x00] [0x01 0x02 0x11 0x01 0x00]]
[[0x11 0x22 0x00 0x33] [0x03 0x11 0x22 0x02 0x33 0x00]]
[[0x11 0x22 0x33 0x44] [0x05 0x11 0x22 0x33 0x44 0x00]]
[[0x11 0x00 0x00 0x00] [0x02 0x11 0x01 0x01 0x01 0x00]]
[(range 1 255) (.. vec 0xFF (range 1 255) 0x00)]
[(range 255) (.. vec 0x01 0xFF (range 1 255) 0x00)]
[(range 1 256) (.. vec 0xFF (range 1 255) 0x02 0xFF 0x00)]
[(.. vec (range 2 256) 0x00) (.. vec 0xFF (range 2 256) 0x01 0x01 0x00)]
[(.. vec (range 3 256) 0x00 0x01) (.. vec 0xFE (range 3 256) 0x02 0x01 0x00)]
]
(assert (= (COBS a) b)))
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "insitux",
"version": "23.9.22",
"version": "23.9.23",
"description": "Extensible scripting language written in portable TypeScript.",
"main": "dist/invoker.js",
"types": "dist/invoker.d.ts",
Expand Down
16 changes: 9 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const insituxVersion = 230922;
export const insituxVersion = 230923;
import { asBoo } from "./checks";
import { arityCheck, keyOpErr, numOpErr, typeCheck, typeErr } from "./checks";
import { isLetter, isDigit, isSpace, isPunc } from "./checks";
Expand Down Expand Up @@ -993,8 +993,8 @@ function exeOp(op: string, args: Val[], ctx: Ctx, errCtx: ErrCtx): Val {
case "lower?": {
const s = str(args[0]);
const code = charCode(s);
if (op === 'upper?') {
return _boo(code >= 65 && code < 91);
if (op === "upper?") {
return _boo(code >= 65 && code < 91);
}
return _boo(code >= 97 && code < 123);
}
Expand Down Expand Up @@ -1366,12 +1366,12 @@ function exeFunc(
ctx: Ctx,
func: Func,
args: Val[],
sharedLets: boolean,
closureOrFor: boolean,
): Val | Val[] {
--ctx.callBudget;
const prevLets = lets;
let myLets: { [key: string]: Val } = {};
if (!sharedLets) {
if (!closureOrFor) {
lets = myLets;
}
const stack: Val[] = [];
Expand Down Expand Up @@ -1534,7 +1534,9 @@ function exeFunc(
stack.push(_nul());
}
i = lim;
forState = "ret";
if (closureOrFor) {
forState = "ret";
}
break;
case "clo": {
//Ensure any in-scope declarations are captured here
Expand Down Expand Up @@ -1656,7 +1658,7 @@ function exeFunc(
}
}
lets = prevLets;
if (sharedLets) {
if (closureOrFor) {
return stack;
}
return stack[len(stack) - 1];
Expand Down
9 changes: 6 additions & 3 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,8 @@ function parseForm(
//Calculate jmp-to-ends
for (let i = 0, max = len(ins); i < max; ++i) {
const next = ins[i];
if (next.typ === 'jmp' && !next.value) {
next.value = (max - i) - 1;
if (next.typ === "jmp" && !next.value) {
next.value = max - i - 1;
}
}
return ins;
Expand Down Expand Up @@ -547,7 +547,10 @@ function parseForm(
const defAndVals: DefAndValIns[] = [];
let bodyAt = 0;
for (let n = 0, lim = len(nodes); n < lim; ++n) {
if (n === lim - 1 || !isToken(nodes[n])) {
if (
n === lim - 1 ||
(!isToken(nodes[n]) && symAt(nodes[n]) !== "vec")
) {
bodyAt = n;
break;
}
Expand Down
63 changes: 35 additions & 28 deletions src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,51 +494,54 @@ async function processCliArguments(args: string[]) {
}
const params = programArgs.map(_str);

const executeInline = args.includes("-e");
const openReplAfter = !executeInline && args.includes("-r");
const parts = openReplAfter ? args.filter(a => a !== "-r") : args;
let executeInline = "";
const executeInlineIdx = args.indexOf("-e");
if (executeInlineIdx !== -1) {
if (executeInlineIdx + 1 === args.length) {
console.error(
"Argument after -e (the code to be executed inline) was not provided.",
);
} else {
executeInline = args[executeInlineIdx + 1];
args.splice(executeInlineIdx, 2);
}
}

const matchParts = (b: RegExp[]) =>
parts.length === b.length && parts.every((part, i) => b[i]?.test(part));
let openReplAfter = false;
const openReplAfterIdx = args.indexOf("-r");
if (openReplAfterIdx !== -1) {
openReplAfter = true;
args.splice(openReplAfterIdx, 1);
}

const matchArgs = (b: RegExp[]) =>
args.length === b.length && args.every((arg, i) => b[i]?.test(arg));

const invoke = (code: string, id = code) =>
printErrorOutput(invoker(ctx, code, id, params).output);

const [arg0, arg1, arg2] = parts;
if (matchArgs([/^\.$/])) {
args[0] = "entry.ix";
}

const [arg0, arg1, arg2] = args;
if (["help", "-h"].includes(arg0)) {
console.log(helpText);
exit();
} else if (executeInline) {
const switchIndex = args.findIndex(x => x === "-e");
if (switchIndex + 1 === args.length) {
console.error("Code after -e was not provided.");
} else {
const code = args[switchIndex + 1];
invoke(code);
}
} else if (matchParts([/^\.$/])) {
//Execute entry.ix in working directory
const path = "entry.ix";
if (!existsSync(path)) {
console.log("entry.ix does not exist in this directory.");
} else {
const code = readFileSync(path).toString();
invoke(code, path);
}
} else if (matchParts([/^i$/, githubRegex])) {
} else if (matchArgs([/^i$/, githubRegex])) {
//Install dependency via Github
await dependencyResolve({ kind: "Github install", repo: arg1 });
} else if (matchParts([/^i$/, /.+/, /https*:/])) {
} else if (matchArgs([/^i$/, /.+/, /https*:/])) {
//Install dependency via HTTP
exitIfBadAlias(arg1);
await dependencyResolve({ kind: "http install", url: arg2, alias: arg1 });
} else if (arg0 === "i") {
await depsFileAction("install");
exit();
} else if (matchParts([/^r$/, githubRegex])) {
} else if (matchArgs([/^r$/, githubRegex])) {
//Remove Github dependency
await dependencyResolve({ kind: "Github remove", repo: arg1 });
} else if (matchParts([/^r$/, /.+/])) {
} else if (matchArgs([/^r$/, /.+/])) {
//Remove aliased dependency
exitIfBadAlias(arg1);
await dependencyResolve({ kind: "alias remove", alias: arg1 });
Expand All @@ -547,14 +550,18 @@ async function processCliArguments(args: string[]) {
exit();
} else {
//Execute files
for (const path of parts) {
for (const path of args) {
if (!existsSync(path)) {
console.log(`${path} not found - ignored.`);
} else {
const code = readFileSync(path).toString();
invoke(code, path);
}
}
//Execute optional inline
if (executeInline) {
invoke(executeInline);
}
}

if (openReplAfter) {
Expand Down
5 changes: 5 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ const tests: {
code: `(for x [0 1 2 3] (when (= x 2) (break)) x)`,
out: `[0 1]`,
},
{
name: "For destructure",
code: `(for [x y] [[1 2]] [x y])`,
out: `[[1 2]]`
},
{
name: "Filter by integer",
code: `(filter 2 [[1] [:a :b :c] "hello" "hi"])`,
Expand Down

0 comments on commit 0346977

Please sign in to comment.