package ws.prova.parser;

/*
 * Copyright (C) 2002-2007 <a href="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import org.mandarax.kernel.ConstantTerm;
import org.mandarax.kernel.Term;
import org.mandarax.reference.ConstantTermImpl;
import ws.prova.Calc;
import ws.prova.RListUtils;
import ws.prova.reagent;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parser of a Prova rulebase called from various consult flavours.
 * <p>Title: Prova </p>
 * <p>Description: Parser of a Prova rulebase called from various consult flavours</p>
 * <p>Copyright: Copyright (C) 2002-2007</p>
 * <p>Company: City University, London, United Kingdom</p>
 *
 * @author Alex Kozlenkov
 * @version 2.0 <25 March 2007>
 */
public class RulebaseParser {
	// The main pattern for the stop symbol of the previous clause or goal
	//    and the beginning of the current clause or goal
	private final static Pattern a0Pattern = Pattern.compile("^(\\.|:-|,|::)([\\w\\.\\[\\|\\,\\]]*)([\\!\\(<>=])|(\\.:-)|(\\.::)");
	// The same as above but other symbols may occur before the pattern
	private final static Pattern aPattern = Pattern.compile("(\\.|:-|,|::)([\\w\\.]*)([\\!\\(<>=])|(\\.:-)|(\\.::)");
	// A Java constant before a goal
	private final static Pattern aaPattern = Pattern.compile("^(=[\\x2A\\x2F\\w\\.+-]*)(\\.:-)|(\\.::)");
	// A Java constant before a clause
	private final static Pattern abPattern = Pattern.compile("^(=[\\x2A\\x2F\\w\\.+-]*)(\\.[\\w.]+\\()");
	private final static Pattern numPattern = Pattern.compile("([,\\(\\[])(-?\\d*\\.?\\d+(?:[eE]-?\\d+)?)");
	private final static Pattern keyPattern = Pattern.compile("\\[(\\w+)[,\\]]");
	private final static Pattern openlitPattern = Pattern.compile("([A-Za-z][\\w\\.]*\\()");
	private final static String openlitPatternString = new String("([A-Za-z][\\w\\.]*\\()");
	private final static Pattern quotePattern = Pattern.compile("(\".*?\")");
	// Function call pattern
	private final static Pattern fPattern = Pattern.compile("^=([\\w\\.]*)(\\([\\w\\.,\\[\\]\\|]*\\)[,\\.])");
	// Tail pattern
	public final static Pattern tailPattern = Pattern.compile("\\|(\\w*)\\]");
	// Var pattern
	public final static Pattern varPattern = Pattern.compile("([,\\[\\(])([A-Z]\\w*)");
	// Double-quoted replacement strings pattern
	private final static Pattern dqPattern = Pattern.compile("\"(\\d+)\"");
	private final static Pattern constPattern = Pattern.compile("(XXX(\\d+)YYY)");
	private final static Pattern embObjFromPattern = Pattern.compile("([,\\]\\[\\)\\(])_(\\d+)");
	private final static String embObjToString = "$1%$2";

	private final Map map_numeric = new HashMap();

	protected Calc calc;
	private static Set builtin = new HashSet();
	private static Set bpred = new HashSet();
	private static Map bpredCode = new HashMap();
	private boolean error;
	private OffsetString data;
	private final Set set_tail = new HashSet();
//	private final List reps = new ArrayList();
	// Double-quote protected strings inside the file being read
	private final List list_dq = new ArrayList();

	// The source being parsed ("reader" or filename)
	private String src;
	// Java objects passed to the parser
	private final Object[] objects;
	private final int shiftObjs;
	// Placeholder representing the reference to the Calc object
	private String localRef;
	// The parsing exception being accumulated
	private ParsingException pex = new ParsingException();
	private int iline;
	private List clauseStart = new ArrayList(128);


	/*
	 * ContractLog: add oid for each rule.
	 * oid is a temporal variable which is empty by default.
	 * if there was an oid found in the prova script, its value is stored in oid.
	 * else the oid is a newly created value.
	 */
	private String oid = "0";

