-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code for issues 56 and 57, including tests #58
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ | |
*/ | ||
package net.objecthunter.exp4j.tokenizer; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
|
@@ -86,9 +88,9 @@ public Token nextToken(){ | |
return parseParentheses(true); | ||
} else if (isCloseParentheses(ch)) { | ||
return parseParentheses(false); | ||
} else if (Operator.isAllowedOperatorChar(ch)) { | ||
} else if (isAllowedOperatorChar(ch)) { | ||
return parseOperatorToken(ch); | ||
} else if (isAlphabetic(ch) || ch == '_') { | ||
} else if (isVariableOrFunctionStartChar(ch)) { | ||
// parse the name which can be a setVariable or a function | ||
if (lastToken != null && | ||
(lastToken.getType() != Token.TOKEN_OPERATOR | ||
|
@@ -105,7 +107,20 @@ public Token nextToken(){ | |
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]"); | ||
} | ||
|
||
private Token parseArgumentSeparatorToken(char ch) { | ||
protected boolean isVariableOrFunctionStartChar(char ch) { | ||
return isAlphabetic(ch) || ch == '_'; | ||
} | ||
|
||
/** | ||
* The set of allowed operator chars | ||
*/ | ||
public static final String DEFAULT_ALLOWED_OPERATOR_CHARS = "+-*/%^!#§$&;:~<>|="; | ||
|
||
public boolean isAllowedOperatorChar(char ch) { | ||
return DEFAULT_ALLOWED_OPERATOR_CHARS.indexOf(ch) >= 0; | ||
} | ||
|
||
private Token parseArgumentSeparatorToken(char ch) { | ||
this.pos++; | ||
this.lastToken = new ArgumentSeparatorToken(); | ||
return lastToken; | ||
|
@@ -133,6 +148,16 @@ private boolean isCloseParentheses(char ch) { | |
return ch == ')' || ch == '}' || ch == ']'; | ||
} | ||
|
||
private Collection<String> undefinedVariables = null; | ||
|
||
public void allowUndefinedVariables(boolean allow) { | ||
this.undefinedVariables = (allow ? new ArrayList<String>() : null); | ||
} | ||
|
||
public String[] getUndefinedVariables() { | ||
return (undefinedVariables != null ? undefinedVariables.toArray(new String[undefinedVariables.size()]) : null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The conclusion after reading Arrays of Wisdom of the Ancients is that we should use
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This I didn't know, I always assumed allocating the right size was best! Thanks for the "wisdom"! |
||
} | ||
|
||
private Token parseFunctionOrVariable() { | ||
final int offset = this.pos; | ||
int lastValidLen = 1; | ||
|
@@ -154,6 +179,12 @@ private Token parseFunctionOrVariable() { | |
if (f != null) { | ||
lastValidLen = len; | ||
lastValidToken = new FunctionToken(f); | ||
} else if (undefinedVariables != null) { | ||
if (! undefinedVariables.contains(name)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If undefinedVariables is declared Collection the check should be there, since we cannot assume the check needn't be done. If it is declared Set, we can omit the check. I usually don't care about these kind of optimisations, for sets that are small. |
||
undefinedVariables.add(name); | ||
} | ||
lastValidLen = len; | ||
lastValidToken = new VariableToken(name); | ||
} | ||
} | ||
len++; | ||
|
@@ -184,7 +215,7 @@ private Token parseOperatorToken(char firstChar) { | |
Operator lastValid = null; | ||
symbol.append(firstChar); | ||
|
||
while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len])) { | ||
while (!isEndOfExpression(offset + len) && isAllowedOperatorChar(expression[offset + len])) { | ||
symbol.append(expression[offset + len++]); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could use
HashSet
, or ratherLinkedHashSet
if you want to preserve tokens order.ArrayList
will consume less memory butHashSet
will perform better. I'm not sure what is more important here.The
ArrayList
:The
HashSet
orLinkedHashSet
:But also, with
HashSet
you could not checkcontains(name)
sinceSet
guaranteesString
s uniqueness onadd(name)
so the number of operations is smaller.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArrayList's add can be O(N), if it needs to reallocate the underlying array. Anyway, it's reasonable to expect the set of unknown variables to be small, so I don't think it really matters.