/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
/*
* TODO: You use #, ~ and \ with <number> and how could they be better coordinated?
*
* AMPERSAND
* BRACKET
* COMMA
* TIMES
*/
package ket.math.convert;
import java.util.*;
// TODO: Remove this dependency either by calling clipboard in
// this method's caller or by specializing clipboard
// functionality.
import ketUI.Ket;
import ket.Edit;
import ket.math.*;
import ket.Selection;
import ketUI.Clipboard;
import ket.math.Highlight;
import ket.MathCollection;
import ketUI.DocumentManager;
import ket.math.purpose.Word;
import ket.math.purpose.Text;
import ket.math.purpose.IntegerValue;
import ket.math.purpose.VariableToken;
import ket.math.purpose.VariableState;
import ket.math.purpose.CompoundState;
import ket.math.convert.Interpretation;
/**
* This class parses a given string representation of functions, variables etc
* into an instance of Argument that represents part of an equation.
*/
public class ArgumentParser {
/**
* If set, print the token list as it is parsed to Ket.out.
*/
static final boolean DEBUG = false;
KnownArguments knownArguments;
Clipboard clipboard;
MathCollection mathCollection;
Interpretation interpretation;
/**
* This method tokenizes and parses a given string into a mathematical expression.
*/
public static Argument parseArgument(String argumentString, KnownArguments knownArguments, Clipboard clipboard, MathCollection mathCollection) {
assert knownArguments==Ket.MATH_ARGUMENTS;
Branch tokenList = Tokenization.tokenize(argumentString, knownArguments);
if (tokenList==null) {
Ket.out.println(" !!! Parsing argument::null token list when parsing !!! ");
Ket.out.println("\tstring = '" + argumentString + "'");
return null;
}
ArgumentParser equationParser = new ArgumentParser(tokenList, knownArguments, clipboard, mathCollection);
equationParser.parseAsMath();
return equationParser.getArgument();
}
public static Argument parseClj(String argumentString, Clipboard clipboard, MathCollection mathCollection) {
System.out.println(" --- parse clj --- ");
Branch tokenList = Tokenization.tokenize(argumentString, Ket.CLJ_ARGUMENTS);
if (tokenList==null) {
Ket.out.println(" !!! Parsing argument::null token list when parsing !!! ");
Ket.out.println("\tstring = '" + argumentString + "'");
return null;
}
ArgumentParser equationParser = new ArgumentParser(tokenList, Ket.CLJ_ARGUMENTS, clipboard, mathCollection);
equationParser.parseAsClj();
return equationParser.getArgument();
}
/**
* Parse raw expressions to avoid hard-coding substitutes while allowing references to 'x' to be replaced with the corresponding variable, x.
*/
public static Argument parseAs(String argumentString, Argument x) { // "sin(x)", token(q) -> sin(q)
Argument a = ArgumentParser.parseArgument(argumentString, Ket.KNOWN_ARGUMENTS, null, null);
if (a==null) return null;
Edit e = new Edit(a);
e.substitute(new Token(new Word("x")), x);
return e.getCurrent();
}
/**
* This method tokenizes and parses a given string into a mathematical expression.
*/
public static Argument parsePrefix(String prefixString, KnownArguments knownArguments, Clipboard clipboard, MathCollection mathCollection) {
Branch tokenList = Tokenization.tokenize(prefixString, knownArguments);
if (tokenList==null) {
Ket.out.println(" !!! Parsing prefix notation::null token list when parsing !!! ");
Ket.out.println("\tstring = '" + prefixString + "'");
return null;
}
int open = 0;
int close = 0;
for (Argument child : tokenList.getChildren()) {
if (child.getPurpose()==Symbol.OPEN_BRACKET) {
open += 1;
} else if (child.getPurpose()==Symbol.CLOSE_BRACKET) {
close += 1;
}
}
if (open>close) {
for (int i=0; i<open-close; i++) {
tokenList.append(new Token(Symbol.CLOSE_BRACKET));
}
}
ArgumentParser equationParser = new ArgumentParser(tokenList, knownArguments, clipboard, mathCollection);
//+? try {
equationParser.processTokenListAsPrefixNotation();
/*} catch (Exception e) {
Ket.out.println(" !!! Exception while parsing !!! ");
//+? Ket.out.println("string= '" + prefixString + "'");
e.printStackTrace();
Ket.out.println();
}*/
return equationParser.getArgument();
}
/**
* Parse a regular expression string that represents a pattern of arguments.
*/
public static Argument parseRegex(String regex, KnownArguments knownArguments, Clipboard clipboard, MathCollection mathCollection) {
// Note: Not yet used.
Branch tokenList = Tokenization.tokenize(regex, knownArguments);
if (tokenList==null) {
Ket.out.println(" !!! Parsing argument regular expression::null token list when parsing !!! ");
Ket.out.println("\tregex = '" + regex + "'");
return null;
}
ArgumentParser equationParser = new ArgumentParser(tokenList, knownArguments, clipboard, mathCollection);
equationParser.processRegex();
return equationParser.getArgument();
}
/**
* Given an argument, return a clone of it and process the clone.
*/
public static Argument cloneArgument(Argument argument, KnownArguments knownArguments, Clipboard clipboard, MathCollection mathCollection) {
Argument clone = Argument.cloneArgument(argument);
Branch tokenList = new Branch(null, clone);
ArgumentParser equationParser = new ArgumentParser(tokenList, knownArguments, clipboard, mathCollection);
equationParser.parseAsMath();
return equationParser.getArgument();
}
/**
* Parse a given equation into a nested series of branches and
* arguments that represent each function or operand. These can then
* be processed and accessed with other class methods.
*
* @param tokenList A series of tokens to be interpreted in terms of an argument.
* @param knownArguments The known functions, symbols and variables used for reference when parsing.
* @param clipboard A collection of replacement branches that can substitute for a given equation.
*/
private ArgumentParser(Branch tokenList,
KnownArguments knownArguments,
Clipboard clipboard,
MathCollection mathCollection) {
this.knownArguments = knownArguments;
this.clipboard = clipboard; // can be null
this.mathCollection = mathCollection;
this.interpretation = new Interpretation(tokenList);
}
private void debug(int x) {
if (DEBUG) {
Ket.out.println(" ("+x+"): " + interpretation.debug());
}
}
private void processTokenListAsPrefixNotation() {
if (DEBUG) Ket.out.println(" --- Process Token List --- ");
debug(0);
interpretation.findImplicitSymbols(knownArguments);
debug(1);
interpretation.recognizeFunctions(Function.BRACKET, Function.SUBSCRIPT, Function.JOIN);
debug(2);
interpretation.removeIntermediateBrackets();
debug(3);
interpretation.addNullRoot();
debug(0);
interpretation.apply(knownArguments);
debug(4);
interpretation.recognizeSubscripts();
debug(5);
interpretation.recognizeJoins();
debug(6);
}
private void parseAsClj() {
interpretation.removeCommas(); // Commas are whitespace.
interpretation.findImplicitSymbols(knownArguments);
interpretation.recognizeSymbolicFunctions(knownArguments);
interpretation.removeIntermediateBrackets(); // BUG? (rand) -> rand
interpretation.ignoreSuccessiveArgs(knownArguments); // Having found vectors etc., flatten the (rest args).
interpretation.recognizeCljFunctions(knownArguments); // BUG: What about '(rand) etc.?
}
// Bug: a_b:x -> composition(subscript(a,b),x), but a_b(x) -> subscript(a,composition(b,x)).
private void parseAsMath() {
if (DEBUG) Ket.out.println(" --- Process Token List --- ");
interpretation.findImplicitSymbols(knownArguments);
//+? interpretation.recognizeFunctions(Function.BRACKET);
interpretation.recognizeSymbolicFunctions(knownArguments); //( f '(' x ')' -> ?( f , x )
interpretation.recognizeSubscripts();
interpretation.recognizeJoins();
interpretation.breakApart(knownArguments); // <-- new bit
interpretation.expandFunctionPrefixNotation(knownArguments); //: f:x -> f(x)
interpretation.makeImplicitFunctionsExplicit(knownArguments); //( ?(f,(x)) -> f(x)
interpretation.recognizeAmpersandFunctions(knownArguments);
interpretation.convertTokensToVariables(knownArguments);
interpretation.removeIntermediateBrackets();
// TODO: What is needed is an interface that ambiguously refers
// to either cursor or argument range, and an object of that
// type may be accessed from selection or mathCollection.
Highlight highlight = null;
if (mathCollection!=null) {
highlight = mathCollection.getHighlight();
interpretation.substituteFromPreviousEquation(highlight);
interpretation.substituteFromCurrentSelection(highlight);
interpretation.substituteClipboard(clipboard);
}
interpretation.substituteEquation(highlight);
interpretation.unknownsToProducts();
interpretation.recognizeBold();
interpretation.recognizeNull();
interpretation.recursiveSubstitute(mathCollection);
if (DEBUG) Ket.out.println();
}
private void processRegex() {
// TODO: Change to reflect differences between regular expressions and arguments.
interpretation.findImplicitSymbols(knownArguments);
interpretation.recognizeSymbolicFunctions(knownArguments);
interpretation.recognizeSubscripts();
interpretation.recognizeJoins();
interpretation.expandFunctionPrefixNotation(knownArguments);
interpretation.makeImplicitFunctionsExplicit(knownArguments);
interpretation.recognizeAmpersandFunctions(knownArguments);
interpretation.convertTokensToVariables(knownArguments);
interpretation.removeIntermediateBrackets();
// TODO: What is needed is an interface that ambiguously refers
// to either cursor or argument range, and an object of that
// type may be accessed from selection or mathCollection.
Highlight highlight = null;
if (mathCollection!=null) {
highlight = mathCollection.getHighlight();
interpretation.substituteFromPreviousEquation(highlight);
interpretation.substituteFromCurrentSelection(highlight);
interpretation.substituteClipboard(clipboard);
}
interpretation.substituteEquation(highlight);
}
public Argument getArgument() {
return interpretation.getArgument();
}
}