///////////////////////////////////////////////////////////////////////
// STANFORD LOGIC GROUP //
// General Game Playing Project //
// //
// Sample Player Implementation //
// //
// (c) 2007. See LICENSE and CONTRIBUTORS. //
///////////////////////////////////////////////////////////////////////
/**
*
*/
package stanfordlogic.gdl;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
/**
* Parser for the Game Description Language. Note that you can only run one
* parse at a time. This is to help save on memory allocation, by recycling the
* same data structures as much as possible.
*
* <p>
* The parser should be reset at the end of every game to clear the symbol
* table.
*
* <p>
* I'm not sure if it's actually necessary to reset the parser. Why not just let
* the symbol table grow? We have 2^31-256 values, so we'll only have overflow
* issues after seeing that many unique tokens. We should only get new tokens
* during game initializations. Assuming that every game creates 5,000 new
* tokens, we would need to play on the order of 429,496 games to fill up our
* symbol table...
*
* @author Based on code by Team Camembert: David Haley, Pierre-Yves Laligand
*/
public class Parser
{
final SymbolTable symbolTable_;
public int TOK_ROLE;
public int TOK_INIT;
public int TOK_TRUE;
public int TOK_DOES;
public int TOK_NEXT;
public int TOK_LEGAL;
public int TOK_GOAL;
public int TOK_TERMINAL;
public int TOK_DISTINCT;
public int TOK_NIL;
// GDL operators
public int TOK_IMPLIEDBY;
public int TOK_OR_OP;
public int TOK_NOT_OP;
public Parser()
{
symbolTable_ = new SymbolTable();
initSymbolTable();
}
// Fill the symbol table with the reserved keywords, so that given a token
// number, we know if it's one of the reserved tokens.
private void initSymbolTable()
{
TOK_ROLE = symbolTable_.get("role");
TOK_INIT = symbolTable_.get("init");
TOK_TRUE = symbolTable_.get("true");
TOK_DOES = symbolTable_.get("does");
TOK_NEXT = symbolTable_.get("next");
TOK_LEGAL = symbolTable_.get("legal");
TOK_GOAL = symbolTable_.get("goal");
TOK_TERMINAL = symbolTable_.get("terminal");
TOK_DISTINCT = symbolTable_.get("distinct");
TOK_NIL = symbolTable_.get("nil");
TOK_IMPLIEDBY = symbolTable_.get("<=");
TOK_OR_OP = symbolTable_.get("or");
TOK_NOT_OP = symbolTable_.get("not");
}
synchronized public void reset()
{
symbolTable_.clear();
initSymbolTable();
}
/**
* Parse a string of input. Note that you should not use this method if
* you have an input stream available; use the InputStream specialized
* method instead. This method creates an input stream specifically for
* reading the string <tt>input</tt>.
*
* @param input The string to parse.
* @return The GdlExpression result.
*/
public GdlList parse(String input)
{
return parse( new ByteArrayInputStream(input.getBytes()) );
}
public GdlList parse(InputStream input)
{
final Lexer lexer = new Lexer(input, symbolTable_);
// Top-level is a list of expressions.
ArrayList<GdlExpression> exprs = new ArrayList<GdlExpression>();
while ( true )
{
final int t = lexer.token();
if ( t == -1 )
break;
lexer.unget(t);
exprs.add(parseExpression(lexer));
}
return new GdlList(symbolTable_, exprs.toArray(new GdlExpression [exprs.size()]));
}
private GdlExpression parseExpression(Lexer lexer)
{
// Get the token:
final int t = lexer.token();
// If it's a (, then we must have a new expression
if ( t == '(' )
return parseList(lexer);
// If it's an identifier, then we have an atom
else if ( t > 255 )
return new GdlAtom(symbolTable_, t);
// If it's a question mark, then we have a variable
else if ( t == '?' )
return parseVariable(lexer);
// else, it must be something bogus.
else {
reportError("Expression: can't handle token " + t + " (char: " + (char)t +")");
return null;
}
}
private GdlList parseList(Lexer lexer)
{
ArrayList<GdlExpression> arr = new ArrayList<GdlExpression>();
int token;
while ( (token = lexer.token()) != ')' )
{
if (token == Lexer.EOF) {
reportError("Unexpected end of file in parseList");
}
// put this token back, and parse the expression
lexer.unget(token);
arr.add( parseExpression(lexer) );
}
// Convert the ArrayList to an array, and return a new GdlList object.
return new GdlList(symbolTable_, arr.toArray(new GdlExpression [arr.size()]) );
}
private GdlVariable parseVariable(Lexer lexer)
{
int token = lexer.token();
// Make sure we actually got an identifier
if ( token <= 255 )
reportError("? token must be followed by an identifier token in variable parsing");
return GdlVariable.getGdlVariable(symbolTable_, token);
}
public SymbolTable getSymbolTable()
{
return symbolTable_;
}
public static void main(String [] args)
{
Parser p = new Parser();
try {
FileInputStream input;
if (args.length == 0)
input = new FileInputStream("def/tictactoe.kif");
else
input = new FileInputStream(args[0]);
GdlExpression exp = p.parse( new BufferedInputStream(input) );
System.out.println(exp);
}
catch (Exception e) {
e.printStackTrace();
}
}
private void reportError(String str)
{
reportError(str, null);
}
private void reportError(String str, Lexer l)
{
String message;
if (l != null) {
message = "Parser error (line " + l.getLineNumber() + "): " + str;
}
else {
message = "Parser error: " + str;
}
throw new RuntimeException(message);
}
}