start of function declaration
This commit is contained in:
@ -208,3 +208,46 @@ const newVar = myVar + 1
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("testing function declaration", () => {
|
||||||
|
test("fn funcN = () => {}", () => {
|
||||||
|
const tokens = lexer("fn funcN = () => {}");
|
||||||
|
// const tokens = lexer("const fn = () => {}");
|
||||||
|
const {body} = abstractSyntaxTree(tokens);
|
||||||
|
// console.log(JSON.stringify(body, null, 2));
|
||||||
|
expect(body).toEqual([
|
||||||
|
{
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"start": 0,
|
||||||
|
"end": 19,
|
||||||
|
"kind": "fn",
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"start": 3,
|
||||||
|
"end": 19,
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 3,
|
||||||
|
"end": 8,
|
||||||
|
"name": "funcN"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"start": 11,
|
||||||
|
"end": 19,
|
||||||
|
"id": null,
|
||||||
|
"params": [],
|
||||||
|
"body": {
|
||||||
|
"type": "BlockStatement",
|
||||||
|
"start": 17,
|
||||||
|
"end": 19,
|
||||||
|
"body": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -39,7 +39,8 @@ type syntaxType =
|
|||||||
| "NewExpression"
|
| "NewExpression"
|
||||||
| "ThisExpression"
|
| "ThisExpression"
|
||||||
| "UpdateExpression"
|
| "UpdateExpression"
|
||||||
| "ArrowFunctionExpression"
|
// | "ArrowFunctionExpression"
|
||||||
|
| "FunctionExpression"
|
||||||
| "YieldExpression"
|
| "YieldExpression"
|
||||||
| "AwaitExpression"
|
| "AwaitExpression"
|
||||||
| "ImportDeclaration"
|
| "ImportDeclaration"
|
||||||
@ -93,25 +94,31 @@ interface ExpressionStatement extends GeneralStatement {
|
|||||||
function makeExpressionStatement(
|
function makeExpressionStatement(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
index: number
|
index: number
|
||||||
): ExpressionStatement {
|
): { expression: ExpressionStatement; lastIndex: number } {
|
||||||
const currentToken = tokens[index];
|
const currentToken = tokens[index];
|
||||||
const { token: nextToken } = nextMeaningfulToken(tokens, index);
|
const { token: nextToken } = nextMeaningfulToken(tokens, index);
|
||||||
if (nextToken.type === "brace" && nextToken.value === "(") {
|
if (nextToken.type === "brace" && nextToken.value === "(") {
|
||||||
const { expression } = makeCallExpression(tokens, index);
|
const { expression, lastIndex } = makeCallExpression(tokens, index);
|
||||||
return {
|
return {
|
||||||
|
expression: {
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
start: currentToken.start,
|
||||||
|
end: expression.end,
|
||||||
|
expression,
|
||||||
|
},
|
||||||
|
lastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { expression, lastIndex } = makeBinaryExpression(tokens, index);
|
||||||
|
return {
|
||||||
|
expression: {
|
||||||
type: "ExpressionStatement",
|
type: "ExpressionStatement",
|
||||||
start: currentToken.start,
|
start: currentToken.start,
|
||||||
end: expression.end,
|
end: expression.end,
|
||||||
expression,
|
expression,
|
||||||
};
|
},
|
||||||
}
|
lastIndex,
|
||||||
|
|
||||||
const { expression } = makeBinaryExpression(tokens, index);
|
|
||||||
return {
|
|
||||||
type: "ExpressionStatement",
|
|
||||||
start: currentToken.start,
|
|
||||||
end: expression.end,
|
|
||||||
expression,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,17 +166,23 @@ function makeArguments(
|
|||||||
} {
|
} {
|
||||||
const braceOrCommaToken = tokens[index];
|
const braceOrCommaToken = tokens[index];
|
||||||
const argumentToken = nextMeaningfulToken(tokens, index);
|
const argumentToken = nextMeaningfulToken(tokens, index);
|
||||||
const shouldFinishRecursion = braceOrCommaToken.type === "brace" && braceOrCommaToken.value === ")";
|
const shouldFinishRecursion =
|
||||||
|
braceOrCommaToken.type === "brace" && braceOrCommaToken.value === ")";
|
||||||
if (shouldFinishRecursion) {
|
if (shouldFinishRecursion) {
|
||||||
return {
|
return {
|
||||||
arguments: previousArgs,
|
arguments: previousArgs,
|
||||||
lastIndex: index,
|
lastIndex: index,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const nextBraceOrCommaToken = nextMeaningfulToken(tokens, argumentToken.index);
|
const nextBraceOrCommaToken = nextMeaningfulToken(
|
||||||
const isIdentifierOrLiteral = nextBraceOrCommaToken.token.type === "comma" || nextBraceOrCommaToken.token.type === "brace"
|
tokens,
|
||||||
|
argumentToken.index
|
||||||
|
);
|
||||||
|
const isIdentifierOrLiteral =
|
||||||
|
nextBraceOrCommaToken.token.type === "comma" ||
|
||||||
|
nextBraceOrCommaToken.token.type === "brace";
|
||||||
if (!isIdentifierOrLiteral) {
|
if (!isIdentifierOrLiteral) {
|
||||||
const { expression, lastIndex} = makeBinaryExpression(tokens, index);
|
const { expression, lastIndex } = makeBinaryExpression(tokens, index);
|
||||||
return makeArguments(tokens, lastIndex, [...previousArgs, expression]);
|
return makeArguments(tokens, lastIndex, [...previousArgs, expression]);
|
||||||
}
|
}
|
||||||
if (argumentToken.token.type === "word") {
|
if (argumentToken.token.type === "word") {
|
||||||
@ -183,7 +196,10 @@ function makeArguments(
|
|||||||
argumentToken.token.type === "string"
|
argumentToken.token.type === "string"
|
||||||
) {
|
) {
|
||||||
const literal = makeLiteral(tokens, argumentToken.index);
|
const literal = makeLiteral(tokens, argumentToken.index);
|
||||||
return makeArguments(tokens, nextBraceOrCommaToken.index, [...previousArgs, literal]);
|
return makeArguments(tokens, nextBraceOrCommaToken.index, [
|
||||||
|
...previousArgs,
|
||||||
|
literal,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
throw new Error("Expected a previous if statement to match");
|
throw new Error("Expected a previous if statement to match");
|
||||||
}
|
}
|
||||||
@ -191,7 +207,7 @@ function makeArguments(
|
|||||||
interface VariableDeclaration extends GeneralStatement {
|
interface VariableDeclaration extends GeneralStatement {
|
||||||
type: "VariableDeclaration";
|
type: "VariableDeclaration";
|
||||||
declarations: VariableDeclarator[];
|
declarations: VariableDeclarator[];
|
||||||
kind: "const" | "unknown"; //| "solid" | "surface" | "face"
|
kind: "const" | "unknown" | "fn"; //| "solid" | "surface" | "face"
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVariableDeclaration(
|
function makeVariableDeclaration(
|
||||||
@ -209,7 +225,12 @@ function makeVariableDeclaration(
|
|||||||
type: "VariableDeclaration",
|
type: "VariableDeclaration",
|
||||||
start: currentToken.start,
|
start: currentToken.start,
|
||||||
end: declarations[declarations.length - 1].end,
|
end: declarations[declarations.length - 1].end,
|
||||||
kind: currentToken.value === "const" ? "const" : "unknown",
|
kind:
|
||||||
|
currentToken.value === "const"
|
||||||
|
? "const"
|
||||||
|
: currentToken.value === "fn"
|
||||||
|
? "fn"
|
||||||
|
: "unknown",
|
||||||
declarations,
|
declarations,
|
||||||
},
|
},
|
||||||
lastIndex,
|
lastIndex,
|
||||||
@ -219,7 +240,7 @@ function makeVariableDeclaration(
|
|||||||
interface VariableDeclarator extends GeneralStatement {
|
interface VariableDeclarator extends GeneralStatement {
|
||||||
type: "VariableDeclarator";
|
type: "VariableDeclarator";
|
||||||
id: Identifier;
|
id: Identifier;
|
||||||
init: Literal | Identifier | BinaryExpression;
|
init: Literal | Identifier | BinaryExpression | FunctionExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVariableDeclarators(
|
function makeVariableDeclarators(
|
||||||
@ -236,7 +257,27 @@ function makeVariableDeclarators(
|
|||||||
const nextAfterInit = nextMeaningfulToken(tokens, contentsStartToken.index);
|
const nextAfterInit = nextMeaningfulToken(tokens, contentsStartToken.index);
|
||||||
let init: VariableDeclarator["init"];
|
let init: VariableDeclarator["init"];
|
||||||
let lastIndex = contentsStartToken.index;
|
let lastIndex = contentsStartToken.index;
|
||||||
if (nextAfterInit.token?.type === "operator") {
|
if (
|
||||||
|
contentsStartToken.token.type === "brace" &&
|
||||||
|
contentsStartToken.token.value === "("
|
||||||
|
) {
|
||||||
|
const closingBraceIndex = findClosingBrace(
|
||||||
|
tokens,
|
||||||
|
contentsStartToken.index
|
||||||
|
);
|
||||||
|
const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex);
|
||||||
|
if (
|
||||||
|
arrowToken.token.type === "operator" &&
|
||||||
|
arrowToken.token.value === "=>"
|
||||||
|
) {
|
||||||
|
const { expression, lastIndex: arrowFunctionLastIndex } =
|
||||||
|
makeFunctionExpression(tokens, contentsStartToken.index);
|
||||||
|
init = expression;
|
||||||
|
lastIndex = arrowFunctionLastIndex;
|
||||||
|
} else {
|
||||||
|
throw new Error("TODO - handle expression with braces");
|
||||||
|
}
|
||||||
|
} else if (nextAfterInit.token?.type === "operator") {
|
||||||
const binExp = makeBinaryExpression(tokens, contentsStartToken.index);
|
const binExp = makeBinaryExpression(tokens, contentsStartToken.index);
|
||||||
init = binExp.expression;
|
init = binExp.expression;
|
||||||
lastIndex = binExp.lastIndex;
|
lastIndex = binExp.lastIndex;
|
||||||
@ -337,6 +378,69 @@ function makeBinaryExpression(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FunctionExpression extends GeneralStatement {
|
||||||
|
type: "FunctionExpression";
|
||||||
|
id: Identifier | null;
|
||||||
|
params: Identifier[];
|
||||||
|
body: BlockStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeFunctionExpression(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): { expression: FunctionExpression; lastIndex: number } {
|
||||||
|
const currentToken = tokens[index];
|
||||||
|
const closingBraceIndex = findClosingBrace(tokens, index);
|
||||||
|
const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex);
|
||||||
|
const bodyStartToken = nextMeaningfulToken(tokens, arrowToken.index);
|
||||||
|
// const { params, lastIndex: paramsLastIndex } = makeParams(
|
||||||
|
// tokens,
|
||||||
|
// nextToken.index
|
||||||
|
// );
|
||||||
|
const params: Identifier[] = [];
|
||||||
|
const { block, lastIndex: bodyLastIndex } = makeBlockStatement(
|
||||||
|
tokens,
|
||||||
|
bodyStartToken.index
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
expression: {
|
||||||
|
type: "FunctionExpression",
|
||||||
|
start: currentToken.start,
|
||||||
|
end: tokens[bodyLastIndex].end,
|
||||||
|
id: null,
|
||||||
|
params,
|
||||||
|
body: block,
|
||||||
|
},
|
||||||
|
lastIndex: bodyLastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlockStatement extends GeneralStatement {
|
||||||
|
type: "BlockStatement";
|
||||||
|
body: Body[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeBlockStatement(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): { block: BlockStatement; lastIndex: number } {
|
||||||
|
const openingCurly = tokens[index];
|
||||||
|
const nextToken = nextMeaningfulToken(tokens, index);
|
||||||
|
const { body, lastIndex } =
|
||||||
|
nextToken.token.value === "}"
|
||||||
|
? { body: [], lastIndex: nextToken.index }
|
||||||
|
: makeBody(tokens, nextToken.index);
|
||||||
|
return {
|
||||||
|
block: {
|
||||||
|
type: "BlockStatement",
|
||||||
|
start: openingCurly.start,
|
||||||
|
end: tokens[lastIndex].end,
|
||||||
|
body,
|
||||||
|
},
|
||||||
|
lastIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type All = Program | ExpressionStatement[] | BinaryExpression | Literal;
|
export type All = Program | ExpressionStatement[] | BinaryExpression | Literal;
|
||||||
|
|
||||||
function nextMeaningfulToken(
|
function nextMeaningfulToken(
|
||||||
@ -357,43 +461,54 @@ function nextMeaningfulToken(
|
|||||||
|
|
||||||
type Body = ExpressionStatement | VariableDeclaration;
|
type Body = ExpressionStatement | VariableDeclaration;
|
||||||
|
|
||||||
|
function makeBody(
|
||||||
|
tokens: Token[],
|
||||||
|
tokenIndex: number = 0,
|
||||||
|
previousBody: Body[] = []
|
||||||
|
): { body: Body[]; lastIndex: number } {
|
||||||
|
if (tokenIndex >= tokens.length) {
|
||||||
|
return { body: previousBody, lastIndex: tokenIndex };
|
||||||
|
}
|
||||||
|
const token = tokens[tokenIndex];
|
||||||
|
if (typeof token === "undefined") {
|
||||||
|
console.log("probably should throw");
|
||||||
|
}
|
||||||
|
if (token.type === "whitespace") {
|
||||||
|
return makeBody(tokens, tokenIndex + 1, previousBody);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
token.type === "word" &&
|
||||||
|
(token.value === "const" || token.value === "fn")
|
||||||
|
) {
|
||||||
|
const { declaration, lastIndex } = makeVariableDeclaration(
|
||||||
|
tokens,
|
||||||
|
tokenIndex
|
||||||
|
);
|
||||||
|
const nextThing = nextMeaningfulToken(tokens, lastIndex);
|
||||||
|
return makeBody(tokens, nextThing.index, [...previousBody, declaration]);
|
||||||
|
}
|
||||||
|
if (token.type === "word" && token.value === "log") {
|
||||||
|
const { expression, lastIndex } = makeExpressionStatement(
|
||||||
|
tokens,
|
||||||
|
tokenIndex
|
||||||
|
);
|
||||||
|
return { body: [...previousBody, expression], lastIndex };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(token.type === "number" || token.type === "word") &&
|
||||||
|
nextMeaningfulToken(tokens, tokenIndex).token.type === "operator"
|
||||||
|
) {
|
||||||
|
const { expression, lastIndex } = makeExpressionStatement(
|
||||||
|
tokens,
|
||||||
|
tokenIndex
|
||||||
|
);
|
||||||
|
// return startTree(tokens, tokenIndex, [...previousBody, makeExpressionStatement(tokens, tokenIndex)]);
|
||||||
|
return { body: [...previousBody, expression], lastIndex };
|
||||||
|
}
|
||||||
|
throw new Error("Unexpected token");
|
||||||
|
}
|
||||||
export const abstractSyntaxTree = (tokens: Token[]): Program => {
|
export const abstractSyntaxTree = (tokens: Token[]): Program => {
|
||||||
const startTree = (
|
const { body } = makeBody(tokens);
|
||||||
tokens: Token[],
|
|
||||||
tokenIndex: number = 0,
|
|
||||||
previousBody: Body[] = []
|
|
||||||
): Body[] => {
|
|
||||||
if (tokenIndex >= tokens.length) {
|
|
||||||
return previousBody;
|
|
||||||
}
|
|
||||||
const token = tokens[tokenIndex];
|
|
||||||
if (typeof token === "undefined") {
|
|
||||||
console.log("probably should throw");
|
|
||||||
}
|
|
||||||
if (token.type === 'whitespace') {
|
|
||||||
return startTree(tokens, tokenIndex + 1, previousBody);
|
|
||||||
}
|
|
||||||
if (token.type === "word" && token.value === "const") {
|
|
||||||
const { declaration, lastIndex } = makeVariableDeclaration(
|
|
||||||
tokens,
|
|
||||||
tokenIndex
|
|
||||||
);
|
|
||||||
const nextThing = nextMeaningfulToken(tokens, lastIndex);
|
|
||||||
return startTree(tokens, nextThing.index, [...previousBody, declaration]);
|
|
||||||
}
|
|
||||||
if (token.type === "word" && token.value === "log") {
|
|
||||||
return [...previousBody, makeExpressionStatement(tokens, tokenIndex)];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(token.type === "number" || token.type === "word") &&
|
|
||||||
nextMeaningfulToken(tokens, tokenIndex).token.type === "operator"
|
|
||||||
) {
|
|
||||||
// return startTree(tokens, tokenIndex, [...previousBody, makeExpressionStatement(tokens, tokenIndex)]);
|
|
||||||
return [...previousBody, makeExpressionStatement(tokens, tokenIndex)];
|
|
||||||
}
|
|
||||||
throw new Error("Unexpected token");
|
|
||||||
};
|
|
||||||
const body = startTree(tokens);
|
|
||||||
const program: Program = {
|
const program: Program = {
|
||||||
type: "Program",
|
type: "Program",
|
||||||
start: 0,
|
start: 0,
|
||||||
|
Reference in New Issue
Block a user