// Path: src/main/java/tech/pinto/Pinto.java
// public static class Expression implements TableFunction {
// 		
// 	private final boolean isSubExpression;
// 	private ArrayList<TableFunction>  functions = new ArrayList<>();
// 	private	Optional<Indexer> indexer = null;
// 	private	Optional<TerminalFunction> terminalFunction = Optional.empty();
// 	private Set<String> dependencies = new HashSet<>();
// 	private StringBuilder text = new StringBuilder();
// 	private	Optional<String> nameLiteral = Optional.empty();
// 		
// 	public Expression(boolean isSubExpression) {
// 		this.isSubExpression = isSubExpression;
// 	}
// 
// 	public Table evaluate(Pinto pinto) {
// 		if(!terminalFunction.isPresent()) {
// 			throw new PintoSyntaxException("Need terminal to evaluate expression.");
// 		}
// 		return terminalFunction.get().apply(pinto, this);
// 	}
// 
// 	@Override
// 	public void accept(Pinto pinto, Table table) {
// 		try {
// 			this.indexer.orElse(new Indexer(true)).accept(pinto, table);
// 			for(int i = 0; i < functions.size(); i++) {
// 				functions.get(i).accept(pinto, table);
// 			}
// 			table.collapseFunction();
// 		} catch(RuntimeException e) {
// 			throw new PintoSyntaxException("Error in expression: \"" + getText().trim() + "\": " + e.getLocalizedMessage() + "\n",e);
// 		}
// 	}
// 		
// 	public void addIndexer(Indexer indexer) {
// 		if(this.indexer == null) {
// 			indexer.setIndexForExpression();
// 			this.indexer = Optional.of(indexer);
// 		} else {
// 			functions.add(indexer);
// 		}
// 	}
// 
// 	public void addFunction(Column<?> col) {
// 		addFunction((StackFunction) (p, s) -> s.addFirst(col));
// 	}
// 
// 	public void addFunction(TableFunction function) {
// 		if(this.indexer == null) {
// 			this.indexer = Optional.empty();
// 		}
// 		functions.add(function);
// 	}
// 
// 	public void setTerminal(TerminalFunction function) {
// 		if(isSubExpression) {
// 			throw new PintoSyntaxException("Sub-expressions cannot include terminal functions");
// 		}
// 		this.terminalFunction = Optional.of(function);
// 	}
// 
// 	public ArrayList<TableFunction> getFunctions() {
// 		return functions;
// 	}
// 		
// 	public boolean isExpressionStart() {
// 		return functions.size() == 0;
// 	}
// 
// 	public boolean isNullary() {
// 		return (!indexer.isPresent()) || indexer.get().isNone();
// 	}
// 
// 	public Set<String> getDependencies() {
// 		return dependencies;
// 	}
// 
// 	public String getText() {
// 		return text.toString();
// 	}
// 
// 	public void addText(String expression) {
// 		this.text.append(" ").append(expression);
// 	}
// 
// 	public Optional<String> getNameLiteral() {
// 		return nameLiteral;
// 	}
// 
// 	public void setNameLiteral(String nameLiteral) {
// 		this.nameLiteral = Optional.of(nameLiteral);
// 	}
// 		
// 	public boolean hasTerminal() {
// 		return terminalFunction.isPresent();
// 	}
// 
// }


import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import tech.pinto.Pinto.Expression;

		getCSV(pinto,request,response,"NA", 10);
	}
	
	private void getSas(Pinto pinto, HttpServletRequest request, HttpServletResponse response) throws IOException {
		getCSV(pinto,request,response,".", 10);
	}

	private void getCSV(Pinto pinto, HttpServletRequest request, HttpServletResponse response, String naLiteral, int digits ) throws IOException {
		try {
			response.setContentType("text/csv");
			response.setCharacterEncoding("UTF-8");
			if(!request.getParameterMap().containsKey("p")) {
				throw new Exception("Empty request.");
			}
			DecimalFormatSymbols dfs = new DecimalFormatSymbols();
			dfs.setNaN(naLiteral);
			dfs.setInfinity(naLiteral);
			DecimalFormat nf = new DecimalFormat();
			nf.setDecimalFormatSymbols(dfs);
			nf.setGroupingUsed(false);
            nf.setMinimumFractionDigits(1);
            nf.setMaximumFractionDigits(8);
			//List<Table> l = pinto.evaluate(request.getParameter("p"));
			Table t = null;
			var lines = request.getParameter("p").split("\n");
			var e = new Pinto.Expression(false);
			for(var line : lines) {
				var expressions =  pinto.parse(line, e);
				var next = expressions.size() > 0 
						&& !expressions.get(expressions.size()-1).hasTerminal() ?

// Path: src/main/java/tech/pinto/Pinto.java
// public static class Expression implements TableFunction {
// 		
// 	private final boolean isSubExpression;
// 	private ArrayList<TableFunction>  functions = new ArrayList<>();
// 	private	Optional<Indexer> indexer = null;
// 	private	Optional<TerminalFunction> terminalFunction = Optional.empty();
// 	private Set<String> dependencies = new HashSet<>();
// 	private StringBuilder text = new StringBuilder();
// 	private	Optional<String> nameLiteral = Optional.empty();
// 		
// 	public Expression(boolean isSubExpression) {
// 		this.isSubExpression = isSubExpression;
// 	}
// 
// 	public Table evaluate(Pinto pinto) {
// 		if(!terminalFunction.isPresent()) {
// 			throw new PintoSyntaxException("Need terminal to evaluate expression.");
// 		}
// 		return terminalFunction.get().apply(pinto, this);
// 	}
// 
// 	@Override
// 	public void accept(Pinto pinto, Table table) {
// 		try {
// 			this.indexer.orElse(new Indexer(true)).accept(pinto, table);
// 			for(int i = 0; i < functions.size(); i++) {
// 				functions.get(i).accept(pinto, table);
// 			}
// 			table.collapseFunction();
// 		} catch(RuntimeException e) {
// 			throw new PintoSyntaxException("Error in expression: \"" + getText().trim() + "\": " + e.getLocalizedMessage() + "\n",e);
// 		}
// 	}
// 		
// 	public void addIndexer(Indexer indexer) {
// 		if(this.indexer == null) {
// 			indexer.setIndexForExpression();
// 			this.indexer = Optional.of(indexer);
// 		} else {
// 			functions.add(indexer);
// 		}
// 	}
// 
// 	public void addFunction(Column<?> col) {
// 		addFunction((StackFunction) (p, s) -> s.addFirst(col));
// 	}
// 
// 	public void addFunction(TableFunction function) {
// 		if(this.indexer == null) {
// 			this.indexer = Optional.empty();
// 		}
// 		functions.add(function);
// 	}
// 
// 	public void setTerminal(TerminalFunction function) {
// 		if(isSubExpression) {
// 			throw new PintoSyntaxException("Sub-expressions cannot include terminal functions");
// 		}
// 		this.terminalFunction = Optional.of(function);
// 	}
// 
// 	public ArrayList<TableFunction> getFunctions() {
// 		return functions;
// 	}
// 		
// 	public boolean isExpressionStart() {
// 		return functions.size() == 0;
// 	}
// 
// 	public boolean isNullary() {
// 		return (!indexer.isPresent()) || indexer.get().isNone();
// 	}
// 
// 	public Set<String> getDependencies() {
// 		return dependencies;
// 	}
// 
// 	public String getText() {
// 		return text.toString();
// 	}
// 
// 	public void addText(String expression) {
// 		this.text.append(" ").append(expression);
// 	}
// 
// 	public Optional<String> getNameLiteral() {
// 		return nameLiteral;
// 	}
// 
// 	public void setNameLiteral(String nameLiteral) {
// 		this.nameLiteral = Optional.of(nameLiteral);
// 	}
// 		
// 	public boolean hasTerminal() {
// 		return terminalFunction.isPresent();
// 	}
// 
// }


// Path: src/main/java/tech/pinto/Servlet.java
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import tech.pinto.Pinto.Expression;

		getCSV(pinto,request,response,"NA", 10);
	}
	
	private void getSas(Pinto pinto, HttpServletRequest request, HttpServletResponse response) throws IOException {
		getCSV(pinto,request,response,".", 10);
	}

	private void getCSV(Pinto pinto, HttpServletRequest request, HttpServletResponse response, String naLiteral, int digits ) throws IOException {
		try {
			response.setContentType("text/csv");
			response.setCharacterEncoding("UTF-8");
			if(!request.getParameterMap().containsKey("p")) {
				throw new Exception("Empty request.");
			}
			DecimalFormatSymbols dfs = new DecimalFormatSymbols();
			dfs.setNaN(naLiteral);
			dfs.setInfinity(naLiteral);
			DecimalFormat nf = new DecimalFormat();
			nf.setDecimalFormatSymbols(dfs);
			nf.setGroupingUsed(false);
            nf.setMinimumFractionDigits(1);
            nf.setMaximumFractionDigits(8);
			//List<Table> l = pinto.evaluate(request.getParameter("p"));
			Table t = null;
			var lines = request.getParameter("p").split("\n");
			var e = new Pinto.Expression(false);
			for(var line : lines) {
				var expressions =  pinto.parse(line, e);
				var next = expressions.size() > 0 
						&& !expressions.get(expressions.size()-1).hasTerminal() ?

								expressions.remove(expressions.size()-1) : new Expression(false);
