Repository navigation
Inline parsing overhaul WIP - #180
Conversation
| import org.commonmark.internal.util.CharMatcher; | ||
| import org.commonmark.internal.util.Parsing; | ||
|
|
||
| public class Scanner { |
There was a problem hiding this comment.
This scanner class helped a lot the code readability. :)
There was a problem hiding this comment.
Yeah, I think so too :).
| return '\n'; | ||
| } else { | ||
| // Don't return newline for end of last line | ||
| return '\0'; |
There was a problem hiding this comment.
I can see we using this symbol in multiple places. What do you think to extract and also use the constant from Character class? Character.MIN_VALUE?
There was a problem hiding this comment.
Good idea, yeah! Character.MIN_VALUE would work, but I think something even more specific would be nicer like EOF or END (in Scanner or Parsing).
| } | ||
| } | ||
|
|
||
| public boolean next(char c) { |
There was a problem hiding this comment.
While reading the code this next method makes me think a different action for this method. When I renamed to isNext(...) it more senses to me. what do you think?
There was a problem hiding this comment.
Yeah, it's really a nextIf. What I like about next is that it reads well in code like if (scanner.next('x')).
With isNext it's not clear that it advances the scanner state to me.
| } | ||
|
|
||
| public static Builder builder() { | ||
| return new Builder(new BitSet()); |
There was a problem hiding this comment.
The Builderclass could have this boot.
return new Builder();
...
private Builder() {
this.set = new BitSet();
}| scanner.next(); | ||
|
|
||
| if (scanner.next('C') && scanner.next('D') && scanner.next('A') && scanner.next('T') && scanner.next('A') | ||
| && scanner.next('[')) { |
There was a problem hiding this comment.
To have an overload next(...) method for multiple char could increase even more the readability here.
Something like
if (scanner.next('C', 'D', 'A', 'T', 'A', '[') {
....
//Scanner.java
public boolean next(char... c) {
for (char character: c) {
if (!next(character)){
return false;
}
}
return true;
}
//ScannerTest.java
@Test
public void testIsNextMultipleCharacter() {
Scanner scanner = new Scanner(Collections.<CharSequence>singletonList("foo bar"), 0, 0);
assertTrue(scanner.next('f', 'o', 'o', ' ', 'b', 'a', 'r'));
assertFalse(scanner.next('r'));
}There was a problem hiding this comment.
Yeah, I was considering this before, maybe even a next(String) instead, which only advances if it finds the whole string.
| if (next == '\n') { | ||
| scanner.next(); | ||
| return ParsedInline.of(new HardLineBreak(), scanner.position()); | ||
| } else if (ESCAPABLE.matcher(String.valueOf(next)).matches()) { | ||
| scanner.next(); | ||
| return ParsedInline.of(new Text(String.valueOf(next)), scanner.position()); | ||
| } else { | ||
| return ParsedInline.of(new Text("\\"), scanner.position()); | ||
| } |
There was a problem hiding this comment.
Just to simplify those if/else and have less indentation.
| if (next == '\n') { | |
| scanner.next(); | |
| return ParsedInline.of(new HardLineBreak(), scanner.position()); | |
| } else if (ESCAPABLE.matcher(String.valueOf(next)).matches()) { | |
| scanner.next(); | |
| return ParsedInline.of(new Text(String.valueOf(next)), scanner.position()); | |
| } else { | |
| return ParsedInline.of(new Text("\\"), scanner.position()); | |
| } | |
| if (next == '\n') { | |
| scanner.next(); | |
| return ParsedInline.of(new HardLineBreak(), scanner.position()); | |
| } | |
| if (ESCAPABLE.matcher(String.valueOf(next)).matches()) { | |
| scanner.next(); | |
| return ParsedInline.of(new Text(String.valueOf(next)), scanner.position()); | |
| } | |
| return ParsedInline.of(new Text("\\"), scanner.position()); |
| sb.append('\n'); | ||
|
|
||
| // Lines between begin and end (we are appending the full line) | ||
| for (int line = begin.lineIndex + 1; line < end.lineIndex; line++) { |
There was a problem hiding this comment.
We do have an attribute in this line called line too. We may call here by simple i,j,....
|
|
||
| public Builder c(char c) { | ||
| if (c > 127) { | ||
| throw new IllegalArgumentException("Can only match ASCII characters"); |
There was a problem hiding this comment.
| throw new IllegalArgumentException("Can only match ASCII characters"); | |
| throw new IllegalArgumentException("Can only match ASCII Printable Characters"); |
| */ | ||
| void parse(String input, Node node); | ||
| // TODO: Should we use a better type here, one that will be able to include source positions? | ||
| void parse(List<CharSequence> lines, Node node); |
There was a problem hiding this comment.
I'm happy with this PR now (for internal API, would still need some more cleanup to be public API), but I still want to spike what this type would look like with source positions, so that we can avoid breaking API twice.
The end goal is to extract all current inline parsers, and then allow customizing inline parsing via the same infrastructure (similar to the current block parsing). A stretch goal is to move away from the parser input having to be a single contiguous String. This could speed up parsing and allow keeping track of source positions (line and column indexes) for inline nodes.
Most complex one yet, but I think it's nice to get rid of the regexes.
Getting really close to not needing `match` in InlineParserImpl anymore!
This is a big one, and was only possible because of the new Scanner infrastructure. This has a couple of advantages: * Less string copying and garbage because we no longer need to have all of a block's content in a single contiguous string, we can keep the lines we use for block parsing directly * Because we have individual lines, we could also keep source positions and get source positions for inline nodes :wow:!
This avoids a lot of copying (we still need to copy the position though). In exchange, we need to be more careful and reset the position if we want to backtrack.
6b2e0f1 to
868d81c
Compare
The end goal is to extract all current inline parsers, and then allow customizing inline parsing via the same infrastructure (similar to the current block parsing).
A stretch goal is to move away from the parser input having to be a single contiguous String. This could speed up parsing and allow keeping track of source positions (line and column indexes) for inline nodes.
Still todo:
indexandmatchinInlineParserImplwithScannerinputfromStringtoList<CharSequence>(Allowing extension of inline parsers can be done in a separate PR, but it will be much simpler now.)