
com.badlogic.gdx.ai.btree.utils.BehaviorTreeReader.rl Maven / Gradle / Ivy
The newest version!
// Do not edit this file! Generated by Ragel.
// Ragel.exe -G2 -J -o BehaviorTreeReader.java BehaviorTreeReader.rl
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.ai.btree.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import com.badlogic.gdx.ai.GdxAI;
import com.badlogic.gdx.ai.btree.BehaviorTree;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.MdxException;
import com.badlogic.gdx.utils.SerializationException;
import com.badlogic.gdx.utils.StreamUtils;
/** An abstract event driven {@link BehaviorTree} parser.
*
* @author davebaol */
public abstract class BehaviorTreeReader {
private static final String LOG_TAG = "BehaviorTreeReader";
protected boolean debug = false;
protected int lineNumber;
protected boolean reportsComments;
protected abstract void startLine (int indent);
protected abstract void startStatement (String name, boolean isSubtreeReference, boolean isGuard);
protected abstract void attribute (String name, Object value);
protected abstract void endStatement ();
protected abstract void endLine ();
protected void comment (String text) {
}
public BehaviorTreeReader () {
this(false);
}
public BehaviorTreeReader (boolean reportsComments) {
this.reportsComments = reportsComments;
}
/** Parses the given string.
* @param string the string
* @throws SerializationException if the string cannot be successfully parsed. */
public void parse (String string) {
char[] data = string.toCharArray();
parse(data, 0, data.length);
}
/** Parses the given reader.
* @param reader the reader
* @throws SerializationException if the reader cannot be successfully parsed. */
public void parse (Reader reader) {
try {
char[] data = new char[1024];
int offset = 0;
while (true) {
int length = reader.read(data, offset, data.length - offset);
if (length == -1) break;
if (length == 0) {
char[] newData = new char[data.length * 2];
System.arraycopy(data, 0, newData, 0, data.length);
data = newData;
} else
offset += length;
}
parse(data, 0, offset);
} catch (IOException ex) {
throw new SerializationException(ex);
} finally {
StreamUtils.closeQuietly(reader);
}
}
/** Parses the given input stream.
* @param input the input stream
* @throws SerializationException if the input stream cannot be successfully parsed. */
public void parse (InputStream input) {
try {
parse(new InputStreamReader(input, "UTF-8"));
} catch (IOException ex) {
throw new SerializationException(ex);
} finally {
StreamUtils.closeQuietly(input);
}
}
/** Parses the given file.
* @param file the file
* @throws SerializationException if the file cannot be successfully parsed. */
public void parse (FileHandle file) {
try {
parse(file.reader("UTF-8"));
} catch (Exception ex) {
throw new SerializationException("Error parsing file: " + file, ex);
}
}
/** Parses the given data buffer from the offset up to the specified number of characters.
* @param data the buffer
* @param offset the initial index
* @param length the specified number of characters to parse.
* @throws SerializationException if the buffer cannot be successfully parsed. */
public void parse (char[] data, int offset, int length) {
int cs, p = offset, pe = length, eof = pe;
int s = 0;
int indent = 0;
int taskIndex = -1;
boolean isGuard = false;
boolean isSubtreeRef = false;
String statementName = null;
boolean taskProcessed = false;
boolean needsUnescape = false;
boolean stringIsUnquoted = false;
RuntimeException parseRuntimeEx = null;
String attrName = null;
lineNumber = 1;
try {
%%{
machine btree;
action attrValue {
String value = new String(data, s, p - s);
s = p;
if (needsUnescape) value = unescape(value);
outer:
if (stringIsUnquoted) {
if (debug) GdxAI.getLogger().info(LOG_TAG, "string: " + attrName + "=" + value);
if (value.equals("true")) {
if (debug) GdxAI.getLogger().info(LOG_TAG, "boolean: " + attrName + "=true");
attribute(attrName, Boolean.TRUE);
break outer;
} else if (value.equals("false")) {
if (debug) GdxAI.getLogger().info(LOG_TAG, "boolean: " + attrName + "=false");
attribute(attrName, Boolean.FALSE);
break outer;
} else if (value.equals("null")) {
attribute(attrName, null);
break outer;
} else { // number
try {
if (containsFloatingPointCharacters(value)) {
if (debug) GdxAI.getLogger().info(LOG_TAG, "double: " + attrName + "=" + Double.parseDouble(value));
attribute(attrName, new Double(value));
break outer;
} else {
if (debug) GdxAI.getLogger().info(LOG_TAG, "double: " + attrName + "=" + Double.parseDouble(value));
attribute(attrName, new Long(value));
break outer;
}
} catch (NumberFormatException nfe) {
throw new MdxException("Attribute value must be a number, a boolean, a string or null");
}
}
}
else {
if (debug) GdxAI.getLogger().info(LOG_TAG, "string: " + attrName + "=\"" + value + "\"");
attribute(attrName, value);
}
stringIsUnquoted = false;
}
action unquotedChars {
if (debug) GdxAI.getLogger().info(LOG_TAG, "unquotedChars");
s = p;
needsUnescape = false;
stringIsUnquoted = true;
outer:
while (true) {
switch (data[p]) {
case '\\':
needsUnescape = true;
break;
case ')':
case '(':
case ' ':
case '\r':
case '\n':
case '\t':
break outer;
}
// if (debug) GdxAI.getLogger().info(LOG_TAG, "unquotedChar (value): '" + data[p] + "'");
p++;
if (p == eof) break;
}
p--;
}
action quotedChars {
if (debug) GdxAI.getLogger().info(LOG_TAG, "quotedChars");
s = ++p;
needsUnescape = false;
outer:
while (true) {
switch (data[p]) {
case '\\':
needsUnescape = true;
p++;
break;
case '"':
break outer;
}
// if (debug) GdxAI.getLogger().info(LOG_TAG, "quotedChar: '" + data[p] + "'");
p++;
if (p == eof) break;
}
p--;
}
action newLine {
indent = 0;
taskIndex = -1;
isGuard = false;
isSubtreeRef = false;
statementName = null;
taskProcessed = false;
lineNumber++;
if (debug) GdxAI.getLogger().info(LOG_TAG, "****NEWLINE**** "+lineNumber);
}
action indent {
indent++;
}
action endLine {
if (taskIndex >= 0) {
endStatement(); // Close the last task of the line
}
taskProcessed = true;
if (statementName != null)
endLine();
if (debug) GdxAI.getLogger().info(LOG_TAG, "endLine: indent: " + indent + " taskName: " + statementName + " data[" + p + "] = " + (p >= eof ? "EOF" : "\"" + data[p] + "\""));
}
action savePos {
s = p;
}
action comment {
if (reportsComments) {
comment(new String(data, s, p - s));
} else {
if (debug) GdxAI.getLogger().info(LOG_TAG, "# Comment");
}
}
action taskName {
if (taskIndex++ < 0) {
startLine(indent); // First task/guard of the line
}
else {
endStatement(); // Close previous task/guard in line
}
statementName = new String(data, s, p - s);
startStatement(statementName, isSubtreeRef, isGuard); // Start this task/guard
isGuard = false;
}
action attrName {
attrName = new String(data, s, p - s);
}
ws = [ \r\t];
nl = ('\n' | '\r\n') @newLine;
id = [a-zA-Z_] [a-zA-Z_0-9]*;
idBegin = [a-zA-Z_] >savePos [a-zA-Z_0-9]*;
comment = '#' /[^\r\n]*/ >savePos %comment;
indent = [ \t] @indent;
attrName = idBegin '?'? %attrName;
attrValue = '"' @quotedChars %attrValue '"' | ^[#:"()\r\n\t ] >unquotedChars %attrValue;
attribute = attrName ws* ':' ws* attrValue;
attributes = (ws+ attribute)+;
taskName = idBegin ('.' id)* ('$' id)* '?'? %{isSubtreeRef = false;} %taskName;
subtreeRef = '$' idBegin '?'? %{isSubtreeRef = true;} %taskName;
task = taskName attributes? | subtreeRef; # either a task name with attributes or a subtree reference
guard = '(' @{isGuard = true;} ws* task? ws* ')' @{isGuard = false;};
guardableTask = (guard ws*)* task;
line = indent* guardableTask? ws* <: comment? %endLine;
main := line (nl line)** nl?;
write init;
write exec;
}%%
} catch (RuntimeException ex) {
parseRuntimeEx = ex;
}
if (p < pe || (statementName != null && !taskProcessed)) {
throw new SerializationException("Error parsing behavior tree on line " + lineNumber + " near: " + new String(data, p, pe - p),
parseRuntimeEx);
} else if (parseRuntimeEx != null) {
throw new SerializationException("Error parsing behavior tree: " + new String(data), parseRuntimeEx);
}
}
%% write data;
private static boolean containsFloatingPointCharacters (String value) {
for (int i = 0, n = value.length(); i < n; i++) {
switch (value.charAt(i)) {
case '.':
case 'E':
case 'e':
return true;
}
}
return false;
}
private static String unescape (String value) {
int length = value.length();
StringBuilder buffer = new StringBuilder(length + 16);
for (int i = 0; i < length;) {
char c = value.charAt(i++);
if (c != '\\') {
buffer.append(c);
continue;
}
if (i == length) break;
c = value.charAt(i++);
if (c == 'u') {
buffer.append(Character.toChars(Integer.parseInt(value.substring(i, i + 4), 16)));
i += 4;
continue;
}
switch (c) {
case '"':
case '\\':
case '/':
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
default:
throw new SerializationException("Illegal escaped character: \\" + c);
}
buffer.append(c);
}
return buffer.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy