diff --git a/compile.ts b/compile.ts index c6731e0..6212d10 100644 --- a/compile.ts +++ b/compile.ts @@ -12,407 +12,58 @@ import {Omit, Overwrite, unique} from './utility' import {Ref, GraphOf, link} from './graph' -import {Token, TokenStream, ParseError, lex, showToken} from './lex' - -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -// AST // -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -// -// Declarations -// - -type DeclareStruct = { - declare: "struct", - name: Token, - generics: Generic[], - fields: { - name: Token, - type: Type, - }[], -} - -type DeclareEnum = { - declare: "enum", - name: Token, - generics: Generic[], - variants: { - name: Token, - type: Type | null, - }[], -} - -type DeclareFunction = { - declare: "function", - name: Token, - generics: Generic[], - effects: Token[], // TODO: more-complex effects? - arguments: { name: Token, type: Type }[], - returns: Type | null, - body: Block, -} - -type DeclareInterface = { - declare: "interface", - name: Token, - parents: Token[], - methods: {name: Token, type: FunctionType}[], -} - -type DeclareInstance = { - declare: "instance", - interface: Token, - type: NamedType, - generics: Generic[], - methods: DeclareFunction[], -} - -type DeclareEffect = { - // TODO: generic effects - declare: "effect", - actions: {name: Token, type: FunctionType}[], -} - -/* -service Count(initial: Integer) for Counter -> Self { - var value: Integer = initial; - yield return var result => { - return result; - } when case get!() => { - continue value; - } when case inc!() => { - value = value + 1; - continue; - } when case incBy!(var n: Integer) => { - value = value + n; - continue; - } -} -*/ - -type DeclareService = { - declare: "service", - name: Token, - effects: Token[], - arguments: {name: Token, type: Type}[], - returns: Type | null, - body: Block, // allows the 'yield' statement once -} - -type Declare = DeclareStruct | DeclareEnum | DeclareFunction | DeclareInterface | DeclareInstance | DeclareEffect | DeclareService; - -// -// Types -// - -type Generic = { - name: Token, - constraints: Token[], -} - -type NamedType = { - type: "named", - name: Token, - parameters: Type[], -}; -type FunctionType = { - type: "function", - funcToken: Token, - generics: Generic[], - effects: Token[], - arguments: Type[], - returns: Type | null, -} - -// TODO: consider first-class services - -type NeverType = { - type: "never", - never: Token, -} +import {Token, TokenStream, ParseError, lex, showToken, TokenSelector, selectsToken} from './lex' + +import { + DeclareStruct, + DeclareEnum, + DeclareFunction, + DeclareInterface, + DeclareInstance, + DeclareEffect, + DeclareService, + Declare, + + Generic, + NamedType, + FunctionType, + NeverType, + SelfType, + Type, + + VariableStatement, + AssignStatement, + IfStatement, + WhileStatement, + ExpressionStatement, + ReturnStatement, + BreakStatement, + ContinueStatement, + YieldStatement, + SwitchStatement, + Statement, + Block, + + IntegerExpression, + StringExpression, + VariableExpression, + DotExpression, + CallExpression, + ServiceExpression, + ObjectExpression, + ArrayExpression, + OperatorExpression, + PrefixExpression, + FunctionExpression, + Expression, +} from './initial_ast'; + +import { + ParserFor, + pure, + matched, +} from './parsing'; -type SelfType = { - type: "self", - self: Token, -} - -type Type = NamedType | FunctionType | NeverType | SelfType; - -// -// Effects -// - -// TODO - -// -// Statements -// - -type VariableStatement = {statement: "var", at: Token, name: Token, type: Type, expression: Expression} // TODO: optional initialization -type AssignStatement = {statement: "assign", at: Token, lhs: Expression, rhs: Expression}; -type IfStatement = {statement: "if", at: Token, condition: Expression, thenBlock: Block, elseBlock: Block | null}; -type WhileStatement = {statement: "while", at: Token, condition: Expression, bodyBlock: Block}; -// TODO: For Statement -type ExpressionStatement = {statement: "expression", at: Token, expression: Expression}; // used for effectful -type ReturnStatement = {statement: "return", at: Token, expression: Expression | null}; -type BreakStatement = {statement: "break", at: Token}; -type ContinueStatement = {statement: "continue", at: Token}; -type YieldStatement = { - statement: "yield", - at: Token, - returns: {result: Token, type: Type, block: Block}, // type must be Self - actions: {parameters: {name: Token, type: Type}[], block: Block}, -} -type SwitchStatement = { - statement: "switch", - at: Token, - expression: Expression, - branches: { - pattern: {name: Token, variable: {name: Token, type: Type} | null}, - block: Block[], - }[], -} -type Statement = VariableStatement | AssignStatement | ExpressionStatement | IfStatement | WhileStatement | ReturnStatement | BreakStatement | ContinueStatement | YieldStatement | SwitchStatement; - -type Block = { - at: Token, - body: Statement[], -} - -// -// Expressions -// - -type IntegerExpression = {expression: "integer", at: Token, token: Token}; -type StringExpression = {expression: "string", at: Token, token: Token}; -type VariableExpression = {expression: "variable", at: Token, variable: Token}; -type DotExpression = {expression: "dot", at: Token, object: Expression, field: Token}; -type CallExpression = {expression: "call", at: Token, hasEffect: boolean, function: Expression, arguments: Expression[]}; -type ServiceExpression = {expression: "service", at: Token, service: Token, arguments: Expression[], body: Expression}; // discharges or reinterprets effects -type ObjectExpression = {expression: "object", at: Token, name: Token, fields: {name: Token, value: Expression}[]}; -type ArrayExpression = {expression: "array", at: Token, name: Token | null, items: Expression[]}; -// TODO: map expression -type OperatorExpression = {expression: "operator", at: Token, operator: Token, left: Expression, right: Expression}; -type PrefixExpression = {expression: "prefix", at: Token, operator: Token, right: Expression}; -// TODO: briefer lambdas + void -type FunctionExpression = {expression: "function", at: Token, generics: Generic[], arguments: {name: Token, type: Type}[], returns: Type, body: Block} - -type Expression = IntegerExpression | StringExpression | VariableExpression | DotExpression | CallExpression | OperatorExpression | PrefixExpression | ServiceExpression | ObjectExpression | ArrayExpression | FunctionExpression; - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Parse // -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type Maker = To | ((x: From) => To); - -type TokenSelector = string; - -function selectsToken(selector: string, token: Token): boolean { - if (selector.charAt(0) == "$") { - return token.type == selector.substr(1); - } else { - return token.text == selector; - } -} - -class ParserFor { - constructor(public run: (stream: TokenStream) => {result: T, rest: TokenStream}) {} - then(other: Maker>): ParserFor { - return new ParserFor(stream => { - let first = this.run(stream); - let second = (typeof other == "function" ? other(first.result) as any : other).run(first.rest); - if (first.result instanceof Array) { - if (Object.keys(second.result).length == 0) { - return {result: first.result, rest: second.rest}; - } - throw {message: "bad - combining array with object", first, second}; - } - if (second.result instanceof Array) { - if (Object.keys(first.result).length == 0) { - return {result: second.result, rest: second.rest}; - } - throw {message: "bad - combining array with object", first, second}; - } - return { - result: Object.assign({}, first.result, second.result), - rest: second.rest, - }; - }); - } - thenInstead(other: (result: T) => ParserFor): ParserFor { - return new ParserFor(stream => { - let first = this.run(stream); - return other(first.result).run(first.rest); - }); - } - thenIn

