Skip to content

Commit

Permalink
change interfaces to extend fields
Browse files Browse the repository at this point in the history
  • Loading branch information
wcho21 committed Jan 31, 2024
1 parent 8952a68 commit 807e373
Show file tree
Hide file tree
Showing 12 changed files with 631 additions and 960 deletions.
2 changes: 1 addition & 1 deletion src/lexer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {
StringLiteralToken,
IllegalStringLiteralToken,
} from "./source-token";
import type Position from "../util/position";
import type { Position } from "../util/position";
import { isDigit, isLetter, isWhitespace } from "./util";

export default class Lexer {
Expand Down
7 changes: 1 addition & 6 deletions src/lexer/source-token/base/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type Position from "../../../util/position";

export interface Range {
readonly begin: Position,
readonly end: Position,
};
import type { Position, Range } from "../../../util/position";

export interface SourceTokenBase<T extends string = string, V = unknown> {
readonly type: T,
Expand Down
39 changes: 16 additions & 23 deletions src/parser/syntax-node/base/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
import type Position from "../../../util/position";
import type { Position, Range } from "../../../util/position";
import { copyRange } from "../../../util/position";

export interface Range {
readonly begin: Position,
readonly end: Position,
};

export interface SyntaxNodeBase<T extends string = string, F extends {} = {}> {
export interface SyntaxNodeBase<T extends string> {
readonly type: T,
readonly range: Range,
readonly fields: F,
};

export function createNodeCreator<N extends SyntaxNodeBase>(type: N["type"]) {
type Node = { type: N["type"], range: N["range"], fields: N["fields"] };
type AdditionalFields<T extends string, N extends SyntaxNodeBase<T>> = Omit<N, keyof SyntaxNodeBase<T>>;

function createNode(fields: N["fields"], range: Range): Node;
function createNode(fields: N["fields"], rangeBegin: Position, rangeEnd: Position): Node;
function createNode(fields: N["fields"], arg1: Range | Position, rangeEnd?: Position): Node {
if (rangeEnd !== undefined) {
const range = {
begin: arg1 as Position,
end: rangeEnd,
};
export function createNodeCreator<T extends string, N extends SyntaxNodeBase<T>>(type: T) {
type Node = { type: T, range: Range } & AdditionalFields<T, N>;

return { type, range, fields };
function createNode(fields: AdditionalFields<T, N>, range: Range): Node;
function createNode(fields: AdditionalFields<T, N>, rangeBegin: Position, rangeEnd: Position): Node;
function createNode(fields: AdditionalFields<T, N>, arg1: Range | Position, rangeEnd?: Position): Node {
if (rangeEnd !== undefined) {
return { type, range: copyRange(arg1 as Position, rangeEnd), ...fields };
}

return { type, range: arg1 as Range, fields };
const range = arg1 as Range;
return { type, range: copyRange(range.begin, range.end), ...fields };
};

return createNode;
};

declare function createNode<N extends SyntaxNodeBase>(fields: N["fields"], range: Range): N;
declare function createNode<N extends SyntaxNodeBase>(fields: N["fields"], rangeBegin: Position, rangeEnd: Position): N;
export type CreateNode<N extends SyntaxNodeBase> = typeof createNode<N>;
declare function createNode<T extends string, N extends SyntaxNodeBase<T>>(fields: AdditionalFields<T, N>, range: Range): N;
declare function createNode<T extends string, N extends SyntaxNodeBase<T>>(fields: AdditionalFields<T, N>, rangeBegin: Position, rangeEnd: Position): N;
export type CreateNode<T extends string, N extends SyntaxNodeBase<T>> = typeof createNode<T, N>;
45 changes: 15 additions & 30 deletions src/parser/syntax-node/expression/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ const cases = [
node: createIdentifierNode({ value: "foo" }, fakePos, fakePos),
expected: {
type: "identifier",
fields: {
value: "foo",
},
value: "foo",
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -34,9 +32,7 @@ const cases = [
node: createNumberNode({ value: 42 }, fakePos, fakePos),
expected: {
type: "number",
fields: {
value: 42,
},
value: 42,
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -45,9 +41,7 @@ const cases = [
node: createStringNode({ value: "foo" }, fakePos, fakePos),
expected: {
type: "string",
fields: {
value: "foo",
},
value: "foo",
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -56,10 +50,8 @@ const cases = [
node: createPrefixNode({ prefix: "+", right: {} as ExpressionNode }, fakePos, fakePos),
expected: {
type: "prefix",
fields: {
prefix: "+",
right: {},
},
prefix: "+",
right: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -68,11 +60,9 @@ const cases = [
node: createInfixNode({ infix: "+", left: {} as ExpressionNode, right: {} as ExpressionNode }, fakePos, fakePos),
expected: {
type: "infix",
fields: {
infix: "+",
left: {},
right: {},
},
infix: "+",
left: {},
right: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -81,10 +71,8 @@ const cases = [
node: createFunctionNode({ parameters: [] as IdentifierNode[], body: {} as BlockNode }, fakePos, fakePos),
expected: {
type: "function",
fields: {
parameters: [],
body: {},
},
parameters: [],
body: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -93,10 +81,8 @@ const cases = [
node: createCallNode({ func: {} as IdentifierNode, args: [] as ExpressionNode[] }, fakePos, fakePos),
expected: {
type: "call",
fields: {
func: {},
args: [],
},
func: {},
args: [],
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -105,14 +91,13 @@ const cases = [
node: createAssignmentNode({ left: {} as IdentifierNode, right: {} as ExpressionNode }, fakePos, fakePos),
expected: {
type: "assignment",
fields: {
left: {},
right: {},
},
left: {},
right: {},
range: { begin: fakePos, end: fakePos },
},
},
];

it.each(cases)("create $name node", ({ node, expected }) => {
expect(node).toEqual(expected);
});
60 changes: 42 additions & 18 deletions src/parser/syntax-node/expression/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,46 @@ export type ExpressionNode = IdentifierNode
| CallNode
| AssignmentNode;

export interface IdentifierNode extends SyntaxNodeBase<"identifier", { value: string }> {};
export interface NumberNode extends SyntaxNodeBase<"number", { value: number }> {};
export interface BooleanNode extends SyntaxNodeBase<"boolean", { value: boolean }> {};
export interface StringNode extends SyntaxNodeBase<"string", { value: string }> {};
export interface PrefixNode extends SyntaxNodeBase<"prefix", { prefix: Prefix, right: ExpressionNode }> {};
export interface InfixNode extends SyntaxNodeBase<"infix", { infix: Infix, left: ExpressionNode, right: ExpressionNode }> {};
export interface FunctionNode extends SyntaxNodeBase<"function", { parameters: IdentifierNode[], body: BlockNode }> {};
export interface CallNode extends SyntaxNodeBase<"call", { func: IdentifierNode | FunctionNode, args: ExpressionNode[] }> {};
export interface AssignmentNode extends SyntaxNodeBase<"assignment", { left: IdentifierNode, right: ExpressionNode }> {};
export interface IdentifierNode extends SyntaxNodeBase<"identifier"> {
value: string,
};
export interface NumberNode extends SyntaxNodeBase<"number"> {
value: number,
};
export interface BooleanNode extends SyntaxNodeBase<"boolean"> {
value: boolean,
};
export interface StringNode extends SyntaxNodeBase<"string"> {
value: string,
};
export interface PrefixNode extends SyntaxNodeBase<"prefix"> {
prefix: Prefix,
right: ExpressionNode,
};
export interface InfixNode extends SyntaxNodeBase<"infix"> {
infix: Infix,
left: ExpressionNode,
right: ExpressionNode,
};
export interface FunctionNode extends SyntaxNodeBase<"function"> {
parameters: IdentifierNode[],
body: BlockNode,
};
export interface CallNode extends SyntaxNodeBase<"call"> {
func: IdentifierNode | FunctionNode,
args: ExpressionNode[],
};
export interface AssignmentNode extends SyntaxNodeBase<"assignment"> {
left: ExpressionNode,
right: ExpressionNode,
};

export const createIdentifierNode: CreateNode<IdentifierNode> = createNodeCreator<IdentifierNode>("identifier");
export const createNumberNode: CreateNode<NumberNode> = createNodeCreator<NumberNode>("number");
export const createBooleanNode: CreateNode<BooleanNode> = createNodeCreator<BooleanNode>("boolean");
export const createStringNode: CreateNode<StringNode> = createNodeCreator<StringNode>("string");
export const createPrefixNode: CreateNode<PrefixNode> = createNodeCreator<PrefixNode>("prefix");
export const createInfixNode: CreateNode<InfixNode> = createNodeCreator<InfixNode>("infix");
export const createFunctionNode: CreateNode<FunctionNode> = createNodeCreator<FunctionNode>("function");
export const createCallNode: CreateNode<CallNode> = createNodeCreator<CallNode>("call");
export const createAssignmentNode: CreateNode<AssignmentNode> = createNodeCreator<AssignmentNode>("assignment");
export const createIdentifierNode: CreateNode<"identifier", IdentifierNode> = createNodeCreator<"identifier", IdentifierNode>("identifier");
export const createNumberNode: CreateNode<"number", NumberNode> = createNodeCreator<"number", NumberNode>("number");
export const createBooleanNode: CreateNode<"boolean", BooleanNode> = createNodeCreator<"boolean", BooleanNode>("boolean");
export const createStringNode: CreateNode<"string", StringNode> = createNodeCreator<"string", StringNode>("string");
export const createPrefixNode: CreateNode<"prefix", PrefixNode> = createNodeCreator<"prefix", PrefixNode>("prefix");
export const createInfixNode: CreateNode<"infix", InfixNode> = createNodeCreator<"infix", InfixNode>("infix");
export const createFunctionNode: CreateNode<"function", FunctionNode> = createNodeCreator<"function", FunctionNode>("function");
export const createCallNode: CreateNode<"call", CallNode> = createNodeCreator<"call", CallNode>("call");
export const createAssignmentNode: CreateNode<"assignment", AssignmentNode> = createNodeCreator<"assignment", AssignmentNode>("assignment");
9 changes: 3 additions & 6 deletions src/parser/syntax-node/group/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ const cases = [
node: createProgramNode({ statements: [] }, fakePos, fakePos),
expected: {
type: "program",
fields: {
statements: [],
},
statements: [],
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -21,13 +19,12 @@ const cases = [
node: createBlockNode({ statements: [] }, fakePos, fakePos),
expected: {
type: "block",
fields: {
statements: [],
},
statements: [],
range: { begin: fakePos, end: fakePos },
},
},
];

it.each(cases)("create $name node", ({ node, expected }) => {
expect(node).toEqual(expected);
});
12 changes: 8 additions & 4 deletions src/parser/syntax-node/group/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import type { StatementNode } from "../statement";
export type GroupNode = ProgramNode | BlockNode;

/** a root node for a syntax tree of a program */
export interface ProgramNode extends SyntaxNodeBase<"program", { statements: StatementNode[] }> {};
export interface ProgramNode extends SyntaxNodeBase<"program"> {
statements: StatementNode[],
};
/** a group of statements */
export interface BlockNode extends SyntaxNodeBase<"block", { statements: StatementNode[] }> {};
export interface BlockNode extends SyntaxNodeBase<"block"> {
statements: StatementNode[],
};

export const createProgramNode: CreateNode<ProgramNode> = createNodeCreator<ProgramNode>("program");
export const createBlockNode: CreateNode<BlockNode> = createNodeCreator<BlockNode>("block");
export const createProgramNode: CreateNode<"program", ProgramNode> = createNodeCreator<"program", ProgramNode>("program");
export const createBlockNode: CreateNode<"block", BlockNode> = createNodeCreator<"block", BlockNode>("block");
23 changes: 8 additions & 15 deletions src/parser/syntax-node/statement/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ const cases = [
node: createBranchNode({ predicate: {} as ExpressionNode, consequence: {} as BlockNode }, fakePos, fakePos),
expected: {
type: "branch",
fields: {
predicate: {},
consequence: {},
},
predicate: {},
consequence: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -29,11 +27,9 @@ const cases = [
node: createBranchNode({ predicate: {} as ExpressionNode, consequence: {} as BlockNode, alternative: {} as BlockNode }, fakePos, fakePos),
expected: {
type: "branch",
fields: {
predicate: {},
consequence: {},
alternative: {},
},
predicate: {},
consequence: {},
alternative: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -42,9 +38,7 @@ const cases = [
node: createReturnNode({ expression: {} as ExpressionNode }, fakePos, fakePos),
expected: {
type: "return",
fields: {
expression: {},
},
expression: {},
range: { begin: fakePos, end: fakePos },
},
},
Expand All @@ -53,13 +47,12 @@ const cases = [
node: createExpressionStatementNode({ expression: {} as ExpressionNode }, fakePos, fakePos),
expected: {
type: "expression statement",
fields: {
expression: {},
},
expression: {},
range: { begin: fakePos, end: fakePos },
},
},
];

it.each(cases)("create $name node", ({ node, expected }) => {
expect(node).toEqual(expected);
});
20 changes: 14 additions & 6 deletions src/parser/syntax-node/statement/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import type { ExpressionNode } from "../expression";

export type StatementNode = BranchNode | ReturnNode | ExpressionStatementNode;

export interface BranchNode extends SyntaxNodeBase<"branch", { predicate: ExpressionNode, consequence: BlockNode, alternative?: BlockNode }> {};
export interface ReturnNode extends SyntaxNodeBase<"return", { expression: ExpressionNode }> {};
export interface BranchNode extends SyntaxNodeBase<"branch"> {
predicate: ExpressionNode,
consequence: BlockNode,
alternative?: BlockNode,
};
export interface ReturnNode extends SyntaxNodeBase<"return"> {
expression: ExpressionNode
};
/** A wrapper type to treat a single expression as a statement. */
export interface ExpressionStatementNode extends SyntaxNodeBase<"expression statement", { expression: ExpressionNode }> {};
export interface ExpressionStatementNode extends SyntaxNodeBase<"expression statement"> {
expression: ExpressionNode
};

export const createBranchNode: CreateNode<BranchNode> = createNodeCreator<BranchNode>("branch");
export const createReturnNode: CreateNode<ReturnNode> = createNodeCreator<ReturnNode>("return");
export const createExpressionStatementNode: CreateNode<ExpressionStatementNode> = createNodeCreator<ExpressionStatementNode>("expression statement");
export const createBranchNode: CreateNode<"branch", BranchNode> = createNodeCreator<"branch", BranchNode>("branch");
export const createReturnNode: CreateNode<"return", ReturnNode> = createNodeCreator<"return", ReturnNode>("return");
export const createExpressionStatementNode: CreateNode<"expression statement", ExpressionStatementNode> = createNodeCreator<"expression statement", ExpressionStatementNode>("expression statement");
Loading

0 comments on commit 807e373

Please sign in to comment.