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

[VOI-98] Simplify the mechanisms behind cloning and parents #44

Merged
Merged
2 changes: 1 addition & 1 deletion src/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const compileModule = (opts: CompileExprOpts<VoidModule>) => {
const compileBlock = (opts: CompileExprOpts<Block>) => {
return opts.mod.block(
null,
opts.expr.body.toArray().map((expr, index, array) => {
opts.expr.body.map((expr, index, array) => {
if (index === array.length - 1) {
return compileExpression({ ...opts, expr, isReturnExpr: true });
}
Expand Down
20 changes: 11 additions & 9 deletions src/lib/fast-shift-array.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { at } from "vitest/dist/chunks/reporters.C_zwCd4j.js";

// TODO: Add map, filter, reduce, amd findIndex
export class FastShiftArray<T> {
private items: T[];
Expand All @@ -18,15 +20,6 @@ export class FastShiftArray<T> {
return value;
}

unshift(...items: T[]): number {
if (items.length === 0) return this.length;
this.headIndex = Math.max(this.headIndex - items.length, 0);
for (let i = 0; i < items.length; i++) {
this.items[this.headIndex + i] = items[i];
}
return this.length;
}

push(...items: T[]): number {
this.items.push(...items);
return this.length;
Expand All @@ -37,6 +30,11 @@ export class FastShiftArray<T> {
return this.items.pop();
}

unshift(...items: T[]): number {
this.items.splice(this.headIndex, 0, ...items);
return this.length;
}

at(index: number): T | undefined {
if (index < 0) {
index = this.length + index;
Expand Down Expand Up @@ -85,4 +83,8 @@ export class FastShiftArray<T> {
this.items.splice(0, this.headIndex);
this.headIndex = 0;
}

forEach(callbackfn: (value: T, index: number, array: T[]) => void): void {
this.items.slice(this.headIndex).forEach(callbackfn);
}
}
2 changes: 1 addition & 1 deletion src/parser/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const isInfixOpIdentifier = (op?: Identifier) =>
export const isOp = (op?: Expr): boolean => isInfixOp(op) || isPrefixOp(op);

export const prefixOps: OpMap = new Map([
["#", 7],
["#", 0],
["&", 7],
["!", 7],
["~", 7],
Expand Down
2 changes: 1 addition & 1 deletion src/parser/parse-chars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const parseChars = (

if (token.is("(")) {
const subList = parseChars(file, { nested: true });
subList.mayBeTuple = true;
subList.setAttribute("tuple?", true);
list.push(subList);
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions src/parser/reader-macros/comment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { noop } from "../../syntax-objects/index.js";
import { nop } from "../../syntax-objects/index.js";
import { ReaderMacro } from "./types.js";

export const comment: ReaderMacro = {
Expand All @@ -9,6 +9,6 @@ export const comment: ReaderMacro = {
file.consumeChar();
}

return noop();
return nop();
},
};
107 changes: 68 additions & 39 deletions src/parser/syntax-macros/functional-notation.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,97 @@
import { idIs, isOp } from "../grammar.js";
import { Expr, List } from "../../syntax-objects/index.js";
import { Expr, List, ListValue } from "../../syntax-objects/index.js";

export const functionalNotation = (list: List): List => {
const array = list.toArray();
let isTuple = false;

const result = list.mapFilter((expr, index, array) => {
if (expr.isList()) return functionalNotation(expr);
if (expr.isWhitespace()) return expr;
const { result } = array.reduce(
(acc, expr, index) => {
if (acc.skip > 0) {
acc.skip--;
return acc;
}

const nextExpr = array[index + 1];
if (nextExpr && nextExpr.isList() && !(isOp(expr) || idIs(expr, ","))) {
return processFnCall(expr, nextExpr, array, index);
}
if (expr.isList()) {
acc.result.push(functionalNotation(expr));
return acc;
}

if (list.mayBeTuple && idIs(expr, ",")) {
isTuple = true;
}
if (expr.isWhitespace()) {
acc.result.push(expr);
return acc;
}

return expr;
});
const nextExpr = array[index + 1];

if (isTuple) {
result.insert("tuple");
result.insert(",");
}
if (nextExpr && nextExpr.isList() && !(isOp(expr) || idIs(expr, ","))) {
return handleNextExpression(acc, expr, nextExpr, array, index);
}

return result;
if (list.getAttribute("tuple?") && idIs(expr, ",")) {
isTuple = true;
}

acc.result.push(expr);
return acc;
},
{ result: [], skip: 0 } as Accumulator
);

return finalizeResult(result, isTuple);
};

const processFnCall = (
type Accumulator = { result: ListValue[]; skip: number };

const handleNextExpression = (
acc: Accumulator,
expr: Expr,
nextExpr: List,
nextExpr: Expr,
array: Expr[],
index: number
): List => {
if (nextExpr.calls("generics")) {
return processGenerics(expr, array, index);
) => {
if ((nextExpr as List).calls("generics")) {
const generics = nextExpr as List;
const nextNextExpr = array[index + 2];
if (nextNextExpr && nextNextExpr.isList()) {
acc.result.push(processGenerics(expr, generics, nextNextExpr as List));
acc.skip = 2; // Skip next two expressions
} else {
acc.result.push(processGenerics(expr, generics));
acc.skip = 1; // Skip next expression
}
} else {
acc.result.push(processParamList(expr, nextExpr as List));
acc.skip = 1; // Skip next expression
}

return processParamList(expr, array, index);
return acc;
};

const processGenerics = (expr: Expr, array: Expr[], index: number): List => {
const generics = array.splice(index + 1, 1)[0] as List;
generics.mayBeTuple = false;
const finalizeResult = (result: ListValue[], isTuple: boolean): List => {
if (isTuple) {
result.unshift(",");
result.unshift("tuple");
}
return new List(result);
};

const list = array[index + 1]?.isList()
? (array.splice(index + 1, 1)[0] as List)
: new List({});
const processGenerics = (expr: Expr, generics: List, params?: List): List => {
generics.setAttribute("tuple?", false);

list.insert(",");
const list = params || new List([]);
list.insert(expr);
list.mayBeTuple = false;
list.insert(",", 1);
list.setAttribute("tuple?", false);
const functional = functionalNotation(list);

functional.insert(functionalNotation(generics), 2);
functional.insert(",", 3);
return functional;
};

const processParamList = (expr: Expr, array: Expr[], index: number): List => {
const list = array.splice(index + 1, 1)[0] as List;
list.insert(expr);
list.insert(",", 1);
list.mayBeTuple = false;
return functionalNotation(list);
const processParamList = (expr: Expr, params: List): List => {
params.insert(expr);
params.insert(",", 1);
params.setAttribute("tuple?", false);
return functionalNotation(params);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exports[`regular macro expansion 1`] = `
[
"exports",
[
"std#730",
"std#729",
],
],
[
Expand Down Expand Up @@ -47,7 +47,7 @@ exports[`regular macro expansion 1`] = `
],
[
"regular-macro",
"\`#759",
"\`#758",
[
"parameters",
],
Expand All @@ -68,7 +68,7 @@ exports[`regular macro expansion 1`] = `
],
[
"regular-macro",
"let#811",
"let#805",
[
"parameters",
],
Expand Down Expand Up @@ -121,7 +121,7 @@ exports[`regular macro expansion 1`] = `
],
[
"regular-macro",
"fn#1462",
"fn#1408",
[
"parameters",
],
Expand Down
4 changes: 2 additions & 2 deletions src/semantics/check-types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
List,
Expr,
noop,
nop,
Identifier,
ObjectType,
Type,
Expand All @@ -22,7 +22,7 @@ import { getExprType } from "./resolution/get-expr-type.js";
import { typesAreEquivalent } from "./resolution/index.js";

export const checkTypes = (expr: Expr | undefined): Expr => {
if (!expr) return noop();
if (!expr) return nop();
if (expr.isBlock()) return checkBlockTypes(expr);
if (expr.isCall()) return checkCallTypes(expr);
if (expr.isFn()) return checkFnTypes(expr);
Expand Down
4 changes: 2 additions & 2 deletions src/semantics/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const registerModule = ({
}

if (!existingModule && (name === "index" || name === "mod")) {
parentModule.unshift(...ast.toArray());
parentModule.unshift(...ast.toArray().reverse());
return;
}

Expand All @@ -73,7 +73,7 @@ const registerModule = ({
}

if (existingModule && !rest.length) {
module.unshift(...ast.toArray());
module.unshift(...ast.toArray().reverse());
}

if (!rest.length) return;
Expand Down
9 changes: 4 additions & 5 deletions src/semantics/regular-macros.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getIdStr } from "../syntax-objects/get-id-str.js";
import { getIdStr } from "../syntax-objects/lib/get-id-str.js";
import {
Bool,
Expr,
Expand All @@ -14,6 +14,7 @@ import {
VoidModule,
Block,
Use,
nop,
} from "../syntax-objects/index.js";
import {
registerExports,
Expand Down Expand Up @@ -256,15 +257,15 @@ const functions: Record<string, MacroFn | undefined> = {
},
quote: (quote: List) => {
const expand = (body: List): List =>
body.mapFilter((exp) => {
body.flatMap((exp) => {
if (exp.isList() && exp.calls("$")) {
const val = exp.at(1) ?? nop();
return evalMacroExpr(val);
}

if (exp.isList() && exp.calls("$@")) {
const val = exp.at(1) ?? nop();
return (evalMacroExpr(val) as List).insert("splice_quote");
return (evalMacroExpr(val) as List).toArray();
}

if (exp.isList()) return expand(exp);
Expand Down Expand Up @@ -385,8 +386,6 @@ const handleOptionalConditionParenthesis = (expr: Expr): Expr => {
return expr;
};

const nop = () => new List({}).push(Identifier.from("splice_quote"));

/** Binary logical comparison */
const bl = (args: List, fn: (l: any, r: any) => boolean) => {
// TODO Assertions / validation
Expand Down
7 changes: 2 additions & 5 deletions src/semantics/resolution/resolve-fn-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,14 @@ export const resolveFnTypes = (fn: Fn, call?: Call): Fn => {

const resolveParameters = (params: Parameter[]) => {
params.forEach((p) => {
if (p.type) {
return;
}
if (p.type) return;

if (!p.typeExpr) {
throw new Error(`Unable to determine type for ${p}`);
}

p.typeExpr = resolveTypes(p.typeExpr);
const type = getExprType(p.typeExpr);
p.type = type;
p.type = getExprType(p.typeExpr);
});
};

Expand Down
4 changes: 2 additions & 2 deletions src/semantics/resolution/resolve-types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Block } from "../../syntax-objects/block.js";
import { Expr } from "../../syntax-objects/expr.js";
import { noop } from "../../syntax-objects/helpers.js";
import { nop } from "../../syntax-objects/helpers.js";
import { List } from "../../syntax-objects/list.js";
import { VoidModule } from "../../syntax-objects/module.js";
import { ObjectLiteral } from "../../syntax-objects/object-literal.js";
Expand All @@ -22,7 +22,7 @@ import { resolveUse } from "./resolve-use.js";
* Returned tree not guaranteed to be same as supplied tree
*/
export const resolveTypes = (expr: Expr | undefined): Expr => {
if (!expr) return noop();
if (!expr) return nop();
if (expr.isBlock()) return resolveBlockTypes(expr);
if (expr.isCall()) return resolveCallTypes(expr);
if (expr.isFn()) return resolveFnTypes(expr);
Expand Down
2 changes: 1 addition & 1 deletion src/semantics/resolution/resolve-use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const resolveExport = (call: Call) => {
const block = call.argAt(0);
if (!block?.isBlock()) return call;

const entities = block.body.toArray().map(resolveTypes);
const entities = block.body.map(resolveTypes);
registerExports(call, entities, resolveModuleTypes);

return call;
Expand Down
19 changes: 19 additions & 0 deletions src/syntax-objects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Syntax Objects

Syntax objects are data structures that represent concepts within the
language. Such as functions, function calls, variables etc.

# Guidelines

- Each Syntax Object should be part of the `Expr` union
- Syntax objects must track their parent-child relationship. A child typically
belongs to a parent when it was directly defined with the parent. I.E.
parameters of functions, expressions / variables of block. These parent
relationships are stored via the parent pointer and must stay up to date
during clones. Use `ChildList` and `ChildMap` data types to help keep
make this easier.
- Resolved values should not be considered a child of the expression they
were resolved from. The type expression (`typeExpr`) of a parameter is
a child of the parameter, but the type it resolves to should never
accidentally be marked as a child of the parameter. Nor should it be
included in the clone (instead, they type resolution can be run again)
Loading