Skip to content

Commit

Permalink
added support for exponent notation to the tokens LONG and DOUBLE
Browse files Browse the repository at this point in the history
  • Loading branch information
BlvckBytes committed Nov 3, 2023
1 parent 422b894 commit c4ccbba
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 11 deletions.
4 changes: 2 additions & 2 deletions README-X.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ available:
| Literal True | true | A positive boolean value |
| Literal False | false | A negative boolean value |
| Literal Null | null | The null value |
| Double | 12.3, .4, -.8, -1 | A non-whole number |
| Long | 123, 4, -8, -1 | A whole number |
| Double | 12.3, .4, -.8, -1, .5e-4 | A non-whole number |
| Long | 123, 4, -8, -1, 2e3 | A whole number |
| String | "my string", "my \\" quote" | An immediate string of characters |
| Identifier | a, my_var, my_func | Either a variable or a function name |

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,8 @@ available:
| Literal True | true | A positive boolean value |
| Literal False | false | A negative boolean value |
| Literal Null | null | The null value |
| Double | 12.3, .4, -.8, -1 | A non-whole number |
| Long | 123, 4, -8, -1 | A whole number |
| Double | 12.3, .4, -.8, -1, .5e-4 | A non-whole number |
| Long | 123, 4, -8, -1, 2e3 | A whole number |
| String | "my string", "my \\" quote" | An immediate string of characters |
| Identifier | a, my_var, my_func | Either a variable or a function name |

Expand All @@ -823,8 +823,8 @@ The following *EBNF* describes the grammar of this small expression language pre
Digit ::= [0-9]
Letter ::= [A-Za-z]
Long ::= "-"? Digit+
Double ::= "-"? Digit* "." Digit+
Long ::= "-"? Digit+ ("e" Digit+)?
Double ::= "-"? Digit* "." Digit+ ("e" "-"? Digit+)?
String ::= '"' ('\"' | [^"] | "\s")* '"'
Identifier ::= Letter (Digit | Letter | '_')*
Literal ::= "true" | "false" | "null"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* MIT License
*
* Copyright (c) 2023 BlvckBytes
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package me.blvckbytes.gpeee.error;

public class NegativeExponentOnLongError extends AEvaluatorError {

public NegativeExponentOnLongError(int row, int col, String rawInput) {
super(row, col, rawInput, "A negative exponent is not allowed on a long, please use a double instead!");
}
}
24 changes: 23 additions & 1 deletion src/main/java/me/blvckbytes/gpeee/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ private AExpression parsePrimaryExpression(ITokenizer tokenizer) throws AEvaluat
switch (tk.getType()) {
case LONG:
logger.log(Level.FINEST, () -> DebugLogSource.PARSER + "Found an integer");
return new LongExpression(Integer.parseInt(tk.getValue()), tk, tk, tokenizer.getRawText());
return new LongExpression(parseIntegerWithPossibleExponent(tk), tk, tk, tokenizer.getRawText());

case DOUBLE:
logger.log(Level.FINEST, () -> DebugLogSource.PARSER + "Found a double");
Expand Down Expand Up @@ -740,4 +740,26 @@ else if (rhsPrecedence == PrecedenceMode.RESET)

return lhs;
}

/**
* Parses an integer token which supports exponent notation, which java does not (only on doubles).
* @param tk Token of integer type
* @return Value represented by the token
*/
private int parseIntegerWithPossibleExponent(Token tk) {
String tokenValue = tk.getValue();
int exponentIndicatorIndex = tokenValue.indexOf('e');

int number;

if (exponentIndicatorIndex < 0)
number = Integer.parseInt(tokenValue);
else {
int numberValue = Integer.parseInt(tokenValue.substring(0, exponentIndicatorIndex));
int exponentValue = Integer.parseInt(tokenValue.substring(exponentIndicatorIndex + 1));
number = numberValue * (int) Math.pow(10, exponentValue);
}

return number;
}
}
51 changes: 49 additions & 2 deletions src/main/java/me/blvckbytes/gpeee/tokenizer/TokenType.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

package me.blvckbytes.gpeee.tokenizer;

import me.blvckbytes.gpeee.error.NegativeExponentOnLongError;
import me.blvckbytes.gpeee.error.UnterminatedStringError;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -63,17 +64,19 @@ public enum TokenType {
return result.toString();
}),

