Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java Left-Right Parser

A left-right parser for the Java language.



Installation

You can download the latest version here. But you can also clone this repository and build the project using maven (mvn >= 3.3 and java 8 are recommended):

mvn clean install

After building the project, grab the target/jleri-0.0-SNAPSHOT.jar file and add it to your own project as library.

Related projects



Quick usage

We recommend using pyleri for creating a grammar and export the grammar to jleri. This way you can create one single grammar and export the grammar to program languages like C, JavaScript, Go, Java and Python.

// MyGrammar.javaimporttechnology.transceptor.jleri.Grammar;
importtechnology.transceptor.jleri.Element;
importtechnology.transceptor.jleri.Sequence;
importtechnology.transceptor.jleri.Regex;
importtechnology.transceptor.jleri.Keyword;
importtechnology.transceptor.jleri.Result;
importtechnology.transceptor.jleri.MaxRecursionException;
publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(K_HI, R_NAME);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
Resultres = grammar.parse("hi \"Iris\"");
/** * res.isValid * true or false depending if the string is successful parsed * by the grammar or not. * res.tree * contains the parse tree. * res.pos * the position in the string where parsing has end. * (if successful this will be equal to the string length) * res.getExpecting() * returns a HashSet<Element> with elements which are expected at * position res.pos. This can be used for auto-completion, * auto correction or suggestions. */System.out.println(res.isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Compile and run:

javac -cp jleri.jar MyGrammar.java && java -cp ./:jleri.jar MyGrammar

Elements

Jleri has several elements which can be used to create a grammar. Each element is a subclass of jleri.Element and accepts an optional first Enum id which can be used to identify the Element, for example in an node tree. The default id is set to null but as a user of jleri you should never set the id to null yourself, just omit the id in that case.

Keyword

importtechnology.transceptor.jleri.Keyword;
// Keyword(Enum id=null, String keyword, boolean ignCase=false)

The parser needs to match the keyword which is just a string. When matching keywords we need to tell the parser what characters are allowed in keywords. By default Jleri uses ^\w+ which is equal to ^[A-Za-z0-9_]+. We can overwrite the default by using a second argument while calling super inside the grammar constructor. Keyword() accepts a boolean argument ignCase which when omitted is set to false and tells the parser if we should match case insensitive.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newKeyword("tic-tac-toe", true);
publicTicTacToe() {
// Let's allow keywords with alphabetic characters and dashes.super(START, "^[A-Za-z-]+");
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic-Tac-Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Regex

importtechnology.transceptor.jleri.Regex;
// Regex(Enum id=null, <java.util.regex.Pattern, String> pattern)

The parser uses a regular expression for matching this element.

See Quick usage for an example on how to use jleri.Regex.

Token

importtechnology.transceptor.jleri.Token;
// Token(Enum id=null, <String, Char> token)

A token can be one or more characters and is usually used to match operators like +, -, // and so on.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newSequence(
K_NI, newToken('+'), K_NI);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni+ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Tokens

importtechnology.transceptor.jleri.Tokens;
// Tokens(Enum id=null, String tokens)

Can be used to register multiple tokens at once. The tokens argument should be a string with tokens separated by spaces. If given tokens are different in size the parser will try to match the longest tokens first.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementTKS = newTokens("+ - !=");
privatestaticfinalElementSTART = newList(
newKeyword("ni"), TKS, 0, null, false
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("ni + ni != ni- ni").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Sequence

importtechnology.transceptor.jleri.Sequence;
// Sequence(Enum id=null, Element... elems)

The parser needs to match each element in a sequence.

Example:

publicclassTicTacToeextendsGrammar {
privatestaticfinalElementSTART = newSequence(
newKeyword("Tic"),
newKeyword("Tac"),
newKeyword("Toe")
);
publicTicTacToe() {
super(START);
}
publicstaticvoidmain(String []args) {
TicTacToegrammar = newTicTacToe();
try {
System.out.println(grammar.parse("Tic Tac Toe").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Choice

importtechnology.transceptor.jleri.Choice;
// Choice(Enum id=null, boolean mostGreedy=true, Element... elems)

The parser needs to choose between one of the given elements. Choice accepts a boolean argument mostGreedy which when omitted defaults to true. When mostGreedy is set to false the parser will stop at the first match. When true the parser will try each element and returns the longest match. Setting mostGreedy to false can provide some extra performance. Note that the parser will try to match each element in the exact same order they are parsed to Choice.

Example: let us use Choice to modify the Quick usage example to allow the string bye "Iris"

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementK_BYE = newKeyword("bye");
privatestaticfinalElementSTART = newSequence(
newChoice(K_HI, K_BYE),
R_NAME
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("bye \"Iris\"").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Repeat

importtechnology.transceptor.jleri.Repeat;
// Repeat(Enum id=null, Element elem, int min=0, Integer max=null)

The parser needs at least min elements and at most max elements. min can be any integer value equal or higher than 0. When max is set to null we allow unlimited number of elements or in case a value is used it must al least equal or higher than min.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newRepeat(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni ni ni ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

It is not allowed to bind a name to the same element twice and Repeat(elem, 1, 1) is a common solution to bind the element a second (or more) time(s).

For example consider the following:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
/** * We should avoid using this: * private static final Element R_ADDRESS = R_NAME * * Instead use Repeat: */privatestaticfinalElementR_ADDRESS = newRepeat(R_NAME, 1, 1);
}

List

importtechnology.transceptor.jleri.List;
/** * List(Enum id=null, * Element elem, * Element delimiter=new Token(','), * int min=0, * Integer max=null, * boolean optClose=false); */

List is like Repeat but with a delimiter. A comma (Token) is used as default delimiter but any element is allowed. mix and max work exactly like with Repeat. Argument optClose can be set to true to allow the list to end with a delimiter. When omitted this is set to false which means the list has to end with an element.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementSTART = newList(newKeyword("ni"));
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(grammar.parse("ni, ni, ni").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Optional

importtechnology.transceptor.jleri.Optional;
// Optional(Enum id=null, Element elem)

The parser looks for an optional element. It is like using Repeat(element, 0, 1) but we encourage to use Optional since it is more readable. (and slightly faster)

Example:

publicclassMyGrammarextendsGrammar {
privatestaticfinalElementR_NAME = newRegex("^(?:\"(?:[^\"]*)\")+");
privatestaticfinalElementK_HI = newKeyword("hi");
privatestaticfinalElementSTART = newSequence(
K_HI,
newOptional(R_NAME)
);
publicMyGrammar() {
super(START);
}
publicstaticvoidmain(String []args) {
MyGrammargrammar = newMyGrammar();
try {
System.out.println(grammar.parse("hi \"Iris\"").isValid); // trueSystem.out.println(grammar.parse("hi").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Ref

importtechnology.transceptor.jleri.Ref;
// Ref()

The grammar can make a forward reference to make recursion possible. In the example below we create a forward reference to START but note that a reference to any element can be made. Ref() accepts no arguments.

Warning: A reference is not protected against testing the same position in in a string. This could potentially lead to an infinite loop. For example:

Elementr = newRef();
((Ref) r).set(newOptional(r)); // DON'T DO THIS

Use Prio if such recursive construction is required.

Example:

publicclassNestedNiextendsGrammar {
privatestaticfinalElementSTART = newRef();
privatestaticfinalElementNI_ITM = newChoice(newKeyword('ni'), START);
publicNestedNi() {
super(START);
((Ref) START).set(newSequence(
newToken('['), newList(NI_ITM), newToken(']')
));
}
publicstaticvoidmain(String []args) {
NestedNigrammar = newNestedNi();
try {
System.out.println(grammar.parse(
"[ni, ni, [ni, [], [ni, ni]]").isValid); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

Prio

importtechnology.transceptor.jleri.Prio;
importtechnology.transceptor.jleri.This; // exposes This.THIS// Prio(Enum id, Element... elems)

Choose the first match from the Prio elements and allow This.THIS for recursive operations. With This.THIS we point to the Prio element. Probably the example below explains how Prio and This.THIS can be used.

Note: Use a Ref when possible. A Prio element is required when the same position in a string is potentially checked more than once.

Example:

publicclassNiextendsGrammar {
privatestaticfinalElementK_NI = newKeyword("ni");
privatestaticfinalElementSTART = newPrio(
K_NI,
newSequence(newToken('('), This.THIS, newToken(')')),
newSequence(This.THIS, newKeyword("or"), This.THIS),
newSequence(This.THIS, newKeyword("and"), This.THIS)
);
publicNi() {
super(START);
}
publicstaticvoidmain(String []args) {
Nigrammar = newNi();
try {
System.out.println(
grammar.parse("(ni or ni) and (ni or ni)").isValid
); // true
} catch (MaxRecursionExceptionex) {
// Maximum recursion occurred
}
}
}

About

Java Left-Right Parser

Resources

Stars

6 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages