759 lines
22 KiB
TypeScript
759 lines
22 KiB
TypeScript
|
import {
|
||
|
BinaryType,
|
||
|
ParsedExpr,
|
||
|
ParsedParameter,
|
||
|
ParsedType,
|
||
|
} from "./parsed.ts";
|
||
|
import {
|
||
|
CompileError,
|
||
|
Position,
|
||
|
Token,
|
||
|
TokenIter,
|
||
|
TokenType,
|
||
|
} from "./token.ts";
|
||
|
|
||
|
export class Parser {
|
||
|
private current: Token;
|
||
|
public errors: CompileError[] = [];
|
||
|
|
||
|
public constructor(private lexer: TokenIter) {
|
||
|
this.current = lexer.next();
|
||
|
}
|
||
|
|
||
|
public parse(): ParsedExpr[] {
|
||
|
while (this.currentIs("semicolon")) this.step();
|
||
|
const statements: ParsedExpr[] = [];
|
||
|
while (!this.done()) {
|
||
|
while (this.currentIs("semicolon")) this.step();
|
||
|
}
|
||
|
return statements;
|
||
|
}
|
||
|
|
||
|
private checkAndSkipStatementLinebreak() {
|
||
|
if (!this.currentIs("semicolon")) {
|
||
|
this.errors.push({
|
||
|
pos: this.position(),
|
||
|
message:
|
||
|
`expected ';' after statement, got '${this.current.tokenType}'`,
|
||
|
});
|
||
|
} else {
|
||
|
this.step();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public parseStatement(): ParsedExpr {
|
||
|
if (this.currentIs("fn")) {
|
||
|
return this.parseFn();
|
||
|
} else if (this.currentIs("if")) {
|
||
|
return this.parseIf();
|
||
|
} else if (this.currentIs("loop")) {
|
||
|
return this.parseLoop();
|
||
|
} else if (this.currentIs("while")) {
|
||
|
return this.parseWhile();
|
||
|
} else if (this.currentIs("return")) {
|
||
|
const statement = this.parseReturn();
|
||
|
this.checkAndSkipStatementLinebreak();
|
||
|
return statement;
|
||
|
} else if (this.currentIs("break")) {
|
||
|
const statement = this.parseBreak();
|
||
|
this.checkAndSkipStatementLinebreak();
|
||
|
return statement;
|
||
|
} else if (this.currentIs("continue")) {
|
||
|
const statement = this.parseContinue();
|
||
|
this.checkAndSkipStatementLinebreak();
|
||
|
return statement;
|
||
|
} else if (this.currentIs("let")) {
|
||
|
const statement = this.parseLet();
|
||
|
this.checkAndSkipStatementLinebreak();
|
||
|
return statement;
|
||
|
} else {
|
||
|
const statement = this.parseAssign();
|
||
|
this.checkAndSkipStatementLinebreak();
|
||
|
return statement;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseFn(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
if (this.done() || this.current.tokenType != "id") {
|
||
|
return this.errorExpr(
|
||
|
`expected 'Id' after 'fn' as function name, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const subject = this.current.value;
|
||
|
this.step();
|
||
|
if (!this.currentIs("lparen")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '(' before function-parameters, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
const params: ParsedParameter[] = [];
|
||
|
params.push(this.parseParam());
|
||
|
while (this.currentIs("comma")) {
|
||
|
this.step();
|
||
|
if (this.done() || this.currentIs("rparen")) break;
|
||
|
params.push(this.parseParam());
|
||
|
}
|
||
|
if (!this.currentIs("rparen")) {
|
||
|
return this.errorExpr(
|
||
|
`expected ')' after function-parameters, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
let returnType: ParsedType | null = null;
|
||
|
if (this.currentIs("minusgt")) {
|
||
|
this.step();
|
||
|
returnType = this.parseType();
|
||
|
}
|
||
|
if (!this.currentIs("lbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '{' after 'loop', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const body = this.parseBlock();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "fn",
|
||
|
subject,
|
||
|
params,
|
||
|
returnType,
|
||
|
body,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseLoop(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
if (!this.currentIs("lbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '{' after 'loop', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const body = this.parseBlock();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "loop",
|
||
|
body,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseWhile(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
const condition = this.parseExpr();
|
||
|
if (!this.currentIs("lbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '{' after while-condition, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const body = this.parseBlock();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "while",
|
||
|
condition,
|
||
|
body,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseReturn(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
let value: ParsedExpr | null = null;
|
||
|
if (!this.done() && this.current.tokenType != "semicolon") {
|
||
|
value = this.parseExpr();
|
||
|
}
|
||
|
return { pos, exprType: "return", value };
|
||
|
}
|
||
|
|
||
|
private parseBreak(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return { pos, exprType: "break" };
|
||
|
}
|
||
|
|
||
|
private parseContinue(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return { pos, exprType: "continue" };
|
||
|
}
|
||
|
|
||
|
private parseLet(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
const param = this.parseParam();
|
||
|
if (!this.currentIs("equal")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '=' after let-parameter, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "let",
|
||
|
param,
|
||
|
value,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseParam(): ParsedParameter {
|
||
|
const pos = this.position();
|
||
|
const mutable = this.currentIs("mut");
|
||
|
if (mutable) this.step();
|
||
|
if (this.done() || this.current.tokenType != "id") {
|
||
|
return this.errorParam(
|
||
|
`expected 'id' in parameter, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const subject = this.current.value;
|
||
|
this.step();
|
||
|
let valueType: ParsedType | null = null;
|
||
|
if (this.currentIs("colon")) {
|
||
|
this.step();
|
||
|
valueType = this.parseType();
|
||
|
}
|
||
|
return {
|
||
|
pos,
|
||
|
paramType: "value",
|
||
|
subject,
|
||
|
valueType,
|
||
|
mutable,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseAssign(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
const subject = this.parseExpr();
|
||
|
if (this.currentIs("equal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "equal",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("plusequal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "add",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("minusequal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "subtract",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("ampersandequal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "and",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("pipeequal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "or",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("hatequal")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "assign",
|
||
|
assignType: "xor",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else {
|
||
|
return subject;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public parseExpr(): ParsedExpr {
|
||
|
return this.parseLogOr();
|
||
|
}
|
||
|
|
||
|
private binaryExpr(
|
||
|
binaryType: BinaryType,
|
||
|
left: ParsedExpr,
|
||
|
right: ParsedExpr,
|
||
|
pos: Position,
|
||
|
): ParsedExpr {
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "binary",
|
||
|
binaryType,
|
||
|
left,
|
||
|
right,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseLogOr(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseLogAnd();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("or")) {
|
||
|
this.step();
|
||
|
const right = this.parseLogAnd();
|
||
|
left = this.binaryExpr("log_or", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseLogAnd(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseBitOr();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("and")) {
|
||
|
this.step();
|
||
|
const right = this.parseBitOr();
|
||
|
left = this.binaryExpr("log_and", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseBitOr(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseBitXor();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("pipe")) {
|
||
|
this.step();
|
||
|
const right = this.parseBitXor();
|
||
|
left = this.binaryExpr("bit_or", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseBitXor(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseBitAnd();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("hat")) {
|
||
|
this.step();
|
||
|
const right = this.parseBitAnd();
|
||
|
left = this.binaryExpr("bit_xor", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseBitAnd(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseEquality();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("ampersand")) {
|
||
|
this.step();
|
||
|
const right = this.parseEquality();
|
||
|
left = this.binaryExpr("bit_and", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseEquality(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseComparison();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("equalequal")) {
|
||
|
this.step();
|
||
|
const right = this.parseComparison();
|
||
|
left = this.binaryExpr("equal", left, right, pos);
|
||
|
} else if (this.currentIs("exclamationequal")) {
|
||
|
this.step();
|
||
|
const right = this.parseComparison();
|
||
|
left = this.binaryExpr("inequal", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseComparison(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
const left = this.parseAddSubtract();
|
||
|
if (this.currentIs("lt")) {
|
||
|
this.step();
|
||
|
const right = this.parseAddSubtract();
|
||
|
return this.binaryExpr("lt", left, right, pos);
|
||
|
} else if (this.currentIs("gt")) {
|
||
|
this.step();
|
||
|
const right = this.parseAddSubtract();
|
||
|
return this.binaryExpr("gt", left, right, pos);
|
||
|
} else if (this.currentIs("ltequal")) {
|
||
|
this.step();
|
||
|
const right = this.parseAddSubtract();
|
||
|
return this.binaryExpr("ltequal", left, right, pos);
|
||
|
} else if (this.currentIs("gtequal")) {
|
||
|
this.step();
|
||
|
const right = this.parseAddSubtract();
|
||
|
return this.binaryExpr("gtequal", left, right, pos);
|
||
|
} else {
|
||
|
return left;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseAddSubtract(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let left = this.parseUnary();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("plus")) {
|
||
|
this.step();
|
||
|
const right = this.parseUnary();
|
||
|
left = this.binaryExpr("add", left, right, pos);
|
||
|
} else if (this.currentIs("minus")) {
|
||
|
this.step();
|
||
|
const right = this.parseUnary();
|
||
|
left = this.binaryExpr("subtract", left, right, pos);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
private parseUnary(): ParsedExpr {
|
||
|
if (this.currentIs("minus")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "unary",
|
||
|
unaryType: "negate",
|
||
|
subject: this.parseUnary(),
|
||
|
};
|
||
|
} else if (this.currentIs("not")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "unary",
|
||
|
unaryType: "log_not",
|
||
|
subject: this.parseUnary(),
|
||
|
};
|
||
|
} else if (this.currentIs("tilde")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "unary",
|
||
|
unaryType: "bit_not",
|
||
|
subject: this.parseUnary(),
|
||
|
};
|
||
|
} else if (this.currentIs("ampersand")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "unary",
|
||
|
unaryType: "addressof",
|
||
|
subject: this.parseUnary(),
|
||
|
};
|
||
|
} else if (this.currentIs("asterisk")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "unary",
|
||
|
unaryType: "dereference",
|
||
|
subject: this.parseUnary(),
|
||
|
};
|
||
|
} else {
|
||
|
return this.parseCallIndex();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseCallIndex(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
let subject = this.parseOperand();
|
||
|
while (!this.done()) {
|
||
|
if (this.currentIs("lparen")) {
|
||
|
this.step();
|
||
|
const args: ParsedExpr[] = [];
|
||
|
if (!this.done() && this.current.tokenType != "rparen") {
|
||
|
args.push(this.parseExpr());
|
||
|
while (this.currentIs("comma")) {
|
||
|
this.step();
|
||
|
if (this.done() || this.currentIs("rparen")) {
|
||
|
break;
|
||
|
}
|
||
|
args.push(this.parseExpr());
|
||
|
}
|
||
|
}
|
||
|
if (!this.currentIs("rparen")) {
|
||
|
return this.errorExpr(
|
||
|
`expected ')', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
subject = {
|
||
|
pos,
|
||
|
exprType: "call",
|
||
|
subject,
|
||
|
args,
|
||
|
};
|
||
|
} else if (this.currentIs("lbracket")) {
|
||
|
this.step();
|
||
|
const value = this.parseExpr();
|
||
|
if (!this.currentIs("rbracket")) {
|
||
|
return this.errorExpr(
|
||
|
`expected ']', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
subject = {
|
||
|
pos,
|
||
|
exprType: "index",
|
||
|
subject,
|
||
|
value,
|
||
|
};
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return subject;
|
||
|
}
|
||
|
|
||
|
private parseOperand(): ParsedExpr {
|
||
|
if (this.current.tokenType == "id") {
|
||
|
const pos = this.position();
|
||
|
const value = this.current.value;
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "id",
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.current.tokenType == "int") {
|
||
|
const pos = this.position();
|
||
|
const value = this.current.value;
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "int",
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("lparen")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
if (this.currentIs("rparen")) {
|
||
|
this.step();
|
||
|
return { pos, exprType: "unit" };
|
||
|
}
|
||
|
const subject = this.parseExpr();
|
||
|
if (!this.currentIs("rparen")) {
|
||
|
return this.errorExpr(
|
||
|
`expected ')', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
subject.pos = pos;
|
||
|
return subject;
|
||
|
} else if (this.currentIs("if")) {
|
||
|
return this.parseIf();
|
||
|
} else if (this.currentIs("lbrace")) {
|
||
|
return this.parseBlock();
|
||
|
} else {
|
||
|
return this.errorExpr(
|
||
|
`expected operand, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseIf(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
const condition = this.parseExpr();
|
||
|
if (!this.currentIs("lbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '{' after if-condition, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
const truthy = this.parseBlock();
|
||
|
let falsy: ParsedExpr | null = null;
|
||
|
if (this.currentIs("else")) {
|
||
|
this.step();
|
||
|
if (!this.currentIs("lbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '{' after 'else', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
falsy = this.parseBlock();
|
||
|
}
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "if",
|
||
|
condition,
|
||
|
truthy,
|
||
|
falsy,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseBlock(): ParsedExpr {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
while (this.currentIs("semicolon")) this.step();
|
||
|
const body: ParsedExpr[] = [];
|
||
|
while (!this.done() && this.current.tokenType != "rbrace") {
|
||
|
body.push(this.parseStatement());
|
||
|
while (this.currentIs("semicolon")) this.step();
|
||
|
}
|
||
|
if (!this.currentIs("rbrace")) {
|
||
|
return this.errorExpr(
|
||
|
`expected '}' after block, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "block",
|
||
|
body,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private parseType(): ParsedType {
|
||
|
return this.parsePointerType();
|
||
|
}
|
||
|
|
||
|
private parsePointerType(): ParsedType {
|
||
|
if (this.currentIs("asterisk")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
let mutable = false;
|
||
|
if (this.currentIs("mut")) {
|
||
|
mutable = true;
|
||
|
this.step();
|
||
|
}
|
||
|
const subject = this.parseTypeOperand();
|
||
|
return {
|
||
|
pos,
|
||
|
typeType: "pointer",
|
||
|
subject,
|
||
|
mutable,
|
||
|
};
|
||
|
} else {
|
||
|
return this.parseTypeOperand();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseTypeOperand(): ParsedType {
|
||
|
if (this.current.tokenType == "id") {
|
||
|
const pos = this.position();
|
||
|
const value = this.current.value;
|
||
|
this.step();
|
||
|
return {
|
||
|
pos,
|
||
|
typeType: "id",
|
||
|
value,
|
||
|
};
|
||
|
} else if (this.currentIs("lparen")) {
|
||
|
const pos = this.position();
|
||
|
this.step();
|
||
|
if (this.currentIs("rparen")) {
|
||
|
this.step();
|
||
|
return { pos, typeType: "unit" };
|
||
|
}
|
||
|
const subject = this.parseType();
|
||
|
if (!this.currentIs("rparen")) {
|
||
|
return this.errorType(
|
||
|
`expected ')', got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
this.step();
|
||
|
subject.pos = pos;
|
||
|
return subject;
|
||
|
} else {
|
||
|
return this.errorType(
|
||
|
`expected type, got '${this.current.tokenType}'`,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private errorExpr(
|
||
|
message: string,
|
||
|
pos: Position = this.position(),
|
||
|
): ParsedExpr {
|
||
|
this.errors.push({ pos, message });
|
||
|
return {
|
||
|
pos,
|
||
|
exprType: "error",
|
||
|
message,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private errorParam(
|
||
|
message: string,
|
||
|
pos: Position = this.position(),
|
||
|
): ParsedParameter {
|
||
|
this.errors.push({ pos, message });
|
||
|
return {
|
||
|
pos,
|
||
|
paramType: "error",
|
||
|
message,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private errorType(
|
||
|
message: string,
|
||
|
pos: Position = this.position(),
|
||
|
): ParsedType {
|
||
|
this.errors.push({ pos, message });
|
||
|
return {
|
||
|
pos,
|
||
|
typeType: "error",
|
||
|
message,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private position() {
|
||
|
return this.current.pos;
|
||
|
}
|
||
|
|
||
|
private step() {
|
||
|
this.current = this.lexer.next();
|
||
|
}
|
||
|
|
||
|
private currentIs(tokenType: TokenType): boolean {
|
||
|
return this.current.tokenType == tokenType;
|
||
|
}
|
||
|
|
||
|
private done(): boolean {
|
||
|
return this.current.tokenType == "eof";
|
||
|
}
|
||
|
}
|