[gnome-calculator] Update parser to allow complex conversion.
- From: Robert Roth <robertroth src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calculator] Update parser to allow complex conversion.
- Date: Mon, 27 Mar 2017 19:18:01 +0000 (UTC)
commit db87ca733af2808c56c4a356a2eac8252433b20b
Author: PioneerAxon <arth svnit gmail com>
Date: Thu Feb 16 01:23:47 2017 +0530
Update parser to allow complex conversion.
This patch rewrites the part of parser that handles conversions.
With the new code, it's possible to convert variables, as well as
complex equations.
With this patch, the following are possible.
10×20 m² in acres
1000+500 GB in GiB
((10 inch in cm) × (1 feet in cm)) cm² in m²
2π radians in degrees
3π + π÷2 radians in degrees
This is perticularly useful for people working with multiple units at
the same time.
e.g. Calculating Gravitational force between a 0.25 pound bag of chips
and 1.25kg laptop that are 17 feet away from each other.
https://bugzilla.gnome.org/show_bug.cgi?id=770344
lib/equation-lexer.vala | 28 ++++-
lib/equation-parser.vala | 321 ++++++++++++++--------------------------------
lib/equation.vala | 18 +++
lib/unit.vala | 9 ++
tests/test-equation.vala | 13 +-
5 files changed, 153 insertions(+), 236 deletions(-)
---
diff --git a/lib/equation-lexer.vala b/lib/equation-lexer.vala
index f1b516a..017360a 100644
--- a/lib/equation-lexer.vala
+++ b/lib/equation-lexer.vala
@@ -52,6 +52,7 @@ public enum LexerTokenType
NSUP_NUMBER, /* Negative Super Number */
SUB_NUMBER, /* Sub Number */
FUNCTION, /* Function */
+ UNIT, /* Unit of conversion */
VARIABLE, /* Variable name */
ASSIGN, /* = */
L_R_BRACKET, /* ( */
@@ -295,11 +296,26 @@ public class Lexer : Object
private bool check_if_function ()
{
var name = prelexer.get_marked_substring ();
+ return parser.function_is_defined (name);
+ }
+
+ private bool check_if_unit ()
+ {
+ int super_count = 0;
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUPER_DIGIT)
+ super_count++;
- if (parser.function_is_defined (name))
+ prelexer.roll_back ();
+
+ var name = prelexer.get_marked_substring ();
+ if (parser.unit_is_defined (name))
return true;
- else
- return false;
+
+ while (super_count-- > 0)
+ prelexer.roll_back ();
+
+ name = prelexer.get_marked_substring ();
+ return parser.unit_is_defined (name);
}
private bool check_if_number ()
@@ -587,6 +603,8 @@ public class Lexer : Object
{
if (check_if_function ())
return insert_token (LexerTokenType.FUNCTION);
+ else if (check_if_unit ())
+ return insert_token (LexerTokenType.UNIT);
else
return insert_token (LexerTokenType.VARIABLE);
}
@@ -602,6 +620,8 @@ public class Lexer : Object
{
if (check_if_function ())
return insert_token (LexerTokenType.FUNCTION);
+ else if (check_if_unit ())
+ return insert_token (LexerTokenType.UNIT);
else
return insert_token (LexerTokenType.VARIABLE);
}
@@ -676,6 +696,8 @@ public class Lexer : Object
return insert_token (LexerTokenType.IN);
if (check_if_function ())
return insert_token (LexerTokenType.FUNCTION);
+ if (check_if_unit ())
+ return insert_token (LexerTokenType.UNIT);
else
return insert_token (LexerTokenType.VARIABLE);
}
diff --git a/lib/equation-parser.vala b/lib/equation-parser.vala
index 70be30b..668e1cd 100644
--- a/lib/equation-parser.vala
+++ b/lib/equation-parser.vala
@@ -24,21 +24,23 @@ private enum Precedence
CONVERT = 0,
/* Unit for conversion */
UNIT = 1,
- ADD_SUBTRACT = 2,
- MULTIPLY = 3,
+ /* Highest precedence of any operator in current level. Only conversion should be above this node in
same depth level. */
+ TOP = 2,
+ ADD_SUBTRACT = 3,
+ MULTIPLY = 4,
/* MOD and DIVIDE must have same preedence. */
- MOD = 4,
- DIVIDE = 4,
- NOT = 5,
- FUNCTION = 6,
- BOOLEAN = 7,
- PERCENTAGE = 8,
+ MOD = 5,
+ DIVIDE = 5,
+ NOT = 6,
+ FUNCTION = 7,
+ BOOLEAN = 8,
+ PERCENTAGE = 9,
/* UNARY_MINUS, ROOT and POWER must have same precedence. */
- UNARY_MINUS = 9,
- POWER = 9,
- ROOT = 9,
- FACTORIAL = 10,
- NUMBER_VARIABLE = 11,
+ UNARY_MINUS = 10,
+ POWER = 10,
+ ROOT = 10,
+ FACTORIAL = 11,
+ NUMBER_VARIABLE = 12,
/* DEPTH should be always at the bottom. It stops node jumping off the current depth level. */
DEPTH
}
@@ -878,20 +880,25 @@ public class ConvertNode : LRNode
public class ConvertBaseNode : ParseNode
{
- public ConvertBaseNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity,
string? value)
+ public ConvertBaseNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity,
string? value = null)
{
base (parser, token, precedence, associativity, value);
}
public override Number? solve ()
{
- if (value == "hex" || value == "hexadecimal")
+ string name = value;
+
+ if (name == null && right != null)
+ name = right.token ().text;
+
+ if (name == "hex" || name == "hexadecimal")
parser.set_representation_base (16);
- else if (value == "dec" || value == "decimal")
+ else if (name == "dec" || name == "decimal")
parser.set_representation_base (10);
- else if (value == "oct" || value == "octal")
+ else if (name == "oct" || name == "octal")
parser.set_representation_base (8);
- else if (value == "bin" || value == "binary")
+ else if (name == "bin" || name == "binary")
parser.set_representation_base (2);
else
{
@@ -1066,6 +1073,11 @@ public class Parser
return false;
}
+ public virtual bool unit_is_defined (string name)
+ {
+ return false;
+ }
+
public virtual Number? convert (Number x, string x_units, string z_units)
{
return null;
@@ -1124,7 +1136,11 @@ public class Parser
return Precedence.FACTORIAL;
if (type == LexerTokenType.NUMBER || type == LexerTokenType.VARIABLE)
return Precedence.NUMBER_VARIABLE;
- return Precedence.UNKNOWN;
+ if (type == LexerTokenType.UNIT)
+ return Precedence.UNIT;
+ if (type == LexerTokenType.IN)
+ return Precedence.CONVERT;
+ return Precedence.TOP;
}
/* Return associativity of specific token type from precedence. */
@@ -1284,22 +1300,6 @@ public class Parser
private bool statement ()
{
var token = lexer.get_next_token ();
-
- if (token.type == LexerTokenType.SUBTRACT)
- {
- var old_token = token;
- token = lexer.get_next_token();
- if (token.type != LexerTokenType.PL_EOS)
- {
- insert_into_tree_unary (new UnaryMinusNode (this, old_token, make_precedence_p
(Precedence.UNARY_MINUS), get_associativity_p (Precedence.UNARY_MINUS)));
- }
- else
- {
- lexer.roll_back();
- token = old_token;
- }
- }
- /* Fall through */
if (token.type == LexerTokenType.VARIABLE || token.type == LexerTokenType.FUNCTION)
{
var token_old = token;
@@ -1314,75 +1314,6 @@ public class Parser
return true;
}
- else if (token.type == LexerTokenType.IN)
- {
- if (!check_base ())
- {
- lexer.roll_back ();
- lexer.roll_back ();
-
- if (!unit ())
- return false;
- lexer.get_next_token ();
-
- insert_into_tree (new ConvertNode (this, token, make_precedence_p(Precedence.CONVERT),
get_associativity (token)));
-
- if (!unit ())
- return false;
-
- return true;
- }
- else
- {
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.VARIABLE)
- {
- insert_into_tree (new VariableNode (this, token_old, make_precedence_t
(token_old.type), get_associativity (token_old)));
- insert_into_tree (new ConvertBaseNode (this, token, 0, get_associativity (token),
token.text));
- return true;
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- set_error (ErrorCode.UNKNOWN_CONVERSION, token.text, token.start_index,
token.end_index);
- return false;
- }
-
- }
- }
- else if (token.type == LexerTokenType.SUP_NUMBER)
- {
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.IN)
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- if (!unit ())
- return false;
- lexer.get_next_token ();
-
- insert_into_tree (new ConvertNode (this, token, make_precedence_p(Precedence.CONVERT),
get_associativity (token)));
-
- if (!unit ())
- return false;
-
- return true;
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
-
- if (!expression ())
- return false;
-
- return true;
- }
- }
else
{
lexer.roll_back ();
@@ -1400,100 +1331,6 @@ public class Parser
return true;
}
}
- else if (token.type == LexerTokenType.NUMBER)
- {
- var token_old = token;
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.VARIABLE)
- {
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.IN)
- {
- lexer.roll_back ();
- lexer.roll_back ();
-
- insert_into_tree (new ConstantNode (this, token_old, make_precedence_t (token_old.type),
get_associativity (token)));
-
- if (!unit ())
- return false;
-
- token = lexer.get_next_token ();
- insert_into_tree (new ConvertNumberNode (this, token,
make_precedence_p(Precedence.CONVERT), get_associativity (token)));
-
- if (!unit ())
- return false;
-
- return true;
- }
- else if (token.type == LexerTokenType.SUP_NUMBER)
- {
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.IN)
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
-
- insert_into_tree (new ConstantNode (this, token_old, make_precedence_t
(token_old.type), get_associativity (token)));
-
- if (!unit ())
- return false;
- token = lexer.get_next_token ();
-
- insert_into_tree (new ConvertNumberNode (this, token,
make_precedence_p(Precedence.CONVERT), get_associativity (token)));
-
- if (!unit ())
- return false;
- return true;
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- if (!expression ())
- return false;
- return true;
- }
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- if (!expression ())
- return false;
- return true;
- }
- }
- else if (token.type == LexerTokenType.IN)
- {
- token = lexer.get_next_token ();
- if (token.type == LexerTokenType.VARIABLE)
- {
- insert_into_tree (new ConstantNode (this, token_old, make_precedence_t (token_old.type),
get_associativity (token)));
- insert_into_tree (new ConvertBaseNode (this, token,
make_precedence_p(Precedence.CONVERT), get_associativity (token), token.text));
- return true;
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- lexer.roll_back ();
- set_error (ErrorCode.UNKNOWN_CONVERSION, token.text, token.start_index, token.end_index);
- return false;
- }
- }
- else
- {
- lexer.roll_back ();
- lexer.roll_back ();
- if (!expression ())
- return false;
- return true;
- }
- }
else
{
lexer.roll_back ();
@@ -1559,38 +1396,67 @@ public class Parser
return true;
}
- private bool check_base ()
+ private bool conversion ()
{
var token = lexer.get_next_token ();
- foreach (string s in "hex,hexadecimal,dec,decimal,oct,octal,bin,binary".split (","))
+ if (token.type == LexerTokenType.IN)
{
- if (token.text == s)
+ var token_in = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.UNIT)
+ {
+ var token_to = token;
+ token = lexer.get_next_token ();
+ /* We can only convert representation base, if it is next to End Of Stream */
+ if (token.type == LexerTokenType.PL_EOS)
+ {
+ insert_into_tree (new ConvertBaseNode (this, token_in, make_precedence_p
(Precedence.CONVERT), get_associativity (token_in)));
+ insert_into_tree (new NameNode (this, token_to, make_precedence_p (Precedence.UNIT),
get_associativity (token_to)));
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ return false;
+ }
+ }
+ else
{
lexer.roll_back ();
- return true;
+ lexer.roll_back ();
+ return false;
}
}
- lexer.roll_back ();
- return false;
- }
-
- private bool unit ()
- {
- var token = lexer.get_next_token ();
- if (token.type == LexerTokenType.VARIABLE)
+ else if (token.type == LexerTokenType.UNIT)
{
- var token_old = token;
+ var token_from = token;
token = lexer.get_next_token ();
- if (token.type == LexerTokenType.SUP_NUMBER)
+ if (token.type == LexerTokenType.IN)
{
- insert_into_tree (new NameNode (this, token_old, make_precedence_p (Precedence.UNIT),
get_associativity (token_old), token_old.text + token.text));
- return true;
+ var token_in = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.UNIT)
+ {
+ insert_into_tree (new NameNode (this, token_from, make_precedence_p (Precedence.UNIT),
get_associativity (token_from)));
+ insert_into_tree (new ConvertNumberNode (this, token_in, make_precedence_p
(Precedence.CONVERT), get_associativity (token_in)));
+ insert_into_tree (new NameNode (this, token, make_precedence_p (Precedence.UNIT),
get_associativity (token)));
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ return false;
+ }
}
else
{
lexer.roll_back ();
- insert_into_tree (new NameNode (this, token_old, make_precedence_p (Precedence.UNIT),
get_associativity (token_old)));
- return true;
+ lexer.roll_back ();
+ return false;
}
}
else
@@ -1606,7 +1472,8 @@ public class Parser
return false;
if (!expression_2 ())
return false;
-
+ /* If there is a possible conversion at this level, insert it in the tree. */
+ conversion ();
return true;
}
@@ -1654,9 +1521,9 @@ public class Parser
{
depth_level++;
- /* Give round, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+ /* Give round, preference of Precedence.TOP aka 2, to keep it on the top of expression. */
- insert_into_tree_unary (new RoundNode (this, token, make_precedence_p (Precedence.UNKNOWN),
get_associativity (token)));
+ insert_into_tree_unary (new RoundNode (this, token, make_precedence_p (Precedence.TOP),
get_associativity (token)));
if (!expression ())
return false;
@@ -1675,9 +1542,9 @@ public class Parser
{
depth_level++;
- /* Give fraction, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+ /* Give fraction, preference of Precedence.TOP aka 2, to keep it on the top of expression. */
- insert_into_tree_unary (new FractionalComponentNode (this, token, make_precedence_p
(Precedence.UNKNOWN), get_associativity (token)));
+ insert_into_tree_unary (new FractionalComponentNode (this, token, make_precedence_p
(Precedence.TOP), get_associativity (token)));
if (!expression ())
return false;
@@ -1696,9 +1563,9 @@ public class Parser
{
depth_level++;
- /* Give abs, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+ /* Give abs, preference of Precedence.TOP aka 2, to keep it on the top of expression. */
- insert_into_tree_unary (new AbsoluteValueNode (this, token, make_precedence_p
(Precedence.UNKNOWN), get_associativity (token)));
+ insert_into_tree_unary (new AbsoluteValueNode (this, token, make_precedence_p (Precedence.TOP),
get_associativity (token)));
if (!expression ())
return false;
@@ -1744,9 +1611,9 @@ public class Parser
else if (token.type == LexerTokenType.L_FLOOR)
{
depth_level++;
- /* Give floor, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+ /* Give floor, preference of Precedence.TOP aka 2, to keep it on the top of expression. */
- insert_into_tree_unary (new FloorNode (this, null, make_precedence_p (Precedence.UNKNOWN),
get_associativity_p (Precedence.UNKNOWN)));
+ insert_into_tree_unary (new FloorNode (this, null, make_precedence_p (Precedence.TOP),
get_associativity_p (Precedence.TOP)));
if (!expression ())
return false;
@@ -1764,9 +1631,9 @@ public class Parser
else if (token.type == LexerTokenType.L_CEILING)
{
depth_level++;
- /* Give ceiling, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+ /* Give ceiling, preference of Precedence.TOP aka 2, to keep it on the top of expression. */
- insert_into_tree_unary (new CeilingNode (this, null, make_precedence_p (Precedence.UNKNOWN),
get_associativity_p (Precedence.UNKNOWN)));
+ insert_into_tree_unary (new CeilingNode (this, null, make_precedence_p (Precedence.TOP),
get_associativity_p (Precedence.TOP)));
if (!expression ())
return false;
diff --git a/lib/equation.vala b/lib/equation.vala
index c0982d1..2e1e73d 100644
--- a/lib/equation.vala
+++ b/lib/equation.vala
@@ -146,6 +146,11 @@ public class Equation : Object
return null;
}
+ public virtual bool unit_is_defined (string name)
+ {
+ return false;
+ }
+
public virtual void set_variable (string name, Number x)
{
}
@@ -216,6 +221,19 @@ private class EquationParser : Parser
return equation.function_is_defined (name);
}
+ protected override bool unit_is_defined (string name)
+ {
+ if (name == "hex" || name == "hexadecimal" || name == "dec" || name == "decimal" || name == "oct" ||
name == "octal" || name == "bin" || name == "binary")
+ return true;
+
+ var unit_manager = UnitManager.get_default ();
+
+ if (unit_manager.unit_is_defined (name))
+ return true;
+
+ return equation.unit_is_defined (name);
+ }
+
protected override Number? convert (Number x, string x_units, string z_units)
{
return equation.convert (x, x_units, z_units);
diff --git a/lib/unit.vala b/lib/unit.vala
index 85b9c9c..f4e0d49 100644
--- a/lib/unit.vala
+++ b/lib/unit.vala
@@ -224,6 +224,15 @@ public class UnitManager : Object
return null;
}
+ public bool unit_is_defined (string name)
+ {
+ var unit = get_unit_by_symbol (name);
+ if (unit != null)
+ return true;
+ else
+ return false;
+ }
+
public Number? convert_by_symbol (Number x, string x_symbol, string z_symbol)
{
foreach (var c in categories)
diff --git a/tests/test-equation.vala b/tests/test-equation.vala
index e9ccbf2..3a90839 100644
--- a/tests/test-equation.vala
+++ b/tests/test-equation.vala
@@ -212,18 +212,19 @@ private void test_equations ()
test ("7₂", "", ErrorCode.INVALID); test ("7₈", "7", 0); test ("7", "7", 0); test ("7₁₆", "7", 0);
test ("8₂", "", ErrorCode.INVALID); test ("8₈", "", ErrorCode.INVALID); test ("8", "8", 0); test ("8₁₆",
"8", 0);
test ("9₂", "", ErrorCode.INVALID); test ("9₈", "", ErrorCode.INVALID); test ("9", "9", 0); test ("9₁₆",
"9", 0);
+ /* Note : "B", "b", "C", "c", "F", "f" are units, and hence have different error code. */
test ("A₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("A₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("A", "",
ErrorCode.UNKNOWN_VARIABLE); test ("A₁₆", "10", 0);
- test ("B₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("B₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("B", "",
ErrorCode.UNKNOWN_VARIABLE); test ("B₁₆", "11", 0);
- test ("C₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("C₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("C", "",
ErrorCode.UNKNOWN_VARIABLE); test ("C₁₆", "12", 0);
+ test ("B₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("B₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("B", "",
ErrorCode.INVALID); test ("B₁₆", "11", 0);
+ test ("C₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("C₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("C", "",
ErrorCode.INVALID); test ("C₁₆", "12", 0);
test ("D₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("D₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("D", "",
ErrorCode.UNKNOWN_VARIABLE); test ("D₁₆", "13", 0);
test ("E₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("E₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("E", "",
ErrorCode.UNKNOWN_VARIABLE); test ("E₁₆", "14", 0);
- test ("F₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("F₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("F", "",
ErrorCode.UNKNOWN_VARIABLE); test ("F₁₆", "15", 0);
+ test ("F₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("F₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("F", "",
ErrorCode.INVALID); test ("F₁₆", "15", 0);
test ("a₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("a₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("a", "",
ErrorCode.UNKNOWN_VARIABLE); test ("a₁₆", "10", 0);
- test ("b₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("b₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("b", "",
ErrorCode.UNKNOWN_VARIABLE); test ("b₁₆", "11", 0);
- test ("c₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("c₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("c", "",
ErrorCode.UNKNOWN_VARIABLE); test ("c₁₆", "12", 0);
+ test ("b₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("b₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("b", "",
ErrorCode.INVALID); test ("b₁₆", "11", 0);
+ test ("c₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("c₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("c", "",
ErrorCode.INVALID); test ("c₁₆", "12", 0);
test ("d₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("d₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("d", "",
ErrorCode.UNKNOWN_VARIABLE); test ("d₁₆", "13", 0);
test ("e₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("e₈", "", ErrorCode.UNKNOWN_VARIABLE); /* e is a
built-in variable */ test ("e₁₆", "14", 0);
- test ("f₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("f₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("f", "",
ErrorCode.UNKNOWN_VARIABLE); test ("f₁₆", "15", 0);
+ test ("f₂", "", ErrorCode.UNKNOWN_VARIABLE); test ("f₈", "", ErrorCode.UNKNOWN_VARIABLE); test ("f", "",
ErrorCode.INVALID); test ("f₁₆", "15", 0);
test ("+1", "1", 0);
test ("−1", "−1", 0);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]