Allow underscores in numbers.

This commit is contained in:
Balajiganapathi S 2017-10-21 01:05:08 +05:30 committed by Christian Parpart
parent 4dc3335cda
commit 0000bfc604
5 changed files with 198 additions and 2 deletions

2
.gitignore vendored
View File

@ -46,3 +46,5 @@ browse.VC.db
CMakeLists.txt.user CMakeLists.txt.user
/CMakeSettings.json /CMakeSettings.json
/.vs /.vs
/.cproject
/.project

View File

@ -147,6 +147,7 @@ Features:
* General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature.
* General: Limit the number of errors output in a single run to 256. * General: Limit the number of errors output in a single run to 256.
* General: Support accessing dynamic return data in post-byzantium EVMs. * General: Support accessing dynamic return data in post-byzantium EVMs.
* General: Allow underscores in numeric and hex literals to separate thousands and quads.
* Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature. * Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature.
* Inheritance: Error when using no parentheses in modifier-style constructor calls as experimental 0.5.0 feature. * Inheritance: Error when using no parentheses in modifier-style constructor calls as experimental 0.5.0 feature.
* Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract.

View File

@ -284,6 +284,9 @@ one side. Examples include ``1.``, ``.1`` and ``1.3``.
Scientific notation is also supported, where the base can have fractions, while the exponent cannot. Scientific notation is also supported, where the base can have fractions, while the exponent cannot.
Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``. Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``.
Underscores can be used to separate digits of a numeric literal to aid readability.
For example, ``123_000``, ``0x2eff_abde``, ``1_233e34_89`` are all valid. Underscores are only allowed between two digits.
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal expression). using them together with a non-literal expression).
This means that computations do not overflow and divisions do not truncate This means that computations do not overflow and divisions do not truncate

View File

@ -726,8 +726,21 @@ Token::Value Scanner::scanHexString()
void Scanner::scanDecimalDigits() void Scanner::scanDecimalDigits()
{ {
while (isDecimalDigit(m_char)) if (!isDecimalDigit(m_char)) // avoid underscore at beginning
return;
while (isDecimalDigit(m_char) || m_char == '_')
{
if (m_char == '_')
{
advance();
if (!isDecimalDigit(m_char)) // avoid trailing underscore
{
rollback(1);
break;
}
}
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
}
} }
Token::Value Scanner::scanNumber(char _charSeen) Token::Value Scanner::scanNumber(char _charSeen)
@ -755,8 +768,19 @@ Token::Value Scanner::scanNumber(char _charSeen)
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (!isHexDigit(m_char)) if (!isHexDigit(m_char))
return Token::Illegal; // we must have at least one hex digit after 'x'/'X' return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
while (isHexDigit(m_char)) while (isHexDigit(m_char) || m_char == '_') // same logic as scanDecimalDigits
{
if (m_char == '_')
{
advance();
if (!isHexDigit(m_char)) // avoid trailing underscore
{
rollback(1);
break;
}
}
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
}
} }
else if (isDecimalDigit(m_char)) else if (isDecimalDigit(m_char))
// We do not allow octal numbers // We do not allow octal numbers

View File

@ -155,6 +155,172 @@ BOOST_AUTO_TEST_CASE(trailing_dot)
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
} }
BOOST_AUTO_TEST_CASE(underscores_in_integer)
{
Scanner scanner(CharStream("var x = 1_23_4;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1234");
BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(underscores_in_scientific_notation)
{
Scanner scanner(CharStream("var x = 1_2e10;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "12e10");
BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(underscores_in_scientific_notation_in_exp_part)
{
Scanner scanner(CharStream("var x = 12e1_0;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "12e10");
BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(underscores_in_hex)
{
Scanner scanner(CharStream("var x = 0xab_19cf;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0xab19cf");
BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(leading_underscore_integer_is_identifier)
{
Scanner scanner(CharStream("var x = _12;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
}
BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier)
{
Scanner scanner(CharStream("var x = _1.2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
}
BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal)
{
Scanner scanner(CharStream("var x = 1._2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier)
{
Scanner scanner(CharStream("var x = _1e2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
}
BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal)
{
Scanner scanner(CharStream("var x = 1e_2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal)
{
Scanner scanner(CharStream("var x = 0x_abc;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(trailing_underscore_integer_illegal)
{
Scanner scanner(CharStream("var x = 12_;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(leading_underscore_after_decimal_illegal)
{
Scanner scanner(CharStream("var x = 1.2_;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(leading_underscore_before_decimal_illegal)
{
Scanner scanner(CharStream("var x = 1_.2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(trailing_underscore_exp_illegal)
{
Scanner scanner(CharStream("var x = 1e2_;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(trailing_underscore_exp_before_e_illegal)
{
Scanner scanner(CharStream("var x = 1_e2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(trailing_underscore_hex_illegal)
{
Scanner scanner(CharStream("var x = 0xabc_;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(double_underscore_illegal)
{
Scanner scanner(CharStream("var x = 1__2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_AUTO_TEST_CASE(negative_numbers)
{ {
Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;")); Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;"));