	static {
		builtin.add("permute_bound");
		// dbopen is now provided in utils.prova
//		builtin.add("dbopen");
		builtin.add("sql_insert");
		builtin.add("sql_update_execute");
		builtin.add("raise");
		builtin.add("create");
		builtin.add("fail");
		builtin.add("parse_list");
		builtin.add("parse_nv");
		builtin.add("fopen");
		builtin.add("readline");
		builtin.add("read_enum");
		builtin.add("capture_enum");
		builtin.add("tokenize_enum");
		builtin.add("tokenize_list");
		builtin.add("println");
		builtin.add("print");
		builtin.add("store_tid");
		builtin.add("sendMsg");
//		builtin.add("rcvMsg");
		builtin.add("assert");
		builtin.add("retract");
		builtin.add("retractall");
		builtin.add("concat");
		builtin.add("exec");
		builtin.add("element");
		builtin.add("free");
		builtin.add("bound");
		builtin.add("type");
		builtin.add("implode");
		builtin.add("unescape");
		builtin.add("copy");
		builtin.add("copy_stream");
		builtin.add("spawn");
		builtin.add("byte_stream");
		builtin.add("listen");
		builtin.add("unlisten");
		builtin.add("ws_call");
		builtin.add("on_exception");
		builtin.add("exception");
		builtin.add("consult");
		builtin.add("asserta");
        builtin.add("insert");
        builtin.add("unique_id");
        builtin.add("update_fact");
        builtin.add("sort_list");
        builtin.add("sparql_select");
        builtin.add("capture_list");
        builtin.add("increment_value");
        builtin.add("init_join");
        builtin.add("join_test");
		builtin = Collections.unmodifiableSet(builtin);

		bpred.add("println");
		bpred.add("retract");
		bpred.add("retractall");
		bpred.add("print");
		bpred.add("copy");
		bpred.add("store_tid");
		bpred.add("assert");
		bpred.add("asserta");
        bpred.add("insert");
        bpred.add("update_fact");
        bpred.add("sort_list");
        bpred.add("increment_value");
        bpred.add("init_join");
		bpred = Collections.unmodifiableSet(bpred);

		bpredCode.put("println", new Integer(0));
		bpredCode.put("retract", new Integer(1));
		bpredCode.put("retractall", new Integer(2));
		bpredCode.put("print", new Integer(3));
		bpredCode.put("copy", new Integer(4));
		bpredCode.put("store_tid", new Integer(5));
		bpredCode.put("assert", new Integer(6));
		bpredCode.put("asserta", new Integer(7));
        bpredCode.put("insert", new Integer(8));
        bpredCode.put("update_fact", new Integer(9));
        bpredCode.put("sort_list", new Integer(10));
        bpredCode.put("increment_value", new Integer(11));
        bpredCode.put("init_join", new Integer(12));
		bpredCode = Collections.unmodifiableMap(bpredCode);
	}

	public RulebaseParser(Calc calc, String src, Object[] objects) {
		this.calc = calc;
		this.src = src;
		this.objects = objects;
		shiftObjs = objects == null ? 0 : objects.length;
		localRef = "%" + shiftObjs;
		pex.setSource(src);
	}

	private boolean testRHS(String s0, int offset) {
		String s = s0.substring(offset);
		boolean ok = num2object(s) != null || Character.isUpperCase(s.charAt(0));
		if (!ok) {
			error = true;
			calc.println("Syntax error in " + src + '[' + data.line() + "]: right-hand side is invalid.");
			pex.addError(data.line(), "right-hand side is invalid");
			return false;
		}
		return true;
	}

