Skip to content

Commit e16b0de

Browse files
Release 1.0
1 parent cbb5109 commit e16b0de

File tree

15 files changed

+218
-252
lines changed

15 files changed

+218
-252
lines changed

README.md

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ gradlew clean jar
2222

2323
This creates `build\libs\markup-blitz.jar` which serves the Markup Blitz API. It is also usable as an executable jar for standalone execution.
2424

25+
# Running tests
26+
27+
For running the tests, use this command:
28+
29+
```sh
30+
gradlew test
31+
```
32+
33+
Markup Blitz comes with a few tests, but it also passes all of the 3091 tests in the Invisible XML community project [ixml][GHIXML]. For running those as well, make sure that the [ixml][GHIXML] project is available next to the Markup Blitz project and use the above command.
34+
2535
# Markup Blitz in Eclipse
2636

2737
The project can be imported into Eclipse as a Gradle project.
@@ -39,15 +49,19 @@ Usage: java -jar markup-blitz.jar [<OPTION>...] <GRAMMAR> <INPUT>
3949

4050
Compile an Invisible XML grammar, and parse input with the resulting parser.
4151

42-
<GRAMMAR> the grammar (file name or URL).
43-
<INPUT> the input (file name or URL).
52+
<GRAMMAR> the grammar (literal, file name or URL), in ixml notation.
53+
<INPUT> the input (literal, file name or URL).
4454

45-
Options:
46-
--verbose, -v print intermediate results (to standard output).
47-
--timing print timing information (to standard output).
48-
--indent, -i generate resulting xml with indentation.
49-
--trace print parser trace (to standard error).
50-
--help, -h, -? print this information.
55+
<OPTION>:
56+
--indent generate resulting xml with indentation.
57+
--trace print parser trace.
58+
--timing print timing information.
59+
--verbose print intermediate results.
60+
61+
A literal grammar or input must be preceded by an exclamation point (!).
62+
All inputs must be presented in UTF-8 encoding, and output is written in
63+
UTF-8 as well. Resulting XML goes to standard output, all diagnostics go
64+
to standard error.
5165
```
5266

5367
# Using the Java API
@@ -73,16 +87,14 @@ public static Parser generate(String grammar, Option... blitzOptions) throws Bli
7387
Parse the given input.
7488

7589
```java
76-
public String parse(String input, Option... options) throws BlitzException
90+
public String parse(String input, Option... options)
7791
```
7892
**Parameters**:
7993
- `String input`: the input string
8094
- `Option options`: options for use at parsing time. If absent, any options passed at generation time will be in effect
8195

8296
**Returns:** `String`: the resulting XML
8397

84-
**Throws:** `BlitzException`: if any error is detected while parsing
85-
8698
### de.bottlecaps.markup.Blitz.Option
8799
Either of the `generate` and `parse` methods accepts `Option` arguments for creating extra diagnostic output. Generation time options are passed to the `Parser` object implicitly, and they are used at parsing time, when `parse`is called without any options.
88100
```java
@@ -117,6 +129,7 @@ The work in this project was supported by the [BaseX][BaseX] organization.
117129
[GLR]: https://en.wikipedia.org/wiki/GLR_parser
118130
[rex-parser-benchmark]: https://github.com/GuntherRademacher/rex-parser-benchmark
119131
[IXML]: https://invisiblexml.org/
132+
[GHIXML]: https://github.com/invisibleXML/ixml
120133
[CFG]: https://en.wikipedia.org/wiki/Context-free_grammar
121134
[parser]: https://en.wikipedia.org/wiki/Parsing#Parser
122135
[parse-tree]: https://en.wikipedia.org/wiki/Parse_tree

build.gradle

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ plugins {
44
id 'signing'
55
}
66

7-
version = '0.7-SNAPSHOT'
7+
version = '1.0'
88
group = 'de.bottlecaps'
99

1010
repositories {
11-
mavenLocal()
1211
mavenCentral()
1312
}
1413

