import { type Token, alt, apply, buildLexer, expectEOF, expectSingleResult, kmid, lrec_sc, rule, seq, tok, } from "typescript-parsec"; import { type Relation, worlds } from "./semantics"; import { type Formula, and, bot, box, diamond, iff, not, or, propVars, propvar, to, top, } from "./syntax"; enum TokenKind { PropVar = 0, Top = 1, Bot = 2, Not = 3, Box = 4, Diamond = 5, And = 6, Or = 7, To = 8, Iff = 9, LParen = 10, RParen = 11, Space = 12, } const lexer = buildLexer([ [true, /^[pqrs]/g, TokenKind.PropVar], [true, /^(T|⊤|1|\\top)/g, TokenKind.Top], [true, /^(F|⊥|0|\\bot)/g, TokenKind.Bot], [true, /^(~|¬|\\neg|\\lnot)/g, TokenKind.Not], [true, /^(\[\]|□|L|\\Box)/g, TokenKind.Box], [true, /^(<>|⋄|M|\\Diamond)/g, TokenKind.Diamond], [true, /^(&|\^|∧|\\wedge|\\land)/g, TokenKind.And], [true, /^(\||v|∨|\\vee|\\lor)/g, TokenKind.Or], [true, /^(->|→|\\rightarrow|\\to|\\implies)/g, TokenKind.To], [true, /^(<->|↔|\\leftrightarrow|\\iff)/g, TokenKind.Iff], [true, /^(\(|\\left\()/g, TokenKind.LParen], [true, /^(\)|\\right\))/g, TokenKind.RParen], [false, /^\s+/g, TokenKind.Space], ]); function atom( value: Token, ): Formula { switch (value.kind) { case TokenKind.PropVar: { if (propVars.includes(value.text)) { return propvar(value.text); } throw new Error(`Unknown atom: ${value.text}`); } case TokenKind.Top: return top; case TokenKind.Bot: return bot; default: throw new Error(`Unknown atom: ${value.text}`); } } function unary([op, value]: [ Token, Formula, ]): Formula { switch (op.kind) { case TokenKind.Not: return not(value); case TokenKind.Box: return box(value); case TokenKind.Diamond: return diamond(value); default: throw new Error(`Unknown unary operator: ${op.text}`); } } function binary( left: Formula, [op, right]: [ Token, Formula, ], ): Formula { switch (op.kind) { case TokenKind.And: return and(left, right); case TokenKind.Or: return or(left, right); case TokenKind.To: return to(left, right); case TokenKind.Iff: return iff(left, right); default: throw new Error(`Unknown binary operator: ${op.text}`); } } const TERM = rule(); const ANDOR = rule(); const EXP = rule(); TERM.setPattern( alt( apply( alt(tok(TokenKind.PropVar), tok(TokenKind.Top), tok(TokenKind.Bot)), atom, ), apply( seq( alt(tok(TokenKind.Not), tok(TokenKind.Box), tok(TokenKind.Diamond)), TERM, ), unary, ), kmid(tok(TokenKind.LParen), EXP, tok(TokenKind.RParen)), ), ); ANDOR.setPattern( lrec_sc(TERM, seq(alt(tok(TokenKind.And), tok(TokenKind.Or)), ANDOR), binary), ); EXP.setPattern( lrec_sc(ANDOR, seq(alt(tok(TokenKind.To), tok(TokenKind.Iff)), EXP), binary), ); export function parse(expr: string): Formula { return expectSingleResult(expectEOF(EXP.parse(lexer.parse(expr)))); } const frameLexer = buildLexer([ [true, /^[abcd]/g, "world" as const], [true, /^(R|<|≺|\\prec)/g, "relation" as const], [true, /^,/g, "separator" as const], [false, /^\s+/g, "spaces" as const], ]); const RELATION = rule< "world" | "relation" | "separator" | "spaces", Set >(); RELATION.setPattern( apply(seq(tok("world"), tok("relation"), tok("world")), ([x, _r, y]) => { const w1 = x.text; const w2 = y.text; if (!worlds.includes(w1) || !worlds.includes(w2)) throw new Error(); return new Set([`${w1}${w2}`]); }), ); const RELATIONS = rule< "world" | "relation" | "separator" | "spaces", Set >(); RELATIONS.setPattern( lrec_sc( RELATION, seq(tok("separator"), RELATIONS), (l, [_s, r]) => new Set([...l, ...r]), ), ); export function parseFrame(expr: string): Set { return expectSingleResult(expectEOF(RELATIONS.parse(frameLexer.parse(expr)))); }