clarity-pattern-parser
Advanced tools
Comparing version 10.1.26 to 10.2.0
@@ -41,2 +41,3 @@ export interface CycleFreeNode { | ||
find(predicate: (node: Node) => boolean): Node | null; | ||
findRoot(): Node; | ||
findAll(predicate: (node: Node) => boolean): Node[]; | ||
@@ -43,0 +44,0 @@ findAncestor(predicate: (node: Node) => boolean): Node | null; |
@@ -32,2 +32,4 @@ import { Pattern } from "../patterns/Pattern"; | ||
private _buildOptions; | ||
private _isRecursive; | ||
private _isRecursivePattern; | ||
private _buildPattern; | ||
@@ -34,0 +36,0 @@ private _saveSequence; |
@@ -22,2 +22,3 @@ import { Node } from "./ast/Node"; | ||
import { Context } from "./patterns/Context"; | ||
export { Node, Grammar, AutoComplete, AutoCompleteOptions, Suggestion, SuggestionOption, Sequence, Cursor, CursorHistory, Match, Context, Literal, Not, Options, Optional, ParseError, ParseResult, Pattern, Reference, Regex, Repeat, grammar, patterns, }; | ||
import { ExpressionPattern } from "./patterns/ExpressionPattern"; | ||
export { Node, Grammar, AutoComplete, AutoCompleteOptions, Suggestion, SuggestionOption, Sequence, Cursor, CursorHistory, Match, Context, ExpressionPattern, Literal, Not, Options, Optional, ParseError, ParseResult, Pattern, Reference, Regex, Repeat, grammar, patterns, }; |
{ | ||
"name": "clarity-pattern-parser", | ||
"version": "10.1.26", | ||
"version": "10.2.0", | ||
"description": "Parsing Library for Typescript and Javascript.", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
@@ -186,2 +186,13 @@ function defaultVisitor(node: Node) { | ||
findRoot() { | ||
let pattern: Node | null = this; | ||
while (true) { | ||
if (pattern.parent == null) { | ||
return pattern; | ||
} | ||
pattern = pattern.parent; | ||
} | ||
} | ||
findAll(predicate: (node: Node) => boolean): Node[] { | ||
@@ -288,3 +299,3 @@ const matches: Node[] = []; | ||
} else { | ||
length = this.children.reduce((acc, c) => acc + c.normalize(acc + startIndex), startIndex); | ||
length = this.children.reduce((acc, c) => acc + c.normalize(acc + startIndex), startIndex) - startIndex; | ||
} | ||
@@ -291,0 +302,0 @@ |
@@ -12,2 +12,3 @@ import { Sequence } from "../patterns/Sequence"; | ||
import { Context } from "../patterns/Context"; | ||
import { patterns } from "./patterns"; | ||
@@ -573,2 +574,16 @@ describe("Grammar", () => { | ||
}); | ||
test("Expression Pattern", ()=>{ | ||
const {variables, expression} = patterns` | ||
variables = "a" | "b" | "c" | ||
ternary = expression + " ? " + expression + " : " + expression | ||
expression = ternary | variables | ||
bad-ternary = bad-expression + " ? " + bad-expression + " : " + bad-expression | ||
bad-expression = bad-ternary | bad-ternary | ||
`; | ||
let result = expression.exec("a ? b : c"); | ||
debugger; | ||
expect(result).toBe(result); | ||
}); | ||
}); |
@@ -14,2 +14,3 @@ import { Node } from "../ast/Node"; | ||
import { Context } from "../patterns/Context"; | ||
import {ExpressionPattern} from "../patterns/ExpressionPattern"; | ||
@@ -252,7 +253,30 @@ let anonymousIndexId = 0; | ||
const patterns = patternNodes.map(n => this._buildPattern(n)); | ||
const hasRecursivePattern = patterns.some(p=>this._isRecursive(name, p)); | ||
if (hasRecursivePattern && !isGreedy){ | ||
try { | ||
const expression = new ExpressionPattern(name, patterns); | ||
return expression; | ||
} catch{} | ||
} | ||
const or = new Options(name, patterns, isGreedy); | ||
return or; | ||
} | ||
private _isRecursive(name: string, pattern: Pattern) { | ||
if (pattern.type === "right-associated" && this._isRecursivePattern(name, pattern.children[0])) { | ||
return true; | ||
} | ||
return this._isRecursivePattern(name, pattern); | ||
} | ||
private _isRecursivePattern(name: string, pattern: Pattern) { | ||
return pattern.type === "sequence" && | ||
pattern.children[0].type === "reference" && | ||
pattern.children[0].name === name && | ||
pattern.children.length > 2; | ||
} | ||
private _buildPattern(node: Node): Pattern { | ||
@@ -259,0 +283,0 @@ const type = node.name; |
@@ -22,2 +22,3 @@ import { Node } from "./ast/Node"; | ||
import { Context } from "./patterns/Context"; | ||
import { ExpressionPattern } from "./patterns/ExpressionPattern"; | ||
@@ -36,2 +37,3 @@ export { | ||
Context, | ||
ExpressionPattern, | ||
Literal, | ||
@@ -38,0 +40,0 @@ Not, |
@@ -6,2 +6,4 @@ import { Node } from "../ast/Node"; | ||
import { Pattern } from "./Pattern"; | ||
import { findPattern } from "./findPattern"; | ||
import { Sequence } from "./Sequence"; | ||
@@ -11,2 +13,6 @@ let indexId = 0; | ||
function createNode(name: string, children: Node[]) { | ||
return new Node("expression", name, 0, 0, children, ""); | ||
} | ||
enum Association { | ||
@@ -22,9 +28,11 @@ left = 0, | ||
private _parent: Pattern | null; | ||
private _token: string; | ||
private _firstIndex: number; | ||
private _originalPatterns: Pattern[]; | ||
private _patterns: Pattern[]; | ||
private _unaryPatterns: Pattern[]; | ||
private _binaryPatterns: Pattern[]; | ||
private _recursivePatterns: Pattern[]; | ||
private _recursiveNames: string[]; | ||
private _binaryAssociation: Association[]; | ||
private _precedenceMap: Record<string, number>; | ||
private _precedenceMap: Record<string, number>; | ||
private _binaryNames: string[]; | ||
@@ -44,6 +52,2 @@ | ||
get token(): string { | ||
return this._token; | ||
} | ||
get parent(): Pattern | null { | ||
@@ -61,2 +65,14 @@ return this._parent; | ||
get unaryPatterns(): readonly Pattern[] { | ||
return this._unaryPatterns; | ||
} | ||
get binaryPatterns(): readonly Pattern[] { | ||
return this._binaryPatterns; | ||
} | ||
get recursivePatterns(): readonly Pattern[] { | ||
return this._recursivePatterns; | ||
} | ||
constructor(name: string, patterns: Pattern[]) { | ||
@@ -70,8 +86,17 @@ if (patterns.length === 0) { | ||
this._name = name; | ||
this._parent = null; | ||
this._firstIndex = -1; | ||
this._unaryPatterns = []; | ||
this._binaryPatterns = []; | ||
this._recursivePatterns = []; | ||
this._recursiveNames = []; | ||
this._binaryNames = []; | ||
this._binaryAssociation = []; | ||
this._precedenceMap = {}; | ||
this._originalPatterns = patterns; | ||
this._patterns = this._organizePatterns(patterns); | ||
this._patterns.forEach(p => p.parent = this); | ||
this._patterns = this._organizePatterns(patterns); | ||
if (this._unaryPatterns.length === 0) { | ||
throw new Error("Need at least one operand pattern with an 'expression' pattern."); | ||
} | ||
} | ||
@@ -83,3 +108,3 @@ | ||
if (this._isBinary(pattern)) { | ||
const binaryName = pattern.name; | ||
const binaryName = this._extractName(pattern); | ||
const clone = this._extractDelimiter(pattern).clone(); | ||
@@ -99,2 +124,10 @@ clone.parent = this; | ||
finalPatterns.push(clone); | ||
} else if (this._isRecursive(pattern)) { | ||
const name = this._extractName(pattern); | ||
const tail = this._extractRecursiveTail(pattern); | ||
tail.parent = this; | ||
this._recursivePatterns.push(tail); | ||
this._recursiveNames.push(name); | ||
finalPatterns.push(tail); | ||
} else { | ||
@@ -129,6 +162,39 @@ const clone = pattern.clone(); | ||
private _extractDelimiter(pattern) { | ||
private _extractDelimiter(pattern: Pattern) { | ||
if (pattern.type === "right-associated") { | ||
return pattern.children[0].children[1]; | ||
} | ||
return pattern.children[1]; | ||
} | ||
private _extractName(pattern: Pattern) { | ||
if (pattern.type === "right-associated") { | ||
return pattern.children[0].name; | ||
} | ||
return pattern.name; | ||
} | ||
private _isRecursive(pattern: Pattern) { | ||
if (pattern.type === "right-associated" && this._isRecursivePattern(pattern.children[0])) { | ||
return true; | ||
} | ||
return this._isRecursivePattern(pattern); | ||
} | ||
private _isRecursivePattern(pattern: Pattern) { | ||
return pattern.type === "sequence" && | ||
pattern.children[0].type === "reference" && | ||
pattern.children[0].name === this.name && | ||
pattern.children.length > 2; | ||
} | ||
private _extractRecursiveTail(pattern: Pattern) { | ||
if (pattern.type === "right-associated") { | ||
return new Sequence(`${pattern.children[0].name}-tail`, pattern.children[0].children.slice(1)); | ||
} | ||
return new Sequence(`${pattern.name}-tail`, pattern.children.slice(1)); | ||
} | ||
parse(cursor: Cursor): Node | null { | ||
@@ -155,7 +221,12 @@ // This is a cache to help with speed | ||
private _tryToParse(cursor: Cursor): Node | null { | ||
if (depthCache.getDepth(this._id, this._firstIndex) > 2) { | ||
cursor.recordErrorAt(this._firstIndex, this._firstIndex, this); | ||
return null; | ||
} | ||
let lastUnaryNode: Node | null = null; | ||
let lastBinaryNode: Node | null = null; | ||
let onIndex = cursor.index; | ||
while (true) { | ||
outer: while (true) { | ||
onIndex = cursor.index; | ||
@@ -167,90 +238,245 @@ | ||
const pattern = this._unaryPatterns[i]; | ||
lastUnaryNode = pattern.parse(cursor); | ||
const node = pattern.parse(cursor); | ||
if (node != null) { | ||
lastUnaryNode = node; | ||
break; | ||
} else { | ||
lastUnaryNode = null; | ||
cursor.resolveError(); | ||
} | ||
} | ||
const canContinue = cursor.hasNext(); | ||
// if (canContinue) { | ||
// cursor.next(); | ||
// } else if (!canContinue && astStack.length > 1) { | ||
// } else if (!canContinue && astStack.length === 1){ | ||
// return astStack[0]; | ||
// } | ||
if (lastUnaryNode == null) { | ||
break; | ||
} | ||
if (cursor.hasNext()) { | ||
cursor.next(); | ||
} else { | ||
if (lastBinaryNode != null && lastUnaryNode != null) { | ||
lastBinaryNode.appendChild(lastUnaryNode); | ||
} | ||
break; | ||
} | ||
onIndex = cursor.index; | ||
for (let i = 0; i < this._recursivePatterns.length; i++) { | ||
const pattern = this._recursivePatterns[i]; | ||
const node = pattern.parse(cursor); | ||
if (node != null) { | ||
if (lastBinaryNode != null && lastUnaryNode != null) { | ||
lastBinaryNode.appendChild(lastUnaryNode); | ||
} | ||
const frontExpression = lastBinaryNode == null ? lastUnaryNode as Node : lastBinaryNode.findRoot(); | ||
const name = this._recursiveNames[i]; | ||
const recursiveNode = createNode(name, [frontExpression, ...node.children]); | ||
recursiveNode.normalize(this._firstIndex); | ||
return recursiveNode; | ||
} | ||
cursor.moveTo(onIndex); | ||
} | ||
onIndex = cursor.index; | ||
for (let i = 0; i < this._binaryPatterns.length; i++) { | ||
cursor.moveTo(onIndex); | ||
const pattern = this._binaryPatterns[i]; | ||
const name = this._binaryNames[i]; | ||
const pattern = this._binaryPatterns[i]; | ||
const delimiterNode = pattern.parse(cursor); | ||
const node = pattern.parse(cursor); | ||
if (delimiterNode == null) { | ||
if (i === this._binaryPatterns.length - 1) { | ||
if (lastBinaryNode == null) { | ||
return lastUnaryNode; | ||
} else if (lastUnaryNode != null) { | ||
lastBinaryNode.appendChild(lastUnaryNode); | ||
} | ||
} | ||
continue; | ||
} | ||
// if (node != null) { | ||
// binaryIndex = i; | ||
if (lastBinaryNode == null && lastUnaryNode != null && delimiterNode != null) { | ||
const node = createNode(name, [lastUnaryNode, delimiterNode]); | ||
lastBinaryNode = node; | ||
} else if (lastBinaryNode != null && lastUnaryNode != null && delimiterNode != null) { | ||
const precedence = this._precedenceMap[name]; | ||
const lastPrecendece = lastBinaryNode == null ? 0 : this._precedenceMap[lastBinaryNode.name]; | ||
const association = this._binaryAssociation[i]; | ||
// const binaryNode = Node.createNode(name, []); | ||
// association = this._binaryAssociation[i]; | ||
if (precedence === lastPrecendece && association === Association.right) { | ||
const node = createNode(name, [lastUnaryNode, delimiterNode]); | ||
lastBinaryNode.appendChild(node); | ||
lastBinaryNode = node; | ||
} else if (precedence === lastPrecendece) { | ||
const node = createNode(name, []); | ||
// if (association === Association.left) { | ||
// if (nodeToAppendTo != null){ | ||
// nodeToAppendTo = binaryNode; | ||
// } else { | ||
// nodeToAppendTo. | ||
// } | ||
// } else { | ||
lastBinaryNode.replaceWith(node); | ||
lastBinaryNode.appendChild(lastUnaryNode); | ||
node.append(lastBinaryNode, delimiterNode); | ||
lastBinaryNode = node; | ||
} else if (precedence > lastPrecendece) { | ||
const root = lastBinaryNode.findRoot(); | ||
lastBinaryNode.appendChild(lastUnaryNode); | ||
// } | ||
// } | ||
if (root != null) { | ||
const node = createNode(name, [root, delimiterNode]); | ||
lastBinaryNode = node; | ||
} else { | ||
const node = createNode(name, [lastUnaryNode, delimiterNode]); | ||
lastBinaryNode = node; | ||
} | ||
} else { | ||
const node = createNode(name, [lastUnaryNode, delimiterNode]); | ||
lastBinaryNode.appendChild(node); | ||
lastBinaryNode = node; | ||
} | ||
} | ||
if (cursor.hasNext()) { | ||
cursor.next(); | ||
} else { | ||
break outer; | ||
} | ||
break; | ||
} | ||
if (lastBinaryNode == null){ | ||
break; | ||
} | ||
} | ||
return null; | ||
} | ||
if (lastBinaryNode == null) { | ||
return lastUnaryNode; | ||
} else { | ||
const root = lastBinaryNode.findAncestor(n => n.parent == null) as Node || lastBinaryNode; | ||
if (lastBinaryNode.children.length < 3) { | ||
lastBinaryNode.remove(); | ||
exec(text: string, record?: boolean | undefined): ParseResult { | ||
throw new Error("Method not implemented."); | ||
if (lastBinaryNode === root) { | ||
return lastUnaryNode; | ||
} | ||
} | ||
root.normalize(this._firstIndex); | ||
return root; | ||
} | ||
} | ||
test(text: string, record?: boolean | undefined): boolean { | ||
throw new Error("Method not implemented."); | ||
test(text: string) { | ||
const cursor = new Cursor(text); | ||
const ast = this.parse(cursor); | ||
return ast?.value === text; | ||
} | ||
clone(name?: string | undefined): Pattern { | ||
throw new Error("Method not implemented."); | ||
exec(text: string, record = false): ParseResult { | ||
const cursor = new Cursor(text); | ||
record && cursor.startRecording(); | ||
const ast = this.parse(cursor); | ||
return { | ||
ast: ast?.value === text ? ast : null, | ||
cursor | ||
}; | ||
} | ||
getTokens(): string[] { | ||
throw new Error("Method not implemented."); | ||
return this.unaryPatterns.map(p => p.getTokens()).flat(); | ||
} | ||
getTokensAfter(childReference: Pattern): string[] { | ||
throw new Error("Method not implemented."); | ||
if (this.unaryPatterns.indexOf(childReference)) { | ||
const recursiveTokens = this._recursivePatterns.map(p => p.getTokens()).flat(); | ||
const binaryTokens = this._binaryPatterns.map(p => p.getTokens()).flat(); | ||
return [...recursiveTokens, ...binaryTokens]; | ||
} | ||
if (this.recursivePatterns.indexOf(childReference)) { | ||
return this._binaryPatterns.map(p => p.getTokens()).flat(); | ||
} | ||
if (this.binaryPatterns.indexOf(childReference)) { | ||
const unaryTokens = this._unaryPatterns.map(p => p.getTokens()).flat(); | ||
if (this._parent != null) { | ||
const nextTokens = this._parent.getTokensAfter(this); | ||
return [...unaryTokens, ...nextTokens]; | ||
} | ||
return unaryTokens; | ||
} | ||
return []; | ||
} | ||
getNextTokens(): string[] { | ||
throw new Error("Method not implemented."); | ||
if (this._parent == null) { | ||
return []; | ||
} | ||
return this._parent.getTokensAfter(this); | ||
} | ||
getPatterns(): Pattern[] { | ||
throw new Error("Method not implemented."); | ||
return this.unaryPatterns.map(p => p.getPatterns()).flat(); | ||
} | ||
getPatternsAfter(childReference: Pattern): Pattern[] { | ||
throw new Error("Method not implemented."); | ||
if (this.unaryPatterns.indexOf(childReference)) { | ||
const recursivePatterns = this._recursivePatterns.map(p => p.getPatterns()).flat(); | ||
const binaryPatterns = this._binaryPatterns.map(p => p.getPatterns()).flat(); | ||
return [...recursivePatterns, ...binaryPatterns]; | ||
} | ||
if (this.recursivePatterns.indexOf(childReference)) { | ||
return this._binaryPatterns.map(p => p.getPatterns()).flat(); | ||
} | ||
if (this.binaryPatterns.indexOf(childReference)) { | ||
const unaryPatterns = this._unaryPatterns.map(p => p.getPatterns()).flat(); | ||
if (this._parent != null) { | ||
const nextPatterns = this._parent.getPatternsAfter(this); | ||
return [...unaryPatterns, ...nextPatterns]; | ||
} | ||
return unaryPatterns; | ||
} | ||
return []; | ||
} | ||
getNextPatterns(): Pattern[] { | ||
throw new Error("Method not implemented."); | ||
if (this._parent == null) { | ||
return []; | ||
} | ||
return this._parent.getPatternsAfter(this); | ||
} | ||
find(predicate: (pattern: Pattern) => boolean): Pattern | null { | ||
throw new Error("Method not implemented."); | ||
find(predicate: (p: Pattern) => boolean): Pattern | null { | ||
return findPattern(this, predicate); | ||
} | ||
isEqual(pattern: Pattern): boolean { | ||
throw new Error("Method not implemented."); | ||
clone(name = this._name): Pattern { | ||
const clone = new ExpressionPattern(name, this._originalPatterns); | ||
clone._id = this._id; | ||
return clone; | ||
} | ||
} | ||
isEqual(pattern: ExpressionPattern): boolean { | ||
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index])); | ||
} | ||
} |
@@ -114,3 +114,2 @@ import { Node } from "../ast/Node"; | ||
if (depthCache.getDepth(this._id, this._firstIndex) > 2) { | ||
cursor.recordErrorAt(this._firstIndex, this._firstIndex, this); | ||
return null; | ||
@@ -117,0 +116,0 @@ } |
@@ -9,9 +9,21 @@ import { Node } from "../ast/Node"; | ||
export class RightAssociatedPattern implements Pattern { | ||
readonly id: string; | ||
readonly type: string; | ||
readonly name: string; | ||
private _id: string; | ||
private _type: string; | ||
private _name: string; | ||
private _parent: Pattern | null; | ||
readonly children: Pattern[]; | ||
private _children: Pattern[]; | ||
get parent() { | ||
get id(): string { | ||
return this._id; | ||
} | ||
get type(): string { | ||
return this._type; | ||
} | ||
get name(): string { | ||
return this._name; | ||
} | ||
get parent(): Pattern | null { | ||
return this._parent; | ||
@@ -24,8 +36,12 @@ } | ||
get children(): Pattern[] { | ||
return this._children; | ||
} | ||
constructor(pattern: Pattern) { | ||
this.id = `right-associated-${indexId++}`; | ||
this.type = "right-associated"; | ||
this.name = ""; | ||
this.parent = null; | ||
this.children = [pattern]; | ||
this._id = `right-associated-${indexId++}`; | ||
this._type = "right-associated"; | ||
this._name = ""; | ||
this._parent = null; | ||
this._children = [pattern.clone()]; | ||
} | ||
@@ -46,3 +62,5 @@ | ||
clone(_name?: string | undefined): Pattern { | ||
return new RightAssociatedPattern(this.children[0]); | ||
const clone = new RightAssociatedPattern(this.children[0]); | ||
clone._id = this._id; | ||
return clone; | ||
} | ||
@@ -49,0 +67,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1219267
170
20847