/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * 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 net.morilib.sh.misc;

import java.util.ArrayList;
import java.util.List;

import net.morilib.unix.regex.BasicPattern;

public final class SimpleExpressionParser {

	private static final KeywordMatcher MT01000 =
		KeywordMatcher.compile(false, "+", "-");
	private static final KeywordMatcher MT02000 =
		KeywordMatcher.compile(false, "*", "/", "%");
	private static final KeywordMatcher MT04000 =
		KeywordMatcher.compile(false,
				"=", "==", "!=", "<", ">", "<=", ">=");
	private static final KeywordMatcher MT05000 =
		KeywordMatcher.compile(false, "&");
	private static final KeywordMatcher MT06000 =
		KeywordMatcher.compile(false, "|");

	private SimpleExpressionParser() {}

	private static Integer reduce(List<Integer> s, int n) {
		for(int i = 0; i < n; i++) {
			s.remove(s.size() - 1);
		}
		return s.get(s.size() - 1);
	}

	private static String _mt(KeywordMatcher m, String v) {
		return v == null ? v : m.matches(v);
	}

	public static long parseExpr(String... tokens) {
		List<Integer> s = new ArrayList<Integer>();
		List<Object>  l = new ArrayList<Object>();
		int stat = 0, k = 0;
		boolean g = false;
		String t, v, a, b;
		Long x, y;
		Object o;

		s.add(0);
		v = k < tokens.length ? tokens[k++] : null;
		while(true) {
			switch(stat) {
			case 0:
				/* 
				 * S -> *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 100);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 100:
				/*
				 * S -> E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null) {
					// accept this grammar
					return ((Long)l.get(l.size() - 1)).longValue();
				} else if((t = _mt(MT01000, v)) != null) {
					s.add(stat = 500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT04000, v)) != null) {
					s.add(stat = 900);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT05000, v)) != null) {
					s.add(stat = 1300);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT06000, v)) != null) {
					s.add(stat = 1500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 200:
				/*
				 * E -> ( *E )
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 300);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 300:
				/*
				 * E -> ( E *)
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v != null && v.equals(")")) {
					s.add(stat = 400);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT01000, v)) != null) {
					s.add(stat = 500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT04000, v)) != null) {
					s.add(stat = 900);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT05000, v)) != null) {
					s.add(stat = 1300);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT06000, v)) != null) {
					s.add(stat = 1500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 400:
				/*
				 * E -> ( E )*
				 */
				stat = reduce(s, 3);  g = true;
				l.remove(l.size() - 1);  o = l.remove(l.size() - 1);
				l.set(l.size() - 1, o);
				break;
			case 500:
				/*
				 * E -> E [+|-] *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 600);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 600:
				/*
				 * E -> E [+|-] E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null || v.equals(")") ||
						(t = _mt(MT01000, v)) != null ||
						(t = _mt(MT04000, v)) != null ||
						(t = _mt(MT05000, v)) != null ||
						(t = _mt(MT06000, v)) != null) {
					stat = reduce(s, 3);  g = true;
					y = (Long)l.remove(l.size() - 1);
					o = l.remove(l.size() - 1);
					x = (Long)l.remove(l.size() - 1);
					l.add(o.equals("+") ? x + y : x - y);
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 700:
				/*
				 * E -> E [*|/|%] *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 800);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 800:
				/*
				 * E -> E [*|/|%] E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null || v.equals(")") ||
						(t = _mt(MT01000, v)) != null ||
						(t = _mt(MT02000, v)) != null ||
						(t = _mt(MT04000, v)) != null ||
						(t = _mt(MT05000, v)) != null ||
						(t = _mt(MT06000, v)) != null) {
					stat = reduce(s, 3);  g = true;
					y = (Long)l.remove(l.size() - 1);
					o = l.remove(l.size() - 1);
					x = (Long)l.remove(l.size() - 1);
					if(o.equals("*")) {
						l.add(x * y);
					} else if(o.equals("/")) {
						l.add(x / y);
					} else {
						l.add(x % y);
					}
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 900:
				/*
				 * E -> E [=|!=|<|>|<=|>=] *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 1000);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 1000:
				/*
				 * E -> E [=|!=|<|>|<=|>=] E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null || v.equals(")") ||
						(t = _mt(MT04000, v)) != null ||
						(t = _mt(MT05000, v)) != null ||
						(t = _mt(MT06000, v)) != null) {
					stat = reduce(s, 3);  g = true;
					y = (Long)l.remove(l.size() - 1);
					o = l.remove(l.size() - 1);
					x = (Long)l.remove(l.size() - 1);
					if(o.equals("=") || o.equals("==")) {
						l.add(x == y ? 1l : 0l);
					} else if(o.equals("!=")) {
						l.add(x != y ? 1l : 0l);
					} else if(o.equals("<")) {
						l.add(x <  y ? 1l : 0l);
					} else if(o.equals(">")) {
						l.add(x >  y ? 1l : 0l);
					} else if(o.equals("<=")) {
						l.add(x <= y ? 1l : 0l);
					} else {
						l.add(x >= y ? 1l : 0l);
					}
				} else if((t = _mt(MT01000, v)) != null) {
					s.add(stat = 500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 1300:
				/*
				 * E -> E & *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 1400);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 1400:
				/*
				 * E -> E & E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null || v.equals(")") ||
						(t = _mt(MT05000, v)) != null ||
						(t = _mt(MT06000, v)) != null) {
					stat = reduce(s, 3);  g = true;
					y = (Long)l.remove(l.size() - 1);
					o = l.remove(l.size() - 1);
					x = (Long)l.remove(l.size() - 1);
					l.add(x != 0 && y != 0 ? 1l : 0l);
				} else if((t = _mt(MT01000, v)) != null) {
					s.add(stat = 500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT04000, v)) != null) {
					s.add(stat = 900);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 1500:
				/*
				 * E -> E | *E
				 * E -> *( E )
				 * E -> *E [+|-] E
				 * E -> *E [*|/|%] E
				 * E -> *E [=|!=|<|>|<=|>=] E
				 * E -> *E & E
				 * E -> *E | E
				 * E -> *id
				 * E -> *id : id
				 */
				if(g) {
					s.add(stat = 1600);  g = false;
				} else if(v != null && v.equals("(")) {
					s.add(stat = 200);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					s.add(stat = 1700);  l.add(v);
					v = k < tokens.length ? tokens[k++] : null;
				}
				break;
			case 1600:
				/*
				 * E -> E | E*
				 * E -> E *[+|-] E
				 * E -> E *[*|/|%] E
				 * E -> E *[=|!=|<|>|<=|>=] E
				 * E -> E *& E
				 * E -> E *| E
				 */
				if(v == null || v.equals(")") ||
						(t = _mt(MT06000, v)) != null) {
					stat = reduce(s, 3);  g = true;
					y = (Long)l.remove(l.size() - 1);
					o = l.remove(l.size() - 1);
					x = (Long)l.remove(l.size() - 1);
					l.add(x != 0 || y != 0 ? 1l : 0l);
				} else if((t = _mt(MT01000, v)) != null) {
					s.add(stat = 500);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT02000, v)) != null) {
					s.add(stat = 700);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT04000, v)) != null) {
					s.add(stat = 900);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else if((t = _mt(MT05000, v)) != null) {
					s.add(stat = 1300);  l.add(t);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					throw new SimpleExpressionParserException();
				}
				break;
			case 1700:
				/*
				 * E -> id*
				 * E -> id *: id
				 */
				if(v != null && v.equals(":")) {
					s.add(stat = 1800);  l.add(null);
					v = k < tokens.length ? tokens[k++] : null;
				} else {
					stat = reduce(s, 1);  g = true;
					try {
						l.add(Long.parseLong(
								l.remove(l.size() - 1).toString()));
					} catch(NumberFormatException e) {
						throw new SimpleExpressionParserException();
					}
				}
				break;
			case 1800:
				/*
				 * E -> id : *id
				 */
				if(v == null) {
					throw new SimpleExpressionParserException();
				}
				s.add(stat = 1900);  l.add(v);
				v = k < tokens.length ? tokens[k++] : null;
				break;
			case 1900:
				/*
				 * E -> id : id*
				 */
				stat = reduce(s, 3);  g = true;
				a = (String)l.remove(l.size() - 1);
				o = l.remove(l.size() - 1);
				b = (String)l.remove(l.size() - 1);
				l.add(BasicPattern.matches(a, b) ? 1l : 0l);
				break;
			default:  throw new RuntimeException();
			}
		}
	}

}
