Over the years I have created many custom file formats which were
read using a java.util.Scanner
. Now, since I did not want to
create a formal grammar just for a simple file format and also
wanted more flexibility than a simple scanner could give me: I
created EasyParseMachine.
You can think of EPM as a parse tree with local states.
See JSONReader for a complete example on how EPM can be used.
There exist 7 state transitions, as specified in the following table and explained in their corresponding subsections.
Transition | Next state | Consume input? |
---|---|---|
Accept | return | No |
Closure | return | Yes |
Consume | self | Yes |
Fail | - | - |
Goto | as specified | No |
Guess | as specified | No |
Split | as specified | Yes |
Use pattern: Consume X until Y
Input consumed: no
Tree transition: go to parent node
State transition: go to state corresponding to parent node
Example:
public IStateChange feed(int c) {
if (Character.isDigit(c)) {
result += (char) c;
return new Consume();
} else {
return new Accept(result);
}
}
Use pattern: End input with an X
Input consumed: yes
Tree transition: go to parent node
State transition: go to state corresponding to parent node
Example:
public IStateChange feed(int c) {
if (c == ')')
return new Closure();
else
return new Fail();
}
Use pattern: Greedy match all input of type X
Input consumed: yes
Tree transition: stay in the same node
State transition: stay in the same state
Example: see Accept
Use pattern: If not conform to language, error out
Input consumed: -
Tree transition: -
State transition: exit execution
Example: see Closure
Use pattern: Start matching with state X
Input consumed: no
Tree transition: new child node
State transition: go to specified state
Example:
public IStateChange feed(int c) {
if (c == '(')
return new Consume();
else
return new Goto("ClosingParenthesis");
}
Use pattern: Start matching with state X or Y
Input consumed: no
Tree transition: new child node, whichever state change does not fail gets to stay
State transition: go to specified states in parallel
Example:
public IStateChange feed(int c) {
if (c == '(')
return new Consume();
else
return new Guess("ClosingParenthesis", "Digits", "Letters");
}
Use pattern: Upon reading X: start matching with state Y or Z
Input consumed: yes
Tree transition: new child node, whichever state change does not fail gets to stay
State transition: go to specified states in parallel
Example:
public IStateChange feed(int c) {
if (c == '(')
return new Split("ClosingParenthesis", "Digits", "Letters");
else
return new Fail();
}