(otherMap: {[k in keyof P]: Maker>}): ParserFor { - if (Object.keys(otherMap).length != 1) { - throw {message: "thenIn given map with more or less than one key", otherMap}; - } - let key: keyof typeof otherMap = Object.keys(otherMap)[0] as any; - let other = otherMap[key]; - return new ParserFor(stream => { - let first = this.run(stream); - let second = (typeof other == "function" ? other(first.result) : other).run(first.rest); - return { - result: Object.assign({}, first.result, {[key]: second.result}) as any, - rest: second.rest, - }; - }); - } - thenToken

(map: P, fail: Maker): ParserFor { - let keys: (keyof P)[] = Object.keys(map) as (keyof P)[]; // QUESTION: why isn't this the inferred type? - if (keys.length != 1) { - throw "bad thenToken call"; - } - let key: keyof P = keys[0]; - let pattern = map[key]; - return this.thenWhen({ - [pattern as string]: (token: Token) => pure<{[k in keyof P]: Token}>({[key]: token} as any), // QUESTION: why does this need a cast? - }, typeof fail == "function" ? ((x: T) => ParserFor.fail(fail(x))) : ParserFor.fail(fail)); - } - manyBetween(between: TokenSelector): ParserFor { - return new ParserFor((stream): {result: T[], rest: TokenStream} => { - let first = this.run(stream); - let current = first.rest; - let pieces = [first.result]; - while (true) { - if (!selectsToken(between, current.head())) { - return {result: pieces, rest: current}; - } - current = current.tail(); // skip the separating token - let next = this.run(current); - pieces.push(next.result); - current = next.rest; - } - }); - } - manyUntil(finish: TokenSelector): ParserFor { - return new ParserFor(stream => { - let current = stream; - let result: T[] = []; - while (true) { - if (selectsToken(finish, current.head())) { - return {result, rest: current}; - } - let piece = this.run(current); - result.push(piece.result); - current = piece.rest; - } - }) - } - manyWhen(leads: TokenSelector[]): ParserFor<{lead: Token, item: T}[]> { - return new ParserFor(stream => { - let current = stream; - let result: {lead: Token, item: T}[] = []; - while (true) { - let found = false; - for (let lead of leads) { - if (selectsToken(lead, current.head())) { - let pieceResult = this.run(current.tail()); - result.push({item: pieceResult.result, lead: current.head()}); - current = pieceResult.rest; - found = true; - break; - } - } - if (!found) { - break; - } - } - return {result, rest: current}; - }); - } - map(f: (x: T) => S): ParserFor { - return new ParserFor(stream => { - let okay = this.run(stream); - return {result: f(okay.result), rest: okay.rest}; - }); - } - merge(m: M): ParserFor { - return this.map(v => { - return Object.assign({}, v, m); - }); - } - static fail(message: string | ((t: Token) => string)): ParserFor { - let messageMaker = typeof message !== "string" ? message : (token: Token) => message + " near " + showToken(token); - return new ParserFor(stream => {throw new ParseError(messageMaker(stream.head()))}); - } - context(message: string): ParserFor { - return new ParserFor(stream => { - try { - return this.run(stream); - } catch (e) { - if (e instanceof ParseError) { - throw new ParseError(message + ("\n" + e.message).replace(/\n/g, "\n\t")); - } - throw e; - } - }); - } - thenWhen(m: {[K in keyof M]: Maker>}, otherwise: Maker>): ParserFor { - if (typeof otherwise == "function") { - return this.then(res => ParserFor.when(m, otherwise(res))); - } else { - return this.then(ParserFor.when(m, otherwise)); - } - } - static when(m: {[K in keyof M]: Maker>}, otherwise: ParserFor): ParserFor { - return new ParserFor((stream: TokenStream): {result: M[keyof M] | E, rest: TokenStream} => { - for (let k in m) { - let p = m[k]; - if (k.charAt(0) == "$") { - if (stream.head().type == k.substr(1)) { - if (typeof p === "function") { - const intermediate = p(stream.head()); - return intermediate.run(stream.tail()); - } else { - return p.run(stream.tail()); - } - } - } else { - if (stream.head().text == k) { - if (typeof p === "function") { - let parser: ParserFor = p(stream.head()); - return parser.run(stream.tail()); - } else { - return p.run(stream.tail()); - } - } - } - } - return otherwise.run(stream); - }); - } -} - -function pure(t: T): ParserFor { - return new ParserFor(stream => { - return {result: t, rest: stream}; - }); -} - -function matched(parser: ParserFor): (open: Token) => ParserFor { - const matching = (open: Token): string => { - if (open.text == "(") { - return ")"; - } - if (open.text == "[") { - return "]"; - } - if (open.text == "{") { - return "}"; - } - throw {message: "invalid opening brace", open}; - }; - return (open: Token) => parser.thenWhen({ [matching(open)]: pure({})}, ParserFor.fail(`expected '${matching(open)}' to close '${open.text}' opened at ${showToken(open)}`)); -} const parseType: ParserFor = new ParserFor(_ => null as any); diff --git a/initial_ast.ts b/initial_ast.ts new file mode 100644 index 0000000..245f641 --- /dev/null +++ b/initial_ast.ts @@ -0,0 +1,228 @@ + + +import { Token } from './lex'; + +// +// Declarations +// + +type DeclareStruct = { + declare: "struct", + name: Token, + generics: Generic[], + fields: { + name: Token, + type: Type, + }[], +} + +type DeclareEnum = { + declare: "enum", + name: Token, + generics: Generic[], + variants: { + name: Token, + type: Type | null, + }[], +} + +type DeclareFunction = { + declare: "function", + name: Token, + generics: Generic[], + effects: Token[], // TODO: more-complex effects? + arguments: { name: Token, type: Type }[], + returns: Type | null, + body: Block, +} + +type DeclareInterface = { + declare: "interface", + name: Token, + parents: Token[], + methods: {name: Token, type: FunctionType}[], +} + +type DeclareInstance = { + declare: "instance", + interface: Token, + type: NamedType, + generics: Generic[], + methods: DeclareFunction[], +} + +type DeclareEffect = { + // TODO: generic effects + declare: "effect", + actions: {name: Token, type: FunctionType}[], +} + +/* +service Count(initial: Integer) for Counter -> Self { + var value: Integer = initial; + yield return var result => { + return result; + } when case get!() => { + continue value; + } when case inc!() => { + value = value + 1; + continue; + } when case incBy!(var n: Integer) => { + value = value + n; + continue; + } +} +*/ + +type DeclareService = { + declare: "service", + name: Token, + effects: Token[], + arguments: {name: Token, type: Type}[], + returns: Type | null, + body: Block, // allows the 'yield' statement once +} + +type Declare = DeclareStruct | DeclareEnum | DeclareFunction | DeclareInterface | DeclareInstance | DeclareEffect | DeclareService; + +// +// Types +// + +type Generic = { + name: Token, + constraints: Token[], +} + +type NamedType = { + type: "named", + name: Token, + parameters: Type[], +}; +type FunctionType = { + type: "function", + funcToken: Token, + generics: Generic[], + effects: Token[], + arguments: Type[], + returns: Type | null, +} + +// TODO: consider first-class services + +type NeverType = { + type: "never", + never: Token, +} + +type SelfType = { + type: "self", + self: Token, +} + +type Type = NamedType | FunctionType | NeverType | SelfType; + +// +// Effects +// + +// TODO + +// +// Statements +// + +type VariableStatement = {statement: "var", at: Token, name: Token, type: Type, expression: Expression} // TODO: optional initialization +type AssignStatement = {statement: "assign", at: Token, lhs: Expression, rhs: Expression}; +type IfStatement = {statement: "if", at: Token, condition: Expression, thenBlock: Block, elseBlock: Block | null}; +type WhileStatement = {statement: "while", at: Token, condition: Expression, bodyBlock: Block}; +// TODO: For Statement +type ExpressionStatement = {statement: "expression", at: Token, expression: Expression}; // used for effectful +type ReturnStatement = {statement: "return", at: Token, expression: Expression | null}; +type BreakStatement = {statement: "break", at: Token}; +type ContinueStatement = {statement: "continue", at: Token}; +type YieldStatement = { + statement: "yield", + at: Token, + returns: {result: Token, type: Type, block: Block}, // type must be Self + actions: {parameters: {name: Token, type: Type}[], block: Block}, +} +type SwitchStatement = { + statement: "switch", + at: Token, + expression: Expression, + branches: { + pattern: {name: Token, variable: {name: Token, type: Type} | null}, + block: Block[], + }[], +} +type Statement = VariableStatement | AssignStatement | ExpressionStatement | IfStatement | WhileStatement | ReturnStatement | BreakStatement | ContinueStatement | YieldStatement | SwitchStatement; + +type Block = { + at: Token, + body: Statement[], +} + +// +// Expressions +// + +type IntegerExpression = {expression: "integer", at: Token, token: Token}; +type StringExpression = {expression: "string", at: Token, token: Token}; +type VariableExpression = {expression: "variable", at: Token, variable: Token}; +type DotExpression = {expression: "dot", at: Token, object: Expression, field: Token}; +type CallExpression = {expression: "call", at: Token, hasEffect: boolean, function: Expression, arguments: Expression[]}; +type ServiceExpression = {expression: "service", at: Token, service: Token, arguments: Expression[], body: Expression}; // discharges or reinterprets effects +type ObjectExpression = {expression: "object", at: Token, name: Token, fields: {name: Token, value: Expression}[]}; +type ArrayExpression = {expression: "array", at: Token, name: Token | null, items: Expression[]}; +// TODO: map expression +type OperatorExpression = {expression: "operator", at: Token, operator: Token, left: Expression, right: Expression}; +type PrefixExpression = {expression: "prefix", at: Token, operator: Token, right: Expression}; +// TODO: briefer lambdas + void +type FunctionExpression = {expression: "function", at: Token, generics: Generic[], arguments: {name: Token, type: Type}[], returns: Type, body: Block} + +type Expression = IntegerExpression | StringExpression | VariableExpression | DotExpression | CallExpression | OperatorExpression | PrefixExpression | ServiceExpression | ObjectExpression | ArrayExpression | FunctionExpression; + +export { + DeclareStruct, + DeclareEnum, + DeclareFunction, + DeclareInterface, + DeclareInstance, + DeclareEffect, + DeclareService, + Declare, + + Generic, + NamedType, + FunctionType, + NeverType, + SelfType, + Type, + + VariableStatement, + AssignStatement, + IfStatement, + WhileStatement, + ExpressionStatement, + ReturnStatement, + BreakStatement, + ContinueStatement, + YieldStatement, + SwitchStatement, + Statement, + Block, + + IntegerExpression, + StringExpression, + VariableExpression, + DotExpression, + CallExpression, + ServiceExpression, + ObjectExpression, + ArrayExpression, + OperatorExpression, + PrefixExpression, + FunctionExpression, + Expression, +}; diff --git a/lex.ts b/lex.ts index 43ac425..315e439 100644 --- a/lex.ts +++ b/lex.ts @@ -86,10 +86,22 @@ class TokenStream { } } +type TokenSelector = string; + +function selectsToken(selector: string, token: Token): boolean { + if (selector.charAt(0) == "$") { + return token.type == selector.substr(1); + } else { + return token.text == selector; + } +} + export { Token, TokenStream, lex, showToken, ParseError, + TokenSelector, + selectsToken, } diff --git a/parsing.ts b/parsing.ts new file mode 100644 index 0000000..0d1b9cf --- /dev/null +++ b/parsing.ts @@ -0,0 +1,211 @@ + +import { + Token, + TokenStream, + TokenSelector, + selectsToken, + showToken, + ParseError, +} from './lex'; + + +type Maker = To | ((x: From) => To); + +class ParserFor { + constructor(public run: (stream: TokenStream) => {result: T, rest: TokenStream}) {} + then(other: Maker>): ParserFor { + return new ParserFor(stream => { + let first = this.run(stream); + let second = (typeof other == "function" ? other(first.result) as any : other).run(first.rest); + if (first.result instanceof Array) { + if (Object.keys(second.result).length == 0) { + return {result: first.result, rest: second.rest}; + } + throw {message: "bad - combining array with object", first, second}; + } + if (second.result instanceof Array) { + if (Object.keys(first.result).length == 0) { + return {result: second.result, rest: second.rest}; + } + throw {message: "bad - combining array with object", first, second}; + } + return { + result: Object.assign({}, first.result, second.result), + rest: second.rest, + }; + }); + } + thenInstead(other: (result: T) => ParserFor): ParserFor { + return new ParserFor(stream => { + let first = this.run(stream); + return other(first.result).run(first.rest); + }); + } + thenIn

(otherMap: {[k in keyof P]: Maker>}): ParserFor { + if (Object.keys(otherMap).length != 1) { + throw {message: "thenIn given map with more or less than one key", otherMap}; + } + let key: keyof typeof otherMap = Object.keys(otherMap)[0] as any; + let other = otherMap[key]; + return new ParserFor(stream => { + let first = this.run(stream); + let second = (typeof other == "function" ? other(first.result) : other).run(first.rest); + return { + result: Object.assign({}, first.result, {[key]: second.result}) as any, + rest: second.rest, + }; + }); + } + thenToken

(map: P, fail: Maker): ParserFor { + let keys: (keyof P)[] = Object.keys(map) as (keyof P)[]; // QUESTION: why isn't this the inferred type? + if (keys.length != 1) { + throw "bad thenToken call"; + } + let key: keyof P = keys[0]; + let pattern = map[key]; + return this.thenWhen({ + [pattern as string]: (token: Token) => pure<{[k in keyof P]: Token}>({[key]: token} as any), // QUESTION: why does this need a cast? + }, typeof fail == "function" ? ((x: T) => ParserFor.fail(fail(x))) : ParserFor.fail(fail)); + } + manyBetween(between: TokenSelector): ParserFor { + return new ParserFor((stream): {result: T[], rest: TokenStream} => { + let first = this.run(stream); + let current = first.rest; + let pieces = [first.result]; + while (true) { + if (!selectsToken(between, current.head())) { + return {result: pieces, rest: current}; + } + current = current.tail(); // skip the separating token + let next = this.run(current); + pieces.push(next.result); + current = next.rest; + } + }); + } + manyUntil(finish: TokenSelector): ParserFor { + return new ParserFor(stream => { + let current = stream; + let result: T[] = []; + while (true) { + if (selectsToken(finish, current.head())) { + return {result, rest: current}; + } + let piece = this.run(current); + result.push(piece.result); + current = piece.rest; + } + }) + } + manyWhen(leads: TokenSelector[]): ParserFor<{lead: Token, item: T}[]> { + return new ParserFor(stream => { + let current = stream; + let result: {lead: Token, item: T}[] = []; + while (true) { + let found = false; + for (let lead of leads) { + if (selectsToken(lead, current.head())) { + let pieceResult = this.run(current.tail()); + result.push({item: pieceResult.result, lead: current.head()}); + current = pieceResult.rest; + found = true; + break; + } + } + if (!found) { + break; + } + } + return {result, rest: current}; + }); + } + map(f: (x: T) => S): ParserFor { + return new ParserFor(stream => { + let okay = this.run(stream); + return {result: f(okay.result), rest: okay.rest}; + }); + } + merge(m: M): ParserFor { + return this.map(v => { + return Object.assign({}, v, m); + }); + } + static fail(message: string | ((t: Token) => string)): ParserFor { + let messageMaker = typeof message !== "string" ? message : (token: Token) => message + " near " + showToken(token); + return new ParserFor(stream => {throw new ParseError(messageMaker(stream.head()))}); + } + context(message: string): ParserFor { + return new ParserFor(stream => { + try { + return this.run(stream); + } catch (e) { + if (e instanceof ParseError) { + throw new ParseError(message + ("\n" + e.message).replace(/\n/g, "\n\t")); + } + throw e; + } + }); + } + thenWhen(m: {[K in keyof M]: Maker>}, otherwise: Maker>): ParserFor { + if (typeof otherwise == "function") { + return this.then(res => ParserFor.when(m, otherwise(res))); + } else { + return this.then(ParserFor.when(m, otherwise)); + } + } + static when(m: {[K in keyof M]: Maker>}, otherwise: ParserFor): ParserFor { + return new ParserFor((stream: TokenStream): {result: M[keyof M] | E, rest: TokenStream} => { + for (let k in m) { + let p = m[k]; + if (k.charAt(0) == "$") { + if (stream.head().type == k.substr(1)) { + if (typeof p === "function") { + const intermediate = p(stream.head()); + return intermediate.run(stream.tail()); + } else { + return p.run(stream.tail()); + } + } + } else { + if (stream.head().text == k) { + if (typeof p === "function") { + let parser: ParserFor = p(stream.head()); + return parser.run(stream.tail()); + } else { + return p.run(stream.tail()); + } + } + } + } + return otherwise.run(stream); + }); + } +} + +function pure(t: T): ParserFor { + return new ParserFor(stream => { + return {result: t, rest: stream}; + }); +} + +function matched(parser: ParserFor): (open: Token) => ParserFor { + const matching = (open: Token): string => { + if (open.text == "(") { + return ")"; + } + if (open.text == "[") { + return "]"; + } + if (open.text == "{") { + return "}"; + } + throw {message: "invalid opening brace", open}; + }; + return (open: Token) => parser.thenWhen({ [matching(open)]: pure({})}, ParserFor.fail(`expected '${matching(open)}' to close '${open.text}' opened at ${showToken(open)}`)); +} + +export { + ParserFor, + pure, + matched, +}; \ No newline at end of file