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-88] Generic parameter parsing #26

Merged
merged 4 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 32 additions & 13 deletions src/parser.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,14 @@ export function parse(file: File, opts: ParseOpts = {}): List {
value: !opts.nested ? ["ast", ","] : [],
});

let prev: Token | undefined = undefined;
let cur: Token | undefined = undefined;
while (file.hasCharacters) {
const token = lexer(file);
prev = cur;
cur = token;

const readerMacro = getReaderMacroForToken(token);

if (readerMacro) {
const result = readerMacro(file, {
token,
reader: (file, terminator, parent) =>
parse(file, {
nested: true,
terminator,
parent: parent ?? opts.parent,
}),
});
if (typeof result !== "undefined") list.push(result);
if (processWithReaderMacro(token, prev, file, opts, list)) {
continue;
}

Expand Down Expand Up @@ -154,3 +146,30 @@ const consumeSpaces = (file: File, token: Token) => {
token.addChar(file.consumeChar());
}
};

/** Returns true if token was matched with and processed by a macro */
const processWithReaderMacro = (
token: Token,
prev: Token | undefined,
file: File,
opts: ParseOpts,
list: List
) => {
const readerMacro = getReaderMacroForToken(token, prev);
if (!readerMacro) return undefined;

const result = readerMacro(file, {
token,
reader: (file, terminator, parent) =>
parse(file, {
nested: true,
terminator,
parent: parent ?? opts.parent,
}),
});

if (!result) return undefined;

list.push(result);
return true;
};
2 changes: 1 addition & 1 deletion src/reader-macros/array-literal.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReaderMacro } from "./types.mjs";

