package cn.liutils.crafting; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; /** * @author EAirPeter */ public class RecipeParser { private Reader reader = null; private int read = 0; private int cur = 0; private String type = null; private ParsedRecipeElement output = null; private ParsedRecipeElement[] input = null; private int width = -1; private int height = -1; private float exp = 0; RecipeParser(String str) throws Throwable { reader = new StringReader(str); getchar(); } RecipeParser(File file) throws Throwable { reader = new FileReader(file); getchar(); } void close() { try { reader.close(); } catch (Throwable e) { e.printStackTrace(); } } public String getType() { return type; } public ParsedRecipeElement getOutput() { return output; } public ParsedRecipeElement[] getInput() { return input; } public int getWidth() { return width; } public int getHeight() { return height; } public float getExperience() { return exp; } private void error(String message) { error(message, null); } private void error(String message, Throwable cause) { throw new RuntimeException("Failed at: " + cur, new RecipeParsingException(message, cause)); } private int getchar() { try { ++cur; read = reader.read(); if(read == ';') { while(read != '\n' && read != '\r' && read != -1) read = reader.read(); } return read; } catch (IOException e) { error("Caught unexpected IOException", e); return -1; } } public boolean parseNext() { type = null; output = null; input = null; width = -1; height = -1; parseNull(); if (read != -1) { parseType(); parseOutput(); parseExp(); parseChar('{'); parseInput(); parseChar('}'); return true; } return false; } private void parseNull() { while (read != -1 && Character.isWhitespace(read)) getchar(); } private void parseType() { parseNull(); StringBuilder sb = new StringBuilder(); while (read != -1 && Character.isUnicodeIdentifierPart(read)) { sb.append(Character.toChars(read)); getchar(); } type = sb.toString(); if (type == null || type.isEmpty()) error("Empty type is not allowed"); } private void parseOutput() { parseChar('('); output = parseElement(); parseChar(')'); } private void parseInput() { List<List<ParsedRecipeElement>> rows = new ArrayList<List<ParsedRecipeElement>>(); for (;;) { try { rows.add(parseList()); } catch (Throwable e) { break; } } for (List<ParsedRecipeElement> row : rows) { if (width == -1) width = row.size(); if (width != row.size()) error("Row size must be the same"); } height = rows.size(); if (width < 1) error("Width must greater than zero"); if (height < 1) error("Height must greater than zero"); input = new ParsedRecipeElement[width * height]; int index = 0; for (List<ParsedRecipeElement> row : rows) for (ParsedRecipeElement element : row) input[index++] = element; } private List<ParsedRecipeElement> parseList() { parseChar('['); ArrayList<ParsedRecipeElement> list = new ArrayList<ParsedRecipeElement>(); for (;;) { list.add(parseElement()); parseNull(); if (read == ',') getchar(); else break; } parseChar(']'); return list; } private ParsedRecipeElement parseElement() { parseNull(); ParsedRecipeElement res = new ParsedRecipeElement(); StringBuilder sb = new StringBuilder(); while (read != -1 && (Character.isUnicodeIdentifierPart(read) || read == ':')) { sb.append(Character.toChars(read)); getchar(); } if (sb.length() < 1) return null; res.name = sb.toString(); parseNull(); if (read == '#') { getchar(); res.data = parseInteger(); parseNull(); } if (read == '*') { getchar(); res.amount = parseInteger(); parseNull(); } return res; } private void parseChar(char c) { if(!tryParseChar(c)) error("Expecting " + c); } private boolean tryParseChar(char c) { parseNull(); if (read == c) { getchar(); return true; } else { return false; } } private int parseInteger() { parseNull(); StringBuilder sb = new StringBuilder(); while (read != -1 && Character.isDigit(read)) { sb.append(Character.toChars(read)); getchar(); } return Integer.valueOf(sb.toString()); } private void parseExp() { if(tryParseChar('[')) { float val = parseFloat(); parseNull(); parseChar(']'); exp = val; } else exp = 0; } private float parseFloat() { parseNull(); StringBuilder sb = new StringBuilder(); boolean hasDot = false; while(read != -1 && Character.isDigit(read) || (!hasDot && read == '.')) { sb.append(Character.toChars(read)); if(read == '.') { hasDot = true; } getchar(); } return Float.valueOf(sb.toString()); } }