Skip to content

Commit

Permalink
3/5 tree walkers done (BlockSetTransformer)
Browse files Browse the repository at this point in the history
Also added a couple of support classes, just for an error free build. They'll probably go later.
  • Loading branch information
mike-lischke committed Oct 14, 2024
1 parent aaaa1a5 commit 99702f2
Show file tree
Hide file tree
Showing 12 changed files with 955 additions and 922 deletions.
23 changes: 23 additions & 0 deletions src/antlr3/FailedPredicateException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import { RecognitionException, type IntStream } from "antlr4ng";

/** A semantic predicate failed during validation. Validation of predicates
* occurs when normally parsing the alternative just like matching a token.
* Disambiguating predicate evaluation occurs when we hoist a predicate into
* a prediction decision.
*/
export class FailedPredicateException extends RecognitionException {
public ruleName: string;
public predicateText: string;

public constructor(input: IntStream, ruleName: string, predicateText: string) {
super({ message: "", recognizer: null, input: null, ctx: null });
this.ruleName = ruleName;
this.predicateText = predicateText;
}
}
28 changes: 3 additions & 25 deletions src/antlr3/MismatchedSetException.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import { RecognitionException, type BitSet, type IntStream } from "antlr4ng";
Expand Down
28 changes: 3 additions & 25 deletions src/antlr3/NoViableAltException.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import { RecognitionException, type IntStream } from "antlr4ng";
Expand Down
11 changes: 7 additions & 4 deletions src/antlr3/tree/BaseTreeAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export abstract class BaseTreeAdaptor implements TreeAdaptor {
}

