tfl-js
Version:
A TypeScript library for parsing and evaluating propositional logic formulas
139 lines • 6.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TFLParser = void 0;
const formula_1 = require("../types/formula");
const errors_1 = require("../errors");
const lexer_1 = require("./lexer");
class TFLParser {
constructor() {
this.pos = 0;
this.tokens = [];
}
parse(input) {
// console.log('\nParsing input:', input);
const lexResult = lexer_1.TFLLexer.tokenize(input);
if (lexResult.errors.length > 0) {
throw new Error(`Lexing errors: ${lexResult.errors.map(e => e.message).join(", ")}`);
}
this.tokens = lexResult.tokens;
// console.log('Tokens:', JSON.stringify(this.tokens, null, 2));
this.pos = 0;
return this.parseFormula(0);
}
parseFormula(precedence) {
// console.log(`\nParseFormula called with precedence ${precedence}`);
// console.log('Current token:', this.pos < this.tokens.length ? JSON.stringify(this.tokens[this.pos]) : 'END');
let left = this.parsePrefix();
// console.log('After parsePrefix, left:', JSON.stringify(left, null, 2));
while (this.pos < this.tokens.length) {
const token = this.tokens[this.pos];
const nextPrecedence = this.getInfixPrecedence(token);
// console.log(`Checking token:`, JSON.stringify(token));
// console.log(`Next precedence: ${nextPrecedence}, Current precedence: ${precedence}`);
if (nextPrecedence <= precedence) {
// console.log('Breaking due to precedence');
break;
}
this.pos++; // consume operator
left = this.parseInfix(left, token, nextPrecedence);
// console.log('After parseInfix, left:', JSON.stringify(left, null, 2));
}
// console.log('Returning from parseFormula:', JSON.stringify(left, null, 2));
return left;
}
parsePrefix() {
const token = this.tokens[this.pos];
// console.log('ParsePrefix token:', JSON.stringify(token));
if (token.tokenType === lexer_1.Tokens.AtomicProp) {
this.pos++;
// console.log('Parsed atomic prop:', token.image);
return new formula_1.Atom(token.image);
}
if (token.tokenType === lexer_1.Tokens.LParen) {
// console.log('Found left paren');
this.pos++;
const expr = this.parseFormula(0);
// console.log('After paren contents:', JSON.stringify(expr, null, 2));
if (this.pos >= this.tokens.length || this.tokens[this.pos].tokenType !== lexer_1.Tokens.RParen) {
throw new errors_1.ParseError('Expected closing parenthesis', 0, 0, 0, 'primary');
}
this.pos++;
// Return the inner expression directly
return expr;
}
if (this.isNegation(token)) {
// console.log('Found negation');
this.pos++;
const operand = this.parseFormula(TFLParser.PRECEDENCE.NOT);
// console.log('Negation operand:', JSON.stringify(operand, null, 2));
return new formula_1.Compound(formula_1.Operator.NOT, operand);
}
throw new errors_1.ParseError(`Unexpected token: ${token.image}`, 0, 0, 0, 'prefix');
}
parseInfix(left, operator, precedence) {
// console.log(`\nParseInfix Details:`);
// console.log('Operator:', JSON.stringify(operator));
// console.log('Operator Type:', operator.tokenType);
// console.log('Left:', JSON.stringify(left, null, 2));
// console.log('Current Precedence:', precedence);
const rightPrecedence = precedence - (this.isRightAssociative(operator) ? 1 : 0);
// console.log('Right Precedence:', rightPrecedence);
const right = this.parseFormula(rightPrecedence);
// console.log('Right:', JSON.stringify(right, null, 2));
let result;
if (this.isAnd(operator))
result = new formula_1.Compound(formula_1.Operator.AND, left, right);
else if (this.isOr(operator))
result = new formula_1.Compound(formula_1.Operator.OR, left, right);
else if (this.isIf(operator)) {
// console.log('Creating IF compound with:');
// console.log(' Left:', JSON.stringify(left, null, 2));
// console.log(' Right:', JSON.stringify(right, null, 2));
result = new formula_1.Compound(formula_1.Operator.IF, left, right);
}
else if (this.isIff(operator))
result = new formula_1.Compound(formula_1.Operator.IFF, left, right);
else
throw new errors_1.ParseError(`Unknown operator: ${operator.image}`, 0, 0, 0, 'infix');
// console.log('Infix result:', JSON.stringify(result, null, 2));
return result;
}
getInfixPrecedence(token) {
const precedence = this.isAnd(token) ? TFLParser.PRECEDENCE.AND
: this.isOr(token) ? TFLParser.PRECEDENCE.OR
: this.isIf(token) ? TFLParser.PRECEDENCE.IF
: this.isIff(token) ? TFLParser.PRECEDENCE.IFF
: -1;
// console.log(`Precedence for token ${JSON.stringify(token)}: ${precedence}`);
return precedence;
}
isRightAssociative(token) {
return this.isIf(token) || this.isIff(token);
}
isNegation(token) {
return [lexer_1.Tokens.StandardNot, lexer_1.Tokens.TflNot, lexer_1.Tokens.EnglishNot].includes(token.tokenType);
}
isAnd(token) {
return [lexer_1.Tokens.StandardAnd, lexer_1.Tokens.TflAnd, lexer_1.Tokens.EnglishAnd].includes(token.tokenType);
}
isOr(token) {
return [lexer_1.Tokens.StandardOr, lexer_1.Tokens.TflOr, lexer_1.Tokens.EnglishOr].includes(token.tokenType);
}
isIf(token) {
return [lexer_1.Tokens.StandardIf, lexer_1.Tokens.TflIf, lexer_1.Tokens.EnglishIf].includes(token.tokenType);
}
isIff(token) {
return [lexer_1.Tokens.StandardIff, lexer_1.Tokens.TflIff, lexer_1.Tokens.EnglishIff].includes(token.tokenType);
}
}
exports.TFLParser = TFLParser;
// Precedence levels
TFLParser.PRECEDENCE = {
ATOM: 0,
IF: 1,
IFF: 1,
OR: 2,
AND: 3,
NOT: 4
};
//# sourceMappingURL=parser.js.map