org.snapscript.parse.SyntaxTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of snap-all Show documentation
Show all versions of snap-all Show documentation
Dynamic scripting for the JVM
package org.snapscript.parse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class SyntaxTree {
private final Comparator comparator;
private final List nodes;
private final GrammarIndexer indexer;
private final AtomicInteger commit;
private final PositionStack stack;
private final TokenMerger merger;
private final TokenLexer lexer;
private final String resource;
private final String grammar;
private final int length;
public SyntaxTree(GrammarIndexer indexer, String resource, String grammar, char[] original, char[] source, short[] lines, short[] types, int count) {
this.lexer = new TokenScanner(indexer, resource, original, source, lines, types, count);
this.comparator = new SyntaxNodeComparator();
this.nodes = new LinkedList();
this.commit = new AtomicInteger();
this.stack = new PositionStack();
this.merger = new TokenMerger();
this.length = source.length;
this.resource = resource;
this.indexer = indexer;
this.grammar = grammar;
}
public SyntaxChecker check() {
int index = indexer.index(grammar);
int depth = stack.depth(0, index);
if (depth >= 0) {
throw new ParseException("Syntax has been validated");
}
stack.push(0, index);
return new SyntaxValidator(lexer);
}
public SyntaxBuilder build() {
int index = indexer.index(grammar);
int mark = lexer.mark();
int count = lexer.count();
if(mark != count) {
int error = commit.get(); // last successful commit
Line line = lexer.line(error);
if(resource != null) {
throw new ParseException("Syntax error in '" + resource + "' at line " + line);
}
throw new ParseException("Syntax error at line " + line);
}
lexer.reset(0);
stack.clear();
stack.push(0,index);
return new SyntaxCursor(lexer, nodes, index, 0);
}
public SyntaxNode commit() {
int size = nodes.size();
if(size > 2) {
throw new ParseException("Syntax tree has more than one root");
}
SyntaxCursor cursor = nodes.get(0);
SyntaxNode node = cursor.create();
if(node == null) {
throw new ParseException("Syntax tree has no root");
}
return node;
}
public int length() {
return length;
}
private class SyntaxValidator extends TokenConsumer implements SyntaxChecker {
public SyntaxValidator(TokenLexer lexer) {
this.lexer = lexer;
}
@Override
public void validate(){
int mark = lexer.mark();
int count = lexer.count();
if(mark != count) {
int error = commit.get(); // last successful commit
Line line = lexer.line(error);
if(resource != null) {
throw new ParseException("Syntax error in '" + resource + "' at line " + line);
}
throw new ParseException("Syntax error at line " + line);
}
}
@Override
public int mark(int grammar) {
int off = lexer.mark();
int index = stack.depth(off, grammar); // this is slow!!
if (index <= 0) {
stack.push(off, grammar);
return off;
}
return -1;
}
@Override
public int reset(int start, int grammar) {
int mark = lexer.mark();
stack.pop(start, grammar);
lexer.reset(start); // sets the global offset
return mark;
}
@Override
public void commit(int start, int grammar) {
int mark = lexer.mark();
int error = commit.get();
int value = stack.pop(start, grammar);
if (value != -1) {
if(mark > error) {
commit.set(mark);
}
}
}
@Override
public int position() {
return lexer.mark();
}
@Override
public int peek() {
return lexer.peek();
}
}
private class SyntaxCursor extends TokenConsumer implements SyntaxBuilder {
private final List parent;
private final List nodes;
private final int grammar;
private final int start;
public SyntaxCursor(TokenLexer lexer, List parent, int grammar, int start) {
this.nodes = new LinkedList();
this.grammar = grammar;
this.parent = parent;
this.lexer = lexer;
this.start = start;
}
public SyntaxNode create() {
return new SyntaxResult(nodes, value, grammar, start);
}
@Override
public SyntaxBuilder mark(int grammar) {
int off = lexer.mark();
int index = stack.depth(off, grammar); // this is slow!!
if (index <= 0) {
stack.push(off, grammar);
return new SyntaxCursor(lexer, nodes, grammar, off);
}
return null;
}
@Override
public boolean literal(String text) {
Token token = lexer.literal(text);
if (token != null) {
value = merger.merge(value, token);
return true;
}
return false;
}
@Override
public int reset() {
int mark = lexer.mark();
stack.pop(start, grammar);
lexer.reset(start); // sets the global offset
return mark;
}
@Override
public void commit() {
int mark = lexer.mark();
int error = commit.get();
int value = stack.pop(start, grammar);
if (value != -1) {
if(mark > error) {
commit.set(mark);
}
parent.add(this);
}
}
@Override
public int position() {
return lexer.mark();
}
@Override
public int peek() {
return lexer.peek();
}
}
private class SyntaxResult implements SyntaxNode {
private List nodes;
private Token token;
private int grammar;
private int start;
public SyntaxResult(List nodes, Token token, int grammar, int start) {
this.grammar = grammar;
this.token = token;
this.start = start;
this.nodes = nodes;
}
@Override
public List getNodes() {
int size = nodes.size();
if(size > 0) {
List result = new ArrayList(size);
for(SyntaxCursor child : nodes) {
SyntaxNode node = child.create();
if(node != null) {
result.add(node);
}
}
if(size > 1) {
Collections.sort(result, comparator);
}
return result;
}
return Collections.emptyList();
}
@Override
public String getGrammar() {
return indexer.value(grammar);
}
@Override
public Line getLine() {
return lexer.line(start);
}
@Override
public Token getToken() {
return token;
}
@Override
public int getStart() {
return start;
}
@Override
public String toString(){
return indexer.value(grammar);
}
}
}