export const arrayLiteralMacro: ReaderMacro = {
tag: "[",
match: (t) => t.value === "[",
macro: (file, { reader }) => {
const items = reader(file, "]");
return items.insert("array").insert(",", 1);
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/boolean.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Bool } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const booleanMacro: ReaderMacro = {
tag: /^true|false$/,
match: (t) => /^true|false$/.test(t.value),
macro: (_, { token }) =>
new Bool({
value: token.is("true"),
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/comment.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { noop } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const comment: ReaderMacro = {
tag: /^\/\/[^\s]*$/,
match: (t) => /^\/\/[^\s]*$/.test(t.value),
macro: (file) => {
while (file.hasCharacters) {
if (file.next === "\n") break;
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/dictionary-literal.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Identifier, List } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const dictionaryLiteralMacro: ReaderMacro = {
tag: "#{",
match: (t) => t.value === "#{",
macro: (file, { reader }) => {
const dict = new Identifier({ value: "dict" });
const items = reader(file, "}");
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/float.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Float } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const floatMacro: ReaderMacro = {
tag: /^[+-]?\d+\.\d+$/,
match: (t) => /^[+-]?\d+\.\d+$/.test(t.value),
macro: (_, { token }) =>
new Float({
value: Number(token.value),
Expand Down
11 changes: 11 additions & 0 deletions src/reader-macros/generics.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ReaderMacro } from "./types.mjs";

export const genericsMacro: ReaderMacro = {
match: (t, prev) => {
return t.value === "<" && !!prev && /\w+/.test(prev.value);
},
macro: (file, { reader }) => {
const items = reader(file, ">");
return items.insert("generics").insert(",", 1);
},
};
30 changes: 6 additions & 24 deletions src/reader-macros/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { scientificENotationMacro } from "./scientific-e-notation.mjs";
import { stringMacro } from "./string.mjs";
import { objectLiteralMacro } from "./object-literal.mjs";
import { ReaderMacro } from "./types.mjs";
import { genericsMacro } from "./generics.mjs";

const macros = [
objectLiteralMacro,
Expand All @@ -20,30 +21,11 @@ const macros = [
stringMacro,
comment,
booleanMacro,
genericsMacro,
];

const readerMacros = macros.reduce(
({ map, patterns }, reader) => {
if (typeof reader.tag === "string") {
map.set(reader.tag, reader.macro);
return { map, patterns };
}

patterns.push({ pattern: reader.tag, macro: reader.macro });
return { map, patterns };
},
{
map: new Map<string, ReaderMacro["macro"]>(),
patterns: [] as { pattern: RegExp; macro: ReaderMacro["macro"] }[],
}
);

export const getReaderMacroForToken = (
token: Token
): ReaderMacro["macro"] | undefined => {
return (
readerMacros.map.get(token.value) ??
readerMacros.patterns.find(({ pattern }) => pattern.test(token.value))
?.macro
);
};
token: Token,
prev?: Token
): ReaderMacro["macro"] | undefined =>
macros.find((m) => m.match(token, prev))?.macro;
2 changes: 1 addition & 1 deletion src/reader-macros/int.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Int } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const intMacro: ReaderMacro = {
tag: /^[+-]?\d+$/,
match: (t) => /^[+-]?\d+$/.test(t.value),
macro: (_, { token }) =>
new Int({
value: Number(token.value),
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/object-literal.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReaderMacro } from "./types.mjs";

export const objectLiteralMacro: ReaderMacro = {
tag: "{",
match: (t) => t.value === "{",
macro: (dream, { reader }) => {
const items = reader(dream, "}");
return items.insert("object").insert(",", 1);
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/scientific-e-notation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ReaderMacro } from "./types.mjs";

export const scientificENotationMacro: ReaderMacro = {
/** Regex from Michael Dumas https://regexlib.com/REDetails.aspx?regexp_id=2457 */
tag: /^[+-]?\d(\.\d+)?[Ee][+-]?\d+$/,
match: (t) => /^[+-]?\d(\.\d+)?[Ee][+-]?\d+$/.test(t.value),
macro: (_, { token }) =>
new List({ location: token.location })
.push(new Identifier({ value: "scientific-e-notion" }))
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/string.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Identifier, StringLiteral } from "../syntax-objects/index.mjs";
import { ReaderMacro } from "./types.mjs";

export const stringMacro: ReaderMacro = {
tag: /^[\"\']$/,
match: (t) => /^[\"\']$/.test(t.value),
macro: (file, { token }) => {
const startChar = token.value;
token.value = "";
Expand Down
2 changes: 1 addition & 1 deletion src/reader-macros/types.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Expr, List } from "../syntax-objects/index.mjs";
import { Token } from "../lib/token.mjs";

export interface ReaderMacro {
tag: string | RegExp;
match: (token: Token, prev?: Token) => boolean;
macro: (
file: File,
opts: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ export const desugarredAst = [
8,
],
["let", ["=", ["tuple", "x", "y"], ["tuple", 1, 2]]],
["+", ["Array", ["generics", "Hey", "There"], 1, 2, 3], 3],
["obj", ["Test", ["generics", "T"]], ["object", [":", "c", "i32"]]],
["fn", ["test", ["generics", "T"], [":", "a", 1]], "->", "i32"],
[
"fn",
["main"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ closure_param_test(1, () => a, 3, () =>

let (x, y) = (1, 2)

Array<Hey, There>(1, 2, 3) + 3

obj Test<T> {
c: i32
}

fn test<T>(a: 1) -> i32

fn main()
let a = ...test.hey + &other.now
let x = 10 +
Expand Down
48 changes: 42 additions & 6 deletions src/syntax-macros/surface-language/functional-notation.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isOp } from "../../lib/grammar.mjs";
import { List } from "../../syntax-objects/index.mjs";
import { Expr, List } from "../../syntax-objects/index.mjs";

export const functionalNotation = (list: List): List => {
let isTuple = false;
Expand All @@ -10,11 +10,7 @@ export const functionalNotation = (list: List): List => {

const nextExpr = array[index + 1];
if (nextExpr && nextExpr.isList() && !isOp(expr)) {
const list = array.splice(index + 1, 1)[0] as List;
list.insert(expr);
list.insert(",", 1);
list.mayBeTuple = false;
return functionalNotation(list);
return processFnCall(expr, nextExpr, array, index);
}

if (list.mayBeTuple && expr.isIdentifier() && expr.is(",")) {
Expand All @@ -26,7 +22,47 @@ export const functionalNotation = (list: List): List => {

if (isTuple) {
result.insert("tuple");
result.insert(",");
}

return result;
};

const processFnCall = (
expr: Expr,
nextExpr: List,
array: Expr[],
index: number
): List => {
if (nextExpr.calls("generics")) {
return processGenerics(expr, array, index);
}

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

const processGenerics = (expr: Expr, array: Expr[], index: number): List => {
const generics = array.splice(index + 1, 1)[0] as List;
generics.mayBeTuple = false;

const list = array[index + 1]?.isList()
? (array.splice(index + 1, 1)[0] as List)
: new List({});

list.insert(",");
list.insert(expr);
list.mayBeTuple = 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);
};
6 changes: 3 additions & 3 deletions src/syntax-macros/surface-language/interpret-whitespace.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export const interpretWhitespace = (list: List, indentLevel?: number): List => {
return transformed;
};

const elideParens = (list: Expr, indentLevel?: number): Expr => {
const elideParens = (list: Expr, startIndentLevel?: number): Expr => {
if (!list.isList()) return list;
const transformed = new List({});
indentLevel = indentLevel ?? nextExprIndentLevel(list);
const indentLevel = startIndentLevel ?? nextExprIndentLevel(list);

const nextLineHasChildExpr = () => nextExprIndentLevel(list) > indentLevel;

Expand Down Expand Up @@ -197,7 +197,7 @@ const addSibling = (child: Expr, siblings: List) => {
return;
}

if (!olderSibling?.isList()) {
if (!olderSibling?.isList() || olderSibling.calls("generics")) {
siblings.push(child);
return;
}
Expand Down