Commit ae21b45f authored by Robert Zenz's avatar Robert Zenz

#48 Changed how a unit conversion is being recognized.

parent 079aa5e8
......@@ -50,13 +50,12 @@ public class Evaluator {
private static final String COMMENT_START = "//";
private static final MathContext DEFAULT_CALCULATION_MATH_CONTEXT = new MathContext(64, RoundingMode.HALF_UP);
private static final MathContext DEFAULT_RESULT_MATH_CONTEXT = new MathContext(32, RoundingMode.HALF_UP);
private static final Pattern EXPRESSION_UNIT_SEPARATOR = ResourceLoader.compileRegex("expression-unit-separator");
private static final Pattern FUNCTION = ResourceLoader.compileRegex("function");
private static final Pattern HEX_NUMBER = ResourceLoader.compileRegex("hex-number");
private static final Pattern ID = ResourceLoader.compileRegex("id");
private static final Pattern LAST_REFERENCE = ResourceLoader.compileRegex("last-reference");
private static final Pattern OCTAL_NUMBER = ResourceLoader.compileRegex("octal-number");
private static final Pattern UNIT_CONVERSION = ResourceLoader.compileRegex("unit-conversion");
private static final Pattern UNIT_CONVERSION_SIMPLE = ResourceLoader.compileRegex("unit-conversion-simple");
private Map<String, String> aliases = new HashMap<>();
private MathContext calculationMathContext = DEFAULT_CALCULATION_MATH_CONTEXT;
private List<EvaluatedExpression> contextExpressions = new ArrayList<>();
......@@ -227,54 +226,64 @@ public class Evaluator {
PrefixedUnit unitFrom = null;
PrefixedUnit unitTo = null;
Matcher unitConversionMatcher = UNIT_CONVERSION.matcher(processedExpression);
Matcher expressionUnitSeparatorMatcher = EXPRESSION_UNIT_SEPARATOR.matcher(processedExpression);
if (unitConversionMatcher.matches()) {
processedExpression = unitConversionMatcher.group("EXPRESSION");
if (expressionUnitSeparatorMatcher.find()) {
String expressionPart = processedExpression.substring(0, expressionUnitSeparatorMatcher.start() + 1);
String unitsPart = processedExpression.substring(expressionUnitSeparatorMatcher.start() + 1);
// Allow to convert from unit to unit without having to specify
// an amount.
if (processedExpression.trim().isEmpty()) {
processedExpression = "1";
}
unitFrom = unitConverter.getPrefixedUnit(unitConversionMatcher.group("FROM"));
if (unitFrom == null) {
throw new InvalidExpressionException("No such unit: " + unitConversionMatcher.group("FROM"));
}
String[] unitParts = splitUnitConversion(unitsPart);
unitTo = unitConverter.getPrefixedUnit(unitConversionMatcher.group("TO"));
if (unitTo == null) {
throw new InvalidExpressionException("No such unit: " + unitConversionMatcher.group("TO"));
}
} else {
unitConversionMatcher = UNIT_CONVERSION_SIMPLE.matcher(processedExpression);
if (unitConversionMatcher.matches()) {
String unitFromString = unitConversionMatcher.group("FROM");
if (unitParts[0] != null && unitParts[0].isEmpty()) {
unitFrom = unitConverter.getPrefixedUnit(expressionPart.trim());
if (!isKnown(unitFromString)) {
unitFrom = unitConverter.getPrefixedUnit(unitFromString);
if (unitFrom != null) {
expressionPart = "1";
} else {
unitFrom = unitConverter.getPrefixedUnit(unitParts[1]);
// Only allow the conversion if we have found
if (unitFrom != null) {
processedExpression = unitConversionMatcher.group("EXPRESSION");
// Allow to convert from unit to unit without having to
// specify
// an amount.
if (processedExpression.trim().isEmpty()) {
processedExpression = "1";
}
unitTo = new PrefixedUnit(Prefix.BASE, unitFrom.getUnit());
} else {
unitFrom = null;
if (unitFrom == null) {
throw new InvalidExpressionException("No such unit: " + unitParts[1]);
}
unitParts[0] = unitParts[1];
unitParts[1] = null;
}
} else if (unitParts[1] != null && unitParts[1].isEmpty()) {
unitFrom = unitConverter.getPrefixedUnit(expressionPart.trim());
if (unitFrom == null) {
throw new InvalidExpressionException("No such unit: " + expressionPart.trim());
}
expressionPart = "1";
unitParts[1] = unitParts[0];
} else {
unitFrom = unitConverter.getPrefixedUnit(unitParts[0]);
if (unitFrom == null) {
throw new InvalidExpressionException("No such unit: " + unitParts[0]);
}
}
if (unitParts[1] == null) {
unitTo = new PrefixedUnit(Prefix.BASE, unitFrom.getUnit());
} else {
unitTo = unitConverter.getPrefixedUnit(unitParts[1]);
if (unitTo == null) {
throw new InvalidExpressionException("No such unit: " + unitParts[1]);
}
}
processedExpression = expressionPart;
} else if (!isKnown(processedExpression)) {
unitFrom = unitConverter.getPrefixedUnit(processedExpression);
if (unitFrom != null) {
unitTo = new PrefixedUnit(Prefix.BASE, unitFrom.getUnit());
processedExpression = "1";
}
}
try {
......@@ -371,6 +380,39 @@ public class Evaluator {
return "0";
}
private String[] splitUnitConversion(String unitConversionString) {
int splitIndex = unitConversionString.indexOf(" as ");
if (splitIndex < 0) {
splitIndex = unitConversionString.indexOf(" in ");
}
if (splitIndex < 0) {
splitIndex = unitConversionString.indexOf(" to ");
}
if (splitIndex >= 0) {
return new String[] {
unitConversionString.substring(0, splitIndex).trim(),
unitConversionString.substring(splitIndex + 4).trim()
};
}
splitIndex = unitConversionString.indexOf(" ");
if (splitIndex >= 0) {
return new String[] {
unitConversionString.substring(0, splitIndex).trim(),
unitConversionString.substring(splitIndex + 1).trim()
};
}
return new String[] {
unitConversionString.trim(),
null
};
}
private String stripComments(String expression) {
String strippedExpression = expression;
......
......@@ -15,19 +15,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Regular Expression for finding a function definition and expression.
# Regular Expression for finding the separation index of an expression and
# the unit.
#
# Matched samples:
# 1+1*4 m to in
^
(?<EXPRESSION>
.*?
)
( )*
(?<FROM>
([a-zA-Z]+)
(\^[0-9]+)?
)
$ # End of string.
\ No newline at end of file
(
[0-9)] *[a-zA-Z]
|[0-9] +1
|[a-zA-Z)] +[a-zA-Z]
|[a-zA-Z)] *1
)
\ No newline at end of file
#
# Copyright 2018, Robert 'Bobby' Zenz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Regular Expression for finding a function definition and expression.
#
# Matched samples:
# 1+1*4 m to in
^
(?<EXPRESSION>
.*?
)
( )*
(?<FROM>
([a-zA-Z]+|[a-zA-Z]*1)
(\^[0-9]+)?
)
(
( to )
|( in )
|( as )
|( )
)
(?<TO>
([a-zA-Z]+|[a-zA-Z]*1)
(\^[0-9]+)?
)
$ # End of string.
\ No newline at end of file
......@@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.List;
import org.bonsaimind.jmathpaper.core.evaluatedexpressions.BooleanEvaluatedExpression;
import org.bonsaimind.jmathpaper.core.resources.ResourceLoader;
import org.bonsaimind.jmathpaper.core.ui.AbstractPapersUi;
import org.bonsaimind.jmathpaper.core.ui.CommandExecutionException;
import org.bonsaimind.jmathpaper.core.ui.UiParameters;
......@@ -128,6 +129,33 @@ public class TestAbstractPapersUi extends AbstractPapersUi {
}, " command value ; 1+1; command \"some ; value 1+1\" \\; 2+2");
}
@Test
public void testUnitConversions() throws CommandExecutionException, InvalidExpressionException {
// Load the defaults
ResourceLoader.processResource("units/iec.prefixes", getPaper().getEvaluator().getUnitConverter()::loadPrefix);
ResourceLoader.processResource("units/si.prefixes", getPaper().getEvaluator().getUnitConverter()::loadPrefix);
ResourceLoader.processResource("units/default.units", getPaper().getEvaluator().getUnitConverter()::loadUnit);
ResourceLoader.processResource("units/default.conversions", getPaper().getEvaluator().getUnitConverter()::loadConversion);
process("1km to m");
assertLastResult("1000");
process("1km in m");
assertLastResult("1000");
process("1km as m");
assertLastResult("1000");
process("1km m");
assertLastResult("1000");
process("1km");
assertLastResult("1000");
process("km");
assertLastResult("1000");
}
private final void assertLastResult(boolean expected) {
EvaluatedExpression lastEvaluatedExpression = paper.evaluatedExpressions.get(paper.evaluatedExpressions.size() - 1);
......
/*
* Copyright 2017, Robert 'Bobby' Zenz
* Copyright 2018, Robert 'Bobby' Zenz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -24,27 +24,39 @@ import java.util.regex.Matcher;
import org.junit.Assert;
import org.junit.Test;
public class TestUnitConversionSimple extends AbstractRegexTest {
public class TestExpressionUnitSeparator extends AbstractRegexTest {
@Test
public void test() {
public void testNoMatch() {
assertNoMatch("");
assertNoMatch("1+1");
assertNoMatch("sin(45) * 34 - 2");
assertNoMatch("=1");
assertNoMatch("0=1");
assertNoMatch("a-r=1");
assertNoMatch("--er=2");
assertNoMatch("a=5");
assertMatch("", "abcd", "abcd");
assertMatch("1", "km", "1km");
assertMatch("1*8 + abcd", "km", "1*8 + abcd km");
assertMatch("abcd", "km", "abcd km");
assertNoMatch("1");
assertNoMatch("12346433");
assertNoMatch("something");
assertNoMatch("321414+something");
assertNoMatch("(5*6)+9^power");
}
@Test
public void testOne() {
assertMatch(2, "1+1 1");
assertMatch(2, "abc 1");
assertMatch(2, "abc1");
assertMatch(12, "sqrt(5*abc+1)1");
assertMatch(12, "sqrt(5*abc+1) 1");
assertMatch(12, "sqrt(5*abc+1) 1/km");
}
protected void assertMatch(String expectedExpression, String expectedFrom, String value) {
@Test
public void testUnits() {
assertMatch(2, "1+1 km");
assertMatch(2, "1+1km");
assertMatch(2, "abc km");
assertMatch(12, "sqrt(5*abc+1)km");
assertMatch(12, "sqrt(5*abc+1) km");
assertMatch(12, "sqrt(5*abc+1) km/km");
assertMatch(0, "1inch to centimeter");
}
protected void assertMatch(int expectedIndex, String value) {
Matcher matcher = pattern.matcher(value);
Assert.assertTrue(
......@@ -52,17 +64,12 @@ public class TestUnitConversionSimple extends AbstractRegexTest {
matcher.find());
Assert.assertEquals(
"Expected EXPRESSION not found.",
expectedExpression,
matcher.group("EXPRESSION"));
Assert.assertEquals(
"Expected FROM not found.",
expectedFrom,
matcher.group("FROM"));
expectedIndex,
matcher.start());
}
@Override
protected String getRegexName() {
return "unit-conversion-simple";
return "expression-unit-separator";
}
}
/*
* Copyright 2017, Robert 'Bobby' Zenz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
* or write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.bonsaimind.jmathpaper.core.resources.regex;
import java.util.regex.Matcher;
import org.junit.Assert;
import org.junit.Test;
public class TestUnitConversion extends AbstractRegexTest {
@Test
public void test() {
assertNoMatch("");
assertNoMatch("abcd");
assertNoMatch("1+1");
assertNoMatch("sin(45) * 34 - 2");
assertNoMatch("=1");
assertNoMatch("0=1");
assertNoMatch("a-r=1");
assertNoMatch("--er=2");
assertNoMatch("a=5");
assertMatch("1", "1", "1", "1 1 to 1");
assertMatch("1", "1", "1", "1 1 in 1");
assertMatch("1", "1", "1", "1 1 1");
assertMatch("1", "k1", "1", "1 k1 to 1");
assertMatch("1", "k1", "1", "1 k1 in 1");
assertMatch("1", "k1", "1", "1 k1 1");
assertMatch("1", "1", "k1", "1 1 to k1");
assertMatch("1", "1", "k1", "1 1 in k1");
assertMatch("1", "1", "k1", "1 1 k1");
assertMatch("sin(45) * sqrt(50) + 49 - 10", "1", "1", "sin(45) * sqrt(50) + 49 - 10 1 to 1");
assertMatch("sin(45) * sqrt(50) + 49 - 10", "1", "1", "sin(45) * sqrt(50) + 49 - 10 1 in 1");
assertMatch("sin(45) * sqrt(50) + 49 - 10", "1", "1", "sin(45) * sqrt(50) + 49 - 10 1 as 1");
assertMatch("sin(45) * sqrt(50) + 49 - 10", "1", "1", "sin(45) * sqrt(50) + 49 - 10 1 1");
assertMatch("1", "km", "m", "1 km to m");
assertMatch("1+1", "km", "m", "1+1 km to m");
assertMatch("(a*5) + b - test", "km", "m", "(a*5) + b - test km to m");
assertMatch("1", "km", "m", "1 km in m");
assertMatch("1+1", "km", "m", "1+1 km in m");
assertMatch("(a*5) + b - test", "km", "m", "(a*5) + b - test km in m");
assertMatch("5", "1^2", "1^2", "5 1^2 to 1^2");
assertMatch("5", "km^2", "mi^2", "5 km^2 to mi^2");
assertMatch("5", "m^3", "cm^3", "5 m^3 to cm^3");
}
protected void assertMatch(String expectedExpression, String expectedFrom, String expectedTo, String value) {
Matcher matcher = pattern.matcher(value);
Assert.assertTrue(
"Match expected for: <" + value + "> but did not match.",
matcher.find());
Assert.assertEquals(
"Expected EXPRESSION not found.",
expectedExpression,
matcher.group("EXPRESSION"));
Assert.assertEquals(
"Expected FROM not found.",
expectedFrom,
matcher.group("FROM"));
Assert.assertEquals(
"Expected TO not found.",
expectedTo,
matcher.group("TO"));
}
@Override
protected String getRegexName() {
return "unit-conversion";
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment