mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-29 17:09:25 -06:00
Reorganize and document Parsely classes
- lib.js only holds Parser so rename it parser.js - Move Symbol and None into terminals.js - Briefly document all parser classes
This commit is contained in:
@@ -1,31 +1,6 @@
|
||||
import { adapt_parser, INVALID, Parser, UNRECOGNIZED, VALUE } from './lib.js';
|
||||
import { Discard, FirstMatch, None, Optional, Repeat, Sequence } from './parsers/combinators.js';
|
||||
import { Literal, StringOf } from './parsers/terminals.js';
|
||||
|
||||
class Symbol extends Parser {
|
||||
_create(symbolName) {
|
||||
this.symbolName = symbolName;
|
||||
}
|
||||
|
||||
_parse (stream) {
|
||||
const parser = this.symbol_registry[this.symbolName];
|
||||
if ( ! parser ) {
|
||||
throw new Error(`No symbol defined named '${this.symbolName}'`);
|
||||
}
|
||||
const subStream = stream.fork();
|
||||
const result = parser.parse(subStream);
|
||||
console.log(`Result of parsing symbol('${this.symbolName}'):`, result);
|
||||
if ( result.status === UNRECOGNIZED ) {
|
||||
return UNRECOGNIZED;
|
||||
}
|
||||
if ( result.status === INVALID ) {
|
||||
return { status: INVALID, value: result };
|
||||
}
|
||||
stream.join(subStream);
|
||||
result.$ = this.symbolName;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
import { adapt_parser, VALUE } from './parser.js';
|
||||
import { Discard, FirstMatch, Optional, Repeat, Sequence } from './parsers/combinators.js';
|
||||
import { Literal, None, StringOf, Symbol } from './parsers/terminals.js';
|
||||
|
||||
class ParserWithAction {
|
||||
#parser;
|
||||
@@ -55,6 +30,12 @@ export class GrammarContext {
|
||||
return new GrammarContext({...this.parsers, ...more_parsers});
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a parsing function for the given grammar.
|
||||
* @param grammar An object of symbol-names to a DSL for parsing that symbol.
|
||||
* @param actions An object of symbol-names to a function run to process the symbol after it has been parsed.
|
||||
* @returns {function(*, *, {must_consume_all_input?: boolean}=): *} A function to run the parser. Throws if parsing fails.
|
||||
*/
|
||||
define_parser (grammar, actions) {
|
||||
const symbol_registry = {};
|
||||
const api = {};
|
||||
@@ -81,7 +62,9 @@ export class GrammarContext {
|
||||
if (!entry_parser) {
|
||||
throw new Error(`Entry symbol '${entry_symbol}' not found in grammar.`);
|
||||
}
|
||||
return entry_parser.parse(stream);
|
||||
const result = entry_parser.parse(stream);
|
||||
// TODO: Ensure all the stream has been consumed
|
||||
return result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ export const UNRECOGNIZED = Symbol('unrecognized');
|
||||
export const INVALID = Symbol('invalid');
|
||||
export const VALUE = Symbol('value');
|
||||
|
||||
/**
|
||||
* Base class for parsers.
|
||||
* To implement your own, subclass it and define these methods:
|
||||
* - _create(): Acts as the constructor
|
||||
* - _parse(stream): Performs the parsing on the stream, and returns either UNRECOGNIZED, INVALID, or a result object.
|
||||
*/
|
||||
export class Parser {
|
||||
result (o) {
|
||||
if (o.value && o.value.$discard) {
|
||||
@@ -1,5 +1,9 @@
|
||||
import { INVALID, UNRECOGNIZED, VALUE, adapt_parser, Parser } from '../lib.js';
|
||||
import { adapt_parser, INVALID, Parser, UNRECOGNIZED, VALUE } from '../parser.js';
|
||||
|
||||
/**
|
||||
* Runs its child parser, and discards its result.
|
||||
* @param parser Child parser
|
||||
*/
|
||||
export class Discard extends Parser {
|
||||
_create (parser) {
|
||||
this.parser = adapt_parser(parser);
|
||||
@@ -19,6 +23,10 @@ export class Discard extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs its child parsers in order, and returns the first successful result.
|
||||
* @param parsers Child parsers
|
||||
*/
|
||||
export class FirstMatch extends Parser {
|
||||
_create (...parsers) {
|
||||
this.parsers = parsers.map(adapt_parser);
|
||||
@@ -42,14 +50,10 @@ export class FirstMatch extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
export class None extends Parser {
|
||||
_create () {}
|
||||
|
||||
_parse (stream) {
|
||||
return { status: VALUE, $: 'none', $discard: true };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs its child parser, and then returns its result, or nothing.
|
||||
* @param parser Child parser
|
||||
*/
|
||||
export class Optional extends Parser {
|
||||
_create (parser) {
|
||||
this.parser = adapt_parser(parser);
|
||||
@@ -66,6 +70,12 @@ export class Optional extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a repeated sequence of values with separators between them.
|
||||
* @param value_parser Parser for the value
|
||||
* @param separator_parser Parser for the separator
|
||||
* @param trailing Whether to allow a trailing separator
|
||||
*/
|
||||
export class Repeat extends Parser {
|
||||
_create (value_parser, separator_parser, { trailing = false } = {}) {
|
||||
this.value_parser = adapt_parser(value_parser);
|
||||
@@ -114,6 +124,10 @@ export class Repeat extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a sequence of child parsers, and returns their result as an array if they all succeed.
|
||||
* @param parsers Child parsers
|
||||
*/
|
||||
export class Sequence extends Parser {
|
||||
_create (...parsers) {
|
||||
this.parsers = parsers.map(adapt_parser);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Parser, UNRECOGNIZED, VALUE } from '../lib.js';
|
||||
import { INVALID, Parser, UNRECOGNIZED, VALUE } from '../parser.js';
|
||||
|
||||
/**
|
||||
* Parses a literal value.
|
||||
* @param value The value to parse
|
||||
*/
|
||||
export class Literal extends Parser {
|
||||
_create (value) {
|
||||
this.value = value;
|
||||
@@ -18,6 +22,10 @@ export class Literal extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string composed of the given values.
|
||||
* @param values An array of strings that will be parsed as the result.
|
||||
*/
|
||||
export class StringOf extends Parser {
|
||||
_create (values) {
|
||||
this.values = values;
|
||||
@@ -43,4 +51,43 @@ export class StringOf extends Parser {
|
||||
stream.join(subStream);
|
||||
return { status: VALUE, $: 'stringOf', value: text };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an object defined by the symbol registry.
|
||||
* @param symbolName The name of the symbol to parse.
|
||||
*/
|
||||
export class Symbol extends Parser {
|
||||
_create(symbolName) {
|
||||
this.symbolName = symbolName;
|
||||
}
|
||||
|
||||
_parse (stream) {
|
||||
const parser = this.symbol_registry[this.symbolName];
|
||||
if ( ! parser ) {
|
||||
throw new Error(`No symbol defined named '${this.symbolName}'`);
|
||||
}
|
||||
const subStream = stream.fork();
|
||||
const result = parser.parse(subStream);
|
||||
if ( result.status === UNRECOGNIZED ) {
|
||||
return UNRECOGNIZED;
|
||||
}
|
||||
if ( result.status === INVALID ) {
|
||||
return { status: INVALID, value: result };
|
||||
}
|
||||
stream.join(subStream);
|
||||
result.$ = this.symbolName;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does no parsing and returns a discarded result.
|
||||
*/
|
||||
export class None extends Parser {
|
||||
_create () {}
|
||||
|
||||
_parse (stream) {
|
||||
return { status: VALUE, $: 'none', $discard: true };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GrammarContext, standard_parsers } from '../../../packages/parsely/exports.js';
|
||||
import { Parser, UNRECOGNIZED, VALUE } from '../../../packages/parsely/lib.js';
|
||||
import { Parser, UNRECOGNIZED, VALUE } from '../../../packages/parsely/parser.js';
|
||||
|
||||
class NumberParser extends Parser {
|
||||
static data = {
|
||||
|
||||
Reference in New Issue
Block a user