	private String assign_arith_literal(String head, String tail, char op) {
		String lit = "[pcalc," + localRef + ",";
		switch (op) {
			case '=':
				int pos = 0;
				if ((pos = tail.indexOf('+')) >= 0) {
					String arg1 = tail.substring(1, pos);
					String arg2 = tail.substring(pos + 1);
					testRHS(arg1, 0);
					testRHS(arg2, 0);
					if (arg2.equals("1")) {
						return lit + "inc," + arg1 + ',' + head + ']';
					}
					return lit + "add," + head + ',' + arg1 + ',' + arg2 + ']';
				}
				if ((pos = tail.indexOf('-')) >= 0) {
					String arg1 = tail.substring(1, pos);
					String arg2 = tail.substring(pos + 1);
					testRHS(arg1, 0);
					testRHS(arg2, 0);
					if (arg2.equals("1")) {
						return lit + "inc," + head + ',' + arg1 + ']';
					}
					return lit + "subtract," + head + ',' + arg1 + ',' + arg2 + ']';
				}
				if ((pos = tail.indexOf('*')) >= 0) {
					String arg1 = tail.substring(1, pos);
					String arg2 = tail.substring(pos + 1);
					testRHS(arg1, 0);
					testRHS(arg2, 0);
					return lit + "multiply," + head + ',' + arg1 + ',' + arg2 + ']';
				}
				if ((pos = tail.indexOf("//")) >= 0) {
					String arg1 = tail.substring(1, pos);
					String arg2 = tail.substring(pos + 2);
					testRHS(arg1, 0);
					testRHS(arg2, 0);
					return lit + "remainder," + head + ',' + arg1 + ',' + arg2 + ']';
				}
				if ((pos = tail.indexOf('/')) >= 0) {
					String arg1 = tail.substring(1, pos);
					String arg2 = tail.substring(pos + 1);
					testRHS(arg1, 0);
					testRHS(arg2, 0);
					return lit + "divide," + head + ',' + arg1 + ',' + arg2 + ']';
				}
//				return lit + "eq," + head + ',' + tail.substring(1) + ']';
				return "[equals," + head + ',' + tail.substring(1) + ']';
			case '>':
				if (tail.startsWith(">=")) {
					testRHS(tail, 2);
					return "[scalc,\"ws.prova.Calc\",ge," + head + ',' + tail.substring(2) + ']';
				}
				testRHS(tail, 1);
				return "[scalc,\"ws.prova.Calc\",gt," + head + ',' + tail.substring(1) + ']';
			case '<':
				if (tail.startsWith("<=")) {
					testRHS(tail, 2);
					return "[scalc,\"ws.prova.Calc\",le," + head + ',' + tail.substring(2) + ']';
				}
				if (tail.startsWith("<>")) {
					testRHS(tail, 2);
					return "[scalc,\"ws.prova.Calc\",ne," + head + ',' + tail.substring(2) + ']';
				}
				return "[scalc,\"ws.prova.Calc\",lt," + head + ',' + tail.substring(1) + ']';
		}
		return "";
	}

	private Object num2object(String num) {
		try {
			if (num.indexOf(".") >= 0 || num.indexOf("e") >= 0 || num.indexOf("E") >= 0) {
				return Double.valueOf(num);
			}
			return Integer.valueOf(num);
		} catch (Exception ex) {
			return null;
		}
	}

	private static String nonemptyComma(String s) {
		return (s.equals("")) ? "" : ',' + s;
	}

	private String removePatternNotIn(BufferedReader reader, String s, String remove, char prot[]) throws IOException {
		final char escape = '\\';
		StringBuffer b = new StringBuffer();
		String dq = "";
		boolean in = false;
		int pos[] = new int[2];
		indexOfOne(s, prot, pos);
//		int len = s.length();
		int pospc = -1;
		while (pos[0] >= 0) {
			if (in) {
				if (pos[1] != 0 && s.charAt(pos[1] - 1) == escape && (pos[1] <= 1 || s.charAt(pos[1] - 2) != escape)) {
					dq += s.substring(0, pos[1] + 1);
					s = s.substring(pos[1] + 1);
					indexOfOne(s, prot, pos);
					continue;
				}
//				if (pos[1] != 0 && pos[1] < len - 1 && s.charAt(pos[1] + 1) == prot[pos[0]]) {
				if (pos[1] != 0 && pos[1] < s.length() - 1 && s.charAt(pos[1] + 1) == prot[pos[0]]) { 
					dq += s.substring(0, pos[1] + 1);
					s = s.substring(pos[1] + 2);
					indexOfOne(s, prot, pos);
					continue;
				}
				dq += s.substring(0, pos[1]);
//				b.append(String.valueOf(list_dq.size())+'"');
				b.append("XXX" + String.valueOf(list_dq.size()) + "YYY");
				list_dq.add(dq);
//				String w = (String)list_dq.get(list_dq.size()-1);
				s = s.substring(pos[1] + 1);
				indexOfOne(s, prot, pos);
			} else {
				pospc = s.substring(0, pos[1]).indexOf('%');
				if (pospc >= 0) {
					s = s.substring(0, pospc);
					b.append(s);
					return b.toString().replaceAll(remove, "");
				}
				dq = "";
//				b.append(s.substring(0,pos+1).replaceAll(remove,""));
				b.append(s.substring(0, pos[1]).replaceAll(remove, ""));
				s = s.substring(pos[1] + 1);
				pos[1] = findMatching(s, prot[pos[0]], prot[1 - pos[0]]);
				while (pos[1] < 0) {
					// The constant is not closed in this line
					dq += s + '\n';
                    s = reader.readLine();
                    if( s==null ) {
                        throw new IOException("The input stream ends with unterminated constant string");
                    }
                    iline++;
					pos[1] = findMatching(s, prot[pos[0]], prot[1 - pos[0]]);
				}
//				pos[1] = s.indexOf(prot[pos[0]]);
			}
			in = !in;
		}
		pospc = s.indexOf('%');
		if (pospc >= 0)
			s = s.substring(0, pospc);
		b.append(s.replaceAll(remove, ""));
		return b.toString();
	}

