Skip to content

Instantly share code, notes, and snippets.

@ulve
Created April 28, 2018 14:34
Show Gist options
  • Save ulve/8a762cc2402a4964574fbf2737e145b0 to your computer and use it in GitHub Desktop.
Save ulve/8a762cc2402a4964574fbf2737e145b0 to your computer and use it in GitHub Desktop.
Nyare parser combinator som är mer generisk
// Okej det är ju inte så kul att alltid få ut listor av bokstäver. Hur vore det om man
// kunde göra om dom till något annat? Typ en sträng? eller vad som helst
// Först kan vi behöva en typ som inte är string. vi gör en generisk success
// Okej nu behöver vi lite typer
interface Success<T> {
kind: "success";
val: [T, string];
}
interface Failure {
kind: "failure";
message: string
}
type Result<T> = Success<T> | Failure
// Sen måste ju parsern också göras om
interface Parser<T> {
fn: (s: string) => Result<T>;
}
const mchar = <T>(c: string): Parser<string> => {
return {fn: (str: string) : Result<string> => {
if(str === "" || str === undefined || str === null) {
return { kind: "failure", message: "Ingen data" }
} else if(str[0] === c) {
return { kind: "success", val: [c, str.substring(1)] }
} else {
return { kind: "failure", message: `Försökte hitta ett ${c} men fick istälet ett ${str[0]}` }
}
}
}
}
// Vi gör om runParser också
const runParser = <T>(parser: Parser<T>, str: string) : Result<T> => {
return parser.fn(str)
}
// fungerar det fortfarande?
const matchB : Parser<string> = mchar("B")
const a1 = runParser(matchB, "BCD")
a1
// Verkar så!
// Då har vi kvar ett par combionators
const andThen = <T>(p1: Parser<T>, p2: Parser<T>): Parser<T[]> => {
return {
fn: (str: string): Result<T[]> => {
const result1: Result<T> = runParser(p1, str)
switch(result1.kind) {
case "success":
const result2 = runParser(p2, result1.val[1])
switch(result2.kind) {
case "success":
return {kind: "success", val: [[result1.val[0], result2.val[0]], result2.val[1]]}
case "failure":
return {kind: "failure", message: result2.message}
};
break;
case "failure":
return {kind: "failure", message: result1.message}
}
}
}
}
const orElse = <T>(p1: Parser<T>, p2: Parser<T>) : Parser<T> => {
return {
fn: (str: string): Result<T> => {
const result1: Result<T> = runParser(p1, str)
switch(result1.kind) {
case "success":
return result1
case "failure":
return runParser(p2, str)
}
}
}
}
// Fungerar dom?
const pA = mchar("A")
const pB = mchar("B")
const aOrB = orElse(pA, pB)
const a2 = runParser(aOrB, "AAA")
a2
const aThenB = andThen(pA, pB)
const a3 = runParser(aThenB, "AB")
a3
// Såja. Hur vore det om vi kunde få något annat ur en parser än en lista med bokstäver?
const map = <U, T>(f : (s: U) => T, p: Parser<U>): Parser<T> => {
return {
fn: (str: string): Result<T> => {
const result : Result<U> = runParser(p, str)
switch(result.kind) {
case "success":
const newValue = f(result.val[0])
return {kind: "success", val: [newValue, result.val[1]]}
case "failure":
return {kind: "failure", message: result.message}
}
}
}
}
const p1 = mchar("1")
const mapper = (s:string) => parseInt(s)
const a4 = runParser(map(mapper, p1), "12")
a4
// det verkar fungera!
// vill vi nu ha en funktion som kollar mot många olika saker så kan man göra
//något i stil med
const anyOf = <T>(list: Parser<T>[]) => list.reduce(orElse)
const p2 = mchar("2")
const p3 = mchar("3")
const p4 = mchar("4")
const p5 = mchar("5")
const p6 = mchar("6")
const p7 = mchar("7")
const p8 = mchar("8")
const p9 = mchar("9")
const p0 = mchar("0")
const numberP = anyOf([p1, p2, p3, p4, p5, p6, p7, p8, p9, p0])
const a5 = runParser(numberP, "034523")
a5
// cool men nu har vi ett problem.
// andThen tar två argument av samma typ. Det här går att fixa snyggare men
// ett alternativ är att använda map
const aOrBl = map(s => [s], aOrB)
// perfekt då kan vi koppla ihop den med andThen
const aThenBThenAorB = andThen(aThenB, aOrBl);
// problemet är nu att vi får en lista med listor, jaja vi löser med map igen!
const aThenBThenAorBf = map(s => [].concat(...s), aThenBThenAorB)
const a6 = runParser(aThenBThenAorBf, "ABBA")
a6
// nu när vi har alla trick så kan vi göra nått roligare
const numberI = map(c => parseInt(c), numberP)
const a7 = runParser(numberI, "1")
a7
// vi skulle vilja kunna ta en lista med parsers lite som anyOf fast en sekvens
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment