-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
3/5 tree walkers done (BlockSetTransformer)
Also added a couple of support classes, just for an error free build. They'll probably go later.
- Loading branch information
1 parent
aaaa1a5
commit 99702f2
Showing
12 changed files
with
955 additions
and
922 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 -> 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."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.