	/**
	 * Return in <code>pos</code> the pair (index of character, position) corresponding to the character among <code>ch</code>
	 * found at the smallest position in <code>s</code>
	 *
	 * @param s   String The String to be searched for characters
	 * @param ch  char[] The array of characters to be searched for
	 * @param pos int[] The pair (index of character,position) corresponding
	 */
	private static void indexOfOne(final String s, final char ch[], int[] pos) {
		int imin = -1;
		int pmin = s.length();
		for (int i = 0; i < ch.length; i++) {
			int p = s.indexOf(ch[i]);
			if (p >= 0 && p < pmin) {
				pmin = p;
				imin = i;
			}
		}
		pos[0] = imin;
		pos[1] = pmin;
	}

	static int findMatching(String s, int ch, int aux) {
		Stack st = new Stack();
		Integer i = null;
		for (int at = 0; at < s.length(); at++) {
			int c = s.charAt(at);
			if (c == '\\') {
				if (s.charAt(at + 1) == '\'' || s.charAt(at + 1) == '\"') {
					at++;
					continue;
				} else if (s.charAt(at + 1) == '\\') {
					at++;
				}
			} else if (c == ch) {
				if (st.empty()) {
					return at;
				}
				if (i != null && i.intValue() == c) {
					st.pop();
					i = st.empty() ? null : (Integer) st.peek();
					continue;
				}
				i = new Integer(c);
				st.push(i);

				continue;
			} else if (c == aux) {
				if (i != null && i.intValue() == c) {
					st.pop();
					i = st.empty() ? null : (Integer) st.peek();
					continue;
				}
				i = new Integer(c);
				st.push(i);
			}
		}
		return -1;
	}

	protected static boolean separatorError(String sep, List seps) {
		if (seps.isEmpty())
			return false;
		String prevSep = (String) seps.get(seps.size() - 1);
		if (prevSep.equals(".")) {
			// ContractLog: add (::)-separator
			if (!sep.equals(".") && !sep.equals(":-") && !sep.equals("::"))
				return true;
		}
        if (prevSep.equals(",")) {
            if (!sep.equals(".") && !sep.equals(","))
                return true;
        }
		return false;
	}

	/**
	 * @param seps
	 */
	private void parseRulebase(List seps) {
		String errorMsg = "";
		String lit = "";
		Matcher m;
		while (true) {
			if (!(m = a0Pattern.matcher(data.getStr())).find()) {
				if (data.equals("."))
					break;
				error = true;
				errorMsg = "Syntax error in " + src + '[' + data.line() + "]: unrecognised clause structure.";
				calc.println(errorMsg);
				pex.addError(data.line(), "unrecognised clause structure");
				(m = aPattern.matcher(data.getStr())).find();
			}
//                System.out.println("Current line: "+data.line()+"; str="+data.substring(0,20));
			boolean res = true;
			// lhs of a function call if present
			String y = null;
			String sep = null;
			while (true) {
				if (m.group(4) != null) {
					// It is a goal
					seps.add(".");
					data.add("");
					data = data.offsetBy(1);
					res = (m = aPattern.matcher(data.getStr())).find();
					if (!res) {
						break;
					}
					continue;
				}
//                String g0 = m.group(0);
				sep = m.group(1);
				if (separatorError(sep, seps)) {
					error = true;
					errorMsg = "Syntax error in " + src + '[' + data.line() + "]: incorrect clause structure due to a separator problem.";
					calc.println(errorMsg);
					pex.addError(data.line(), "incorrect clause structure due to a separator problem");
				}
				seps.add(sep);
				lit = m.group(2);				
				data = data.offsetBy(m.start(3));
				// Find the next clause start (based on newlines) after the current offset
				while( clauseStart.size()!=0 && data.offset>=((Integer) clauseStart.get(0)).intValue() ) {
					clauseStart.remove(0);
				}
				if (data.charAt(0) == '!') {
					// It is a cut
					data.add("[cut]");
					res = (m = aPattern.matcher(data.getStr())).find();
					if (!res) {
						break;
					}
					if (m.group(4) != null)
						data.offsetBy(1);
					continue;
				} else if (data.charAt(0) != '(') {
					// It is <,>, or =
					if (data.charAt(0) == '=') {
						// Find out if it is a function call
						String s = data.getStr().toString();
						int len = ((Integer) clauseStart.get(0)).intValue()-data.offset;
						if( len<data.getStr().length() ) {
							s = s.substring(0,len);
						}
						boolean f = (m = fPattern.matcher(s)).find();
						if (f) {
							if (!m.group(1).startsWith("XXX")) {
								y = lit;
								lit = m.group(1);
								data = data.offsetBy(m.start(2));
								break;
							}
						}
					}
					// The next two tests relate to a Java constant occurring
					//   immediately before the next (a) goal or (b) clause
					res = (m = aaPattern.matcher(data.getStr())).find();
					if (!res)
						res = (m = abPattern.matcher(data.getStr())).find();
					if (res) {
						String g1 = m.group(1);
//							String g2 = m.group(2);
						lit = assign_arith_literal(lit, g1, g1.charAt(0));
						data.add(lit);
						data = data.offsetBy(m.start(2));
						res = (m = aPattern.matcher(data.getStr())).find();
						continue;
					}
					res = (m = aPattern.matcher(data.getStr())).find();
					if (!res) {
						lit = assign_arith_literal(lit, data.substring(0, data.length() - 1), data.charAt(0));
						data.add(lit);
						break;
					}
					lit = assign_arith_literal(lit, data.substring(0, m.start(1)), data.charAt(0));
					data.add(lit);
					continue;
				}
				break;
			}
			if (!res) {
				break;
			}
			int[] pos = {0};
			int nPars = 0;
			int diff = data.pair_diff('(', ')', pos);
			if (diff != 0) {
				// Unmatched parentheses
				errorMsg = "Syntax error in " + src + '[' + data.line() + "]: unmatched brackets.";
				error = true;
				calc.println(errorMsg);
				pex.addError(data.line(), "unmatched brackets");
				return;
			}
//				adata[0] = data.getStr();
//				nPars = pair_diff( adata, '(', ')', pos );
//				data.setStr( adata[0] );
			String errorDesc = null;
			if (nPars == 0) {
				// literal closed along the way
				int curr = pos[0];
				Class cl = null;
				if (y != null) {
					// Function call: check if it is a constructor
					try {
						cl = ws.prova.Calc.forName("ws.prova." + lit);
					} catch (ClassNotFoundException ex) {
						try {
							cl = ws.prova.Calc.forName(lit);
						} catch (ClassNotFoundException ex2) {
							try {
								cl = ws.prova.Calc.forName("java.lang." + lit);
							} catch (ClassNotFoundException ex3) {
								errorDesc = "class " + lit + " not recognised";
								errorMsg = "Syntax error in " + src + '[' + data.line() + "]: class " + lit + " not recognised.";
							}
						}
					}
					if (cl != null) {
						lit = "[pcalc," + localRef + ",construct," + '"' + cl.getName() + '"' + ',' + y + ",[" + data.substring(1, curr - 1) + "]]";
						data.add(lit);
						data.offsetBy(curr);
						continue;
					}
				}
				// Check if lit has embedded dots
				int lastdot = lit.lastIndexOf(".");
				if (lastdot >= 0) {
					String h = lit.substring(0, lastdot);
					try {
						try {
							try {
								cl = ws.prova.Calc.forName("ws.prova." + h);
							} catch (Exception exc) {
								cl = ws.prova.Calc.forName("java.lang." + h);
							}
						} catch (Exception ex) {
							cl = Class.forName(h);
						}
					} catch (Throwable ex) {
//							System.out.println("---h-3"+h+"ex"+ex);
					}
					if (cl == null) {
//							System.out.println("------"+cl);
						// Let's try and see if the substring before the last but one dot is a class name
						boolean itIsAnObject = true;
						int lastdot2 = h.lastIndexOf(".");
						if (lastdot2 >= 0) {
							String h2 = h.substring(0, lastdot2);
							cl = ws.prova.Calc.findClass(h2);
							if (cl == null) {
								// We are in trouble, something is strange in this literal
								errorMsg = "Syntax error in " + src + '[' + data.line() + "]: class " + h2 + " not recognised.";
								error = true;
								calc.println(errorMsg);
								pex.addError(data.line(), "class " + h2 + " not recognised");
								itIsAnObject = false;
							}
						}
						if (itIsAnObject) {
							// It is an object, so generate an instance method invocation
							if (y == null)
								lit = "[pcalc," + h + ',' + lit.substring(lastdot + 1) + nonemptyComma(data.substring(1, curr - 1)) + ']';
							else
								lit = "[fcalc," + h + ',' + lit.substring(lastdot + 1) + ',' + y + nonemptyComma(data.substring(1, curr - 1)) + ']';
						}
					} else {
						// It is a class name, so generate a static method invocation
						if (y == null) {
							lit = "[scalc," + '"' + h + '"' + ',' + lit.substring(lastdot + 1) + nonemptyComma(data.substring(1, curr - 1)) + ']';
						} else {
							lit = "[sfcalc," + '"' + h + '"' + ',' + lit.substring(lastdot + 1) + ',' + y + nonemptyComma(data.substring(1, curr - 1)) + ']';
						}
					}
					data.add(lit);
				} else if (y == null) {
					if (!sep.equals(".") && builtin.contains(lit)) {
						if (lit.equals("fail")) {
							lit = "[fail]";
						} else if (bpred.contains(lit)) {
							lit = "[bcalc," + localRef + "," + bpredCode.get(lit) + nonemptyComma(data.substring(1, curr - 1)) + ']';
						} else {
							lit = "[pcalc," + localRef + "," + lit + nonemptyComma(data.substring(1, curr - 1)) + ']';
						}
					} else if (Character.isUpperCase(lit.charAt(0))) {
						// A variable predicate symbol
						lit = "[derive,[" + lit + nonemptyComma(data.substring(1, curr - 1)) + "]]";
					} else if (lit.equals("spec") || lit.equals("meta")) {
						// spec and meta representaitons are flattened to be compatible with the older tradition
						String s0 = data.substring(1, curr - 1);
						if (s0.indexOf("(") >= 0) {
							lit = '[' + lit + nonemptyComma(data.substring(1, curr - 1));
							int pos0 = lit.indexOf("(");
							lit = lit.substring(0, pos0) + ',' + lit.substring(pos0 + 1);
						} else {
							lit = '[' + lit + '|' + data.substring(1, curr - 1) + ']';
						}
					} else {
						lit = '[' + lit + nonemptyComma(data.substring(1, curr - 1)) + ']';
					}
					data.add(lit);
				} else {
					error = true;
					calc.println(errorMsg);
					pex.addError(data.line(), errorDesc);
				}
				data = data.offsetBy(curr);
			}
		}
	}

