From 3283c789401ce3092dfc56315530a7a91b810d2e Mon Sep 17 00:00:00 2001 From: Won-hee Cho <89635107+wcho21@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:04:23 +0900 Subject: [PATCH] change type and interface (#52) --- .../char-reader/source-char/index.ts | 2 +- src/lexer/index.ts | 2 +- src/lexer/source-token/base/index.ts | 7 +- src/parser/syntax-node/base/index.ts | 39 +- .../syntax-node/expression/index.test.ts | 45 +- src/parser/syntax-node/expression/index.ts | 60 +- src/parser/syntax-node/group/index.test.ts | 9 +- src/parser/syntax-node/group/index.ts | 12 +- .../syntax-node/statement/index.test.ts | 23 +- src/parser/syntax-node/statement/index.ts | 20 +- src/parser/v2.test.ts | 1370 +++++++---------- src/parser/v2.ts | 2 +- src/util/position/index.ts | 2 +- 13 files changed, 632 insertions(+), 961 deletions(-) diff --git a/src/lexer/char-buffer/char-reader/source-char/index.ts b/src/lexer/char-buffer/char-reader/source-char/index.ts index 3577751..2a372cf 100644 --- a/src/lexer/char-buffer/char-reader/source-char/index.ts +++ b/src/lexer/char-buffer/char-reader/source-char/index.ts @@ -1,4 +1,4 @@ -import Position from "../../../../util/position"; +import type { Position } from "../../../../util/position"; export default interface SourceChar { value: string, diff --git a/src/lexer/index.ts b/src/lexer/index.ts index c315684..af6d69e 100644 --- a/src/lexer/index.ts +++ b/src/lexer/index.ts @@ -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 { diff --git a/src/lexer/source-token/base/index.ts b/src/lexer/source-token/base/index.ts index 92512f9..79f4abe 100644 --- a/src/lexer/source-token/base/index.ts +++ b/src/lexer/source-token/base/index.ts @@ -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 { readonly type: T, diff --git a/src/parser/syntax-node/base/index.ts b/src/parser/syntax-node/base/index.ts index dc1fa6e..5b6ec81 100644 --- a/src/parser/syntax-node/base/index.ts +++ b/src/parser/syntax-node/base/index.ts @@ -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 { +export interface SyntaxNodeBase { readonly type: T, readonly range: Range, - readonly fields: F, }; -export function createNodeCreator(type: N["type"]) { - type Node = { type: N["type"], range: N["range"], fields: N["fields"] }; +type AdditionalFields> = Omit>; - 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>(type: T) { + type Node = { type: T, range: Range } & AdditionalFields; - return { type, range, fields }; + function createNode(fields: AdditionalFields, range: Range): Node; + function createNode(fields: AdditionalFields, rangeBegin: Position, rangeEnd: Position): Node; + function createNode(fields: AdditionalFields, 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(fields: N["fields"], range: Range): N; -declare function createNode(fields: N["fields"], rangeBegin: Position, rangeEnd: Position): N; -export type CreateNode = typeof createNode; +declare function createNode>(fields: AdditionalFields, range: Range): N; +declare function createNode>(fields: AdditionalFields, rangeBegin: Position, rangeEnd: Position): N; +export type CreateNode> = typeof createNode; diff --git a/src/parser/syntax-node/expression/index.test.ts b/src/parser/syntax-node/expression/index.test.ts index 3f40b1c..03a520c 100644 --- a/src/parser/syntax-node/expression/index.test.ts +++ b/src/parser/syntax-node/expression/index.test.ts @@ -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 }, }, }, @@ -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 }, }, }, @@ -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 }, }, }, @@ -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 }, }, }, @@ -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 }, }, }, @@ -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 }, }, }, @@ -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 }, }, }, @@ -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); }); diff --git a/src/parser/syntax-node/expression/index.ts b/src/parser/syntax-node/expression/index.ts index 8f7fc9a..9bfb3d9 100644 --- a/src/parser/syntax-node/expression/index.ts +++ b/src/parser/syntax-node/expression/index.ts @@ -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 = createNodeCreator("identifier"); -export const createNumberNode: CreateNode = createNodeCreator("number"); -export const createBooleanNode: CreateNode = createNodeCreator("boolean"); -export const createStringNode: CreateNode = createNodeCreator("string"); -export const createPrefixNode: CreateNode = createNodeCreator("prefix"); -export const createInfixNode: CreateNode = createNodeCreator("infix"); -export const createFunctionNode: CreateNode = createNodeCreator("function"); -export const createCallNode: CreateNode = createNodeCreator("call"); -export const createAssignmentNode: CreateNode = createNodeCreator("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"); diff --git a/src/parser/syntax-node/group/index.test.ts b/src/parser/syntax-node/group/index.test.ts index 0187f3c..8231d98 100644 --- a/src/parser/syntax-node/group/index.test.ts +++ b/src/parser/syntax-node/group/index.test.ts @@ -10,9 +10,7 @@ const cases = [ node: createProgramNode({ statements: [] }, fakePos, fakePos), expected: { type: "program", - fields: { - statements: [], - }, + statements: [], range: { begin: fakePos, end: fakePos }, }, }, @@ -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); }); diff --git a/src/parser/syntax-node/group/index.ts b/src/parser/syntax-node/group/index.ts index 5a2d20b..099f83c 100644 --- a/src/parser/syntax-node/group/index.ts +++ b/src/parser/syntax-node/group/index.ts @@ -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 = createNodeCreator("program"); -export const createBlockNode: CreateNode = createNodeCreator("block"); +export const createProgramNode: CreateNode<"program", ProgramNode> = createNodeCreator<"program", ProgramNode>("program"); +export const createBlockNode: CreateNode<"block", BlockNode> = createNodeCreator<"block", BlockNode>("block"); diff --git a/src/parser/syntax-node/statement/index.test.ts b/src/parser/syntax-node/statement/index.test.ts index 9e873d4..2c824a5 100644 --- a/src/parser/syntax-node/statement/index.test.ts +++ b/src/parser/syntax-node/statement/index.test.ts @@ -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 }, }, }, @@ -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 }, }, }, @@ -42,9 +38,7 @@ const cases = [ node: createReturnNode({ expression: {} as ExpressionNode }, fakePos, fakePos), expected: { type: "return", - fields: { - expression: {}, - }, + expression: {}, range: { begin: fakePos, end: fakePos }, }, }, @@ -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); }); diff --git a/src/parser/syntax-node/statement/index.ts b/src/parser/syntax-node/statement/index.ts index 88b0fd5..72b19d8 100644 --- a/src/parser/syntax-node/statement/index.ts +++ b/src/parser/syntax-node/statement/index.ts @@ -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 = createNodeCreator("branch"); -export const createReturnNode: CreateNode = createNodeCreator("return"); -export const createExpressionStatementNode: CreateNode = createNodeCreator("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"); diff --git a/src/parser/v2.test.ts b/src/parser/v2.test.ts index 3bb902f..514f1e9 100644 --- a/src/parser/v2.test.ts +++ b/src/parser/v2.test.ts @@ -2,14 +2,10 @@ import Lexer from "../lexer"; import Parser from "./v2"; import { ParserError, - BadPrefixError, BadExpressionError, } from "./v2"; import type { ProgramNode, - AssignmentNode, - IdentifierNode, - ExpressionStatementNode, } from "./syntax-node"; type SuccessTestCase = { name: string, input: string, expected: E }; @@ -45,21 +41,15 @@ describe("parseSource()", () => { input: "42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "number", - fields: { - value: 42, - }, - } - }, - }, - ], - }, + statements: [ + { + type: "expression statement", + expression: { + type: "number", + value: 42, + } + }, + ], }, }, { @@ -67,21 +57,15 @@ describe("parseSource()", () => { input: "참", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "boolean", - fields: { - value: true, - }, - } - }, - }, - ], - }, + statements: [ + { + type: "expression statement", + expression: { + type: "boolean", + value: true, + } + }, + ], }, }, { @@ -89,21 +73,15 @@ describe("parseSource()", () => { input: "'foo bar'", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "string", - fields: { - value: "foo bar", - }, - } - }, + statements: [ + { + type: "expression statement", + expression: { + type: "string", + value: "foo bar", }, - ], - }, + }, + ], }, }, { @@ -111,21 +89,15 @@ describe("parseSource()", () => { input: "foo", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "identifier", - fields: { - value: "foo", - }, - } - }, - }, - ], - }, + statements: [ + { + type: "expression statement", + expression: { + type: "identifier", + value: "foo", + } + }, + ], }, }, ]; @@ -141,27 +113,19 @@ describe("parseSource()", () => { input: "+42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "prefix", - fields: { - prefix: "+", - right: { - type: "number", - fields: { - value: 42, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "prefix", + prefix: "+", + right: { + type: "number", + value: 42, }, - }, - ], - }, + } + }, + ], }, }, { @@ -169,27 +133,19 @@ describe("parseSource()", () => { input: "-42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "prefix", - fields: { - prefix: "-", - right: { - type: "number", - fields: { - value: 42, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "prefix", + prefix: "-", + right: { + type: "number", + value: 42, }, - }, - ], - }, + } + }, + ], }, }, { @@ -197,33 +153,23 @@ describe("parseSource()", () => { input: "--42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "prefix", - fields: { - prefix: "-", - right: { - type: "prefix", - fields: { - prefix: "-", - right: { - type: "number", - fields: { - value: 42, - }, - }, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "prefix", + prefix: "-", + right: { + type: "prefix", + prefix: "-", + right: { + type: "number", + value: 42, + }, }, }, - ], - }, + }, + ], }, }, ]; @@ -243,45 +189,31 @@ describe("parseSource()", () => { input: `11 ${infix} 22 ${infix} 33`, expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: { - infix, - left: { - type: "infix", - fields: { - infix, - left: { - type: "number", - fields: { - value: 11, - }, - }, - right: { - type: "number", - fields: { - value: 22, - }, - }, - }, - }, - right: { - type: "number", - fields: { - value: 33, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix, + left: { + type: "infix", + infix, + left: { + type: "number", + value: 11, + }, + right: { + type: "number", + value: 22, + }, + }, + right: { + type: "number", + value: 33, }, }, - ], - }, + }, + ], }, })); @@ -295,67 +227,47 @@ describe("parseSource()", () => { input: "11+22*33/44-55", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix: "-", + left: { + type: "infix", + infix: "+", + left: { + type: "number", + value: 11, + }, + right: { type: "infix", - fields: { - infix: "-", + infix: "/", + left: { + type: "infix", + infix: "*", left: { - type: "infix", - fields: { - infix: "+", - left: { - type: "number", - fields: { value: 11 }, - }, - right: { - type: "infix", - fields: { - infix: "/", - left: { - type: "infix", - fields: { - infix: "*", - left: { - type: "number", - fields: { - value: 22 - }, - }, - right: { - type: "number", - fields: { - value: 33 - }, - }, - }, - }, - right: { - type: "number", - fields: { - value: 44 - }, - }, - }, - }, - }, + type: "number", + value: 22 }, right: { type: "number", - fields: { - value: 55, - }, + value: 33 }, }, - } + right: { + type: "number", + value: 44, + }, + }, + }, + right: { + type: "number", + value: 55, }, }, - ], - }, + }, + ], }, }, { @@ -363,45 +275,31 @@ describe("parseSource()", () => { input: "11+(22+33)", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: { - infix: "+", - left: { - type: "number", - fields: { - value: 11, - }, - }, - right: { - type: "infix", - fields: { - infix: "+", - left: { - type: "number", - fields: { - value: 22, - }, - }, - right: { - type: "number", - fields: { - value: 33, - }, - }, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix: "+", + left: { + type: "number", + value: 11, + }, + right: { + type: "infix", + infix: "+", + left: { + type: "number", + value: 22, + }, + right: { + type: "number", + value: 33, + }, }, }, - ], - }, + }, + ], }, }, ]; @@ -419,27 +317,19 @@ describe("parseSource()", () => { input: "!foo", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "prefix", - fields: { - prefix: "!", - right: { - type: "identifier", - fields: { - value: "foo", - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "prefix", + prefix: "!", + right: { + type: "identifier", + value: "foo", }, }, - ], - }, + }, + ], }, }, { @@ -447,33 +337,23 @@ describe("parseSource()", () => { input: "!!foo", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "prefix", - fields: { - prefix: "!", - right: { - type: "prefix", - fields: { - prefix: "!", - right: { - type: "identifier", - fields: { - value: "foo", - }, - }, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "prefix", + prefix: "!", + right: { + type: "prefix", + prefix: "!", + right: { + type: "identifier", + value: "foo", }, }, }, - ], - }, + }, + ], }, }, ]; @@ -495,33 +375,23 @@ describe("parseSource()", () => { input: `foo ${infix} bar`, expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: { - infix, - left: { - type: "identifier", - fields: { - value: "foo", - }, - }, - right: { - type: "identifier", - fields: { - value: "bar", - }, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix, + left: { + type: "identifier", + value: "foo", + }, + right: { + type: "identifier", + value: "bar", }, }, - ], - }, + }, + ], }, })); @@ -538,45 +408,31 @@ describe("parseSource()", () => { input: `foo ${infix} bar ${infix} baz`, expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: { - infix, - left: { - type: "identifier", - fields: { - value: "foo", - }, - }, - right: { - type: "infix", - fields: { - infix, - left: { - type: "identifier", - fields: { - value: "bar", - }, - }, - right: { - type: "identifier", - fields: { - value: "baz", - }, - }, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix, + left: { + type: "identifier", + value: "foo", + }, + right: { + type: "infix", + infix, + left: { + type: "identifier", + value: "bar", + }, + right: { + type: "identifier", + value: "baz", }, }, }, - ], - }, + }, + ], }, })); @@ -590,45 +446,31 @@ describe("parseSource()", () => { input: "(foo == bar) != baz", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: { - infix: "!=", - left: { - type: "infix", - fields: { - infix: "==", - left: { - type: "identifier", - fields: { - value: "foo", - }, - }, - right: { - type: "identifier", - fields: { - value: "bar", - }, - }, - }, - }, - right: { - type: "identifier", - fields: { - value: "baz" - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "infix", + infix: "!=", + left: { + type: "infix", + infix: "==", + left: { + type: "identifier", + value: "foo", + }, + right: { + type: "identifier", + value: "bar", }, }, + right: { + type: "identifier", + value: "baz", + }, }, - ], - }, + }, + ], }, }, ]; @@ -644,32 +486,22 @@ describe("parseSource()", () => { input: "x = 42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "assignment", - fields: { - left: { - type: "identifier", - fields: { - value: "x", - }, - }, - right: { - type: "number", - fields: { - value: 42, - }, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "assignment", + left: { + type: "identifier", + value: "x", + }, + right: { + type: "number", + value: 42, }, }, - ], - }, + }, + ], }, }, { @@ -677,43 +509,29 @@ describe("parseSource()", () => { input: "x = y = 42", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "assignment", - fields: { - left: { - type: "identifier", - fields: { - value: "x", - }, - }, - right: { - type: "assignment", - fields: { - left: { - type: "identifier", - fields: { - value: "y", - }, - }, - right: { - type: "number", - fields: { - value: 42, - }, - }, - }, - }, - }, - } + statements: [ + { + type: "expression statement", + expression: { + type: "assignment", + left: { + type: "identifier", + value: "x", + }, + right: { + type: "assignment", + left: { + type: "identifier", + value: "y", + }, + right: { + type: "number", + value: 42, + }, }, }, - ], - }, + }, + ], }, }, ]; @@ -728,40 +546,28 @@ describe("parseSource()", () => { input: "foo(bar, 42)", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "call", - fields: { - func: { - type: "identifier", - fields: { - value: "foo", - }, - }, - args: [ - { - type: "identifier", - fields: { - value: "bar", - }, - }, - { - type: "number", - fields: { - value: 42, - }, - }, - ], - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "call", + func: { + type: "identifier", + value: "foo", }, + args: [ + { + type: "identifier", + value: "bar", + }, + { + type: "number", + value: 42, + }, + ], }, - ], - }, + }, + ], }, }, { @@ -769,35 +575,25 @@ describe("parseSource()", () => { input: "함수(foo){ foo }(42)", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "call", - fields: { - func: { - type: "function", - fields: { - parameters: {}, // omit - body: {}, // omit - }, - }, - args: [ - { - type: "number", - fields: { - value: 42, - }, - }, - ], - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "call", + func: { + type: "function", + parameters: {}, // omit + body: {}, // omit }, + args: [ + { + type: "number", + value: 42, + }, + ], }, - ], - }, + }, + ], }, }, ]; @@ -812,56 +608,42 @@ describe("parseSource()", () => { input: "함수 (foo, bar) { foo + bar }", expected: { type: "program", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "function", - fields: { - parameters: [ - { - type: "identifier", - fields: { - value: "foo", - }, - }, - { - type: "identifier", - fields: { - value: "bar", - }, - }, - ], - body: { - type: "block", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "infix", - fields: {}, // omit - }, - }, - }, - ], - }, + statements: [ + { + type: "expression statement", + expression: { + type: "function", + parameters: [ + { + type: "identifier", + value: "foo", + }, + { + type: "identifier", + value: "bar", + }, + ], + body: { + type: "block", + statements: [ + { + type: "expression statement", + expression: { + type: "infix", }, }, - }, + ], }, }, - ], - }, + }, + ], }, }, ]; it.each(cases)("$name", testSuccess); }); + describe("return statement", () => { const cases: SuccessTestCase[] = [ { @@ -869,21 +651,15 @@ describe("parseSource()", () => { input: "결과 42", expected: { type: "program", - fields: { - statements: [ - { - type: "return", - fields: { - expression: { - type: "number", - fields: { - value: 42, - }, - }, - }, + statements: [ + { + type: "return", + expression: { + type: "number", + value: 42, }, - ], - }, + }, + ], }, }, ]; @@ -898,39 +674,27 @@ describe("parseSource()", () => { input: "만약 foo { bar }", expected: { type: "program", - fields: { - statements: [ - { - type: "branch", - fields: { - predicate: { - type: "identifier", - fields: { - value: "foo", - }, - }, - consequence: { - type: "block", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "identifier", - fields: { - value: "bar", - }, - }, - }, - }, - ], + statements: [ + { + type: "branch", + predicate: { + type: "identifier", + value: "foo", + }, + consequence: { + type: "block", + statements: [ + { + type: "expression statement", + expression: { + type: "identifier", + value: "bar", }, }, - }, + ], }, - ], - }, + }, + ], }, }, { @@ -938,57 +702,39 @@ describe("parseSource()", () => { input: "만약 foo { bar } 아니면 { baz }", expected: { type: "program", - fields: { - statements: [ - { - type: "branch", - fields: { - predicate: { - type: "identifier", - fields: { - value: "foo", - }, - }, - consequence: { - type: "block", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "identifier", - fields: { - value: "bar", - }, - }, - }, - }, - ], + statements: [ + { + type: "branch", + predicate: { + type: "identifier", + value: "foo", + }, + consequence: { + type: "block", + statements: [ + { + type: "expression statement", + expression: { + type: "identifier", + value: "bar", }, }, - alternative: { - type: "block", - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "identifier", - fields: { - value: "baz", - }, - }, - }, - }, - ], + ], + }, + alternative: { + type: "block", + statements: [ + { + type: "expression statement", + expression: { + type: "identifier", + value: "baz", }, }, - }, + ], }, - ], - }, + }, + ], }, }, ]; @@ -1044,20 +790,16 @@ describe("parseSource()", () => { expected: { type: "program", range, - fields: { - statements: [ - { - type: "expression statement", + statements: [ + { + type: "expression statement", + range, + expression: { + type, range, - fields: { - expression: { - type, - range, - } - }, }, - ], - }, + }, + ], }, })); @@ -1075,42 +817,36 @@ describe("parseSource()", () => { begin: { row: 0, col: 0 }, end: { row: 0, col: 5 }, }, - fields: { - statements: [ - { - type: "expression statement", + statements: [ + { + type: "expression statement", + range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 5 }, + }, + expression: { + type: "assignment", range: { begin: { row: 0, col: 0 }, end: { row: 0, col: 5 }, }, - fields: { - expression: { - type: "assignment", - range: { - begin: { row: 0, col: 0 }, - end: { row: 0, col: 5 }, - }, - fields: { - left: { - type: "identifier", - range: { - begin: { row: 0, col: 0 }, - end: { row: 0, col: 0 }, - }, - }, - right: { - type: "number", - range: { - begin: { row: 0, col: 4 }, - end: { row: 0, col: 5 }, - }, - }, - }, + left: { + type: "identifier", + range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 0 }, + }, + }, + right: { + type: "number", + range: { + begin: { row: 0, col: 4 }, + end: { row: 0, col: 5 }, }, }, }, - ], - }, + }, + ], }, }, { @@ -1122,42 +858,36 @@ describe("parseSource()", () => { begin: { row: 0, col: 0 }, end: { row: 0, col: 6 }, }, - fields: { - statements: [ - { - type: "expression statement", + statements: [ + { + type: "expression statement", + range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 6 }, + }, + expression: { + type: "infix", range: { begin: { row: 0, col: 0 }, end: { row: 0, col: 6 }, }, - fields: { - expression: { - type: "infix", - range: { - begin: { row: 0, col: 0 }, - end: { row: 0, col: 6 }, - }, - fields: { - left: { - type: "number", - range: { - begin: { row: 0, col: 0 }, - end: { row: 0, col: 1 }, - }, - }, - right: { - type: "number", - range: { - begin: { row: 0, col: 5 }, - end: { row: 0, col: 6 }, - }, - }, - }, + left: { + type: "number", + range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 1 }, + }, + }, + right: { + type: "number", + range: { + begin: { row: 0, col: 5 }, + end: { row: 0, col: 6 }, }, }, }, - ], - }, + }, + ], }, }, { @@ -1166,41 +896,39 @@ describe("parseSource()", () => { expected: { type: "program", range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 8 }, }, - fields: { - statements: [ - { - type: "expression statement", + statements: [ + { + type: "expression statement", + range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 8 }, + }, + expression: { + type: "infix", range: { + begin: { row: 0, col: 0 }, + end: { row: 0, col: 8 }, }, - fields: { - expression: { - type: "infix", - range: { - begin: { row: 0, col: 0 }, - end: { row: 0, col: 8 }, - }, - fields: { - left: { - type: "number", - range: { - begin: { row: 0, col: 1 }, - end: { row: 0, col: 2 }, - }, - }, - right: { - type: "number", - range: { - begin: { row: 0, col: 6 }, - end: { row: 0, col: 7 }, - }, - }, - }, + left: { + type: "number", + range: { + begin: { row: 0, col: 1 }, + end: { row: 0, col: 2 }, + }, + }, + right: { + type: "number", + range: { + begin: { row: 0, col: 6 }, + end: { row: 0, col: 7 }, }, }, }, - ], - }, + }, + ], }, }, { @@ -1209,52 +937,28 @@ describe("parseSource()", () => { expected: { type: "program", range: { - begin: { - row: 0, - col: 0, - }, - end: { - row: 2, - col: 0, - }, + begin: { row: 0, col: 0, }, + end: { row: 2, col: 0, }, }, - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "function", - range: { - begin: { - row: 0, - col: 0, - }, - end: { - row: 2, - col: 0, - }, - }, - fields: { - body: { - type: "block", - range: { - begin: { - row: 0, - col: 8, - }, - end: { - row: 2, - col: 0, - }, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "function", + range: { + begin: { row: 0, col: 0, }, + end: { row: 2, col: 0, }, + }, + body: { + type: "block", + range: { + begin: { row: 0, col: 8, }, + end: { row: 2, col: 0, }, }, }, }, - ], - }, + }, + ], }, }, { @@ -1264,33 +968,23 @@ describe("parseSource()", () => { type: "program", range: { }, - fields: { - statements: [ - { - type: "expression statement", - fields: { - expression: { - type: "call", - range: { - begin: { - row: 0, - col: 0, - }, - end: { - row: 0, - col: 12, - }, - }, - }, + statements: [ + { + type: "expression statement", + expression: { + type: "call", + range: { + begin: { row: 0, col: 0, }, + end: { row: 0, col: 12, }, }, }, - ], - }, + }, + ], }, }, ]; - it.each(cases.slice(4))("$name", testSuccess); + it.each(cases)("$name", testSuccess); }); describe("single statements", () => { @@ -1302,55 +996,33 @@ describe("parseSource()", () => { type: "program", range: { }, - fields: { - statements: [ - { - type: "branch", + statements: [ + { + type: "branch", + range: { + }, + predicate: { range: { + begin: { row: 0, col: 3, }, + end: { row: 0, col: 5, }, }, - fields: { - predicate: { - range: { - begin: { - row: 0, - col: 3, - }, - end: { - row: 0, - col: 5, - }, - }, - }, - consequence: { - type: "block", - range: { - begin: { - row: 0, - col: 7, - }, - end: { - row: 2, - col: 0, - }, - }, - }, - alternative: { - type: "block", - range: { - begin: { - row: 2, - col: 6, - }, - end: { - row: 4, - col: 0, - }, - }, - }, + }, + consequence: { + type: "block", + range: { + begin: { row: 0, col: 7, }, + end: { row: 2, col: 0, }, }, }, - ], - }, + alternative: { + type: "block", + range: { + begin: { row: 2, col: 6, }, + end: { row: 4, col: 0, }, + }, + }, + }, + ], }, }, ]; diff --git a/src/parser/v2.ts b/src/parser/v2.ts index 4df21f6..a639696 100644 --- a/src/parser/v2.ts +++ b/src/parser/v2.ts @@ -7,7 +7,7 @@ import { copyRange, type Range } from "../util/position"; export class ParserError extends Error { public received: string; public expected: string; - public range: { begin: { col: number, row: number }, end: { col: number, row: number }}; + public range: Range; constructor(received: string, expected: string, range: Range) { super(); diff --git a/src/util/position/index.ts b/src/util/position/index.ts index c2d6e4d..18d8dcb 100644 --- a/src/util/position/index.ts +++ b/src/util/position/index.ts @@ -1,4 +1,4 @@ -export default interface Position { +export interface Position { readonly row: number, readonly col: number, }