add function declaration with input params<
This commit is contained in:
@ -3,21 +3,22 @@ import { lexer } from "./tokeniser";
|
|||||||
|
|
||||||
describe("findClosingBrace", () => {
|
describe("findClosingBrace", () => {
|
||||||
test("finds the closing brace", () => {
|
test("finds the closing brace", () => {
|
||||||
const basic = "( hey )"
|
const basic = "( hey )";
|
||||||
expect(findClosingBrace(lexer(basic), 0)).toBe(4)
|
expect(findClosingBrace(lexer(basic), 0)).toBe(4);
|
||||||
|
|
||||||
const handlesNonZeroIndex = "(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)"
|
const handlesNonZeroIndex =
|
||||||
expect(findClosingBrace(lexer(handlesNonZeroIndex), 2)).toBe(4)
|
"(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)";
|
||||||
expect(findClosingBrace(lexer(handlesNonZeroIndex), 0)).toBe(6)
|
expect(findClosingBrace(lexer(handlesNonZeroIndex), 2)).toBe(4);
|
||||||
|
expect(findClosingBrace(lexer(handlesNonZeroIndex), 0)).toBe(6);
|
||||||
const handlesNested = "{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }"
|
|
||||||
expect(findClosingBrace(lexer(handlesNested), 0)).toBe(18)
|
const handlesNested =
|
||||||
|
"{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }";
|
||||||
|
expect(findClosingBrace(lexer(handlesNested), 0)).toBe(18);
|
||||||
|
|
||||||
// throws when not started on a brace
|
// throws when not started on a brace
|
||||||
expect(() => findClosingBrace(lexer(handlesNested), 1)).toThrow()
|
expect(() => findClosingBrace(lexer(handlesNested), 1)).toThrow();
|
||||||
|
});
|
||||||
})
|
});
|
||||||
})
|
|
||||||
|
|
||||||
describe("testing AST", () => {
|
describe("testing AST", () => {
|
||||||
test("test 5 + 6", () => {
|
test("test 5 + 6", () => {
|
||||||
@ -167,44 +168,44 @@ const newVar = myVar + 1
|
|||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens);
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
"type": "ExpressionStatement",
|
type: "ExpressionStatement",
|
||||||
"start": 0,
|
start: 0,
|
||||||
"end": 28,
|
end: 28,
|
||||||
"expression": {
|
expression: {
|
||||||
"type": "CallExpression",
|
type: "CallExpression",
|
||||||
"start": 0,
|
start: 0,
|
||||||
"end": 28,
|
end: 28,
|
||||||
"callee": {
|
callee: {
|
||||||
"type": "Identifier",
|
type: "Identifier",
|
||||||
"start": 0,
|
start: 0,
|
||||||
"end": 3,
|
end: 3,
|
||||||
"name": "log"
|
name: "log",
|
||||||
},
|
},
|
||||||
"arguments": [
|
arguments: [
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
type: "Literal",
|
||||||
"start": 4,
|
start: 4,
|
||||||
"end": 5,
|
end: 5,
|
||||||
"value": 5,
|
value: 5,
|
||||||
"raw": "5"
|
raw: "5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
type: "Literal",
|
||||||
"start": 7,
|
start: 7,
|
||||||
"end": 14,
|
end: 14,
|
||||||
"value": "hello",
|
value: "hello",
|
||||||
"raw": "\"hello\""
|
raw: '"hello"',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Identifier",
|
type: "Identifier",
|
||||||
"start": 16,
|
start: 16,
|
||||||
"end": 27,
|
end: 27,
|
||||||
"name": "aIdentifier"
|
name: "aIdentifier",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"optional": false
|
optional: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -212,42 +213,117 @@ const newVar = myVar + 1
|
|||||||
describe("testing function declaration", () => {
|
describe("testing function declaration", () => {
|
||||||
test("fn funcN = () => {}", () => {
|
test("fn funcN = () => {}", () => {
|
||||||
const tokens = lexer("fn funcN = () => {}");
|
const tokens = lexer("fn funcN = () => {}");
|
||||||
// const tokens = lexer("const fn = () => {}");
|
const { body } = abstractSyntaxTree(tokens);
|
||||||
const {body} = abstractSyntaxTree(tokens);
|
|
||||||
// console.log(JSON.stringify(body, null, 2));
|
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
"type": "VariableDeclaration",
|
type: "VariableDeclaration",
|
||||||
"start": 0,
|
start: 0,
|
||||||
"end": 19,
|
end: 19,
|
||||||
"kind": "fn",
|
kind: "fn",
|
||||||
"declarations": [
|
declarations: [
|
||||||
{
|
{
|
||||||
"type": "VariableDeclarator",
|
type: "VariableDeclarator",
|
||||||
"start": 3,
|
start: 3,
|
||||||
"end": 19,
|
end: 19,
|
||||||
"id": {
|
id: {
|
||||||
"type": "Identifier",
|
type: "Identifier",
|
||||||
"start": 3,
|
start: 3,
|
||||||
"end": 8,
|
end: 8,
|
||||||
"name": "funcN"
|
name: "funcN",
|
||||||
},
|
},
|
||||||
"init": {
|
init: {
|
||||||
"type": "FunctionExpression",
|
type: "FunctionExpression",
|
||||||
"start": 11,
|
start: 11,
|
||||||
"end": 19,
|
end: 19,
|
||||||
"id": null,
|
id: null,
|
||||||
"params": [],
|
params: [],
|
||||||
"body": {
|
body: {
|
||||||
"type": "BlockStatement",
|
type: "BlockStatement",
|
||||||
"start": 17,
|
start: 17,
|
||||||
"end": 19,
|
end: 19,
|
||||||
"body": []
|
body: [],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
test("fn funcN = (a, b) => {return a + b}", () => {
|
||||||
|
const tokens = lexer(
|
||||||
|
["fn funcN = (a, b) => {", " return a + b", "}"].join("\n")
|
||||||
|
);
|
||||||
|
const { body } = abstractSyntaxTree(tokens);
|
||||||
|
expect(body).toEqual([
|
||||||
|
{
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
start: 0,
|
||||||
|
end: 39,
|
||||||
|
kind: "fn",
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
type: "VariableDeclarator",
|
||||||
|
start: 3,
|
||||||
|
end: 39,
|
||||||
|
id: {
|
||||||
|
type: "Identifier",
|
||||||
|
start: 3,
|
||||||
|
end: 8,
|
||||||
|
name: "funcN",
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
type: "FunctionExpression",
|
||||||
|
start: 11,
|
||||||
|
end: 39,
|
||||||
|
id: null,
|
||||||
|
params: [
|
||||||
|
{
|
||||||
|
type: "Identifier",
|
||||||
|
start: 12,
|
||||||
|
end: 13,
|
||||||
|
name: "a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Identifier",
|
||||||
|
start: 15,
|
||||||
|
end: 16,
|
||||||
|
name: "b",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: {
|
||||||
|
type: "BlockStatement",
|
||||||
|
start: 21,
|
||||||
|
end: 39,
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: "ReturnStatement",
|
||||||
|
start: 25,
|
||||||
|
end: 37,
|
||||||
|
argument: {
|
||||||
|
type: "BinaryExpression",
|
||||||
|
start: 32,
|
||||||
|
end: 37,
|
||||||
|
left: {
|
||||||
|
type: "Identifier",
|
||||||
|
start: 32,
|
||||||
|
end: 33,
|
||||||
|
name: "a",
|
||||||
|
},
|
||||||
|
operator: "+",
|
||||||
|
right: {
|
||||||
|
type: "Identifier",
|
||||||
|
start: 36,
|
||||||
|
end: 37,
|
||||||
|
name: "b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -88,7 +88,7 @@ interface GeneralStatement {
|
|||||||
|
|
||||||
interface ExpressionStatement extends GeneralStatement {
|
interface ExpressionStatement extends GeneralStatement {
|
||||||
type: "ExpressionStatement";
|
type: "ExpressionStatement";
|
||||||
expression: BinaryExpression | CallExpression;
|
expression: Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeExpressionStatement(
|
function makeExpressionStatement(
|
||||||
@ -125,7 +125,7 @@ function makeExpressionStatement(
|
|||||||
interface CallExpression extends GeneralStatement {
|
interface CallExpression extends GeneralStatement {
|
||||||
type: "CallExpression";
|
type: "CallExpression";
|
||||||
callee: Identifier;
|
callee: Identifier;
|
||||||
arguments: VariableDeclarator["init"][];
|
arguments: Value[];
|
||||||
optional: boolean;
|
optional: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,9 +159,9 @@ function makeCallExpression(
|
|||||||
function makeArguments(
|
function makeArguments(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
index: number,
|
index: number,
|
||||||
previousArgs: VariableDeclarator["init"][] = []
|
previousArgs: Value[] = []
|
||||||
): {
|
): {
|
||||||
arguments: VariableDeclarator["init"][];
|
arguments: Value[];
|
||||||
lastIndex: number;
|
lastIndex: number;
|
||||||
} {
|
} {
|
||||||
const braceOrCommaToken = tokens[index];
|
const braceOrCommaToken = tokens[index];
|
||||||
@ -237,10 +237,54 @@ function makeVariableDeclaration(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Value =
|
||||||
|
| Literal
|
||||||
|
| Identifier
|
||||||
|
| BinaryExpression
|
||||||
|
| FunctionExpression
|
||||||
|
| CallExpression;
|
||||||
|
|
||||||
|
function makeValue(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): { value: Value; lastIndex: number } {
|
||||||
|
const currentToken = tokens[index];
|
||||||
|
const { token: nextToken } = nextMeaningfulToken(tokens, index);
|
||||||
|
if (nextToken.type === "brace" && nextToken.value === "(") {
|
||||||
|
const { expression, lastIndex } = makeCallExpression(tokens, index);
|
||||||
|
return {
|
||||||
|
value: expression,
|
||||||
|
lastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (currentToken.type === "word" && nextToken.type === "operator") {
|
||||||
|
const { expression, lastIndex } = makeBinaryExpression(tokens, index);
|
||||||
|
return {
|
||||||
|
value: expression,
|
||||||
|
lastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (currentToken.type === "word") {
|
||||||
|
const identifier = makeIdentifier(tokens, index);
|
||||||
|
return {
|
||||||
|
value: identifier,
|
||||||
|
lastIndex: index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (currentToken.type === "number" || currentToken.type === "string") {
|
||||||
|
const literal = makeLiteral(tokens, index);
|
||||||
|
return {
|
||||||
|
value: literal,
|
||||||
|
lastIndex: index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error("Expected a previous if statement to match");
|
||||||
|
}
|
||||||
|
|
||||||
interface VariableDeclarator extends GeneralStatement {
|
interface VariableDeclarator extends GeneralStatement {
|
||||||
type: "VariableDeclarator";
|
type: "VariableDeclarator";
|
||||||
id: Identifier;
|
id: Identifier;
|
||||||
init: Literal | Identifier | BinaryExpression | FunctionExpression;
|
init: Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVariableDeclarators(
|
function makeVariableDeclarators(
|
||||||
@ -255,7 +299,7 @@ function makeVariableDeclarators(
|
|||||||
const assignmentToken = nextMeaningfulToken(tokens, index);
|
const assignmentToken = nextMeaningfulToken(tokens, index);
|
||||||
const contentsStartToken = nextMeaningfulToken(tokens, assignmentToken.index);
|
const contentsStartToken = nextMeaningfulToken(tokens, assignmentToken.index);
|
||||||
const nextAfterInit = nextMeaningfulToken(tokens, contentsStartToken.index);
|
const nextAfterInit = nextMeaningfulToken(tokens, contentsStartToken.index);
|
||||||
let init: VariableDeclarator["init"];
|
let init: Value;
|
||||||
let lastIndex = contentsStartToken.index;
|
let lastIndex = contentsStartToken.index;
|
||||||
if (
|
if (
|
||||||
contentsStartToken.token.type === "brace" &&
|
contentsStartToken.token.type === "brace" &&
|
||||||
@ -348,23 +392,40 @@ interface BinaryExpression extends GeneralStatement {
|
|||||||
right: BinaryPart;
|
right: BinaryPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeBinaryPart(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): { part: BinaryPart; lastIndex: number } {
|
||||||
|
const currentToken = tokens[index];
|
||||||
|
if (currentToken.type === "word") {
|
||||||
|
const identifier = makeIdentifier(tokens, index);
|
||||||
|
return {
|
||||||
|
part: identifier,
|
||||||
|
lastIndex: index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (currentToken.type === "number" || currentToken.type === "string") {
|
||||||
|
const literal = makeLiteral(tokens, index);
|
||||||
|
return {
|
||||||
|
part: literal,
|
||||||
|
lastIndex: index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error("Expected a previous if statement to match");
|
||||||
|
}
|
||||||
|
|
||||||
function makeBinaryExpression(
|
function makeBinaryExpression(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
index: number
|
index: number
|
||||||
): { expression: BinaryExpression; lastIndex: number } {
|
): { expression: BinaryExpression; lastIndex: number } {
|
||||||
const currentToken = tokens[index];
|
const currentToken = tokens[index];
|
||||||
let left: BinaryPart;
|
const { part: left } = makeBinaryPart(tokens, index);
|
||||||
if (currentToken.type === "word") {
|
|
||||||
left = makeIdentifier(tokens, index);
|
|
||||||
} else {
|
|
||||||
left = makeLiteral(tokens, index);
|
|
||||||
}
|
|
||||||
const { token: operatorToken, index: operatorIndex } = nextMeaningfulToken(
|
const { token: operatorToken, index: operatorIndex } = nextMeaningfulToken(
|
||||||
tokens,
|
tokens,
|
||||||
index
|
index
|
||||||
);
|
);
|
||||||
const rightToken = nextMeaningfulToken(tokens, operatorIndex);
|
const rightToken = nextMeaningfulToken(tokens, operatorIndex);
|
||||||
const right = makeLiteral(tokens, rightToken.index);
|
const { part: right } = makeBinaryPart(tokens, rightToken.index);
|
||||||
return {
|
return {
|
||||||
expression: {
|
expression: {
|
||||||
type: "BinaryExpression",
|
type: "BinaryExpression",
|
||||||
@ -393,11 +454,7 @@ function makeFunctionExpression(
|
|||||||
const closingBraceIndex = findClosingBrace(tokens, index);
|
const closingBraceIndex = findClosingBrace(tokens, index);
|
||||||
const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex);
|
const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex);
|
||||||
const bodyStartToken = nextMeaningfulToken(tokens, arrowToken.index);
|
const bodyStartToken = nextMeaningfulToken(tokens, arrowToken.index);
|
||||||
// const { params, lastIndex: paramsLastIndex } = makeParams(
|
const { params } = makeParams(tokens, index);
|
||||||
// tokens,
|
|
||||||
// nextToken.index
|
|
||||||
// );
|
|
||||||
const params: Identifier[] = [];
|
|
||||||
const { block, lastIndex: bodyLastIndex } = makeBlockStatement(
|
const { block, lastIndex: bodyLastIndex } = makeBlockStatement(
|
||||||
tokens,
|
tokens,
|
||||||
bodyStartToken.index
|
bodyStartToken.index
|
||||||
@ -415,6 +472,31 @@ function makeFunctionExpression(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeParams(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number,
|
||||||
|
previousParams: Identifier[] = []
|
||||||
|
): { params: Identifier[]; lastIndex: number } {
|
||||||
|
const braceOrCommaToken = tokens[index];
|
||||||
|
const argumentToken = nextMeaningfulToken(tokens, index);
|
||||||
|
const shouldFinishRecursion =
|
||||||
|
(argumentToken.token.type === "brace" &&
|
||||||
|
argumentToken.token.value === ")") ||
|
||||||
|
(braceOrCommaToken.type === "brace" && braceOrCommaToken.value === ")");
|
||||||
|
if (shouldFinishRecursion) {
|
||||||
|
return { params: previousParams, lastIndex: index };
|
||||||
|
}
|
||||||
|
const nextBraceOrCommaToken = nextMeaningfulToken(
|
||||||
|
tokens,
|
||||||
|
argumentToken.index
|
||||||
|
);
|
||||||
|
const identifier = makeIdentifier(tokens, argumentToken.index);
|
||||||
|
return makeParams(tokens, nextBraceOrCommaToken.index, [
|
||||||
|
...previousParams,
|
||||||
|
identifier,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
interface BlockStatement extends GeneralStatement {
|
interface BlockStatement extends GeneralStatement {
|
||||||
type: "BlockStatement";
|
type: "BlockStatement";
|
||||||
body: Body[];
|
body: Body[];
|
||||||
@ -441,6 +523,29 @@ function makeBlockStatement(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ReturnStatement extends GeneralStatement {
|
||||||
|
type: "ReturnStatement";
|
||||||
|
argument: Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeReturnStatement(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): { statement: ReturnStatement; lastIndex: number } {
|
||||||
|
const currentToken = tokens[index];
|
||||||
|
const nextToken = nextMeaningfulToken(tokens, index);
|
||||||
|
const { value, lastIndex } = makeValue(tokens, nextToken.index);
|
||||||
|
return {
|
||||||
|
statement: {
|
||||||
|
type: "ReturnStatement",
|
||||||
|
start: currentToken.start,
|
||||||
|
end: tokens[lastIndex].end,
|
||||||
|
argument: value,
|
||||||
|
},
|
||||||
|
lastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type All = Program | ExpressionStatement[] | BinaryExpression | Literal;
|
export type All = Program | ExpressionStatement[] | BinaryExpression | Literal;
|
||||||
|
|
||||||
function nextMeaningfulToken(
|
function nextMeaningfulToken(
|
||||||
@ -459,7 +564,7 @@ function nextMeaningfulToken(
|
|||||||
return { token, index: newIndex };
|
return { token, index: newIndex };
|
||||||
}
|
}
|
||||||
|
|
||||||
type Body = ExpressionStatement | VariableDeclaration;
|
type Body = ExpressionStatement | VariableDeclaration | ReturnStatement;
|
||||||
|
|
||||||
function makeBody(
|
function makeBody(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
@ -469,7 +574,11 @@ function makeBody(
|
|||||||
if (tokenIndex >= tokens.length) {
|
if (tokenIndex >= tokens.length) {
|
||||||
return { body: previousBody, lastIndex: tokenIndex };
|
return { body: previousBody, lastIndex: tokenIndex };
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = tokens[tokenIndex];
|
const token = tokens[tokenIndex];
|
||||||
|
if (token.type === "brace" && token.value === "}") {
|
||||||
|
return { body: previousBody, lastIndex: tokenIndex };
|
||||||
|
}
|
||||||
if (typeof token === "undefined") {
|
if (typeof token === "undefined") {
|
||||||
console.log("probably should throw");
|
console.log("probably should throw");
|
||||||
}
|
}
|
||||||
@ -487,6 +596,11 @@ function makeBody(
|
|||||||
const nextThing = nextMeaningfulToken(tokens, lastIndex);
|
const nextThing = nextMeaningfulToken(tokens, lastIndex);
|
||||||
return makeBody(tokens, nextThing.index, [...previousBody, declaration]);
|
return makeBody(tokens, nextThing.index, [...previousBody, declaration]);
|
||||||
}
|
}
|
||||||
|
if (token.type === "word" && token.value === "return") {
|
||||||
|
const { statement, lastIndex } = makeReturnStatement(tokens, tokenIndex);
|
||||||
|
const nextThing = nextMeaningfulToken(tokens, lastIndex);
|
||||||
|
return makeBody(tokens, nextThing.index, [...previousBody, statement]);
|
||||||
|
}
|
||||||
if (token.type === "word" && token.value === "log") {
|
if (token.type === "word" && token.value === "log") {
|
||||||
const { expression, lastIndex } = makeExpressionStatement(
|
const { expression, lastIndex } = makeExpressionStatement(
|
||||||
tokens,
|
tokens,
|
||||||
|
Reference in New Issue
Block a user