	/**
	 * @param in
	 * @return next line from the input reader
	 */
	protected String nextLine(BufferedReader in) throws IOException {
		return in.readLine();
	}

	/**
	 * Parse a rulebase in <code>BufferedReader</code> and add the resulting goals and clauses to the knowledge base
	 *
	 * @param temp_kb
	 * @param in
	 * @return
	 * @throws ParsingException
	 * @throws IOException
	 */
	public Boolean parse(AtomicConsult temp_kb, final BufferedReader in) throws ParsingException, IOException {
		error = false;
		list_dq.clear();
//		AtomicConsult temp_kb = new AtomicConsult();
//		try {
		// (A) Read the whole file and remove all comments (start with % or //) and spaces
		String data0 = ".";
		String line = "";
		int offset = 1;
		iline = 0;
		char prot[] = {'\'', '"'};
		Vector linePos = new Vector(128);
		while ((line = in.readLine()) != null) {
			iline++;
            try {
                line = removePatternNotIn(in, line, "\\s", prot);
            } catch( IOException ioex ) {
                calc.println("Syntax error in " + src + "[EOF]: " + ioex.getMessage());
                pex.addError(-1,ioex.getMessage());
                pex.setDesc(ioex.getMessage());
                throw pex;
            }
            offset += line.length();
			linePos.add(new Integer(offset));
			data0 += line;
			if( line.length()!=0 && line.charAt(line.length()-1)=='.' )
				clauseStart.add(new Integer(offset));
		}
//		String line = "";
//		int offset = 1;
//		int iline = 0;
//		char prot[] = {'\'', '"'};
//		Vector linePos = new Vector(128);
//		while( (line = in.readLine()) != null ) {
//			iline++;
//			line = removePatternNotIn(line, "\\s", prot);
//			linePos.add(new Integer(offset += line.length()));
//			data0 += line;
//		}
		// (B) Fill in the list of "literals" along with separators that follow them
		List seps = new ArrayList();
		List temp = new ArrayList();
		data = new OffsetString(data0, linePos);
		Matcher m;
		parseRulebase(seps);

		// (C) Group literals into clauses by using the discovered separators
		String head = "[";
		String cond = "";
		seps.remove(0);
		seps.add(".");

		localRef = "," + localRef;
		List objs0 = new ArrayList();
		for (int k = 0; k < shiftObjs; k++)
			objs0.add(objects[k]);
		ListIterator litli = data.listIterator();
		int index = 0;
		for (ListIterator sepli = seps.listIterator(); sepli.hasNext() && litli.hasNext();) {
			String sep = (String) sepli.next();
			String lit = (String) litli.next();
			Matcher embmatcher = embObjFromPattern.matcher(lit);
			lit = embmatcher.replaceAll(embObjToString);
			if (sep.equals(".")) {
				// End of clause
				if (cond.length() == 0) {
					cond = "[]";
					head = lit;
				} else {
					cond += lit + ']';
				}
				String key = "";
				if (head.equals("")) {
					// It is a goal
					head = "[]";
				} else {
					// It is a clause
					// Define the key as the predicate name in the head
					boolean rc = (m = keyPattern.matcher(head)).find();
					if (!rc) {
						// Unrecoverable errors occurred previously
						pex.setDesc("Unrecoverable syntax errors occurred when parsing");
						throw pex;
					}
					key = m.group(1);
				}
				// Build the list of objects if they are present
				List[] objs = {new ArrayList(), new ArrayList()};
				String[] comp = {head, cond};
				Term[] terms = new Term[2];
				String num = "";
				int i = 0;
				StringBuffer res = new StringBuffer();
				set_tail.clear();
				for (i = 0; i < 2; i++) {
					objs[i].clear();
					objs[i].addAll(objs0);
					String s = comp[i];
					// . Find Calc references
					if (s.indexOf(localRef) >= 0) {
						objs[i].add(calc);
					}
					// . Take care of the numeric constants if they are present
					map_numeric.clear();
					m = numPattern.matcher(s);
					res.delete(0, res.length());
					while (m.find()) {
						char ch = m.group(1).charAt(0);
						num = m.group(2);
						int nobj;
						if (map_numeric.containsKey(num)) {
							nobj = ((Integer) map_numeric.get(num)).intValue();
						} else {
							nobj = objs[i].size();
							map_numeric.put(num, new Integer(nobj));
							Object o = num2object(num);
							objs[i].add(o);
						}
						m.appendReplacement(res, ch + "%" + nobj);
					}
					m.appendTail(res);
					comp[i] = res.toString();
					s = comp[i];
					// . Find and record list tails if they are present
					m = tailPattern.matcher(s);
					while (m.find()) {
						String tail = m.group(1);
						set_tail.add(tail);
					}
					comp[i] = res.toString();
					// . Deal with embedded literals (e.g., as in 'findall' construct)
					res.delete(0, res.length());
					m = openlitPattern.matcher(comp[i]);
					while (m.find()) {
						s = m.group(1);
						String lit1 = s.substring(0, s.length() - 1);
						if (Character.isLowerCase(lit1.charAt(0))) {
							lit1 = '"' + lit1 + '"';
						}
						String rep = '[' + lit1;
						char ch = comp[i].charAt(m.start(1) + s.length());
						if (ch != ']') {
							rep += ',';
						}
						m.appendReplacement(res, rep);
					}
					m.appendTail(res);
					comp[i] = res.toString();
				}
				// Make those variables that match tail symbols to be of type RList
				for (i = 0; i < 2; i++) {
					String s = comp[i];
					m = varPattern.matcher(s);
					res.delete(0, res.length());
					while (m.find()) {
						char ch = m.group(1).charAt(0);
						String var = m.group(2);
						if (set_tail.contains(var)) {
							m.appendReplacement(res, ch + "RList." + var);
						}
					}
					m.appendTail(res);
					comp[i] = res.toString();
				}
				// Restore the previously replaced constant strings in double quotes
				Map varsHead = new HashMap();
				Map vars = new HashMap();
				for (i = 0; i < 2; i++) {
					if (i != 0) {
						varsHead.putAll(vars);
					}
					String s = comp[i];
					// Prepare replacements of constant strings
//					reps.clear();
					String w = "";
					m = constPattern.matcher(s);
					while (m.find()) {
						String pseudoVar = m.group(1);
						w = (String) list_dq.get(Integer.parseInt(m.group(2)));
						final ConstantTerm t = reagent.cons(w, String.class);
						vars.put(pseudoVar, t);
//						reps.add(new Replacement(reagent.variable(pseudoVar, Object.class), t));
					}
					// Now we have all substitutions and objects ready
					try {
						if (objs[i].size() > 0) {
							terms[i] = RListUtils.rlist_with_exceptions(vars, comp[i], objs[i].toArray());
						} else {
							terms[i] = RListUtils.rlist_with_exceptions(vars, comp[i]);
						}
						// Remove variables that are in fact constant strings found in the consulted code
						for (Iterator keysit = vars.keySet().iterator(); keysit.hasNext();) {
							final Object varName = keysit.next();
							if (vars.get(varName) instanceof ConstantTermImpl || (i != 0 && varsHead.containsKey(varName)))
								keysit.remove();

						}
//						// Substitute the pre-assembled substitutions
//						for( ListIterator it = reps.listIterator(); it.hasNext(); ) {
//							terms[i] = terms[i].apply((Replacement) it.next());
//						}
					} catch (Exception e) {
						error = true;
						calc.println("Syntax error in " + src + '[' + iline + "]: " + e);
						pex.addError(iline, e.toString());
					}
					// Useful print-out
//System.out.println(comp[i]);
				}
				// Now vars contains all variables in the body minus those in the head
				// Now varsHead contains all variables in the head
				try {
					if (terms[0] != null && terms[1] != null) {
						
						//ContractLog: add oid
						Object[] retobj = new Object[6];
						
						retobj[0] = key;
						retobj[1] = terms[0];
						retobj[2] = terms[1];
						retobj[3] = varsHead;
						retobj[4] = vars;
						
						// ContractLog: add oid
						retobj[5] = oid;
						
						// Accumulate goals and clauses for subsequent commit
						temp_kb.insert_item(retobj);
					}
				} catch (Exception e) {
					error = true;
					calc.println("Syntax error in " + src + '[' + iline + "]: code could not be generated.");
					pex.addError(iline, "code could not be generated");
				}
				head = "";
				cond = "";
				oid= "0";
			} 
				// ContractLog: add (::)-Separator
			  else if (sep.equals("::")) {
				  // cut the oid(...)-Predicate to get the blank oid value
				oid = lit.substring(5,lit.length()-1);
				iline = data.atomLine(index++);
			
			  } else if (sep.equals(":-")) {
				head = lit;
				cond = "[";
				iline = data.atomLine(index++);
			} else {
				// A comma
				cond += lit + ',';
				iline = data.atomLine(index++);
			}
		}
//		}
//		catch ( IOException e ) {
//			return Boolean.FALSE;
//		}
		if (error) {
			pex.setDesc("Syntax errors occurred when parsing");
			throw pex;
		}

		return Boolean.TRUE;
	}
}
