Initial
This commit is contained in:
commit
664c9a4f31
25
pom.xml
Normal file
25
pom.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.annimon</groupId>
|
||||||
|
<artifactId>ParboiledExample</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.parboiled</groupId>
|
||||||
|
<artifactId>parboiled-core</artifactId>
|
||||||
|
<version>1.1.7</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.parboiled</groupId>
|
||||||
|
<artifactId>parboiled-java</artifactId>
|
||||||
|
<version>1.1.7</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
10
src/main/java/com/annimon/parboiled/calc/AstNode.java
Normal file
10
src/main/java/com/annimon/parboiled/calc/AstNode.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface AstNode {
|
||||||
|
|
||||||
|
int eval();
|
||||||
|
}
|
36
src/main/java/com/annimon/parboiled/calc/BlockNode.java
Normal file
36
src/main/java/com/annimon/parboiled/calc/BlockNode.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.parboiled.trees.ImmutableTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class BlockNode extends ImmutableTreeNode implements AstNode {
|
||||||
|
|
||||||
|
private final List<AstNode> nodes;
|
||||||
|
|
||||||
|
public BlockNode(AstNode node) {
|
||||||
|
this(Arrays.asList(node));
|
||||||
|
System.out.println(node.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockNode(AstNode... nodes) {
|
||||||
|
this(Arrays.asList(nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockNode(List<AstNode> nodes) {
|
||||||
|
super(nodes);
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eval() {
|
||||||
|
nodes.stream()
|
||||||
|
.filter(n -> n != null)
|
||||||
|
.forEach(AstNode::eval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/com/annimon/parboiled/calc/NumberNode.java
Normal file
25
src/main/java/com/annimon/parboiled/calc/NumberNode.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
import org.parboiled.trees.ImmutableTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class NumberNode extends ImmutableTreeNode implements AstNode {
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
public NumberNode(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eval() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/com/annimon/parboiled/calc/OkaNode.java
Normal file
19
src/main/java/com/annimon/parboiled/calc/OkaNode.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
import org.parboiled.trees.ImmutableTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class OkaNode extends ImmutableTreeNode implements AstNode {
|
||||||
|
|
||||||
|
public OkaNode() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eval() {
|
||||||
|
System.out.println("Я Ока");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
73
src/main/java/com/annimon/parboiled/calc/OkaParser.java
Normal file
73
src/main/java/com/annimon/parboiled/calc/OkaParser.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import static org.parboiled.BaseParser.EOI;
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import static org.parboiled.errors.ErrorUtils.printParseErrors;
|
||||||
|
import org.parboiled.parserunners.RecoveringParseRunner;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
public class OkaParser extends BaseParser<AstNode> {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
final OkaParser parser = Parboiled.createParser(OkaParser.class);
|
||||||
|
ParsingResult<?> result = new RecoveringParseRunner(parser.Program())
|
||||||
|
.run("ок ори 3 ори 2 ока ока ори 2");
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
System.out.println("\nParse Errors:\n" + printParseErrors(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode node = (AstNode) result.resultValue;
|
||||||
|
node.eval();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Program() {
|
||||||
|
return Sequence(Block(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Block() {
|
||||||
|
final List<AstNode> nodes = new ArrayList<>();
|
||||||
|
return Sequence(
|
||||||
|
Statement(),
|
||||||
|
ZeroOrMore(nodes.add(pop()), Statement()),
|
||||||
|
push(new BlockNode(nodes))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Statement() {
|
||||||
|
return FirstOf(Oka(), Ori());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Oka() {
|
||||||
|
return Sequence(
|
||||||
|
IgnoreCase("ока "),
|
||||||
|
push(new OkaNode())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Ori() {
|
||||||
|
return Sequence(
|
||||||
|
Sequence(IgnoreCase("ори "), Number()),
|
||||||
|
push(new OriNode(pop()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Number() {
|
||||||
|
return Sequence(
|
||||||
|
OneOrMore(Digit()),
|
||||||
|
push(new NumberNode(Integer.parseInt(matchOrDefault("0")))),
|
||||||
|
WhiteSpace()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule WhiteSpace() {
|
||||||
|
return ZeroOrMore(AnyOf(" \t\f"));
|
||||||
|
}
|
||||||
|
}
|
28
src/main/java/com/annimon/parboiled/calc/OriNode.java
Normal file
28
src/main/java/com/annimon/parboiled/calc/OriNode.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.annimon.parboiled.calc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import org.parboiled.trees.ImmutableTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class OriNode extends ImmutableTreeNode implements AstNode {
|
||||||
|
|
||||||
|
private final AstNode node;
|
||||||
|
|
||||||
|
public OriNode(AstNode node) {
|
||||||
|
super(Arrays.asList(node));
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eval() {
|
||||||
|
if (node == null) return 0;
|
||||||
|
|
||||||
|
IntStream.range(0, node.eval())
|
||||||
|
.forEach(i -> System.out.println("ору"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
50
src/main/java/org/parboiled/examples/abc/AbcParser.java
Normal file
50
src/main/java/org/parboiled/examples/abc/AbcParser.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.abc;
|
||||||
|
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parser for the classic non-context free language example { a^n b^n c^n : n >= 1 }
|
||||||
|
* S <- &(A c) a+ B !(a|b|c)
|
||||||
|
* A <- a A? b
|
||||||
|
* B <- b B? c
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"InfiniteRecursion"})
|
||||||
|
@BuildParseTree
|
||||||
|
public class AbcParser extends BaseParser<Object> {
|
||||||
|
|
||||||
|
public Rule S() {
|
||||||
|
return Sequence(
|
||||||
|
Test(A(), 'c'),
|
||||||
|
OneOrMore('a'),
|
||||||
|
B(),
|
||||||
|
TestNot(AnyOf("abc"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule A() {
|
||||||
|
return Sequence('a', Optional(A()), 'b');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule B() {
|
||||||
|
return Sequence('b', Optional(B()), 'c');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
src/main/java/org/parboiled/examples/abc/Main.java
Normal file
49
src/main/java/org/parboiled/examples/abc/Main.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.abc;
|
||||||
|
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.common.StringUtils;
|
||||||
|
import org.parboiled.errors.ErrorUtils;
|
||||||
|
import org.parboiled.parserunners.RecoveringParseRunner;
|
||||||
|
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
|
||||||
|
|
||||||
|
import org.parboiled.parserunners.ReportingParseRunner;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
AbcParser parser = Parboiled.createParser(AbcParser.class);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.print("Enter an a^n b^n c^n expression (single RETURN to exit)!\n");
|
||||||
|
String input = new Scanner(System.in).nextLine();
|
||||||
|
if (StringUtils.isEmpty(input)) break;
|
||||||
|
|
||||||
|
ParsingResult<?> result = new ReportingParseRunner(parser.S()).run(input);
|
||||||
|
|
||||||
|
if (!result.parseErrors.isEmpty())
|
||||||
|
System.out.println(ErrorUtils.printParseError(result.parseErrors.get(0)));
|
||||||
|
else
|
||||||
|
System.out.println(printNodeTree(result) + '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.parserunners.RecoveringParseRunner;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.common.StringUtils;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
import org.parboiled.support.ToStringFormatter;
|
||||||
|
import org.parboiled.trees.GraphNode;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import static org.parboiled.errors.ErrorUtils.printParseErrors;
|
||||||
|
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
|
||||||
|
import static org.parboiled.trees.GraphUtils.printTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class of all calculator parsers in the org.parboiled.examples.calculators package.
|
||||||
|
* Simply adds the public static main entry point.
|
||||||
|
*
|
||||||
|
* @param <V> the type of the main value object created by the parser
|
||||||
|
*/
|
||||||
|
public abstract class CalculatorParser<V> extends BaseParser<V> {
|
||||||
|
|
||||||
|
public abstract Rule InputLine();
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked"})
|
||||||
|
public static <V, P extends CalculatorParser<V>> void main(Class<P> parserClass) {
|
||||||
|
CalculatorParser<V> parser = Parboiled.createParser(parserClass);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.print("Enter a calculators expression (single RETURN to exit)!\n");
|
||||||
|
String input = new Scanner(System.in).nextLine();
|
||||||
|
if (StringUtils.isEmpty(input)) break;
|
||||||
|
|
||||||
|
ParsingResult<?> result = new RecoveringParseRunner(parser.InputLine()).run(input);
|
||||||
|
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
System.out.println("\nParse Errors:\n" + printParseErrors(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = result.parseTreeRoot.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
String str = value.toString();
|
||||||
|
int ix = str.indexOf('|');
|
||||||
|
if (ix >= 0) str = str.substring(ix + 2); // extract value part of AST node toString()
|
||||||
|
System.out.println(input + " = " + str + '\n');
|
||||||
|
}
|
||||||
|
if (value instanceof GraphNode) {
|
||||||
|
System.out.println("\nAbstract Syntax Tree:\n" +
|
||||||
|
printTree((GraphNode) value, new ToStringFormatter(null)) + '\n');
|
||||||
|
} else {
|
||||||
|
System.out.println("\nParse Tree:\n" + printNodeTree(result) + '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic calculator parser without any actions.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class CalculatorParser0 extends CalculatorParser<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rule InputLine() {
|
||||||
|
return Sequence(Expression(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Expression() {
|
||||||
|
return Sequence(Term(), ZeroOrMore(AnyOf("+-"), Term()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Term() {
|
||||||
|
return Sequence(Factor(), ZeroOrMore(AnyOf("*/"), Factor()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Factor() {
|
||||||
|
return FirstOf(Number(), Parens());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Parens() {
|
||||||
|
return Sequence('(', Expression(), ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Number() {
|
||||||
|
return OneOrMore(Digit());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************** MAIN ****************
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
main(CalculatorParser0.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
import org.parboiled.annotations.SuppressSubnodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A calculator parser building calculation results directly in the parsers value stack.
|
||||||
|
* All calculations are implemented directly in action expressions.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class CalculatorParser1 extends CalculatorParser<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rule InputLine() {
|
||||||
|
return Sequence(Expression(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Expression() {
|
||||||
|
return Sequence(
|
||||||
|
Term(), // a successful match of a Term pushes one Integer value onto the value stack
|
||||||
|
ZeroOrMore(
|
||||||
|
FirstOf(
|
||||||
|
// the action that is run after the '+' and the Term have been matched consumes the
|
||||||
|
// two top value stack elements and replaces them with the calculation result
|
||||||
|
Sequence('+', Term(), push(pop() + pop())),
|
||||||
|
|
||||||
|
// same for the '-' operator, however, here the order of the "pop"s matters, we need to
|
||||||
|
// retrieve the second to last value first, which is what the pop(1) call does
|
||||||
|
Sequence('-', Term(), push(pop(1) - pop()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Term() {
|
||||||
|
return Sequence(
|
||||||
|
Factor(), // a successful match of a Factor pushes one Integer value onto the value stack
|
||||||
|
ZeroOrMore(
|
||||||
|
FirstOf(
|
||||||
|
// the action that is run after the '*' and the Factor have been matched consumes the
|
||||||
|
// two top value stack elements and replaces them with the calculation result
|
||||||
|
Sequence('*', Factor(), push(pop() * pop())),
|
||||||
|
|
||||||
|
// same for the '/' operator, however, here the order of the "pop"s matters, we need to
|
||||||
|
// retrieve the second to last value first, which is what the pop(1) call does
|
||||||
|
Sequence('/', Factor(), push(pop(1) / pop()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Factor() {
|
||||||
|
return FirstOf(Number(), Parens()); // a factor "produces" exactly one Integer value on the value stack
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Parens() {
|
||||||
|
return Sequence('(', Expression(), ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Number() {
|
||||||
|
return Sequence(
|
||||||
|
Digits(),
|
||||||
|
|
||||||
|
// parse the input text matched by the preceding "Digits" rule,
|
||||||
|
// convert it into an Integer and push it onto the value stack
|
||||||
|
// the action uses a default string in case it is run during error recovery (resynchronization)
|
||||||
|
push(Integer.parseInt(matchOrDefault("0")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressSubnodes
|
||||||
|
public Rule Digits() {
|
||||||
|
return OneOrMore(Digit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************** MAIN ****************
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
main(CalculatorParser1.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
import org.parboiled.annotations.SuppressSubnodes;
|
||||||
|
import org.parboiled.examples.calculators.CalculatorParser2.CalcNode;
|
||||||
|
import org.parboiled.support.Var;
|
||||||
|
import org.parboiled.trees.ImmutableBinaryTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A calculator parser building an AST representing the expression structure before performing the actual calculation.
|
||||||
|
* The parser value stack is used to build the AST nodes of type CalcNode.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class CalculatorParser2 extends CalculatorParser<CalcNode> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rule InputLine() {
|
||||||
|
return Sequence(Expression(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Expression() {
|
||||||
|
Var<Character> op = new Var<Character>(); // we use an action variable to hold the operator character
|
||||||
|
return Sequence(
|
||||||
|
Term(),
|
||||||
|
ZeroOrMore(
|
||||||
|
AnyOf("+-"),
|
||||||
|
op.set(matchedChar()), // set the action variable to the matched operator char
|
||||||
|
Term(),
|
||||||
|
|
||||||
|
// create an AST node for the operation that was just matched
|
||||||
|
// we consume the two top stack elements and replace them with a new AST node
|
||||||
|
// we use an alternative technique to the one shown in CalculatorParser1 to reverse
|
||||||
|
// the order of the two top value stack elements
|
||||||
|
swap() && push(new CalcNode(op.get(), pop(), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Term() {
|
||||||
|
Var<Character> op = new Var<Character>(); // we use an action variable to hold the operator character
|
||||||
|
return Sequence(
|
||||||
|
Factor(),
|
||||||
|
ZeroOrMore(
|
||||||
|
AnyOf("*/"),
|
||||||
|
op.set(matchedChar()), // set the action variable to the matched operator char
|
||||||
|
Factor(),
|
||||||
|
|
||||||
|
// create an AST node for the operation that was just matched
|
||||||
|
// we consume the two top stack elements and replace them with a new AST node
|
||||||
|
// we use an alternative technique to the one shown in CalculatorParser1 to reverse
|
||||||
|
// the order of the two top value stack elements
|
||||||
|
swap() && push(new CalcNode(op.get(), pop(), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Factor() {
|
||||||
|
return FirstOf(Number(), Parens());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Parens() {
|
||||||
|
return Sequence('(', Expression(), ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Number() {
|
||||||
|
return Sequence(
|
||||||
|
Digits(),
|
||||||
|
|
||||||
|
// parse the input text matched by the preceding "Digits" rule,
|
||||||
|
// convert it into an Integer and push a new AST node for it onto the value stack
|
||||||
|
// the action uses a default string in case it is run during error recovery (resynchronization)
|
||||||
|
push(new CalcNode(Integer.parseInt(matchOrDefault("0"))))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressSubnodes
|
||||||
|
public Rule Digits() {
|
||||||
|
return OneOrMore(Digit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AST node for the calculators. The type of the node is carried as a Character that can either contain
|
||||||
|
* an operator char or be null. In the latter case the AST node is a leaf directly containing a value.
|
||||||
|
*/
|
||||||
|
public static class CalcNode extends ImmutableBinaryTreeNode<CalcNode> {
|
||||||
|
private int value;
|
||||||
|
private Character operator;
|
||||||
|
|
||||||
|
public CalcNode(int value) {
|
||||||
|
super(null, null);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalcNode(Character operator, CalcNode left, CalcNode right) {
|
||||||
|
super(left, right);
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
if (operator == null) return value;
|
||||||
|
switch (operator) {
|
||||||
|
case '+':
|
||||||
|
return left().getValue() + right().getValue();
|
||||||
|
case '-':
|
||||||
|
return left().getValue() - right().getValue();
|
||||||
|
case '*':
|
||||||
|
return left().getValue() * right().getValue();
|
||||||
|
case '/':
|
||||||
|
return left().getValue() / right().getValue();
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (operator == null ? "Value " + value : "Operator '" + operator + '\'') + " | " + getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************** MAIN ****************
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
main(CalculatorParser2.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
import org.parboiled.examples.calculators.CalculatorParser3.CalcNode;
|
||||||
|
import org.parboiled.support.Var;
|
||||||
|
import org.parboiled.trees.ImmutableBinaryTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A calculator parser building an AST representing the expression structure before performing the actual calculation.
|
||||||
|
* The value field of the parse tree nodes is used for AST nodes.
|
||||||
|
* As opposed to the CalculatorParser2 this parser also supports floating point operations, negative numbers, a "power"
|
||||||
|
* and a "SQRT" operation as well as optional whitespace between the various expressions components.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class CalculatorParser3 extends CalculatorParser<CalcNode> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rule InputLine() {
|
||||||
|
return Sequence(Expression(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Expression() {
|
||||||
|
Var<Character> op = new Var<Character>();
|
||||||
|
return Sequence(
|
||||||
|
Term(),
|
||||||
|
ZeroOrMore(
|
||||||
|
// we use a FirstOf(String, String) instead of a AnyOf(String) so we can use the
|
||||||
|
// fromStringLiteral transformation (see below), which automatically consumes trailing whitespace
|
||||||
|
FirstOf("+ ", "- "), op.set(matchedChar()),
|
||||||
|
Term(),
|
||||||
|
|
||||||
|
// same as in CalculatorParser2
|
||||||
|
push(new CalcNode(op.get(), pop(1), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Term() {
|
||||||
|
Var<Character> op = new Var<Character>();
|
||||||
|
return Sequence(
|
||||||
|
Factor(),
|
||||||
|
ZeroOrMore(
|
||||||
|
FirstOf("* ", "/ "), op.set(matchedChar()),
|
||||||
|
Factor(),
|
||||||
|
push(new CalcNode(op.get(), pop(1), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Factor() {
|
||||||
|
return Sequence(
|
||||||
|
Atom(),
|
||||||
|
ZeroOrMore(
|
||||||
|
"^ ",
|
||||||
|
Atom(),
|
||||||
|
push(new CalcNode('^', pop(1), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Atom() {
|
||||||
|
return FirstOf(Number(), SquareRoot(), Parens());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule SquareRoot() {
|
||||||
|
return Sequence(
|
||||||
|
"SQRT ",
|
||||||
|
Parens(),
|
||||||
|
|
||||||
|
// create a new AST node with a special operator 'R' and only one child
|
||||||
|
push(new CalcNode('R', pop(), null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Parens() {
|
||||||
|
return Sequence("( ", Expression(), ") ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Number() {
|
||||||
|
return Sequence(
|
||||||
|
// we use another Sequence in the "Number" Sequence so we can easily access the input text matched
|
||||||
|
// by the three enclosed rules with "match()" or "matchOrDefault()"
|
||||||
|
Sequence(
|
||||||
|
Optional('-'),
|
||||||
|
OneOrMore(Digit()),
|
||||||
|
Optional('.', OneOrMore(Digit()))
|
||||||
|
),
|
||||||
|
|
||||||
|
// the matchOrDefault() call returns the matched input text of the immediately preceding rule
|
||||||
|
// or a default string (in this case if it is run during error recovery (resynchronization))
|
||||||
|
push(new CalcNode(Double.parseDouble(matchOrDefault("0")))),
|
||||||
|
WhiteSpace()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule WhiteSpace() {
|
||||||
|
return ZeroOrMore(AnyOf(" \t\f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// we redefine the rule creation for string literals to automatically match trailing whitespace if the string
|
||||||
|
// literal ends with a space character, this way we don't have to insert extra whitespace() rules after each
|
||||||
|
// character or string literal
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rule fromStringLiteral(String string) {
|
||||||
|
return string.endsWith(" ") ?
|
||||||
|
Sequence(String(string.substring(0, string.length() - 1)), WhiteSpace()) :
|
||||||
|
String(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AST node for the calculators. The type of the node is carried as a Character that can either contain
|
||||||
|
* an operator char or be null. In the latter case the AST node is a leaf directly containing a value.
|
||||||
|
*/
|
||||||
|
public static class CalcNode extends ImmutableBinaryTreeNode<CalcNode> {
|
||||||
|
private double value;
|
||||||
|
private Character operator;
|
||||||
|
|
||||||
|
public CalcNode(double value) {
|
||||||
|
super(null, null);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalcNode(Character operator, CalcNode left, CalcNode right) {
|
||||||
|
super(left, right);
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
if (operator == null) return value;
|
||||||
|
switch (operator) {
|
||||||
|
case '+':
|
||||||
|
return left().getValue() + right().getValue();
|
||||||
|
case '-':
|
||||||
|
return left().getValue() - right().getValue();
|
||||||
|
case '*':
|
||||||
|
return left().getValue() * right().getValue();
|
||||||
|
case '/':
|
||||||
|
return left().getValue() / right().getValue();
|
||||||
|
case '^':
|
||||||
|
return Math.pow(left().getValue(), right().getValue());
|
||||||
|
case 'R':
|
||||||
|
return Math.sqrt(left().getValue());
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (operator == null ? "Value " + value : "Operator '" + operator + '\'') + " | " + getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************** MAIN ****************
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
main(CalculatorParser3.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.calculators;
|
||||||
|
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
import org.parboiled.examples.calculators.CalculatorParser3.CalcNode;
|
||||||
|
import org.parboiled.support.Var;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A calculator parser defining the same language as the CalculatorParser3 but using a rule building helper methods
|
||||||
|
* to Factor out common constructs.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class CalculatorParser4 extends CalculatorParser<CalcNode> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rule InputLine() {
|
||||||
|
return Sequence(Expression(), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Expression() {
|
||||||
|
return OperatorRule(Term(), FirstOf("+ ", "- "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Term() {
|
||||||
|
return OperatorRule(Factor(), FirstOf("* ", "/ "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Factor() {
|
||||||
|
// by using toRule("^ ") instead of Ch('^') we make use of the fromCharLiteral(...) transformation below
|
||||||
|
return OperatorRule(Atom(), toRule("^ "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OperatorRule(Rule subRule, Rule operatorRule) {
|
||||||
|
Var<Character> op = new Var<Character>();
|
||||||
|
return Sequence(
|
||||||
|
subRule,
|
||||||
|
ZeroOrMore(
|
||||||
|
operatorRule, op.set(matchedChar()),
|
||||||
|
subRule,
|
||||||
|
push(new CalcNode(op.get(), pop(1), pop()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Atom() {
|
||||||
|
return FirstOf(Number(), SquareRoot(), Parens());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SquareRoot() {
|
||||||
|
return Sequence("SQRT", Parens(), push(new CalcNode('R', pop(), null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Parens() {
|
||||||
|
return Sequence("( ", Expression(), ") ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Number() {
|
||||||
|
return Sequence(
|
||||||
|
Sequence(
|
||||||
|
Optional(Ch('-')),
|
||||||
|
OneOrMore(Digit()),
|
||||||
|
Optional(Ch('.'), OneOrMore(Digit()))
|
||||||
|
),
|
||||||
|
// the action uses a default string in case it is run during error recovery (resynchronization)
|
||||||
|
push(new CalcNode(Double.parseDouble(matchOrDefault("0")))),
|
||||||
|
WhiteSpace()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule WhiteSpace() {
|
||||||
|
return ZeroOrMore(AnyOf(" \t\f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// we redefine the rule creation for string literals to automatically match trailing whitespace if the string
|
||||||
|
// literal ends with a space character, this way we don't have to insert extra whitespace() rules after each
|
||||||
|
// character or string literal
|
||||||
|
@Override
|
||||||
|
protected Rule fromStringLiteral(String string) {
|
||||||
|
return string.endsWith(" ") ?
|
||||||
|
Sequence(String(string.substring(0, string.length() - 1)), WhiteSpace()) :
|
||||||
|
String(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************** MAIN ****************
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
main(CalculatorParser4.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.parboiled.examples.indenting;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IndentNode {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final List<IndentNode> children = new ArrayList<IndentNode>();
|
||||||
|
|
||||||
|
public IndentNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addChild(IndentNode child) {
|
||||||
|
children.add(child);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IndentNode> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IndentNode [name=" + name + ", children=" + children + "]";
|
||||||
|
}
|
||||||
|
}
|
30
src/main/java/org/parboiled/examples/indenting/Main.java
Normal file
30
src/main/java/org/parboiled/examples/indenting/Main.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package org.parboiled.examples.indenting;
|
||||||
|
|
||||||
|
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
|
||||||
|
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.buffers.IndentDedentInputBuffer;
|
||||||
|
import org.parboiled.errors.ErrorUtils;
|
||||||
|
import org.parboiled.parserunners.ReportingParseRunner;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SimpleIndent parser = Parboiled.createParser(SimpleIndent.class);
|
||||||
|
String input = "NodeA \n\tNodeB\n\tNodeC \n\t\tNodeD \nNodeE";
|
||||||
|
|
||||||
|
ParsingResult<?> result = new ReportingParseRunner(parser.Parent())
|
||||||
|
.run(new IndentDedentInputBuffer(input.toCharArray(), 2, ";", true, true));
|
||||||
|
|
||||||
|
if (!result.parseErrors.isEmpty()) {
|
||||||
|
System.out.println(ErrorUtils.printParseError(result.parseErrors
|
||||||
|
.get(0)));
|
||||||
|
} else {
|
||||||
|
System.out.println("NodeTree: " + printNodeTree(result) + '\n');
|
||||||
|
Object value = result.parseTreeRoot.getValue();
|
||||||
|
System.out.println(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package org.parboiled.examples.indenting;
|
||||||
|
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
|
||||||
|
@BuildParseTree
|
||||||
|
public class SimpleIndent extends BaseParser<IndentNode> {
|
||||||
|
|
||||||
|
Rule Parent() {
|
||||||
|
return Sequence(push(new IndentNode("root")), OneOrMore(Data()), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Data() {
|
||||||
|
return Sequence(Identifier(), push(new IndentNode(match())), peek(1)
|
||||||
|
.addChild(peek()),
|
||||||
|
Optional(Sequence(Spacing(), ChildNodeList())), drop());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule ChildNodeList() {
|
||||||
|
return Sequence(INDENT, Spacing(), OneOrMore(Data(), Spacing()), DEDENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Identifier() {
|
||||||
|
return Sequence(PN_CHARS_U(), ZeroOrMore(PN_CHARS_DIGIT_U()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS_DIGIT_U() {
|
||||||
|
return FirstOf(PN_CHARS_U(), DIGIT());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS_U() {
|
||||||
|
return FirstOf(PN_CHARS_BASE(), '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS_BASE() {
|
||||||
|
return FirstOf(
|
||||||
|
CharRange('A', 'Z'),
|
||||||
|
CharRange('a', 'z'),
|
||||||
|
CharRange('\u00C0', '\u00D6'),
|
||||||
|
CharRange('\u00D8', '\u00F6'),
|
||||||
|
CharRange('\u00F8', '\u02FF'),
|
||||||
|
CharRange('\u0370', '\u037D'),
|
||||||
|
CharRange('\u037F', '\u1FFF'),
|
||||||
|
CharRange('\u200C', '\u200D'),
|
||||||
|
CharRange('\u2070', '\u218F'),
|
||||||
|
CharRange('\u2C00', '\u2FEF'),
|
||||||
|
CharRange('\u3001', '\uD7FF'),
|
||||||
|
CharRange('\uF900', '\uFDCF'),
|
||||||
|
CharRange('\uFDF0', '\uFFFD')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DIGIT() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Spacing() {
|
||||||
|
return ZeroOrMore(AnyOf(" \t\r\n\f").label("Whitespace"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.java;
|
||||||
|
|
||||||
|
import org.parboiled.MatcherContext;
|
||||||
|
import org.parboiled.matchers.CustomMatcher;
|
||||||
|
|
||||||
|
public abstract class AbstractJavaCharacterMatcher extends CustomMatcher {
|
||||||
|
|
||||||
|
protected AbstractJavaCharacterMatcher(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isSingleCharMatcher() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean canMatchEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStarterChar(char c) {
|
||||||
|
return acceptChar(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final char getStarterChar() {
|
||||||
|
return 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
public final <V> boolean match(MatcherContext<V> context) {
|
||||||
|
if (!acceptChar(context.getCurrentChar())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
context.advanceIndex(1);
|
||||||
|
context.createNode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean acceptChar(char c);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.java;
|
||||||
|
|
||||||
|
public class JavaLetterMatcher extends AbstractJavaCharacterMatcher {
|
||||||
|
|
||||||
|
public JavaLetterMatcher() {
|
||||||
|
super("Letter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean acceptChar(char c) {
|
||||||
|
return Character.isJavaIdentifierStart(c);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.java;
|
||||||
|
|
||||||
|
public class JavaLetterOrDigitMatcher extends AbstractJavaCharacterMatcher {
|
||||||
|
|
||||||
|
public JavaLetterOrDigitMatcher() {
|
||||||
|
super("LetterOrDigit");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean acceptChar(char c) {
|
||||||
|
return Character.isJavaIdentifierPart(c);
|
||||||
|
}
|
||||||
|
}
|
1141
src/main/java/org/parboiled/examples/java/JavaParser.java
Normal file
1141
src/main/java/org/parboiled/examples/java/JavaParser.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.java;
|
||||||
|
|
||||||
|
import org.parboiled.parserunners.ProfilingParseRunner;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
public class JavaParserProfiler extends Main {
|
||||||
|
|
||||||
|
private ProfilingParseRunner parseRunner;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new JavaParserProfiler().run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run(String[] args) {
|
||||||
|
super.run(args);
|
||||||
|
ProfilingParseRunner.Report report = parseRunner.getReport();
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(report.print());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected ParsingResult<?> run(Rule rootRule, String sourceText) {
|
||||||
|
if (parseRunner == null) {
|
||||||
|
parseRunner = new ProfilingParseRunner(rootRule);
|
||||||
|
}
|
||||||
|
return parseRunner.run(sourceText);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
171
src/main/java/org/parboiled/examples/java/Main.java
Normal file
171
src/main/java/org/parboiled/examples/java/Main.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.java;
|
||||||
|
|
||||||
|
import static org.parboiled.common.Preconditions.*;
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.parserunners.ReportingParseRunner;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.parboiled.errors.ErrorUtils.printParseErrors;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Main().run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"ConstantConditions"})
|
||||||
|
protected void run(String[] args) {
|
||||||
|
System.out.println("parboiled Java parser, performance test");
|
||||||
|
System.out.println("---------------------------------------");
|
||||||
|
|
||||||
|
System.out.print("Creating parser... :");
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
Parboiled.createParser(JavaParser.class);
|
||||||
|
time(start);
|
||||||
|
|
||||||
|
System.out.print("Creating 100 more parser instances... :");
|
||||||
|
JavaParser parser = null;
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
parser = Parboiled.createParser(JavaParser.class);
|
||||||
|
}
|
||||||
|
time(start);
|
||||||
|
|
||||||
|
System.out.print("Creating 100 more parser instances using BaseParser.newInstance() ... :");
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
parser = parser.newInstance();
|
||||||
|
}
|
||||||
|
time(start);
|
||||||
|
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
File baseDir = args.length == 1 ? new File(args[0]) : null;
|
||||||
|
if (baseDir == null || !baseDir.exists()) baseDir = new File(".");
|
||||||
|
System.out.printf("Retrieving file list from '%s'", baseDir);
|
||||||
|
List<File> sources = recursiveGetAllJavaSources(baseDir, new ArrayList<File>());
|
||||||
|
time(start);
|
||||||
|
|
||||||
|
System.out.printf("Parsing all %s given java sources", sources.size());
|
||||||
|
Rule rootRule = parser.CompilationUnit().suppressNode(); // we want to see the parse-tree-less performance
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
long lines = 0, characters = 0;
|
||||||
|
for (File sourceFile : sources) {
|
||||||
|
long dontCountStart = System.currentTimeMillis();
|
||||||
|
String sourceText = readAllText(sourceFile);
|
||||||
|
start += System.currentTimeMillis() - dontCountStart; // do not count the time for reading the text file
|
||||||
|
|
||||||
|
ParsingResult<?> result = null;
|
||||||
|
try {
|
||||||
|
result = run(rootRule, sourceText);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.printf("\nException while parsing file '%s':\n%s", sourceFile, e);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
if (!result.matched) {
|
||||||
|
System.out.printf("\nParse error(s) in file '%s':\n%s", sourceFile, printParseErrors(result));
|
||||||
|
System.exit(1);
|
||||||
|
} else {
|
||||||
|
System.out.print('.');
|
||||||
|
}
|
||||||
|
lines += result.inputBuffer.getLineCount();
|
||||||
|
characters += sourceText.length();
|
||||||
|
}
|
||||||
|
long time = time(start);
|
||||||
|
|
||||||
|
System.out.println("Parsing performance:");
|
||||||
|
System.out.printf(" %6d Files -> %6.2f Files/sec\n", sources.size(), sources.size() * 1000.0 / time);
|
||||||
|
System.out.printf(" %6d Lines -> %6d Lines/sec\n", lines, lines * 1000 / time);
|
||||||
|
System.out.printf(" %6d Chars -> %6d Chars/sec\n", characters, characters * 1000 / time);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ParsingResult<?> run(Rule rootRule, String sourceText) {
|
||||||
|
return new ReportingParseRunner(rootRule).run(sourceText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long time(long start) {
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
System.out.printf(" %s ms\n", end - start);
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final FileFilter fileFilter = new FileFilter() {
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.isDirectory() || file.getName().endsWith(".java");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static List<File> recursiveGetAllJavaSources(File file, ArrayList<File> list) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
for (File f : file.listFiles(fileFilter)) {
|
||||||
|
recursiveGetAllJavaSources(f, list);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.add(file);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readAllText(File file) {
|
||||||
|
checkArgNotNull(file, "file");
|
||||||
|
return readAllText(file, Charset.forName("UTF8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readAllText(File file, Charset charset) {
|
||||||
|
checkArgNotNull(file, "file");
|
||||||
|
checkArgNotNull(charset, "charset");
|
||||||
|
try {
|
||||||
|
return readAllText(new FileInputStream(file), charset);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readAllText(InputStream stream, Charset charset) {
|
||||||
|
checkArgNotNull(charset, "charset");
|
||||||
|
if (stream == null) return null;
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset));
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
copyAll(reader, writer);
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyAll(Reader reader, Writer writer) {
|
||||||
|
checkArgNotNull(reader, "reader");
|
||||||
|
checkArgNotNull(writer, "writer");
|
||||||
|
try {
|
||||||
|
char[] data = new char[4096]; // copy in chunks of 4K
|
||||||
|
int count;
|
||||||
|
while ((count = reader.read(data)) >= 0) writer.write(data, 0, count);
|
||||||
|
|
||||||
|
reader.close();
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
796
src/main/java/org/parboiled/examples/sparql/SparqlParser.java
Normal file
796
src/main/java/org/parboiled/examples/sparql/SparqlParser.java
Normal file
@ -0,0 +1,796 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009 Ken Wenzel
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package org.parboiled.examples.sparql;
|
||||||
|
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPARQL Parser
|
||||||
|
*
|
||||||
|
* @author Ken Wenzel, adapted by Mathias Doenitz
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"InfiniteRecursion"})
|
||||||
|
public class SparqlParser extends BaseParser<Object> {
|
||||||
|
// <Parser>
|
||||||
|
public Rule Query() {
|
||||||
|
return Sequence(WS(), Prologue(), FirstOf(SelectQuery(),
|
||||||
|
ConstructQuery(), DescribeQuery(), AskQuery()), EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Prologue() {
|
||||||
|
return Sequence(Optional(BaseDecl()), ZeroOrMore(PrefixDecl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BaseDecl() {
|
||||||
|
return Sequence(BASE(), IRI_REF());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PrefixDecl() {
|
||||||
|
return Sequence(PREFIX(), PNAME_NS(), IRI_REF());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SelectQuery() {
|
||||||
|
return Sequence(SELECT(), Optional(FirstOf(DISTINCT(),
|
||||||
|
REDUCED())), FirstOf(OneOrMore(Var()), ASTERISK()),
|
||||||
|
ZeroOrMore(DatasetClause()), WhereClause(), SolutionModifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ConstructQuery() {
|
||||||
|
return Sequence(CONSTRUCT(), ConstructTemplate(),
|
||||||
|
ZeroOrMore(DatasetClause()), WhereClause(), SolutionModifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DescribeQuery() {
|
||||||
|
return Sequence(DESCRIBE(), FirstOf(OneOrMore(VarOrIRIref()),
|
||||||
|
ASTERISK()), ZeroOrMore(DatasetClause()),
|
||||||
|
Optional(WhereClause()), SolutionModifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule AskQuery() {
|
||||||
|
return Sequence(ASK(), ZeroOrMore(DatasetClause()), WhereClause());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DatasetClause() {
|
||||||
|
return Sequence(FROM(), FirstOf(DefaultGraphClause(),
|
||||||
|
NamedGraphClause()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DefaultGraphClause() {
|
||||||
|
return SourceSelector();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NamedGraphClause() {
|
||||||
|
return Sequence(NAMED(), SourceSelector());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SourceSelector() {
|
||||||
|
return IriRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule WhereClause() {
|
||||||
|
return Sequence(Optional(WHERE()), GroupGraphPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SolutionModifier() {
|
||||||
|
return Sequence(Optional(OrderClause()), Optional(LimitOffsetClauses()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LimitOffsetClauses() {
|
||||||
|
return FirstOf(Sequence(LimitClause(), Optional(OffsetClause())),
|
||||||
|
Sequence(OffsetClause(), Optional(LimitClause())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OrderClause() {
|
||||||
|
return Sequence(ORDER(), BY(), OneOrMore(OrderCondition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OrderCondition() {
|
||||||
|
return FirstOf(
|
||||||
|
Sequence(FirstOf(ASC(), DESC()), BrackettedExpression()),
|
||||||
|
FirstOf(Constraint(), Var()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LimitClause() {
|
||||||
|
return Sequence(LIMIT(), INTEGER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OffsetClause() {
|
||||||
|
return Sequence(OFFSET(), INTEGER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GroupGraphPattern() {
|
||||||
|
return Sequence(OPEN_CURLY_BRACE(), Optional(TriplesBlock()),
|
||||||
|
ZeroOrMore(Sequence(
|
||||||
|
FirstOf(GraphPatternNotTriples(), Filter()),
|
||||||
|
Optional(DOT()), Optional(TriplesBlock()))),
|
||||||
|
CLOSE_CURLY_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule TriplesBlock() {
|
||||||
|
return Sequence(TriplesSameSubject(), Optional(Sequence(DOT(),
|
||||||
|
Optional(TriplesBlock()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GraphPatternNotTriples() {
|
||||||
|
return FirstOf(OptionalGraphPattern(), GroupOrUnionGraphPattern(),
|
||||||
|
GraphGraphPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OptionalGraphPattern() {
|
||||||
|
return Sequence(OPTIONAL(), GroupGraphPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GraphGraphPattern() {
|
||||||
|
return Sequence(GRAPH(), VarOrIRIref(), GroupGraphPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GroupOrUnionGraphPattern() {
|
||||||
|
return Sequence(GroupGraphPattern(), ZeroOrMore(Sequence(UNION(),
|
||||||
|
GroupGraphPattern())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Filter() {
|
||||||
|
return Sequence(FILTER(), Constraint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Constraint() {
|
||||||
|
return FirstOf(BrackettedExpression(), BuiltInCall(), FunctionCall());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule FunctionCall() {
|
||||||
|
return Sequence(IriRef(), ArgList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ArgList() {
|
||||||
|
return FirstOf(Sequence(OPEN_BRACE(), CLOSE_BRACE()), Sequence(
|
||||||
|
OPEN_BRACE(), Expression(), ZeroOrMore(Sequence(COMMA(),
|
||||||
|
Expression())), CLOSE_BRACE()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ConstructTemplate() {
|
||||||
|
return Sequence(OPEN_CURLY_BRACE(), Optional(ConstructTriples()),
|
||||||
|
CLOSE_CURLY_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ConstructTriples() {
|
||||||
|
return Sequence(TriplesSameSubject(), Optional(Sequence(DOT(),
|
||||||
|
Optional(ConstructTriples()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule TriplesSameSubject() {
|
||||||
|
return FirstOf(Sequence(VarOrTerm(), PropertyListNotEmpty()), Sequence(
|
||||||
|
TriplesNode(), PropertyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PropertyListNotEmpty() {
|
||||||
|
return Sequence(Verb(), ObjectList(), ZeroOrMore(Sequence(SEMICOLON(),
|
||||||
|
Optional(Sequence(Verb(), ObjectList())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PropertyList() {
|
||||||
|
return Optional(PropertyListNotEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ObjectList() {
|
||||||
|
return Sequence(Object_(), ZeroOrMore(Sequence(COMMA(), Object_())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Object_() {
|
||||||
|
return GraphNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Verb() {
|
||||||
|
return FirstOf(VarOrIRIref(), A());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule TriplesNode() {
|
||||||
|
return FirstOf(Collection(), BlankNodePropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BlankNodePropertyList() {
|
||||||
|
return Sequence(OPEN_SQUARE_BRACE(), PropertyListNotEmpty(),
|
||||||
|
CLOSE_SQUARE_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Collection() {
|
||||||
|
return Sequence(OPEN_BRACE(), OneOrMore(GraphNode()), CLOSE_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GraphNode() {
|
||||||
|
return FirstOf(VarOrTerm(), TriplesNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule VarOrTerm() {
|
||||||
|
return FirstOf(Var(), GraphTerm());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule VarOrIRIref() {
|
||||||
|
return FirstOf(Var(), IriRef());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Var() {
|
||||||
|
return FirstOf(VAR1(), VAR2());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GraphTerm() {
|
||||||
|
return FirstOf(IriRef(), RdfLiteral(), NumericLiteral(),
|
||||||
|
BooleanLiteral(), BlankNode(), Sequence(OPEN_BRACE(),
|
||||||
|
CLOSE_BRACE()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule Expression() {
|
||||||
|
return ConditionalOrExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ConditionalOrExpression() {
|
||||||
|
return Sequence(ConditionalAndExpression(), ZeroOrMore(Sequence(OR(),
|
||||||
|
ConditionalAndExpression())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ConditionalAndExpression() {
|
||||||
|
return Sequence(ValueLogical(), ZeroOrMore(Sequence(AND(),
|
||||||
|
ValueLogical())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ValueLogical() {
|
||||||
|
return RelationalExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule RelationalExpression() {
|
||||||
|
return Sequence(NumericExpression(), Optional(FirstOf(//
|
||||||
|
Sequence(EQUAL(), NumericExpression()), //
|
||||||
|
Sequence(NOT_EQUAL(), NumericExpression()), //
|
||||||
|
Sequence(LESS(), NumericExpression()), //
|
||||||
|
Sequence(GREATER(), NumericExpression()), //
|
||||||
|
Sequence(LESS_EQUAL(), NumericExpression()), //
|
||||||
|
Sequence(GREATER_EQUAL(), NumericExpression()) //
|
||||||
|
) //
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NumericExpression() {
|
||||||
|
return AdditiveExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule AdditiveExpression() {
|
||||||
|
return Sequence(MultiplicativeExpression(), //
|
||||||
|
ZeroOrMore(FirstOf(
|
||||||
|
Sequence(PLUS(), MultiplicativeExpression()), //
|
||||||
|
Sequence(MINUS(), MultiplicativeExpression()), //
|
||||||
|
NumericLiteralPositive(), NumericLiteralNegative()) //
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule MultiplicativeExpression() {
|
||||||
|
return Sequence(UnaryExpression(), ZeroOrMore(FirstOf(Sequence(
|
||||||
|
ASTERISK(), UnaryExpression()), Sequence(DIVIDE(),
|
||||||
|
UnaryExpression()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule UnaryExpression() {
|
||||||
|
return FirstOf(Sequence(NOT(), PrimaryExpression()), Sequence(PLUS(),
|
||||||
|
PrimaryExpression()), Sequence(MINUS(), PrimaryExpression()),
|
||||||
|
PrimaryExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PrimaryExpression() {
|
||||||
|
return FirstOf(BrackettedExpression(), BuiltInCall(),
|
||||||
|
IriRefOrFunction(), RdfLiteral(), NumericLiteral(),
|
||||||
|
BooleanLiteral(), Var());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BrackettedExpression() {
|
||||||
|
return Sequence(OPEN_BRACE(), Expression(), CLOSE_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BuiltInCall() {
|
||||||
|
return FirstOf(
|
||||||
|
Sequence(STR(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(LANG(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(LANGMATCHES(), OPEN_BRACE(), Expression(), COMMA(),
|
||||||
|
Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(DATATYPE(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(BOUND(), OPEN_BRACE(), Var(), CLOSE_BRACE()),
|
||||||
|
Sequence(SAMETERM(), OPEN_BRACE(), Expression(), COMMA(),
|
||||||
|
Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(ISIRI(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(ISURI(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(ISBLANK(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
Sequence(ISLITERAL(), OPEN_BRACE(), Expression(), CLOSE_BRACE()),
|
||||||
|
RegexExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule RegexExpression() {
|
||||||
|
return Sequence(REGEX(), OPEN_BRACE(), Expression(), COMMA(),
|
||||||
|
Expression(), Optional(Sequence(COMMA(), Expression())),
|
||||||
|
CLOSE_BRACE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule IriRefOrFunction() {
|
||||||
|
return Sequence(IriRef(), Optional(ArgList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule RdfLiteral() {
|
||||||
|
return Sequence(String(), Optional(FirstOf(LANGTAG(), Sequence(
|
||||||
|
REFERENCE(), IriRef()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NumericLiteral() {
|
||||||
|
return FirstOf(NumericLiteralUnsigned(), NumericLiteralPositive(),
|
||||||
|
NumericLiteralNegative());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NumericLiteralUnsigned() {
|
||||||
|
return FirstOf(DOUBLE(), DECIMAL(), INTEGER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NumericLiteralPositive() {
|
||||||
|
return FirstOf(DOUBLE_POSITIVE(), DECIMAL_POSITIVE(),
|
||||||
|
INTEGER_POSITIVE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NumericLiteralNegative() {
|
||||||
|
return FirstOf(DOUBLE_NEGATIVE(), DECIMAL_NEGATIVE(),
|
||||||
|
INTEGER_NEGATIVE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BooleanLiteral() {
|
||||||
|
return FirstOf(TRUE(), FALSE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule String() {
|
||||||
|
return FirstOf(STRING_LITERAL_LONG1(), STRING_LITERAL1(),
|
||||||
|
STRING_LITERAL_LONG2(), STRING_LITERAL2());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule IriRef() {
|
||||||
|
return FirstOf(IRI_REF(), PrefixedName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PrefixedName() {
|
||||||
|
return FirstOf(PNAME_LN(), PNAME_NS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BlankNode() {
|
||||||
|
return FirstOf(BLANK_NODE_LABEL(), Sequence(OPEN_SQUARE_BRACE(),
|
||||||
|
CLOSE_SQUARE_BRACE()));
|
||||||
|
}
|
||||||
|
// </Parser>
|
||||||
|
|
||||||
|
// <Lexer>
|
||||||
|
|
||||||
|
public Rule WS() {
|
||||||
|
return ZeroOrMore(FirstOf(COMMENT(), WS_NO_COMMENT()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule WS_NO_COMMENT() {
|
||||||
|
return FirstOf(Ch(' '), Ch('\t'), Ch('\f'), EOL());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PNAME_NS() {
|
||||||
|
return Sequence(Optional(PN_PREFIX()), ChWS(':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PNAME_LN() {
|
||||||
|
return Sequence(PNAME_NS(), PN_LOCAL());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BASE() {
|
||||||
|
return StringIgnoreCaseWS("BASE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PREFIX() {
|
||||||
|
return StringIgnoreCaseWS("PREFIX");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SELECT() {
|
||||||
|
return StringIgnoreCaseWS("SELECT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DISTINCT() {
|
||||||
|
return StringIgnoreCaseWS("DISTINCT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule REDUCED() {
|
||||||
|
return StringIgnoreCaseWS("REDUCED");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule CONSTRUCT() {
|
||||||
|
return StringIgnoreCaseWS("CONSTRUCT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DESCRIBE() {
|
||||||
|
return StringIgnoreCaseWS("DESCRIBE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ASK() {
|
||||||
|
return StringIgnoreCaseWS("ASK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule FROM() {
|
||||||
|
return StringIgnoreCaseWS("FROM");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NAMED() {
|
||||||
|
return StringIgnoreCaseWS("NAMED");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule WHERE() {
|
||||||
|
return StringIgnoreCaseWS("WHERE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ORDER() {
|
||||||
|
return StringIgnoreCaseWS("ORDER");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BY() {
|
||||||
|
return StringIgnoreCaseWS("BY");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ASC() {
|
||||||
|
return StringIgnoreCaseWS("ASC");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DESC() {
|
||||||
|
return StringIgnoreCaseWS("DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LIMIT() {
|
||||||
|
return StringIgnoreCaseWS("LIMIT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OFFSET() {
|
||||||
|
return StringIgnoreCaseWS("OFFSET");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OPTIONAL() {
|
||||||
|
return StringIgnoreCaseWS("OPTIONAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GRAPH() {
|
||||||
|
return StringIgnoreCaseWS("GRAPH");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule UNION() {
|
||||||
|
return StringIgnoreCaseWS("UNION");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule FILTER() {
|
||||||
|
return StringIgnoreCaseWS("FILTER");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule A() {
|
||||||
|
return ChWS('a');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule STR() {
|
||||||
|
return StringIgnoreCaseWS("STR");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LANG() {
|
||||||
|
return StringIgnoreCaseWS("LANG");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LANGMATCHES() {
|
||||||
|
return StringIgnoreCaseWS("LANGMATCHES");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DATATYPE() {
|
||||||
|
return StringIgnoreCaseWS("DATATYPE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BOUND() {
|
||||||
|
return StringIgnoreCaseWS("BOUND");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SAMETERM() {
|
||||||
|
return StringIgnoreCaseWS("SAMETERM");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ISIRI() {
|
||||||
|
return StringIgnoreCaseWS("ISIRI");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ISURI() {
|
||||||
|
return StringIgnoreCaseWS("ISURI");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ISBLANK() {
|
||||||
|
return StringIgnoreCaseWS("ISBLANK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ISLITERAL() {
|
||||||
|
return StringIgnoreCaseWS("ISLITERAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule REGEX() {
|
||||||
|
return StringIgnoreCaseWS("REGEX");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule TRUE() {
|
||||||
|
return StringIgnoreCaseWS("TRUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule FALSE() {
|
||||||
|
return StringIgnoreCaseWS("FALSE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule IRI_REF() {
|
||||||
|
return Sequence(LESS_NO_COMMENT(), //
|
||||||
|
ZeroOrMore(Sequence(TestNot(FirstOf(LESS_NO_COMMENT(), GREATER(), '"', OPEN_CURLY_BRACE(),
|
||||||
|
CLOSE_CURLY_BRACE(), '|', '^', '\\', '`', CharRange('\u0000', '\u0020'))), ANY)), //
|
||||||
|
GREATER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule BLANK_NODE_LABEL() {
|
||||||
|
return Sequence("_:", PN_LOCAL(), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule VAR1() {
|
||||||
|
return Sequence('?', VARNAME(), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule VAR2() {
|
||||||
|
return Sequence('$', VARNAME(), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LANGTAG() {
|
||||||
|
return Sequence('@', OneOrMore(PN_CHARS_BASE()), ZeroOrMore(Sequence(
|
||||||
|
MINUS(), OneOrMore(Sequence(PN_CHARS_BASE(), DIGIT())))), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule INTEGER() {
|
||||||
|
return Sequence(OneOrMore(DIGIT()), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DECIMAL() {
|
||||||
|
return Sequence(FirstOf( //
|
||||||
|
Sequence(OneOrMore(DIGIT()), DOT(), ZeroOrMore(DIGIT())), //
|
||||||
|
Sequence(DOT(), OneOrMore(DIGIT())) //
|
||||||
|
), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DOUBLE() {
|
||||||
|
return Sequence(FirstOf(//
|
||||||
|
Sequence(OneOrMore(DIGIT()), DOT(), ZeroOrMore(DIGIT()),
|
||||||
|
EXPONENT()), //
|
||||||
|
Sequence(DOT(), OneOrMore(DIGIT()), EXPONENT()), //
|
||||||
|
Sequence(OneOrMore(DIGIT()), EXPONENT())), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule INTEGER_POSITIVE() {
|
||||||
|
return Sequence(PLUS(), INTEGER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DECIMAL_POSITIVE() {
|
||||||
|
return Sequence(PLUS(), DECIMAL());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DOUBLE_POSITIVE() {
|
||||||
|
return Sequence(PLUS(), DOUBLE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule INTEGER_NEGATIVE() {
|
||||||
|
return Sequence(MINUS(), INTEGER());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DECIMAL_NEGATIVE() {
|
||||||
|
return Sequence(MINUS(), DECIMAL());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DOUBLE_NEGATIVE() {
|
||||||
|
return Sequence(MINUS(), DOUBLE());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule EXPONENT() {
|
||||||
|
return Sequence(IgnoreCase('e'), Optional(FirstOf(PLUS(), MINUS())),
|
||||||
|
OneOrMore(DIGIT()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule STRING_LITERAL1() {
|
||||||
|
return Sequence("'", ZeroOrMore(FirstOf(Sequence(TestNot(FirstOf("'",
|
||||||
|
'\\', '\n', '\r')), ANY), ECHAR())), "'", WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule STRING_LITERAL2() {
|
||||||
|
return Sequence('"', ZeroOrMore(FirstOf(Sequence(TestNot(AnyOf("\"\\\n\r")), ANY), ECHAR())), '"', WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule STRING_LITERAL_LONG1() {
|
||||||
|
return Sequence("'''", ZeroOrMore(Sequence(
|
||||||
|
Optional(FirstOf("''", "'")), FirstOf(Sequence(TestNot(FirstOf(
|
||||||
|
"'", "\\")), ANY), ECHAR()))), "'''", WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule STRING_LITERAL_LONG2() {
|
||||||
|
return Sequence("\"\"\"", ZeroOrMore(Sequence(Optional(FirstOf("\"\"", "\"")),
|
||||||
|
FirstOf(Sequence(TestNot(FirstOf("\"", "\\")), ANY), ECHAR()))), "\"\"\"", WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ECHAR() {
|
||||||
|
return Sequence('\\', AnyOf("tbnrf\\\"\'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS_U() {
|
||||||
|
return FirstOf(PN_CHARS_BASE(), '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule VARNAME() {
|
||||||
|
return Sequence(FirstOf(PN_CHARS_U(), DIGIT()), ZeroOrMore(FirstOf(
|
||||||
|
PN_CHARS_U(), DIGIT(), '\u00B7', CharRange('\u0300', '\u036F'), CharRange('\u203F', '\u2040'))), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS() {
|
||||||
|
return FirstOf(MINUS(), DIGIT(), PN_CHARS_U(), '\u00B7',
|
||||||
|
CharRange('\u0300', '\u036F'), CharRange('\u203F', '\u2040'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_PREFIX() {
|
||||||
|
return Sequence(PN_CHARS_BASE(), Optional(ZeroOrMore(FirstOf(PN_CHARS(), Sequence(DOT(), PN_CHARS())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_LOCAL() {
|
||||||
|
return Sequence(FirstOf(PN_CHARS_U(), DIGIT()),
|
||||||
|
Optional(ZeroOrMore(FirstOf(PN_CHARS(), Sequence(DOT(), PN_CHARS())))), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PN_CHARS_BASE() {
|
||||||
|
return FirstOf( //
|
||||||
|
CharRange('A', 'Z'),//
|
||||||
|
CharRange('a', 'z'), //
|
||||||
|
CharRange('\u00C0', '\u00D6'), //
|
||||||
|
CharRange('\u00D8', '\u00F6'), //
|
||||||
|
CharRange('\u00F8', '\u02FF'), //
|
||||||
|
CharRange('\u0370', '\u037D'), //
|
||||||
|
CharRange('\u037F', '\u1FFF'), //
|
||||||
|
CharRange('\u200C', '\u200D'), //
|
||||||
|
CharRange('\u2070', '\u218F'), //
|
||||||
|
CharRange('\u2C00', '\u2FEF'), //
|
||||||
|
CharRange('\u3001', '\uD7FF'), //
|
||||||
|
CharRange('\uF900', '\uFDCF'), //
|
||||||
|
CharRange('\uFDF0', '\uFFFD') //
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DIGIT() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule COMMENT() {
|
||||||
|
return Sequence('#', ZeroOrMore(Sequence(TestNot(EOL()), ANY)), EOL());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule EOL() {
|
||||||
|
return AnyOf("\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule REFERENCE() {
|
||||||
|
return StringWS("^^");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LESS_EQUAL() {
|
||||||
|
return StringWS("<=");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GREATER_EQUAL() {
|
||||||
|
return StringWS(">=");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NOT_EQUAL() {
|
||||||
|
return StringWS("!=");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule AND() {
|
||||||
|
return StringWS("&&");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OR() {
|
||||||
|
return StringWS("||");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OPEN_BRACE() {
|
||||||
|
return ChWS('(');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule CLOSE_BRACE() {
|
||||||
|
return ChWS(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OPEN_CURLY_BRACE() {
|
||||||
|
return ChWS('{');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule CLOSE_CURLY_BRACE() {
|
||||||
|
return ChWS('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule OPEN_SQUARE_BRACE() {
|
||||||
|
return ChWS('[');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule CLOSE_SQUARE_BRACE() {
|
||||||
|
return ChWS(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule SEMICOLON() {
|
||||||
|
return ChWS(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DOT() {
|
||||||
|
return ChWS('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule PLUS() {
|
||||||
|
return ChWS('+');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule MINUS() {
|
||||||
|
return ChWS('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule ASTERISK() {
|
||||||
|
return ChWS('*');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule COMMA() {
|
||||||
|
return ChWS(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule NOT() {
|
||||||
|
return ChWS('!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule DIVIDE() {
|
||||||
|
return ChWS('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule EQUAL() {
|
||||||
|
return ChWS('=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LESS_NO_COMMENT() {
|
||||||
|
return Sequence(Ch('<'), ZeroOrMore(WS_NO_COMMENT()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule LESS() {
|
||||||
|
return ChWS('<');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule GREATER() {
|
||||||
|
return ChWS('>');
|
||||||
|
}
|
||||||
|
// </Lexer>
|
||||||
|
|
||||||
|
public Rule ChWS(char c) {
|
||||||
|
return Sequence(Ch(c), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule StringWS(String s) {
|
||||||
|
return Sequence(String(s), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rule StringIgnoreCaseWS(String string) {
|
||||||
|
return Sequence(IgnoreCase(string), WS());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/main/java/org/parboiled/examples/time/Main.java
Normal file
48
src/main/java/org/parboiled/examples/time/Main.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.time;
|
||||||
|
|
||||||
|
import org.parboiled.Parboiled;
|
||||||
|
import org.parboiled.parserunners.RecoveringParseRunner;
|
||||||
|
import org.parboiled.common.StringUtils;
|
||||||
|
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
|
||||||
|
import org.parboiled.support.ParsingResult;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TimeParser parser = Parboiled.createParser(TimeParser.class);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.print("Enter a time expression (hh:mm(:ss)?, hh(mm(ss)?)? or h(mm)?, single RETURN to exit)!\n");
|
||||||
|
String input = new Scanner(System.in).nextLine();
|
||||||
|
if (StringUtils.isEmpty(input)) break;
|
||||||
|
|
||||||
|
ParsingResult<?> result = new RecoveringParseRunner(parser.Time()).run(input);
|
||||||
|
|
||||||
|
System.out.println(input + " = " + result.parseTreeRoot.getValue() + '\n');
|
||||||
|
System.out.println(printNodeTree(result) + '\n');
|
||||||
|
|
||||||
|
if (!result.matched) {
|
||||||
|
System.out.println(StringUtils.join(result.parseErrors, "---\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
src/main/java/org/parboiled/examples/time/TimeParser.java
Normal file
103
src/main/java/org/parboiled/examples/time/TimeParser.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2011 Mathias Doenitz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.parboiled.examples.time;
|
||||||
|
|
||||||
|
import org.parboiled.BaseParser;
|
||||||
|
import org.parboiled.Rule;
|
||||||
|
import org.parboiled.annotations.BuildParseTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser for very relaxed time literals. Demonstrates usage of the value stack with default values for unmatched rules.
|
||||||
|
*/
|
||||||
|
@BuildParseTree
|
||||||
|
public class TimeParser extends BaseParser<Object> {
|
||||||
|
|
||||||
|
public Rule Time() {
|
||||||
|
return FirstOf(Time_HH_MM_SS(), Time_HHMMSS(), Time_HMM());
|
||||||
|
}
|
||||||
|
|
||||||
|
// h(h)?:mm(:ss)?
|
||||||
|
Rule Time_HH_MM_SS() {
|
||||||
|
return Sequence(
|
||||||
|
OneOrTwoDigits(), ':',
|
||||||
|
TwoDigits(),
|
||||||
|
FirstOf(Sequence(':', TwoDigits()), push(0)),
|
||||||
|
EOI,
|
||||||
|
swap3() && push(convertToTime(popAsInt(), popAsInt(), popAsInt()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hh(mm(ss)?)?
|
||||||
|
Rule Time_HHMMSS() {
|
||||||
|
return Sequence(
|
||||||
|
TwoDigits(),
|
||||||
|
FirstOf(
|
||||||
|
Sequence(
|
||||||
|
TwoDigits(),
|
||||||
|
FirstOf(TwoDigits(), push(0))
|
||||||
|
),
|
||||||
|
pushAll(0, 0)
|
||||||
|
),
|
||||||
|
EOI,
|
||||||
|
swap3() && push(convertToTime(popAsInt(), popAsInt(), popAsInt()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// h(mm)?
|
||||||
|
Rule Time_HMM() {
|
||||||
|
return Sequence(
|
||||||
|
OneDigit(),
|
||||||
|
FirstOf(TwoDigits(), push(0)),
|
||||||
|
EOI,
|
||||||
|
swap() && push(convertToTime(popAsInt(), popAsInt()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule OneOrTwoDigits() {
|
||||||
|
return FirstOf(TwoDigits(), OneDigit());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule OneDigit() {
|
||||||
|
return Sequence(Digit(), push(Integer.parseInt(matchOrDefault("0"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule TwoDigits() {
|
||||||
|
return Sequence(Sequence(Digit(), Digit()), push(Integer.parseInt(matchOrDefault("0"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule Digit() {
|
||||||
|
return CharRange('0', '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ************************* ACTIONS *****************************
|
||||||
|
|
||||||
|
protected Integer popAsInt() {
|
||||||
|
return (Integer) pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String convertToTime(Integer hours, Integer minutes) {
|
||||||
|
return convertToTime(hours, minutes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String convertToTime(Integer hours, Integer minutes, Integer seconds) {
|
||||||
|
return String.format("%s h, %s min, %s s",
|
||||||
|
hours != null ? hours : 0,
|
||||||
|
minutes != null ? minutes : 0,
|
||||||
|
seconds != null ? seconds : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user