case 3: {
const [tokenType, fromToken, text] = args as [number, Token, string];
const [tokenType, fromToken, text] = args as [number, Token | null, string];

if (fromToken === null) {
return this.create(tokenType, text);
Expand Down Expand Up @@ -92,7 +92,11 @@ export abstract class BaseTreeAdaptor implements TreeAdaptor {
return new CommonErrorNode(input, start, stop, e);
}

public isNil(tree: Tree): boolean {
public isNil(tree: Tree | null): boolean {
if (tree === null) {
return true;
}

return tree.isNil();
}

Expand Down Expand Up @@ -193,9 +197,8 @@ export abstract class BaseTreeAdaptor implements TreeAdaptor {
* Transform ^(nil x) to x and nil to null
*/
public rulePostProcessing(root: Tree): Tree | null {
//System.out.println("rulePostProcessing: "+((Tree)root).toStringTree());
let r: Tree | null = root;
if (r !== null && r.isNil()) {
if (r.isNil()) {
if (r.getChildCount() === 0) {
r = null;
} else if (r.getChildCount() === 1) {
Expand Down
6 changes: 3 additions & 3 deletions src/antlr3/tree/CommonTreeAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class CommonTreeAdaptor extends BaseTreeAdaptor {
* seems like this will yield start=i and stop=i-1 in a nil node.
* Might be useful info so I'll not force to be i..i.
*/
public setTokenBoundaries(t: Tree | null, startToken: Token, stopToken: Token): void {
public setTokenBoundaries(t: Tree | null, startToken: Token | null, stopToken: Token | null): void {
if (t === null) {
return;
}
Expand Down Expand Up @@ -129,7 +129,7 @@ export class CommonTreeAdaptor extends BaseTreeAdaptor {
return (t as Tree).getTokenStopIndex();
}

public override getText(t: Tree): string | null {
public override getText(t: Tree | null): string | null {
if (t === null) {
return null;
}
Expand Down Expand Up @@ -158,7 +158,7 @@ export class CommonTreeAdaptor extends BaseTreeAdaptor {
return null; // no idea what to do
}

public override getChild(t: Tree, i: number): Tree | null {
public override getChild(t: Tree | null, i: number): Tree | null {
if (t === null) {
return null;
}
Expand Down
151 changes: 151 additions & 0 deletions src/antlr3/tree/RewriteRuleElementStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import type { Tree } from "./Tree.js";
import type { TreeAdaptor } from "./TreeAdaptor.js";

/** A generic list of elements tracked in an alternative to be used in
* a -> rewrite rule. We need to subclass to fill in the next() method,
* which returns either an AST node wrapped around a token payload or
* an existing subtree.
*
* Once you start next()ing, do not try to add more elements. It will
* break the cursor tracking I believe.
*
* @see org.antlr.runtime.tree.RewriteRuleSubtreeStream
* @see org.antlr.runtime.tree.RewriteRuleTokenStream
*
* TODO: add mechanism to detect/puke on modification after reading from stream
*/
export abstract class RewriteRuleElementStream {
/** Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(),
* which bumps it to 1 meaning no more elements.
*/
protected cursor = 0;

/** The list of tokens or subtrees we are tracking */
protected elements: Tree[];

/** Once a node / subtree has been used in a stream, it must be dup'd
* from then on. Streams are reset after subrules so that the streams
* can be reused in future subrules. So, reset must set a dirty bit.
* If dirty, then next() always returns a dup.
*
* I wanted to use "naughty bit" here, but couldn't think of a way
* to use "naughty".
*
* TODO: unused?
*/
protected dirty = false;

/** The element or stream description; usually has name of the token or
* rule reference that this list tracks. Can include rule name too, but
* the exception would track that info.
*/
protected elementDescription: string;
protected adaptor: TreeAdaptor;

public constructor(adaptor: TreeAdaptor, elementDescription: string, elements?: Tree[]) {
this.elementDescription = elementDescription;
this.adaptor = adaptor;
this.elements = elements ?? [];
}

/** Reset the condition of this stream so that it appears we have
* not consumed any of its elements. Elements themselves are untouched.
* Once we reset the stream, any future use will need duplicates. Set
* the dirty bit.
*/
public reset(): void {
this.cursor = 0;
this.dirty = true;
}

public add(el: Tree | null): void {
if (el) {
this.elements.push(el);
}
}

/** Return the next element in the stream. If out of elements, throw
* an exception unless size()==1. If size is 1, then return elements[0].
* Return a duplicate node/subtree if stream is out of elements and
* size==1. If we've already used the element, dup (dirty bit set).
*/
public nextTree(): Tree {
const n = this.size();
if (this.dirty || (this.cursor >= n && n === 1)) {
// if out of elements and size is 1, dup
const el = this._next();

return this.dup(el);
}
// test size above then fetch
const el = this._next();

return el;
}

public hasNext(): boolean {
return this.cursor < this.elements.length;
}

public size(): number {
return this.elements.length;
}

public getDescription(): string {
return this.elementDescription;
}

/** do the work of getting the next element, making sure that it's
* a tree node or subtree. Throw an exception
* if the stream is empty or we're out of elements and size > 1.
* protected so you can override in a subclass if necessary.
*/
protected _next(): Tree {
const n = this.size();
if (n === 0) {
throw new Error(this.elementDescription);
}

if (this.cursor >= n) { // out of elements?
if (n === 1) { // if size is 1, it's ok; return and we'll dup
return this.toTree(this.elements[0]);
}

// out of elements and size was not 1, so we can't dup
throw new Error(this.elementDescription);
}

if (n === 1) {
this.cursor++; // move cursor even for single element list

return this.toTree(this.elements[0]);
}

// must have more than one in list, pull from elements
const o = this.toTree(this.elements[this.cursor]);
this.cursor++;

return o;
}

/** Ensure stream emits trees; tokens must be converted to AST nodes.
* AST nodes can be passed through unmolested.
*/
protected toTree(el: Tree): Tree {
return el;
}

/** When constructing trees, sometimes we need to dup a token or AST
* subtree. Dup'ing a token means just creating another AST node
* around it. For trees, you must call the adaptor.dupTree() unless
* the element is for a tree root; then it must be a node dup.
*/
protected abstract dup(el: Tree): Tree;

}
28 changes: 28 additions & 0 deletions src/antlr3/tree/RewriteRuleNodeStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import { RewriteRuleElementStream } from "./RewriteRuleElementStream.js";
import type { Tree } from "./Tree.js";

/** Queues up nodes matched on left side of -&gt; in a tree parser. This is
* the analog of RewriteRuleTokenStream for normal parsers.
*/
export class RewriteRuleNodeStream extends RewriteRuleElementStream {

public nextNode(): Tree {
return this._next();
}

protected override toTree(el: Tree): Tree {
return this.adaptor.dupNode(el);
}

protected dup(el: Tree): Tree {
// we dup every node, so don't have to worry about calling dup; short-
// circuited next() so it doesn't call.
throw new Error("dup can't be called for a node stream.");
}
}
48 changes: 48 additions & 0 deletions src/antlr3/tree/RewriteRuleSubtreeStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/

import { RewriteRuleElementStream } from "./RewriteRuleElementStream.js";
import type { Tree } from "./Tree.js";

export class RewriteRuleSubtreeStream extends RewriteRuleElementStream {

/** Treat next element as a single node even if it's a subtree.
* This is used instead of next() when the result has to be a
* tree root node. Also prevents us from duplicating recently-added
* children; e.g., ^(type ID)+ adds ID to type and then 2nd iteration
* must dup the type node, but ID has been added.
*
* Referencing a rule result twice is ok; dup entire tree as
* we can't be adding trees as root; e.g., expr expr.
*
* Hideous code duplication here with super.next(). Can't think of
* a proper way to refactor. This needs to always call dup node
* and super.next() doesn't know which to call: dup node or dup tree.
*/
public nextNode(): Tree {
const n = this.size();
if (this.dirty || (this.cursor >= n && n === 1)) {
// if out of elements and size is 1, dup (at most a single node
// since this is for making root nodes).
const el = this._next();

return this.adaptor.dupNode(el);
}

// test size above then fetch
let tree = this._next();
while (this.adaptor.isNil(tree) && this.adaptor.getChildCount(tree) === 1)
tree = this.adaptor.getChild(tree, 0)!;

const el = this.adaptor.dupNode(tree); // dup just the root (want node here)

return el;
}

protected dup(el: Tree): Tree {
return this.adaptor.dupTree(el);
}
}
Loading

0 comments on commit 99702f2

Please sign in to comment.