// -?[0-9]+
// -?[0-9]+(e[0-9]+)?
LONG(TokenCategory.VALUE, null, tokenizer -> {
StringBuilder result = new StringBuilder();

if (collectDigits(tokenizer, result, false) != CollectorResult.READ_OKAY)
return null;

possiblyCollectExponent(tokenizer, result, false);

return result.toString();
}),

// -?[0-9]*.?[0-9]+
// -?[0-9]*.?[0-9]+(e-?[0-9]+)?
DOUBLE(TokenCategory.VALUE, null, tokenizer -> {
StringBuilder result = new StringBuilder();

Expand Down Expand Up @@ -103,6 +106,8 @@ public enum TokenType {
if (collectDigits(tokenizer, result, false) != CollectorResult.READ_OKAY)
return null;

possiblyCollectExponent(tokenizer, result, true);

return result.toString();
}),

Expand Down Expand Up @@ -270,6 +275,48 @@ public FTokenReader getTokenReader() {
return tokenReader;
}

private static void possiblyCollectExponent(ITokenizer tokenizer, StringBuilder result, boolean allowNegativeExponent) {
if (!tokenizer.hasNextChar() || tokenizer.peekNextChar() != 'e')
return;

// It could occur that an 'e' immediately follows a long or double but is not meant as an exponent.
// If parsing the exponent number is not successful, these characters are to be put back, as they likely
// carry another meaning. The initial number should still be parsed though, so a failure would not be appropriate.
tokenizer.saveState(true);

// Append 'e'
result.append(tokenizer.nextChar());

boolean hasNegativeSign = false;
int negativeSignRow = tokenizer.getCurrentRow();
int negativeSignCol = tokenizer.getCurrentCol();

// Allow a negative sign in the exponent, which is usually taken care of by the minus token for all numbers
if (tokenizer.hasNextChar() && tokenizer.peekNextChar() == '-') {
hasNegativeSign = true;
result.append(tokenizer.nextChar());
}

// Collect exponent digits
if (collectDigits(tokenizer, result, false) == CollectorResult.READ_OKAY) {
if (!allowNegativeExponent && hasNegativeSign)
throw new NegativeExponentOnLongError(negativeSignRow, negativeSignCol, tokenizer.getRawText());

tokenizer.discardState(true);
return;
}

// Was likely not meant as an exponent
tokenizer.restoreState(true);

// Remove the '-' again
if (hasNegativeSign)
result.deleteCharAt(result.length() - 1);

// Remove the 'e' again
result.deleteCharAt(result.length() - 1);
}

private static CollectorResult collectDigits(ITokenizer tokenizer, StringBuilder result, boolean stopBeforeDot) {
if (!tokenizer.hasNextChar())
return CollectorResult.NO_NEXT_CHAR;
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/grammar.ebnf
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Digit ::= [0-9]
Letter ::= [A-Za-z]

Long ::= "-"? Digit+
Double ::= "-"? Digit* "." Digit+
Long ::= "-"? Digit+ ("e" Digit+)?
Double ::= "-"? Digit* "." Digit+ ("e" "-"? Digit+)?
String ::= '"' ('\"' | [^"] | "\s")* '"'
Identifier ::= Letter (Digit | Letter | '_')*
Literal ::= "true" | "false" | "null"
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/me/blvckbytes/gpeee/TerminalTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,18 @@ public void shouldEvaluateTerminals() {
validator.validate(".3", .3);
validator.validate("-3", -3);
validator.validateThrows("-3a", UndefinedVariableError.class);
validator.validateThrows("3e", UndefinedVariableError.class);
validator.validate("3e3", 3 * (int) Math.pow(10, 3));
validator.validate("-3e3", -3 * (int) Math.pow(10, 3));
validator.validateThrows("3e-3", NegativeExponentOnLongError.class);

// Double
validator.validate("3.3", 3.3);
validator.validate("-3.3", -3.3);
validator.validateThrows("3.3e", UndefinedVariableError.class);
validator.validate("3.3e3", 3.3 * Math.pow(10, 3));
validator.validate("-3.3e3", -3.3 * Math.pow(10, 3));
validator.validate("-3.3e-3", -3.3 * Math.pow(10, -3));

// Would be parsed as a member access of the member .3 on 3
validator.validateThrows("3..3", UnknownMemberError.class);
Expand Down

0 comments on commit c4ccbba

Please sign in to comment.