puter/packages/strataparse/parse.js
2024-06-08 01:07:42 -04:00

141 lines
3.9 KiB
JavaScript

/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Phoenix Shell.
*
* Phoenix Shell is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export class Parser {
constructor ({
impl,
assign,
}) {
this.impl = impl;
this.assign = assign ?? {};
}
parse (lexer) {
const unadaptedResult = this.impl.parse(lexer);
const pr = unadaptedResult instanceof ParseResult
? unadaptedResult : new ParseResult(unadaptedResult);
if ( pr.status === ParseResult.VALUE ) {
pr.value = {
...pr.value,
...this.assign,
};
}
return pr;
}
}
export class ParseResult {
static UNRECOGNIZED = { name: 'unrecognized' };
static VALUE = { name: 'value' };
static INVALID = { name: 'invalid' };
constructor (value, opt_status) {
if (
value === ParseResult.UNRECOGNIZED ||
value === ParseResult.INVALID
) {
this.status = value;
return;
}
this.status = opt_status ?? (
value === undefined
? ParseResult.UNRECOGNIZED
: ParseResult.VALUE
);
this.value = value;
}
}
class ConcreteSyntaxParserDecorator {
constructor (delegate) {
this.delegate = delegate;
}
parse (lexer, ...a) {
const start = lexer.seqNo;
const result = this.delegate.parse(lexer, ...a);
if ( result.status === ParseResult.VALUE ) {
const end = lexer.seqNo;
result.value.$cst = { start, end };
}
return result;
}
}
class RememberSourceParserDecorator {
constructor (delegate) {
this.delegate = delegate;
}
parse (lexer, ...a) {
const start = lexer.seqNo;
const result = this.delegate.parse(lexer, ...a);
if ( result.status === ParseResult.VALUE ) {
const end = lexer.seqNo;
result.value.$source = lexer.reach(start, end);
}
return result;
}
}
export class ParserFactory {
constructor () {
this.concrete = false;
this.rememberSource = false;
}
decorate (obj) {
if ( this.concrete ) {
obj = new ConcreteSyntaxParserDecorator(obj);
}
if ( this.rememberSource ) {
obj = new RememberSourceParserDecorator(obj);
}
return obj;
}
create (cls, parserParams, resultParams) {
parserParams = parserParams ?? {};
resultParams = resultParams ?? {};
resultParams.assign = resultParams.assign ?? {};
const impl = new cls(parserParams);
const parser = new Parser({
impl,
assign: resultParams.assign
});
// return parser;
return this.decorate(parser);
}
}
export class SingleParserFactory {
create () {
throw new Error('abstract create() must be implemented');
}
}
export class AcceptParserUtil {
static adapt (parser) {
if ( parser === undefined ) return undefined;
if ( parser instanceof SingleParserFactory ) {
parser = parser.create();
}
if ( ! (parser instanceof Parser) ) {
parser = new Parser({ impl: parser });
}
return parser;
}
}