improved hgs double support

This commit is contained in:
hneemann 2019-05-16 15:54:32 +02:00
parent c4abf9a395
commit 5af455adbb
5 changed files with 137 additions and 11 deletions

View File

@ -12,6 +12,7 @@ import de.neemann.digital.lang.Lang;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
/**
* The evaluation context
@ -254,7 +255,7 @@ public class Context {
for (int i = 1; i < args.size(); i++)
eval.add(args.get(i).value(c));
return String.format(Value.toString(args.get(0).value(c)), eval.toArray());
return String.format(Locale.US, Value.toString(args.get(0).value(c)), eval.toArray());
}
private static final class FunctionIsPresent extends InnerFunction {

View File

@ -70,7 +70,7 @@ public class Parser {
/**
* Creates a new instance
*
* @param reader the reader to parse
* @param reader the reader to parse
* @param srcFile the source file name if any
*/
public Parser(Reader reader, String srcFile) {
@ -323,6 +323,14 @@ public class Parser {
}
}
private double convToDouble(String num) throws ParserException {
try {
return Double.parseDouble(num);
} catch (NumberFormatException e) {
throw newParserException("not a number: " + tok.getIdent());
}
}
private ParserException newUnexpectedToken(Tokenizer.Token token) {
String name = token == IDENT ? tok.getIdent() : token.name();
return newParserException("unexpected Token: " + name);
@ -356,13 +364,13 @@ public class Parser {
case NOTEQUAL:
return c -> !Value.equals(a.value(c), b.value(c));
case LESS:
return c -> Value.toLong(a.value(c)) < Value.toLong(b.value(c));
return c -> Value.less(a.value(c), b.value(c));
case LESSEQUAL:
return c -> Value.toLong(a.value(c)) <= Value.toLong(b.value(c));
return c -> Value.lessEqual(a.value(c), b.value(c));
case GREATER:
return c -> Value.toLong(a.value(c)) > Value.toLong(b.value(c));
return c -> Value.less(b.value(c), a.value(c));
case GREATEREQUAL:
return c -> Value.toLong(a.value(c)) >= Value.toLong(b.value(c));
return c -> Value.lessEqual(b.value(c), a.value(c));
default:
throw newUnexpectedToken(t);
}
@ -435,7 +443,7 @@ public class Parser {
while (nextIs(SUB)) {
Expression a = ac;
Expression b = parseMul();
ac = c -> Value.toLong(a.value(c)) - Value.toLong(b.value(c));
ac = c -> Value.sub(a.value(c), b.value(c));
}
return ac;
}
@ -445,7 +453,7 @@ public class Parser {
while (nextIs(MUL)) {
Expression a = ac;
Expression b = parseDiv();
ac = c -> Value.toLong(a.value(c)) * Value.toLong(b.value(c));
ac = c -> Value.mul(a.value(c), b.value(c));
}
return ac;
}
@ -455,7 +463,7 @@ public class Parser {
while (nextIs(DIV)) {
Expression a = ac;
Expression b = parseMod();
ac = c -> Value.toLong(a.value(c)) / Value.toLong(b.value(c));
ac = c -> Value.div(a.value(c), b.value(c));
}
return ac;
}
@ -480,6 +488,9 @@ public class Parser {
case NUMBER:
long num = convToLong(tok.getIdent());
return c -> num;
case DOUBLE:
double d = convToDouble(tok.getIdent());
return c -> d;
case STRING:
String s = tok.getIdent();
return c -> s;

View File

@ -18,7 +18,7 @@ class Tokenizer {
UNKNOWN, IDENT, AND, OR, XOR, NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL,
ADD, SUB, MUL, GREATER, LESS, DIV, MOD, END, IF, ELSE, FOR, WHILE, SEMICOLON, NOTEQUAL, STRING,
OPENBRACE, CLOSEDBRACE, CODEEND, OPENSQUARE, CLOSEDSQUARE, DOT, STATIC, FUNC, GREATEREQUAL, LESSEQUAL,
REPEAT, RETURN, COLON, UNTIL
REPEAT, RETURN, COLON, UNTIL, DOUBLE
}
private static HashMap<String, Token> statementMap = new HashMap<>();
@ -275,6 +275,9 @@ class Tokenizer {
c = readChar();
if (isNumberChar(c) || isHexChar(c) || c == 'x' || c == 'X') {
builder.append((char) c);
} else if (c == '.') {
builder.append((char) c);
token = Token.DOUBLE;
} else {
unreadChar(c);
wasChar = false;

View File

@ -123,7 +123,9 @@ public final class Value {
* @return true if both values are equal
*/
public static boolean equals(Object a, Object b) {
if (a instanceof Number && b instanceof Number)
if (a instanceof Double || b instanceof Double)
return a.equals(b);
else if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() == ((Number) b).longValue();
else if (a instanceof String || b instanceof String)
return a.toString().equals(b.toString());
@ -140,6 +142,8 @@ public final class Value {
* @throws HGSEvalException HGSEvalException
*/
public static Object add(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) + toDouble(b);
if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() + ((Number) b).longValue();
if (a instanceof String || b instanceof String)
@ -147,6 +151,54 @@ public final class Value {
throw new HGSEvalException("arguments must be int or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Subtracts two values
*
* @param a a value
* @param b a value
* @return the sum
* @throws HGSEvalException HGSEvalException
*/
public static Object sub(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) - toDouble(b);
if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() - ((Number) b).longValue();
throw new HGSEvalException("arguments must be int or double, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Multiplies two values
*
* @param a a value
* @param b a value
* @return the product
* @throws HGSEvalException HGSEvalException
*/
public static Object mul(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) * toDouble(b);
if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() * ((Number) b).longValue();
throw new HGSEvalException("arguments must be int or double, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Divides two numbers
*
* @param a a value
* @param b a value
* @return the quotient
* @throws HGSEvalException HGSEvalException
*/
public static Object div(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) / toDouble(b);
if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() / ((Number) b).longValue();
throw new HGSEvalException("arguments must be int or double, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Performs an or operation
*
@ -202,6 +254,42 @@ public final class Value {
return !toBool(value);
}
/**
* Helper compare two values
*
* @param a a value
* @param b a value
* @return true if a&lt;b
* @throws HGSEvalException HGSEvalException
*/
public static boolean less(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) < toDouble(b);
if (a instanceof Number && b instanceof Number)
return toLong(a) < toLong(b);
if (a instanceof String && b instanceof String)
return a.toString().compareTo(b.toString()) < 0;
throw new HGSEvalException("arguments must be int, double or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Helper compare two values
*
* @param a a value
* @param b a value
* @return true if a<=b
* @throws HGSEvalException HGSEvalException
*/
public static boolean lessEqual(Object a, Object b) throws HGSEvalException {
if (a instanceof Double || b instanceof Double)
return toDouble(a) <= toDouble(b);
if (a instanceof Number && b instanceof Number)
return toLong(a) <= toLong(b);
if (a instanceof String && b instanceof String)
return a.toString().compareTo(b.toString()) <= 0;
throw new HGSEvalException("arguments must be int, double or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
}
/**
* Trims spaces at the right side of the string.
*

View File

@ -78,6 +78,21 @@ public class ParserTest extends TestCase {
assertEquals("Hallo_true", new Parser("\"Hallo_\" + (1<2)").parseExp().value(new Context()));
}
public void testParseExpArithDouble() throws IOException, ParserException, HGSEvalException {
assertEquals(4.0, new Parser("1.5+2.5").parseExp().value(new Context()));
assertEquals(0.5, new Parser("1.5-1").parseExp().value(new Context()));
assertEquals(3.0, new Parser("1.5*2").parseExp().value(new Context()));
assertEquals(3.0, new Parser("2*1.5").parseExp().value(new Context()));
assertEquals(1L, new Parser("3/2").parseExp().value(new Context()));
assertEquals(1.5, new Parser("3.0/2").parseExp().value(new Context()));
assertEquals(1.5, new Parser("3/2.0").parseExp().value(new Context()));
assertEquals(1.5, new Parser("3.0/2.0").parseExp().value(new Context()));
assertEquals(true, new Parser("1.0001>1").parseExp().value(new Context()));
assertEquals(false, new Parser("1>1.0001").parseExp().value(new Context()));
assertEquals(false, new Parser("1.0001<1").parseExp().value(new Context()));
assertEquals(true, new Parser("1<1.0001").parseExp().value(new Context()));
}
public void testParseExpCompare() throws IOException, ParserException, HGSEvalException {
assertEquals(true, new Parser("5=5").parseExp().value(new Context()));
assertEquals(true, new Parser("\"Hello\"=\"Hello\"").parseExp().value(new Context()));
@ -85,6 +100,10 @@ public class ParserTest extends TestCase {
assertEquals(false, new Parser("5!=5").parseExp().value(new Context()));
assertEquals(false, new Parser("\"Hello\"!=\"Hello\"").parseExp().value(new Context()));
assertEquals(true, new Parser("\"Hello\"!=\"World\"").parseExp().value(new Context()));
assertEquals(true, new Parser("\"a\"<\"b\"").parseExp().value(new Context()));
assertEquals(false, new Parser("\"b\"<\"a\"").parseExp().value(new Context()));
assertEquals(false, new Parser("\"a\">\"b\"").parseExp().value(new Context()));
assertEquals(true, new Parser("\"b\">\"a\"").parseExp().value(new Context()));
assertEquals(false, new Parser("5<5").parseExp().value(new Context()));
assertEquals(true, new Parser("4<5").parseExp().value(new Context()));
@ -265,6 +284,10 @@ public class ParserTest extends TestCase {
Context c = new Context().declareVar("Bits", 17);
exec("<? a:=format(\"hex=%x;\",Bits); print(a);?>", c);
assertEquals("hex=11;", c.toString());
c = new Context().declareVar("freq", Math.PI*100);
exec("<? a:=format(\"f=%.2f;\",freq); print(a);?>", c);
assertEquals("f=314.16;", c.toString());
}
public void testComment() throws IOException, ParserException, HGSEvalException {