All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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