Skip to content

Commit

Permalink
[lang2] loop
Browse files Browse the repository at this point in the history
  • Loading branch information
xieyuheng committed Apr 15, 2024
1 parent 2f62119 commit 17f2858
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 20 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Like lang0 but with JS-like syntax.
[lang2] `sequence`
[lang2] `loopUntil`
[lang2] parser combinators

# lang0
Expand Down
43 changes: 24 additions & 19 deletions src/lang2/parser/Parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from "node:assert"
import { test } from "node:test"
import { createLexer } from "./Lexer.js"
import { choose, type ParserResult, type Token } from "./index.js"
import { choose, loop, type ParserResult, type Token } from "./index.js"

type Sexp = string | Array<Sexp>

Expand All @@ -22,32 +22,37 @@ function parseSymbol(tokens: Array<Token>): ParserResult<string> {
return [token.value, tokens.slice(1)]
}

function parseList(tokens: Array<Token>): ParserResult<Array<Sexp>> {
function parseOpenParenthesis(tokens: Array<Token>): ParserResult<undefined> {
const token = tokens[0]
if (token === undefined) {
throw new Error("[parseList] 1")
throw new Error("[parseOpenParenthesis] 1")
}

if (token.label !== "symbol" || token.value !== "(") {
throw new Error("[parseList] 2")
throw new Error("[parseOpenParenthesis] 2")
}

tokens = tokens.slice(1)
const list: Array<Sexp> = []
while (true) {
const token = tokens[0]
if (token === undefined) {
throw new Error("[parseList] 3")
}

if (token.label === "symbol" && token.value === ")") {
return [list, tokens.slice(1)]
}

const [sexp, remain] = parseSexp(tokens)
tokens = remain
list.push(sexp)
return [undefined, tokens.slice(1)]
}

function parseEndParenthesis(tokens: Array<Token>): ParserResult<undefined> {
const token = tokens[0]
if (token === undefined) {
throw new Error("[parseEndParenthesis] 1")
}

if (token.label !== "symbol" || token.value !== ")") {
throw new Error("[parseEndParenthesis] 2")
}

return [undefined, tokens.slice(1)]
}

function parseList(tokens: Array<Token>): ParserResult<Array<Sexp>> {
return loop(parseSexp, {
start: parseOpenParenthesis,
end: parseEndParenthesis,
})(tokens)
}

const lexer = createLexer({
Expand Down
27 changes: 27 additions & 0 deletions src/lang2/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,30 @@ export function choose<A>(parsers: Array<Parser<A>>): Parser<A> {
throw new Error(`[choose]`)
}
}

export function loop<A>(
parser: Parser<A>,
options: {
start?: Parser<any>
end: Parser<any>
},
): Parser<Array<A>> {
return (tokens) => {
const list: Array<A> = []
if (options.start) {
const [_, remain] = options.start(tokens)
tokens = remain
}

while (true) {
try {
const [_, remain] = options.end(tokens)
return [list, remain]
} catch (_error) {
const [element, remain] = parser(tokens)
list.push(element)
tokens = remain
}
}
}
}

0 comments on commit 17f2858

Please sign in to comment.