diff --git a/example/bancha/bancha.html b/example/bancha/bancha.html index ca1edbb..8653293 100644 --- a/example/bancha/bancha.html +++ b/example/bancha/bancha.html @@ -1,295 +1,295 @@ - - - - - - - - -

BanchaScript Playground

-
- - - - - - - -
- -
-
Compiler Messages:
- -
Compiled Source Text:
- -
Output:
- -
- - - - - - + + + + + + + + +

BanchaScript Playground

+
+ + + + + + + +
+ +
+
Compiler Messages:
+ +
Compiled Source Text:
+ +
Output:
+ +
+ + + + + + \ No newline at end of file diff --git a/example/bancha/bancha.ts b/example/bancha/bancha.ts index 681c19e..e6e91d5 100644 --- a/example/bancha/bancha.ts +++ b/example/bancha/bancha.ts @@ -1,279 +1,279 @@ -/// - -// Sample program: BanchaScript compiler -// -// BanchaScript is a AltJS, a programming language compiled into JavaScript source code. -// See bancha.html for sample soruce codes. -// - -module bancha { - import p = Parsect; - - // Lexer. 字句解析器 - var identStart = p.oneOf("_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - var identLetter = p.oneOf("_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); - var opStart = p.oneOf("+-*/=!$%&^~@?_><:|\\."); - var opLetter = p.oneOf("+-*/=!$%&^~@?_><:|\\."); - var lexer: p.GenTokenParser = p.makeTokenParser({ - commentStart: p.string('/*'), - commentEnd: p.string('*/'), - commentLine: p.string('//'), - nestedComments: true, - identStart: identStart, - identLetter: identLetter, - opStart: opStart, - opLetter: opLetter, - reservedNames: ["function", "return", "operator", "infix", "infixl", "infixr", "prefix", "postfix", "var", "if", "else", "for", "native"], - reservedOpNames: [], - caseSensitive: true - }); - - export class Scope { - - operators: p.OperatorTable[] = []; - - // 演算子の関数化のためのテーブル - binaryOps: { [symbol: string]: (x: string, y: string) => string } = {}; - unaryOps: { [symbol: string]: (x: string ) => string } = {}; - - constructor(){ - this.operators[0] = new p.OperatorTable(); - - // 関数の中置記法 - this.operators[0].infix.push( - lexer.lexeme(p.seq(s=>{ - s(p.string("`")); // バッククォートの後ろに空白を許可していない - var ops = s(identStart); - var opl = s(p.many(identLetter)); - s(p.string("`")); - return (x: string, y: string)=> "(" + ops + opl.join('') + "(" + x + "," + y + "))"; - })) - ); - } - } - - export function compile(scope: Scope, source: string): p.Reply { - - // mutable - var _expression: p.Parser = null; - - var expression: p.Parser = p.lazy((): p.Parser => { - if( ! _expression){ - var _table = Object.keys(scope.operators).sort().map(k => scope.operators[k]); - _expression = p.makeExpressionParser(_table, term); - } - return _expression; - }); - - var term: p.Parser = p.label("term", p.seq((s: p.Context): string =>{ - - var arrowFunctionArgs = p.or( - p.fmap((x: string): string[] => [x], lexer.identifier), - lexer.parens(p.sepBy(lexer.identifier, lexer.comma)) - ); - var arrowFunctionBody = p.or( - p.fmap((xs: string[]): string => "{" + xs.join('') + "}", lexer.braces(p.many(statement))), - expression - ); - var arrowFunction: p.Parser = p.seq((s: p.Context): string =>{ - var args: string[] = s(arrowFunctionArgs); - s(lexer.symbol("=>")); - var e: string = s(arrowFunctionBody); - return s.success && "(function(" + args.join(',') + "){return " + e + "})"; - }); - - var nativeDirective: p.Parser = p.seq((s: p.Context): string =>{ - s(lexer.reserved("native")); - return s(lexer.stringLiteral); - }); - - var functionalOperator: p.Parser = p.fmap((op: string): string =>{ - if(scope.binaryOps[op]) return '(function(x,y){return ' + scope.binaryOps[op]("x", "y") + '})'; - else if(scope.unaryOps [op]) return '(function(x){return ' + scope.unaryOps[op]("x") + '})'; - else throw new Error("Unknown operator: " + op); - }, lexer.parens(lexer.operator)); - - var arrayLiteral: p.Parser = p.fmap((xs: string[]): string => "[" + xs.join(',') + "]", lexer.brackets(p.sepBy(expression, lexer.comma))); - - var rightSection: p.Parser = p.seq((s: p.Context): string =>{ - s(lexer.symbol("(")); - var op = s(lexer.operator); - var e = s(simpleExpressionParser); - s(lexer.symbol(")")); - if(scope.binaryOps[op]){ - return "(function(x){return " + scope.binaryOps[op]("x", e) + "})"; - } - }); - - var leftSection: p.Parser = p.seq((s: p.Context): string =>{ - s(lexer.symbol("(")); - var e = s(simpleExpressionParser); - var op = s(lexer.operator); - s(lexer.symbol(")")); - if(scope.binaryOps[op]){ - return "(function(x){return " + scope.binaryOps[op](e, "x") + "})"; - } - }); - - var simpleExpressionParser: p.Parser = p.or( - p.triable(functionalOperator), // `演算子` - p.triable(arrowFunction), // アロー関数式 - arrayLiteral, // 配列リテラル - p.triable(rightSection), - p.triable(leftSection), - p.fmap(x => '(' + x + ')', lexer.parens(expression)), // ( 式 ) - p.fmap(x => '"' + x + '"', lexer.stringLiteral), // 文字列リテラル - p.fmap(x => x.toString(), lexer.naturalOrFloat), // 数値リテラル - nativeDirective, // native ディレクティブ - lexer.identifier // 識別子 - ); - - - var simpleExpression: string = s(simpleExpressionParser); - - // Function application syntax parser. 関数適用 - var functionApplication: p.Parser = p.fmap( - (args: string[]): string => { - if(args.every(x => !!x)){ - // 通常の関数適用 - return simpleExpression + "(" + args.join(',') + ")" - }else{ - // 関数の部分適用 - var params: string[] = []; - var remains: string[] = []; - for(var i = 0; i < args.length; i++){ - if(args[i] === null){ - var v = String.fromCharCode(97 + i).toString(); - remains.push(v); - params.push(v); - }else{ - params.push(args[i]); - } - } - return "(function(fn){return function(" + remains.join(',') + "){return fn(" + params.join(',') + ")}}(" + simpleExpression + "))"; - } - }, - lexer.parens(p.sepBy(p.option(null, expression), lexer.comma)) - ); // 関数呼び出し - - return s(p.option(simpleExpression, functionApplication)); - })); - - var exprStatement: p.Parser = p.fmap((e: string): string => e + ";", p.head(expression, lexer.semi)); - - var varExpression: p.Parser = p.seq((s: p.Context): string => { - s(lexer.reserved("var")); - var name: string = s(lexer.identifier); - s(lexer.symbol("=")); - var e: string = s(expression); - return "var " + name + "=" + e; - }); - - - var varStatement: p.Parser = p.seq((s: p.Context): string => { - var e: string = s(varExpression); - s(lexer.semi); - return e + ";"; - }); - - var returnStatement: p.Parser = p.fmap((e: string): string => "return "+e+";", p.between(lexer.reserved("return"), expression, lexer.semi)); - - var operatorStatement: p.Parser = p.seq((s: p.Context): string =>{ - s(lexer.reserved("operator")); - var op: string = s(lexer.operator); - var type = s(p.choice(["infixl", "infixr", "infix", "prefix", "postfix"].map(lexer.reserved))); - var precedence = s(lexer.natural); - s(lexer.symbol("=")); - - function addOperator(unary: (x: string)=>string, binary: (x: string, y: string)=>string): void { - var table = scope.operators[precedence]; - if( ! table){ - table = new p.OperatorTable(); - scope.operators[precedence] = table; - } - switch(type){ - case "infixl" : table.infixl .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; - case "infixr" : table.infixr .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; - case "infix" : table.infix .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; - case "prefix" : table.prefix .push(p.fmap(_ => unary , lexer.reservedOp(op))); scope.unaryOps [op] = unary; break; - case "postfix": table.postfix.push(p.fmap(_ => unary , lexer.reservedOp(op))); scope.unaryOps [op] = unary; break; - } - _expression = null; - } - - s(p.or( - // Function Alias Operator - p.fmap(func => { - addOperator( - (x: string ) => [func, "(", x, ")"].join(''), - (x: string, y: string) => [func, "(", x, ",", y, ")"].join('') - ); - }, lexer.identifier), - - // Native Operator - p.fmap(lit => { - addOperator( - (x: string ) => lit.replace("{0}", x ), - (x: string, y: string) => lit.replace("{0}", x).replace("{1}", y) - ); - }, lexer.stringLiteral) - )); - s(lexer.semi); - return ""; - }); - - var ifStatement: p.Parser = p.seq(s=>{ - s(lexer.reserved("if")); - var condition = s(lexer.parens(expression)); - var thenClause = s(block); - var elseClause = s(p.option("", p.seq(s=>{ - s(lexer.reserved("else")); - return "else" + s(p.or(block, ifStatement)); - }))); - return "if(" + condition + ")" + thenClause + elseClause; - }); - - var forStatement: p.Parser = p.seq(s=>{ - s(lexer.reserved("for")); - var header = s(lexer.parens(p.seq(s=>{ - var init = s(p.option("", p.or(varExpression, expression))); - s(lexer.semi); - var cond = s(expression); - s(lexer.semi); - var next = s(expression); - return "(" + init + ";" + cond + ";" + next + ")"; - }))); - var body = s(block); - return "for" + header + body; - }); - - var functionStatement = p.seq((s: p.Context)=>{ - s(lexer.reserved("function")); - var name: string = s(lexer.identifier); - var args: string[] = s(lexer.parens(p.sepBy(lexer.identifier, lexer.comma))); - var body: string[] = s(lexer.braces(p.many(statement))); - return s.success && ["function ", name, "(", args.join(','), "){", body.join(""), "}"].join(''); - }); - - var statement = p.or(functionStatement, returnStatement, ifStatement, forStatement, varStatement, exprStatement); - - var block = p.or(p.fmap((xs: string[]) => "{" + xs.join('') + "}", lexer.braces(p.many(statement))), expression); - - var topLevelStatement: p.Parser = p.or(functionStatement, operatorStatement, ifStatement, forStatement, varStatement, exprStatement); - - // スクリプト全体を解析するパーサ - var script: p.Parser = p.between( - // パーサは常にトークンの直前で停止しているものとします。 - // 最初のトークンの直前まで空白を読み飛ばします - lexer.whiteSpace, - - // スクリプト本体の構文解析 - p.fmap((xs: string[])=>xs.join(''), p.many(topLevelStatement)), - - // 入力の終端を確認するために、eof を使います。 - p.eof - ); - - return p.parse(script, new p.State(source, 0)); - } +/// + +// Sample program: BanchaScript compiler +// +// BanchaScript is a AltJS, a programming language compiled into JavaScript source code. +// See bancha.html for sample soruce codes. +// + +module bancha { + import p = Parsect; + + // Lexer. 字句解析器 + var identStart = p.oneOf("_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + var identLetter = p.oneOf("_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + var opStart = p.oneOf("+-*/=!$%&^~@?_><:|\\."); + var opLetter = p.oneOf("+-*/=!$%&^~@?_><:|\\."); + var lexer: p.GenTokenParser = p.makeTokenParser({ + commentStart: p.string('/*'), + commentEnd: p.string('*/'), + commentLine: p.string('//'), + nestedComments: true, + identStart: identStart, + identLetter: identLetter, + opStart: opStart, + opLetter: opLetter, + reservedNames: ["function", "return", "operator", "infix", "infixl", "infixr", "prefix", "postfix", "var", "if", "else", "for", "native"], + reservedOpNames: [], + caseSensitive: true + }); + + export class Scope { + + operators: p.OperatorTable[] = []; + + // 演算子の関数化のためのテーブル + binaryOps: { [symbol: string]: (x: string, y: string) => string } = {}; + unaryOps: { [symbol: string]: (x: string ) => string } = {}; + + constructor(){ + this.operators[0] = new p.OperatorTable(); + + // 関数の中置記法 + this.operators[0].infix.push( + lexer.lexeme(p.seq(s=>{ + s(p.string("`")); // バッククォートの後ろに空白を許可していない + var ops = s(identStart); + var opl = s(p.many(identLetter)); + s(p.string("`")); + return (x: string, y: string)=> "(" + ops + opl.join('') + "(" + x + "," + y + "))"; + })) + ); + } + } + + export function compile(scope: Scope, source: string): p.Reply { + + // mutable + var _expression: p.Parser = null; + + var expression: p.Parser = p.lazy((): p.Parser => { + if( ! _expression){ + var _table = Object.keys(scope.operators).sort().map(k => scope.operators[k]); + _expression = p.makeExpressionParser(_table, term); + } + return _expression; + }); + + var term: p.Parser = p.label("term", p.seq((s: p.Context): string =>{ + + var arrowFunctionArgs = p.or( + p.fmap((x: string): string[] => [x], lexer.identifier), + lexer.parens(p.sepBy(lexer.identifier, lexer.comma)) + ); + var arrowFunctionBody = p.or( + p.fmap((xs: string[]): string => "{" + xs.join('') + "}", lexer.braces(p.many(statement))), + expression + ); + var arrowFunction: p.Parser = p.seq((s: p.Context): string =>{ + var args: string[] = s(arrowFunctionArgs); + s(lexer.symbol("=>")); + var e: string = s(arrowFunctionBody); + return s.success && "(function(" + args.join(',') + "){return " + e + "})"; + }); + + var nativeDirective: p.Parser = p.seq((s: p.Context): string =>{ + s(lexer.reserved("native")); + return s(lexer.stringLiteral); + }); + + var functionalOperator: p.Parser = p.fmap((op: string): string =>{ + if(scope.binaryOps[op]) return '(function(x,y){return ' + scope.binaryOps[op]("x", "y") + '})'; + else if(scope.unaryOps [op]) return '(function(x){return ' + scope.unaryOps[op]("x") + '})'; + else throw new Error("Unknown operator: " + op); + }, lexer.parens(lexer.operator)); + + var arrayLiteral: p.Parser = p.fmap((xs: string[]): string => "[" + xs.join(',') + "]", lexer.brackets(p.sepBy(expression, lexer.comma))); + + var rightSection: p.Parser = p.seq((s: p.Context): string =>{ + s(lexer.symbol("(")); + var op = s(lexer.operator); + var e = s(simpleExpressionParser); + s(lexer.symbol(")")); + if(scope.binaryOps[op]){ + return "(function(x){return " + scope.binaryOps[op]("x", e) + "})"; + } + }); + + var leftSection: p.Parser = p.seq((s: p.Context): string =>{ + s(lexer.symbol("(")); + var e = s(simpleExpressionParser); + var op = s(lexer.operator); + s(lexer.symbol(")")); + if(scope.binaryOps[op]){ + return "(function(x){return " + scope.binaryOps[op](e, "x") + "})"; + } + }); + + var simpleExpressionParser: p.Parser = p.or( + p.triable(functionalOperator), // `演算子` + p.triable(arrowFunction), // アロー関数式 + arrayLiteral, // 配列リテラル + p.triable(rightSection), + p.triable(leftSection), + p.fmap(x => '(' + x + ')', lexer.parens(expression)), // ( 式 ) + p.fmap(x => '"' + x + '"', lexer.stringLiteral), // 文字列リテラル + p.fmap(x => x.toString(), lexer.naturalOrFloat), // 数値リテラル + nativeDirective, // native ディレクティブ + lexer.identifier // 識別子 + ); + + + var simpleExpression: string = s(simpleExpressionParser); + + // Function application syntax parser. 関数適用 + var functionApplication: p.Parser = p.fmap( + (args: string[]): string => { + if(args.every(x => !!x)){ + // 通常の関数適用 + return simpleExpression + "(" + args.join(',') + ")" + }else{ + // 関数の部分適用 + var params: string[] = []; + var remains: string[] = []; + for(var i = 0; i < args.length; i++){ + if(args[i] === null){ + var v = String.fromCharCode(97 + i).toString(); + remains.push(v); + params.push(v); + }else{ + params.push(args[i]); + } + } + return "(function(fn){return function(" + remains.join(',') + "){return fn(" + params.join(',') + ")}}(" + simpleExpression + "))"; + } + }, + lexer.parens(p.sepBy(p.option(null, expression), lexer.comma)) + ); // 関数呼び出し + + return s(p.option(simpleExpression, functionApplication)); + })); + + var exprStatement: p.Parser = p.fmap((e: string): string => e + ";", p.head(expression, lexer.semi)); + + var varExpression: p.Parser = p.seq((s: p.Context): string => { + s(lexer.reserved("var")); + var name: string = s(lexer.identifier); + s(lexer.symbol("=")); + var e: string = s(expression); + return "var " + name + "=" + e; + }); + + + var varStatement: p.Parser = p.seq((s: p.Context): string => { + var e: string = s(varExpression); + s(lexer.semi); + return e + ";"; + }); + + var returnStatement: p.Parser = p.fmap((e: string): string => "return "+e+";", p.between(lexer.reserved("return"), expression, lexer.semi)); + + var operatorStatement: p.Parser = p.seq((s: p.Context): string =>{ + s(lexer.reserved("operator")); + var op: string = s(lexer.operator); + var type = s(p.choice(["infixl", "infixr", "infix", "prefix", "postfix"].map(lexer.reserved))); + var precedence = s(lexer.natural); + s(lexer.symbol("=")); + + function addOperator(unary: (x: string)=>string, binary: (x: string, y: string)=>string): void { + var table = scope.operators[precedence]; + if( ! table){ + table = new p.OperatorTable(); + scope.operators[precedence] = table; + } + switch(type){ + case "infixl" : table.infixl .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; + case "infixr" : table.infixr .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; + case "infix" : table.infix .push(p.fmap(_ => binary, lexer.reservedOp(op))); scope.binaryOps[op] = binary; break; + case "prefix" : table.prefix .push(p.fmap(_ => unary , lexer.reservedOp(op))); scope.unaryOps [op] = unary; break; + case "postfix": table.postfix.push(p.fmap(_ => unary , lexer.reservedOp(op))); scope.unaryOps [op] = unary; break; + } + _expression = null; + } + + s(p.or( + // Function Alias Operator + p.fmap(func => { + addOperator( + (x: string ) => [func, "(", x, ")"].join(''), + (x: string, y: string) => [func, "(", x, ",", y, ")"].join('') + ); + }, lexer.identifier), + + // Native Operator + p.fmap(lit => { + addOperator( + (x: string ) => lit.replace("{0}", x ), + (x: string, y: string) => lit.replace("{0}", x).replace("{1}", y) + ); + }, lexer.stringLiteral) + )); + s(lexer.semi); + return ""; + }); + + var ifStatement: p.Parser = p.seq(s=>{ + s(lexer.reserved("if")); + var condition = s(lexer.parens(expression)); + var thenClause = s(block); + var elseClause = s(p.option("", p.seq(s=>{ + s(lexer.reserved("else")); + return "else" + s(p.or(block, ifStatement)); + }))); + return "if(" + condition + ")" + thenClause + elseClause; + }); + + var forStatement: p.Parser = p.seq(s=>{ + s(lexer.reserved("for")); + var header = s(lexer.parens(p.seq(s=>{ + var init = s(p.option("", p.or(varExpression, expression))); + s(lexer.semi); + var cond = s(expression); + s(lexer.semi); + var next = s(expression); + return "(" + init + ";" + cond + ";" + next + ")"; + }))); + var body = s(block); + return "for" + header + body; + }); + + var functionStatement = p.seq((s: p.Context)=>{ + s(lexer.reserved("function")); + var name: string = s(lexer.identifier); + var args: string[] = s(lexer.parens(p.sepBy(lexer.identifier, lexer.comma))); + var body: string[] = s(lexer.braces(p.many(statement))); + return s.success && ["function ", name, "(", args.join(','), "){", body.join(""), "}"].join(''); + }); + + var statement = p.or(functionStatement, returnStatement, ifStatement, forStatement, varStatement, exprStatement); + + var block = p.or(p.fmap((xs: string[]) => "{" + xs.join('') + "}", lexer.braces(p.many(statement))), expression); + + var topLevelStatement: p.Parser = p.or(functionStatement, operatorStatement, ifStatement, forStatement, varStatement, exprStatement); + + // スクリプト全体を解析するパーサ + var script: p.Parser = p.between( + // パーサは常にトークンの直前で停止しているものとします。 + // 最初のトークンの直前まで空白を読み飛ばします + lexer.whiteSpace, + + // スクリプト本体の構文解析 + p.fmap((xs: string[])=>xs.join(''), p.many(topLevelStatement)), + + // 入力の終端を確認するために、eof を使います。 + p.eof + ); + + return p.parse(script, new p.State(source, 0)); + } } \ No newline at end of file diff --git a/example/bancha/sample0.bancha b/example/bancha/sample0.bancha index 3e7561c..857be05 100644 --- a/example/bancha/sample0.bancha +++ b/example/bancha/sample0.bancha @@ -1,31 +1,31 @@ - -// piyo -log(4); - -// hogehoge -operator : infix 5 = concat; - - - - -operator >|^_^| infix 5 = log; - -"わたしです " >|^_^| 0; - - - -def factrial(k){ - sdf + sdfsdf; - 10 * hoge : 10; - return 1; -} -def factrial(n){ - hoge(1 + 2, 4); - return n * (n - 1); -} - - - -/* hogeho ge */ - -log(19); + +// piyo +log(4); + +// hogehoge +operator : infix 5 = concat; + + + + +operator >|^_^| infix 5 = log; + +"わたしです " >|^_^| 0; + + + +def factrial(k){ + sdf + sdfsdf; + 10 * hoge : 10; + return 1; +} +def factrial(n){ + hoge(1 + 2, 4); + return n * (n - 1); +} + + + +/* hogeho ge */ + +log(19); diff --git a/readme.md b/readme.md index db08e71..0a2666d 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ **Parsect** is a parser combinator library for [TypeScript](http://www.typescriptlang.org/) or JavaScript. It provides a easy way to write a readable parser in only TypeScript/JavaScript without any other domain-specific languages like yacc/lex, ANTLR or PEG.js. Parsect can be used from not only TypeScript and JavaScript but also other [AltJS](http://altjs.org/)s. -I got the idea for Parsect from [Parsec](http://www.haskell.org/haskellwiki/Parsec) parser combinator library in Haskell, however this is not a porting of Parsec. Unfortunately, this library doesn't have underlying Monad or Fanctor and it doesn't deal a string as a list of charactor. However, you can combine parsers in the same manner as Parsec with Parsect. +I got the idea for Parsect from [Parsec](http://www.haskell.org/haskellwiki/Parsec) parser combinator library in Haskell, however this is not a porting of Parsec. Unfortunately, this library doesn't have underlying Monad or Fanctor and it doesn't deal a string as a list of character. However, you can combine parsers in the same manner as Parsec with Parsect. * Parsec-like API * Statically typed: The API of Parsect is statically typed like Parsec with TypeScript. However, you can also use it from JavaScript as dynamically typed API. @@ -28,7 +28,7 @@ Parsect has some functions that creates a parser. `string` function is one of th var reply: Parsect.Reply = Parsect.parse(parser, state); console.log(reply.value); // prints "apple". -Note a difference from `string` in Parsec. If the parser recieve a unexpected input string like `"application"`, the parser would consume no charactors and fail. The both two words have first four letter `"appl"` but the parser don't throw a exception and parsing would continue to search other matchings. +Note a difference from `string` in Parsec. If the parser recieve a unexpected input string like `"application"`, the parser would consume no characters and fail. The both two words have first four letter `"appl"` but the parser don't throw a exception and parsing would continue to search other matchings. ### `many` Parser Combinator @@ -309,7 +309,7 @@ Invoke parser with the state. -### Charactor Parsers +### Character Parsers #### `oneOf(chars: string): Parser` @@ -341,7 +341,7 @@ Invoke parser with the state. #### `anyChar: Parser` -#### `satisfy(condition: (charactor: string, code: number)=>boolean): Parser` +#### `satisfy(condition: (character: string, code: number)=>boolean): Parser` #### `charCode(charCode: number): Parser`