New logic parser

This commit is contained in:
Anuken
2021-03-09 15:47:19 -05:00
parent 4f37f29ae8
commit ae2736d393
5 changed files with 158 additions and 126 deletions

View File

@@ -1,17 +1,33 @@
package mindustry.logic;
import arc.struct.*;
import arc.util.*;
import mindustry.gen.*;
import mindustry.logic.LStatements.*;
public class LParser{
private static final String[] tokens = new String[16];
private static final int maxJumps = 500;
private static final StringMap opNameChanges = StringMap.of(
"atan2", "angle",
"dst", "len"
);
private static final Seq<JumpIndex> jumps = new Seq<>();
private static final ObjectIntMap<String> jumpLocations = new ObjectIntMap<>();
Seq<LStatement> statements = new Seq<>();
char[] chars;
int pos;
int pos, line, tok;
LParser(String text){
this.chars = text.toCharArray();
}
/** Parses a sequence of statements from a string. */
public static Seq<LStatement> parse(String text){
//don't waste time parsing null/empty text
if(text == null || text.isEmpty()) return new Seq<>();
return new LParser(text).parse();
}
@@ -20,40 +36,158 @@ public class LParser{
while(pos < chars.length && chars[pos++] != '\n');
}
void label(){
while(pos < chars.length){
void error(String message){
//TODO
throw new RuntimeException("Invalid code. " + message);
}
String string(){
int from = pos;
while(pos++ < chars.length){
var c = chars[pos];
if(c == '\n'){
error("Missing closing quote \" before end of line.");
}else if(c == '"'){
break;
}
}
if(chars[pos] != '"') error("Missing closing quote \" before end of file.");
return new String(chars, from, ++pos - from);
}
String token(){
int from = pos;
while(pos < chars.length){
char c = chars[pos];
if(c == '\n' || c == ' ' || c == '#' || c == '\t' || c == ';') break;
pos ++;
}
return new String(chars, from, pos - from);
}
/** Apply changes after reading a list of tokens. */
void checkRead(){
if(tokens[0].equals("op")){
//legacy name change
tokens[1] = opNameChanges.get(tokens[1], tokens[1]);
}
}
/** Reads the next statement until EOL/EOF. */
void statement(){
//read jump
if(chars[pos] == '['){
}
boolean expectNext = false;
tok = 0;
while(pos < chars.length){
char c = chars[pos++];
char c = chars[pos];
if(tok >= tokens.length) error("Line too long; may only contain " + tokens.length + " tokens");
//reached end of line, bail out.
if(c == '\n') break;
if(c == '\n' || c == ';') break;
if(expectNext && c != ' ' && c != '#' && c != '\t'){
error("Expected space after string/token.");
}
expectNext = false;
if(c == '#'){
comment();
break;
}else if(c == '"'){
tokens[tok ++] = string();
expectNext = true;
}else if(c != ' ' && c != '\t'){
tokens[tok ++] = token();
expectNext = true;
}else{
pos ++;
}
}
//only process lines with at least 1 token
if(tok > 0){
checkRead();
//store jump location, always ends with colon
if(tok == 1 && tokens[0].charAt(tokens[0].length() - 1) == ':'){
if(jumpLocations.size >= maxJumps){
error("Too many jump locations. Max jumps: " + maxJumps);
}
jumpLocations.put(tokens[0].substring(0, tokens[0].length() - 1), line);
}else{
boolean wasJump;
String jumpLoc = null;
//clean up jump position before parsing
if(wasJump = (tokens[0].equals("jump") && tok > 1 && !Strings.canParseInt(tokens[1]))){
jumpLoc = tokens[1];
tokens[1] = "-1";
}
LStatement st;
try{
st = LogicIO.read(tokens, tok);
}catch(Exception e){
//log invalid statements
Log.err(e);
st = new InvalidStatement();
}
//store jumps that use labels
if(st instanceof JumpStatement jump && wasJump){
jumps.add(new JumpIndex(jump, jumpLoc));
}
if(st != null){
statements.add(st);
}else{
//attempt parsing using custom parser if a match is found; this is for mods
if(LAssembler.customParsers.containsKey(tokens[0])){
statements.add(LAssembler.customParsers.get(tokens[0]).get(tokens));
}else{
//unparseable statement
statements.add(new InvalidStatement());
}
}
line ++;
}
}
}
Seq<LStatement> parse(){
while(pos < chars.length){
switch(chars[pos++]){
case '\n', ' ' -> {} //skip newlines and spaces
case '\r' -> pos ++; //skip the newline after the \r
jumps.clear();
jumpLocations.clear();
while(pos < chars.length && line < LExecutor.maxInstructions){
switch(chars[pos]){
case '\n', ' ' -> pos ++; //skip newlines and spaces
case '\r' -> pos += 2; //skip the newline after the \r
default -> statement();
}
}
//load destination indices
for(var i : jumps){
i.jump.destIndex = jumpLocations.get(i.location, -1);
}
return statements;
}
static class JumpIndex{
JumpStatement jump;
String location;
public JumpIndex(JumpStatement jump, String location){
this.jump = jump;
this.location = location;
}
}
}