Skip to content

Instantly share code, notes, and snippets.

@ya-s-u
Created March 3, 2022 14:14
Show Gist options
  • Save ya-s-u/cdc9f40c6232acd9038ab4a2bc0ed97c to your computer and use it in GitHub Desktop.
Save ya-s-u/cdc9f40c6232acd9038ab4a2bc0ed97c to your computer and use it in GitHub Desktop.
パーサーコンビネータ
interface Done<T> {
type: 'done';
result: T;
tail: string;
};
interface Failed {
type: 'failed';
};
type Reply<T> = Done<T> | Failed
interface Parser<T> {
parse: (input: string) => Reply<T>
}
// 文字列
const token = (token: string): Parser<string> => ({
parse: (input: string) => {
if (input.startsWith(token)) {
return {
type: 'done',
result: token,
tail: input.substring(token.length)
}
} else {
return {
type: 'failed'
}
}
}
})
const hogeParser = token('hoge');
// hogeParser.parse('hogehoge')
// {
// "type": "done",
// "result": "hoge",
// "tail": "hoge"
// }
// hogeParser.parse('fugafuga')
// {
// "type": "failed"
// }
// 繰り返し
const many = (parser: Parser<string>): Parser<string[]> => ({
parse: (input: string) => {
const results: string[] = [];
var tail = input;
loop: {
while (true) {
const reply = parser.parse(tail)
switch (reply.type) {
case 'done':
results.push(reply.result)
tail = reply.tail
break
case 'failed':
break loop
}
}
}
return {
type: 'done',
result: results,
tail
}
}
})
// many(hogeParser).parse('hogehoge')
// {
// "type": "done",
// "result": [
// "hoge",
// "hoge"
// ],
// "tail": ""
// }
// many(hogeParser).parse('hogehogefuga')
// {
// "type": "done",
// "result": [
// "hoge",
// "hoge"
// ],
// "tail": "fuga"
// }
// 選択
const choice = (parser1: Parser<string>, parser2: Parser<string>): Parser<string> => ({
parse: (input: string) => {
const result1 = parser1.parse(input)
const result2 = parser2.parse(input)
if (result1.type === 'done') {
return result1
} else if (result2.type === 'done') {
return result2
} else {
return {
type: 'failed'
}
}
}
})
const fugaParser = token('fuga')
// choice(hogeParser, fugaParser).parse('hoge')
// {
// "type": "done",
// "result": "hoge",
// "tail": ""
// }
// choice(hogeParser, fugaParser).parse('fuga')
// {
// "type": "done",
// "result": "fuga",
// "tail": ""
// }
// choice(hogeParser, fugaParser).parse('piyo')
// {
// "type": "failed"
// }
// 加工
const map = (parser: Parser<string>, transform: (from: string) => string): Parser<string> => ({
parse: (input: string) => {
const result = parser.parse(input)
switch (result.type) {
case 'done':
return {
type: 'done',
result: transform(result.result),
tail: result.tail
}
case 'failed':
return result
}
}
})
const describeParser = map(hogeParser, (from: string) => `${from}パースした`)
// describeParser.parse('hoge')
// {
// "type": "done",
// "result": "hogeパースした",
// "tail": ""
// }
// 連結
const seq = (parsers: Parser<string>[]): Parser<string[]> => ({
parse: (input: string) => {
const results: string[] = [];
var tail = input;
parsers.forEach(parser => {
const result = parser.parse(tail)
switch (result.type) {
case 'done':
results.push(result.result);
tail = result.tail;
break
case 'failed':
return result;
}
})
return {
type: 'done',
result: results,
tail
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment