diff --git a/src/org/nwapw/abacus/tree/FunctionNode.java b/src/org/nwapw/abacus/tree/FunctionNode.java new file mode 100644 index 0000000..bd82d38 --- /dev/null +++ b/src/org/nwapw/abacus/tree/FunctionNode.java @@ -0,0 +1,47 @@ +package org.nwapw.abacus.tree; + +import java.util.ArrayList; + +public class FunctionNode extends TreeNode { + + private String function; + private ArrayList children; + + private FunctionNode() { } + + public FunctionNode(String function){ + this.function = function; + children = new ArrayList<>(); + } + + public String getFunction() { + return function; + } + + public void addChild(TreeNode node){ + children.add(node); + } + + @Override + public T reduce(Reducer reducer) { + Object[] reducedChildren = new Object[children.size()]; + for(int i = 0; i < reducedChildren.length; i++){ + reducedChildren[i] = children.get(i).reduce(reducer); + if(reducedChildren[i] == null) return null; + } + return reducer.reduceNode(this, reducedChildren); + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(function); + buffer.append("("); + for(int i = 0; i < children.size(); i++){ + buffer.append(children.get(i)); + buffer.append(i == children.size() - 1 ? "" : ", "); + } + buffer.append(")"); + return buffer.toString(); + } +} diff --git a/src/org/nwapw/abacus/tree/TokenType.java b/src/org/nwapw/abacus/tree/TokenType.java index ce92eb3..06301ae 100644 --- a/src/org/nwapw/abacus/tree/TokenType.java +++ b/src/org/nwapw/abacus/tree/TokenType.java @@ -6,7 +6,7 @@ package org.nwapw.abacus.tree; */ public enum TokenType { - ANY(0), OP(1), NUM(2), WORD(3), OPEN_PARENTH(4), CLOSE_PARENTH(5); + INTERNAL_FUNCTION_END(-1), INTERNAL_FUNCTION_START(-1), ANY(0), COMMA(1), OP(2), NUM(3), WORD(4), OPEN_PARENTH(5), CLOSE_PARENTH(6); /** * The priority by which this token gets sorted. diff --git a/src/org/nwapw/abacus/tree/TreeNode.java b/src/org/nwapw/abacus/tree/TreeNode.java index accdefa..f028274 100644 --- a/src/org/nwapw/abacus/tree/TreeNode.java +++ b/src/org/nwapw/abacus/tree/TreeNode.java @@ -14,6 +14,7 @@ public abstract class TreeNode { * The lexer used to lex tokens. */ protected static Lexer lexer = new Lexer(){{ + register(",", TokenType.COMMA); register("\\+|-|\\*|/|^", TokenType.OP); register("[0-9]+(\\.[0-9]+)?", TokenType.NUM); register("[a-zA-Z]+", TokenType.WORD); @@ -67,9 +68,10 @@ public abstract class TreeNode { Stack> tokenStack = new Stack<>(); while(!from.isEmpty()){ Match match = from.remove(0); - if(match.getType() == TokenType.NUM) { + TokenType matchType = match.getType(); + if(matchType == TokenType.NUM || matchType == TokenType.WORD) { output.add(match); - } else if(match.getType() == TokenType.OP){ + } else if(matchType == TokenType.OP){ String tokenString = source.substring(match.getFrom(), match.getTo()); int precedence = precedenceMap.get(tokenString); OperatorAssociativity associativity = associativityMap.get(tokenString); @@ -86,14 +88,24 @@ public abstract class TreeNode { output.add(tokenStack.pop()); } tokenStack.push(match); - } else if(match.getType() == TokenType.OPEN_PARENTH){ + } else if(matchType == TokenType.OPEN_PARENTH){ + if(!output.isEmpty() && output.get(output.size() - 1).getType() == TokenType.WORD){ + tokenStack.push(output.remove(output.size() - 1)); + output.add(new Match<>(0, 0, TokenType.INTERNAL_FUNCTION_END)); + } tokenStack.push(match); - } else if(match.getType() == TokenType.CLOSE_PARENTH){ + } else if(matchType == TokenType.CLOSE_PARENTH || matchType == TokenType.COMMA){ while(!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH){ output.add(tokenStack.pop()); } if(tokenStack.empty()) return null; - tokenStack.pop(); + if(matchType == TokenType.CLOSE_PARENTH){ + tokenStack.pop(); + if(!tokenStack.empty() && tokenStack.peek().getType() == TokenType.WORD) { + output.add(tokenStack.pop()); + output.add(new Match<>(0, 0, TokenType.INTERNAL_FUNCTION_START)); + } + } } } while(!tokenStack.empty()){ @@ -119,6 +131,19 @@ public abstract class TreeNode { else return new OpNode(source.substring(match.getFrom(), match.getTo()), left, right); } else if(match.getType() == TokenType.NUM){ return new NumberNode(Double.parseDouble(source.substring(match.getFrom(), match.getTo()))); + } else if(match.getType() == TokenType.INTERNAL_FUNCTION_START){ + if(matches.isEmpty() || matches.get(0).getType() != TokenType.WORD) return null; + Match stringName = matches.remove(0); + String functionName = source.substring(stringName.getFrom(), stringName.getTo()); + FunctionNode node = new FunctionNode(functionName); + while(!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END){ + TreeNode argument = fromStringRecursive(source, matches); + if(argument == null) return null; + node.addChild(argument); + } + if(matches.isEmpty()) return null; + matches.remove(0); + return node; } return null; }