Add packages/kripke

This commit is contained in:
2025-02-17 23:21:53 +09:00
parent 1465acdf24
commit 2e36390a5d
11 changed files with 505 additions and 0 deletions

148
packages/kripke/parser.ts Normal file
View File

@@ -0,0 +1,148 @@
import {
type Token,
alt,
apply,
buildLexer,
expectEOF,
expectSingleResult,
kmid,
lrec_sc,
rule,
seq,
tok,
} from "typescript-parsec";
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<TokenKind.PropVar | TokenKind.Top | TokenKind.Bot>,
): 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<TokenKind.Not | TokenKind.Box | TokenKind.Diamond>,
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<TokenKind.And | TokenKind.Or | TokenKind.To | TokenKind.Iff>,
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<TokenKind, Formula>();
const ANDOR = rule<TokenKind, Formula>();
const EXP = rule<TokenKind, Formula>();
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))));
}