@@ -50,7 +49,7 @@ void configureMetadata(MavenPublication mavenPublication) {
5049
pom {
5150
name = groupId + ':' + artifactId
5251
description = 'Markup Blitz Parser Generator'
53-
url = 'https://bottlecaps.de/markup-blitz'
52+
url = 'https://github.com/GuntherRademacher/markup-blitz'
5453
licenses {
5554
license {
5655
name = 'Apache-2.0'

src/main/java/de/bottlecaps/markup/Blitz.java

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
public class Blitz {
2828
/** Generation time and parse time options. */
2929
public enum Option {
30-
/** Print information on intermediate results. */ VERBOSE,
31-
/** Print timing information. */ TIMING,
3230
/** Generate XML with indentation. */ INDENT,
33-
/** Print parser trace. */ TRACE;
31+
/** Print parser trace. */ TRACE,
32+
/** Print timing information. */ TIMING,
33+
/** Print information on intermediate results. */ VERBOSE;
3434
}
3535

3636
/**
@@ -64,7 +64,7 @@ public static Parser generate(String grammar, Blitz.Option... blitzOptions) thro
6464
}
6565

6666
/**
67-
* Generate a parser from an Invisible XML grammar in XML, passed as a String.
67+
* Generate a parser from an Invisible XML grammar in XML, passed as an InputStream.
6868
*
6969
* @param xml the Invisible XML grammar in XML
7070
* @param blitzOptions options for use at generation time and parsing time
@@ -103,14 +103,14 @@ public static void main(String[] args) throws IOException {
103103
Set<Option> options = new HashSet<>();
104104
int i = 0;
105105
for (; i < args.length; ++i) {
106-
if (args[i].equals("-v") || args[i].equals("--verbose"))
107-
options.add(Option.VERBOSE);
108-
else if (args[i].equals("-t") || args[i].equals("--trace"))
109-
options.add(Option.TRACE);
110-
else if (args[i].equals("-i") || args[i].equals("--indent"))
106+
if (args[i].equals("--indent"))
111107
options.add(Option.INDENT);
112-
else if (args[i].equals("-?") || args[i].equals("--help"))
113-
usage(0);
108+
else if (args[i].equals("--trace"))
109+
options.add(Option.TRACE);
110+
else if (args[i].equals("--timing"))
111+
options.add(Option.TIMING);
112+
else if (args[i].equals("--verbose"))
113+
options.add(Option.VERBOSE);
114114
else if (args[i].startsWith("-"))
115115
usage(1);
116116
else
@@ -144,16 +144,16 @@ private static void usage(int exitCode) {
144144
System.err.println(" <GRAMMAR> the grammar (literal, file name or URL), in ixml notation.");
145145
System.err.println(" <INPUT> the input (literal, file name or URL).");
146146
System.err.println();
147-
System.err.println(" Options:");
148-
System.err.println(" --verbose, -v print intermediate results (to standard output).");
149-
System.err.println(" --timing print timing information (to standard output).");
150-
System.err.println(" --indent, -i generate resulting xml with indentation.");
151-
System.err.println(" --trace print parser trace (to standard error).");
152-
System.err.println(" --help, -h, -? print this information.");
147+
System.err.println(" <OPTION>:");
148+
System.err.println(" --indent generate resulting xml with indentation.");
149+
System.err.println(" --trace print parser trace.");
150+
System.err.println(" --timing print timing information.");
151+
System.err.println(" --verbose print intermediate results.");
153152
System.err.println();
154153
System.err.println(" A literal grammar or input must be preceded by an exclamation point (!).");
155154
System.err.println(" All inputs must be presented in UTF-8 encoding, and output is written in");
156-
System.err.println(" UTF-8 as well.");
155+
System.err.println(" UTF-8 as well. Resulting XML goes to standard output, all diagnostics go");
156+
System.err.println(" to standard error.");
157157
System.err.println();
158158
System.exit(exitCode);
159159
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package de.bottlecaps.markup;
2+
3+
import de.bottlecaps.markup.blitz.Errors;
4+
5+
public class BlitzIxmlException extends BlitzException {
6+
private static final long serialVersionUID = 1L;
7+
8+
private Errors error;
9+
10+
public BlitzIxmlException(Errors error, String message) {
11+
super(message);
12+
this.error = error;
13+
}
14+
15+
public Errors getError() {
16+
return error;
17+
}
18+
}

src/main/java/de/bottlecaps/markup/blitz/Errors.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package de.bottlecaps.markup.blitz;
22

3-
import de.bottlecaps.markup.BlitzException;
3+
import de.bottlecaps.markup.BlitzIxmlException;
44

55
public enum Errors {
66
S01("Two rules are not separated by at least one whitespace character or comment."),
@@ -33,6 +33,6 @@ public void thro(String... args) {
3333
String msg = args.length == 0
3434
? text
3535
: text.replaceFirst("\\.$", ": ") + String.join(", ", args) + ".";
36-
throw new BlitzException("[" + name() + "] " + msg);
36+
throw new BlitzIxmlException(this, "[" + name() + "] " + msg);
3737
}
3838
}

src/main/java/de/bottlecaps/markup/blitz/Parser.java

Lines changed: 67 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import de.bottlecaps.markup.Blitz.Option;
2020
import de.bottlecaps.markup.BlitzException;
21+
import de.bottlecaps.markup.BlitzIxmlException;
2122
import de.bottlecaps.markup.BlitzParseException;
2223
import de.bottlecaps.markup.blitz.codepoints.Codepoint;
2324
import de.bottlecaps.markup.blitz.codepoints.RangeSet;
@@ -29,6 +30,7 @@
2930

3031
public class Parser
3132
{
33+
public static final String IXML_NAMESPACE = "http://invisiblexml.org/NS";
3234
private static int STALL_THRESHOLD = 8;
3335

3436
private static class ParseException extends Exception
@@ -173,14 +175,9 @@ public void sendContent(EventHandler e)
173175
}
174176

175177
public void addChildren(Symbol... newChildren) {
176-
if (newChildren == null) {
177-
children = newChildren;
178-
}
179-
else {
180-
int length = children.length;
181-
children = Arrays.copyOf(children, length + newChildren.length);
182-
System.arraycopy(newChildren, 0, children, length, newChildren.length);
183-
}
178+
int length = children.length;
179+
children = Arrays.copyOf(children, length + newChildren.length);
180+
System.arraycopy(newChildren, 0, children, length, newChildren.length);
184181
}
185182
}
186183

@@ -487,9 +484,8 @@ private String lineAndColumn(int pos) {
487484
* @param input the input string
488485
* @param options options for use at parsing time. If absent, any options passed at generation time will be in effect
489486
* @return the resulting XML
490-
* @throws BlitzException if any error is detected while parsing
491487
*/
492-
public String parse(String input, Option... options) throws BlitzException {
488+
public String parse(String input, Option... options) {
493489
Set<Option> currentOptions = options.length == 0
494490
? defaultOptions
495491
: Set.of(options);
@@ -510,59 +506,71 @@ public String parse(String input, Option... options) throws BlitzException {
510506
size = input.length();
511507
maxId = 0;
512508

513-
ParsingThread thread;
514509
try {
515-
thread = parse();
516-
}
517-
catch (ParseException pe) {
518-
int begin = pe.getBegin();
519-
String prefix = input.substring(0, begin);
520-
int offending = pe.getOffending();
521-
int line = prefix.replaceAll("[^\n]", "").length() + 1;
522-
int column = prefix.length() - prefix.lastIndexOf('\n');
523-
throw new BlitzParseException(
524-
"Failed to parse input:\n" + getErrorMessage(pe),
525-
offending >= 0 ? terminal[offending].shortName()
526-
: begin < input.length() ? ("'" + Character.toString(input.codePointAt(begin)) + "'")
527-
: "$",
528-
line,
529-
column
530-
);
531-
}
532-
finally {
533-
if (trace) {
534-
writeTrace("</trace>\n");
535-
try {
536-
err.flush();
537-
}
538-
catch (IOException e) {
539-
throw new BlitzException(e);
510+
ParsingThread thread;
511+
try {
512+
thread = parse();
513+
}
514+
catch (ParseException pe) {
515+
int begin = pe.getBegin();
516+
String prefix = input.substring(0, begin);
517+
int offending = pe.getOffending();
518+
int line = prefix.replaceAll("[^\n]", "").length() + 1;
519+
int column = prefix.length() - prefix.lastIndexOf('\n');
520+
throw new BlitzParseException(
521+
"Failed to parse input:\n" + getErrorMessage(pe),
522+
offending >= 0 ? terminal[offending].shortName()
523+
: begin < input.length() ? ("'" + Character.toString(input.codePointAt(begin)) + "'")
524+
: "$",
525+
line,
526+
column
527+
);
528+
}
529+
finally {
530+
if (trace) {
531+
writeTrace("</trace>\n");
532+
try {
533+
err.flush();
534+
}
535+
catch (IOException e) {
536+
}
540537
}
538+
w.flush();
541539
}
542-
w.flush();
543-
}
544540

545-
Nonterminal startSymbol = ((Nonterminal) b.stack[0]);
546-
if (startSymbol.children == null || startSymbol.children.length == 0)
547-
Errors.D01.thro(); // not well-formed
548-
if (! (startSymbol.children[0] instanceof Nonterminal))
549-
Errors.D06.thro(); // not exactly one element
550-
Nonterminal nonterminal = (Nonterminal) startSymbol.children[0];
551-
if (nonterminal.isAttribute)
552-
Errors.D05.thro(); // attribute as root
553-
if (startSymbol.children.length != 1)
554-
Errors.D06.thro(); // not exactly one element
555-
556-
if (thread.isAmbiguous || isVersionMismatch) {
557-
nonterminal.addChildren(attribute("xmlns:ixml", "http://invisiblexml.org/NS"));
558-
List<String> state = new ArrayList<>();
559-
if (thread.isAmbiguous)
560-
state.add("ambiguous");
561-
// if (...)
562-
// state.add("stalled");
563-
if (isVersionMismatch)
564-
state.add("version-mismatch");
565-
nonterminal.addChildren(attribute("ixml:state", String.join(" ", state)));
541+
Nonterminal startSymbol = ((Nonterminal) b.stack[0]);
542+
if (startSymbol.children == null || startSymbol.children.length == 0)
543+
Errors.D01.thro(); // not well-formed
544+
if (! (startSymbol.children[0] instanceof Nonterminal))
545+
Errors.D06.thro(); // not exactly one element
546+
Nonterminal nonterminal = (Nonterminal) startSymbol.children[0];
547+
if (nonterminal.isAttribute)
548+
Errors.D05.thro(); // attribute as root
549+
if (startSymbol.children.length != 1)
550+
Errors.D06.thro(); // not exactly one element
551+
552+
if (thread.isAmbiguous || isVersionMismatch) {
553+
nonterminal.addChildren(attribute("xmlns:ixml", IXML_NAMESPACE));
554+
List<String> state = new ArrayList<>();
555+
if (thread.isAmbiguous)
556+
state.add("ambiguous");
557+
if (isVersionMismatch)
558+
state.add("version-mismatch");
559+
nonterminal.addChildren(attribute("ixml:state", String.join(" ", state)));
560+
}
561+
}
562+
catch (BlitzIxmlException e) {
563+
Nonterminal ixml = new Nonterminal("ixml", new Symbol[] {new Insertion(e.getMessage().codePoints().toArray())});
564+
ixml.addChildren(attribute("xmlns:ixml", IXML_NAMESPACE));
565+
ixml.addChildren(attribute("ixml:state", "failed"));
566+
ixml.addChildren(attribute("ixml:error-code", e.getError().name()));
567+
b.stack[0] = new Nonterminal("root", new Symbol[] {ixml});
568+
}
569+
catch (BlitzException e) {
570+
Nonterminal ixml = new Nonterminal("ixml", new Symbol[] {new Insertion(e.getMessage().codePoints().toArray())});
571+
ixml.addChildren(attribute("xmlns:ixml", IXML_NAMESPACE));
572+
ixml.addChildren(attribute("ixml:state", "failed"));
573+
b.stack[0] = new Nonterminal("root", new Symbol[] {ixml});
566574
}
567575

568576
b.serialize(s);

0 commit comments

Comments
 (0)