-
-
Save iglosiggio/b9f0452123160fe147488bf348047605 to your computer and use it in GitHub Desktop.
A simple recursive-descent parser for logic formulas (the precedences are probably wrong)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const nextToken = s => { | |
// Remove leading spaces | |
s = s.trim() | |
if (s.length === 0) return [{ kind: 'eof' }, s.slice(0)] | |
if (s.startsWith('V')) return [{ kind: 'or' }, s.slice(1)] | |
if (s.startsWith('^')) return [{ kind: 'and' }, s.slice(1)] | |
if (s.startsWith('->')) return [{ kind: 'then' }, s.slice(2)] | |
if (s.startsWith('<->')) return [{ kind: 'iff' }, s.slice(3)] | |
if (s.startsWith('(')) return [{ kind: 'lparen' }, s.slice(1)] | |
if (s.startsWith(')')) return [{ kind: 'rparen' }, s.slice(1)] | |
if (s.startsWith('Posta')) return [{ kind: 'true' }, s.slice(5)] | |
if (s.startsWith('Chamuyo')) return [{ kind: 'false' }, s.slice(7)] | |
// Identifier | |
const ident_pattern = /^[a-z]+/ | |
const match = ident_pattern.exec(s) | |
if (match != null) { | |
const ident = match[0] | |
return [{ kind: 'ident', ident }, s.slice(ident.length)] | |
} | |
} | |
function tokenize(s) { | |
let tokens = [] | |
let token | |
do { | |
[token, s] = nextToken(s) | |
tokens.push(token) | |
} while (token.kind !== 'eof') | |
return tokens | |
} | |
function parse(s) { | |
let tokens = tokenize(s) | |
function peek() { | |
return tokens[0] | |
} | |
function pop() { | |
return tokens.shift() | |
} | |
return parse_iff() | |
function parse_iff() { | |
let expr = parse_then() | |
while (peek().kind === 'iff') { | |
pop() | |
expr = { kind: 'iff', lhs: expr, rhs: parse_then() } | |
} | |
return expr | |
} | |
function parse_then() { | |
let expr = parse_or() | |
while (peek().kind === 'then') { | |
pop() | |
expr = { kind: 'then', lhs: expr, rhs: parse_or() } | |
} | |
return expr | |
} | |
function parse_or() { | |
let expr = parse_and() | |
while (peek().kind === 'or') { | |
pop() | |
expr = { kind: 'or', lhs: expr, rhs: parse_and() } | |
} | |
return expr | |
} | |
function parse_and() { | |
let expr = parse_paren() || parse_var() || parse_lit() | |
if (expr === undefined) throw new Error('Something somewhere failed!') | |
while (peek().kind === 'and') { | |
pop() | |
expr = { kind: 'and', lhs: expr, rhs: parse_paren() || parse_var() || parse_lit() } | |
} | |
return expr | |
} | |
function parse_var() { | |
let token = peek() | |
if (token.kind === 'ident') { | |
pop() | |
return token.ident | |
} | |
} | |
function parse_lit() { | |
let token = peek() | |
if (token.kind === 'true') { | |
pop() | |
return true | |
} | |
if (token.kind === 'false') { | |
pop() | |
return false | |
} | |
} | |
function parse_paren() { | |
if (peek().kind === 'lparen') { | |
pop() | |
let result = parse_iff() | |
if (result !== undefined && peek().kind === 'rparen') { | |
pop() | |
return result | |
} | |
} | |
} | |
} | |
console.log(JSON.stringify(parse('Posta -> (a -> Chamuyo <-> b ^ c V a^j)'), null, 2)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment