
com.bigcustard.glide.language.Syntax Maven / Gradle / Ivy
package com.bigcustard.glide.language;
import com.badlogic.gdx.utils.Disposable;
import com.bigcustard.glide.code.SyntaxPart;
import com.bigcustard.util.Tokenizer;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.ObjectArrays;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import static com.bigcustard.glide.code.SyntaxPart.Type.*;
public class Syntax implements Disposable {
private static String[] TOKENS = new String[] {
" ", "\t", "\n", "\r", "\f", "(", ")", "{", "}", "\"", ".", "[", "]", "==", "<", ">", "<=", ">=",
"!", "!=", "=", "++", "*=", "/=", "--", "+=", "-=", "+", "-", " / ", "*", "&&", "||", ",", "$", "%", ";", ":"};
private Keywords languageKeywords;
private Function> errorChecker;
private AtomicReference> lastKnownResult = new AtomicReference<>();
private ExecutorService executorService = Executors.newFixedThreadPool(2);
private Future> futureSyntaxCheck;
public Syntax(Keywords languageKeywords, Function> errorChecker) {
this.languageKeywords = languageKeywords;
this.errorChecker = errorChecker;
}
public List parse(String program) {
List classifiedWordsAndSpaces = categoriseWordsIntoTypes(program);
return collapseAdjacentPartsWithSameType(classifiedWordsAndSpaces);
}
public boolean isValid(String program) {
return error(program) == null;
}
public Pair error(String program) {
if (futureSyntaxCheck == null || futureSyntaxCheck.isDone()) {
futureSyntaxCheck = executorService.submit(() -> {
Pair error = errorChecker.apply(program);
lastKnownResult.set(error);
});
}
return lastKnownResult.get();
}
@SuppressWarnings("unchecked")
private List categoriseWordsIntoTypes(String program) {
List wordsAndSpaces = new Tokenizer(program, TOKENS).run();
return Lists.transform(wordsAndSpaces, word -> new SyntaxPart(word, getType(word)));
}
private List collapseAdjacentPartsWithSameType(List classifiedWordsAndSpaces) {
List collapsed = new ArrayList<>();
for (SyntaxPart newElement : classifiedWordsAndSpaces) {
if (!collapsed.isEmpty()) {
SyntaxPart lastElement = collapsed.get(collapsed.size() - 1);
if (lastElement.type() == Comment && !newElement.text().contains("\n")) {
newElement.type(Comment);
}
if (lastElement.type() == UnclosedQuote) {
if (newElement.type() == UnclosedQuote) {
lastElement.type(Quoted);
newElement.type(Quoted);
} else {
newElement.type(UnclosedQuote);
}
}
if (lastElement.type() == newElement.type()) {
newElement = new SyntaxPart(lastElement.text() + newElement.text(), newElement.type());
collapsed.remove(collapsed.size() - 1);
}
}
collapsed.add(newElement);
}
return collapsed;
}
private SyntaxPart.Type getType(String word) {
if (Sets.newHashSet(keywords()).contains(word)) {
return Keyword;
} else if (word.startsWith(languageKeywords.comment())) {
return Comment;
} else if (word.equals("\"")) {
return UnclosedQuote;
} else if (Arrays.asList(TOKENS).contains(word)) {
return Operator;
}
return Unclassified;
}
private String[] keywords() {
return ObjectArrays.concat(new FrameworkKeywords().get(), languageKeywords.get(), String.class);
}
@Override
public void dispose() {
executorService.shutdown();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy