Skip to content

Commit

Permalink
find-idx; improve var let error messages; m.
Browse files Browse the repository at this point in the history
  • Loading branch information
phunanon committed Aug 4, 2023
1 parent a5522cd commit 6f2aa98
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 95 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,11 @@ etc
(find (< 5) [4 5 6 7]) → 6
(find ["a" "b"] "Able") → "b"

;Returns the index of the first item or character in a vector or string
; matching a predicate.
(find-idx odd? [6 8 9 0]) → 2
(find-idx (< 5) [2 3 4 5 6 7]) → 4

;Returns the number of vector items, string characters, or dictionary entries
; matching a predicate.
(count odd? (range 10)) → 5
Expand Down
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.7.23",
"version": "23.7.28",
"description": "Extensible scripting language written in portable TypeScript.",
"main": "dist/invoker.js",
"types": "dist/invoker.d.ts",
Expand Down
45 changes: 23 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const insituxVersion = 230723;
export const insituxVersion = 230728;
import { asBoo } from "./checks";
import { arityCheck, keyOpErr, numOpErr, typeCheck, typeErr } from "./checks";
import { isLetter, isDigit, isSpace, isPunc } from "./checks";
Expand All @@ -13,8 +13,8 @@ const { trim, trimStart, trimEnd, strIdx, replace, rreplace } = pf;
const { charCode, codeChar, getTimeMs, randInt, randNum } = pf;
const { isNum, len, objKeys, range, toNum, isArray, isObj } = pf;
import { doTests } from "./test";
import { assertUnreachable, Env, InvokeError, InvokeResult } from "./types";
import { ExternalFunctions, syntaxes } from "./types";
import { Env, InvokeError, InvokeResult, InvokeValResult } from "./types";
import { assertUnreachable, ExternalFunctions, syntaxes } from "./types";
import { Ctx, Dict, ErrCtx, Func, Ins, Val, ops, typeNames } from "./types";
import { asArray, dictDrops, isEqual, num, stringify, val2str } from "./val";
import { dic, vec, dictDrop, dictGet, dictSet, toDict, pathSet } from "./val";
Expand Down Expand Up @@ -407,30 +407,31 @@ function exeOp(op: string, args: Val[], ctx: Ctx, errCtx: ErrCtx): Val {
return { t: "clo", v: <Func>{ name, ins } };
}
case "map":
case "for": {
const collections = slice(args, 1);
const badArg = collections.findIndex(
({ t }) => t !== "vec" && t !== "str" && t !== "dict",
);
if (badArg !== -1) {
const badType = typeNames[collections[badArg].t];
throwTypeErr(
`argument ${
badArg + 2
} must be either: string, vector, dictionary, not ${badType}`,
errCtx,
);
}
}
case "flat-map":
case "for":
case "reduce":
case "reductions":
case "filter":
case "remove":
case "find":
case "find-idx":
case "count":
case "all?": {
const closure = getExe(ctx, args.shift()!, errCtx);
if (op === "map" || op === "for") {
const badArg = args.findIndex(
({ t }) => t !== "vec" && t !== "str" && t !== "dict",
);
if (badArg !== -1) {
const badType = typeNames[args[badArg].t];
throwTypeErr(
`argument ${
badArg + 2
} must be either: string, vector, dictionary, not ${badType}`,
errCtx,
);
}
}

if (op === "for") {
const arrays = args.map(asArray);
Expand Down Expand Up @@ -475,7 +476,7 @@ function exeOp(op: string, args: Val[], ctx: Ctx, errCtx: ErrCtx): Val {
if (op !== "reduce" && op !== "reductions") {
const array = asArray(args[0]);
const isRemove = op === "remove",
isFind = op === "find",
isFind = op === "find" || op === "find-idx",
isCount = op === "count",
isAll = op === "all?";
const filtered: Val[] = [];
Expand All @@ -490,7 +491,7 @@ function exeOp(op: string, args: Val[], ctx: Ctx, errCtx: ErrCtx): Val {
count += b ? 1 : 0;
} else if (isFind) {
if (b) {
return array[i];
return op === "find" ? array[i] : _num(i);
}
} else if (b !== isRemove) {
filtered.push(array[i]);
Expand Down Expand Up @@ -1588,7 +1589,7 @@ function parseAndExe(
function ingestExternalOperations(functions: ExternalFunctions) {
objKeys(functions).forEach(name => {
if (ops[name] && !ops[name].external) {
throw "Redefining internal operations is disallowed.";
throw `Redefining internal operations (${name}) is disallowed.`;
}
ops[name] = { ...functions[name].definition, external: true };
});
Expand Down Expand Up @@ -1696,7 +1697,7 @@ export function invokeVal(
errCtx: ErrCtx,
val: Val,
params: Val[],
): InvokeResult {
): InvokeValResult {
const ins: Ins[] = [
...params.map(value => <Ins>{ typ: "val", value, errCtx }),
{ typ: "val", value: val, errCtx },
Expand Down
27 changes: 19 additions & 8 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { arityCheck, keyOpErr, numOpErr, typeCheck } from "./checks";
import { makeClosure } from "./closure";
import * as pf from "./poly-fills";
const { has, flat, push, slice, splice } = pf;
const { has, flat, push, slice, splice, isNum, len, toNum } = pf;
const { slen, starts, sub, substr, strIdx, subIdx } = pf;
const { isNum, len, toNum } = pf;
import { ParamsShape, Func, Funcs, Ins, ops, Val, syntaxes } from "./types";
import { assertUnreachable, InvokeError, ErrCtx } from "./types";
import { val2str } from "./val";

export type Token = {
typ: "str" | "num" | "sym" | "rem" | "(" | ")";
Expand Down Expand Up @@ -453,23 +453,34 @@ function parseForm(
return err("provide a value after each declaration name");
}
const ins: ParserIns[] = [];
const symErrMsg = `${op} name must be a new symbol or destructuring`;
for (let d = 0, lim = len(defs); d < lim; ++d) {
push(ins, nodeParser(vals[d]));
const def = defs[d];
if (isToken(def)) {
const defIns = nodeParser(defs[d]);
if (len(defIns) > 1 || defIns[0].typ !== "ref") {
return err(symErrMsg, defIns[0].errCtx);
const [defIns] = nodeParser(defs[d]);
if (defIns.typ === "ref") {
if (has(syntaxes, defIns.value)) {
return err(
`"${defIns.value}" cannot be redefined: already exists`,
);
}
ins.push({ typ: op, value: defIns.value, errCtx });
continue;
}
ins.push({ typ: op, value: defIns[0].value, errCtx });
const errMsg =
defIns.typ === "val"
? `"${val2str(defIns.value)}" cannot be redefined: already exists`
: `invalid ${op} name`;
return err(errMsg, defIns.errCtx);
} else {
const { shape, errors } = parseParams([def], true);
if (len(errors)) {
return errors;
}
if (!len(shape)) {
return err(symErrMsg);
return err(
`${op} name must be a symbol or destructuring, not expression`,
);
}
const typ = op === "var" ? "dva" : "dle";
ins.push({ typ, value: shape, errCtx });
Expand Down
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type Val =
| { t: "vec"; v: Val[] }
| { t: "str" | "func" | "key" | "ref" | "unm"; v: string }
| { t: "str" | "func" | "key" | "unm"; v: string }
| { t: "null"; v: undefined }
| { t: "wild"; v: undefined }
| { t: "bool"; v: boolean }
Expand All @@ -21,6 +21,7 @@ export type InvokeResult =
| { kind: "empty" }
| Val
| { kind: "errors"; errors: InvokeError[] };
export type InvokeValResult = Val | { kind: "errors"; errors: InvokeError[] };

export type Dict = {
keys: Val[];
Expand Down Expand Up @@ -282,6 +283,11 @@ export const ops: {
returns: ["vec", "str", "dict"],
},
find: { exactArity: 2, params: ["any", ["vec", "dict", "str"]] },
"find-idx": {
exactArity: 2,
params: ["any", ["vec", "dict", "str"]],
returns: ["num"],
},
"take-while": {
exactArity: 2,
params: ["any", ["vec", "str"]],
Expand Down
123 changes: 61 additions & 62 deletions src/val-translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,66 @@ import { val2str } from "./val";

/** Incomplete. */
export function jsToIx(
v: unknown,
ifUndetermined = (x: unknown) => <Val>{ t: "str", v: `${x}` },
): Val {
if (isStr(v)) {
return { t: "str", v };
}
if (isNum(v)) {
return { t: "num", v };
}
if (v === true || v === false) {
return { t: "bool", v };
}
if (v === null) {
return { t: "null", v: undefined };
}
const mapper = (v: unknown[]) => v.map(x => jsToIx(x, ifUndetermined));
if (isArray(v)) {
return { t: "vec", v: mapper(v) };
}
if (isObj(v)) {
return {
t: "dict",
v: { keys: mapper(objKeys(v)), vals: mapper(objVals(v)) },
};
}
return ifUndetermined(v);
v: unknown,
ifUndetermined = (x: unknown) => <Val>{ t: "str", v: `${x}` },
): Val {
if (isStr(v)) {
return { t: "str", v };
}

/** Incomplete. */
export function ixToJs(
v: Val,
ifUndetermined = (x: Val) => x.v,
):
| string
| number
| boolean
| null
| Record<string, unknown>
| unknown[]
| unknown {
if (v.t === "str" || v.t === "num" || v.t === "bool" || v.t === "key") {
return v.v;
}
if (v.t === "vec") {
return v.v.map(x => ixToJs(x, ifUndetermined));
}
if (v.t === "null") {
return null;
}
if (v.t === "dict") {
const keys = v.v.keys.map(x => val2str(x));
const vals = v.v.vals.map(x => ixToJs(x, ifUndetermined));
const obj: Record<string, unknown> = {};
keys.forEach((k, i) => {
obj[k] = vals[i];
});
return obj;
}
if (v.t === "ext") {
return v.v;
}
return ifUndetermined(v);
if (isNum(v)) {
return { t: "num", v };
}

if (v === true || v === false) {
return { t: "bool", v };
}
if (v === null) {
return { t: "null", v: undefined };
}
const mapper = (v: unknown[]) => v.map(x => jsToIx(x, ifUndetermined));
if (isArray(v)) {
return { t: "vec", v: mapper(v) };
}
if (isObj(v)) {
return {
t: "dict",
v: { keys: mapper(objKeys(v)), vals: mapper(objVals(v)) },
};
}
return ifUndetermined(v);
}

/** Incomplete. */
export function ixToJs(
v: Val,
ifUndetermined = (x: Val) => x.v,
):
| string
| number
| boolean
| null
| Record<string, unknown>
| unknown[]
| unknown {
if (v.t === "str" || v.t === "num" || v.t === "bool" || v.t === "key") {
return v.v;
}
if (v.t === "vec") {
return v.v.map(x => ixToJs(x, ifUndetermined));
}
if (v.t === "null") {
return null;
}
if (v.t === "dict") {
const keys = v.v.keys.map(x => val2str(x));
const vals = v.v.vals.map(x => ixToJs(x, ifUndetermined));
const obj: Record<string, unknown> = {};
keys.forEach((k, i) => {
obj[k] = vals[i];
});
return obj;
}
if (v.t === "ext") {
return v.v;
}
return ifUndetermined(v);
}
1 change: 0 additions & 1 deletion src/val.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const isEqual = (a: Val, b: Val) => {
return len(a.v.keys) === len(bd.keys) && isVecEqual(a.v.keys, bd.keys);
}
case "str":
case "ref":
case "key":
case "func":
return str(a) === str(b);
Expand Down

0 comments on commit 6f2aa98

Please sign in to comment.