mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined literal suffixes.
This commit is contained in:
parent
3c0a7355d0
commit
216df46ff4
@ -378,6 +378,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
|||||||
|
|
||||||
void ConstantEvaluator::endVisit(Literal const& _literal)
|
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
// TODO handle user suffix
|
||||||
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
||||||
m_values[&_literal] = constantToTypedValue(*literalType);
|
m_values[&_literal] = constantToTypedValue(*literalType);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,8 @@ bool FunctionCallGraphBuilder::visit(EmitStatement const& _emitStatement)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO why don't we handle IdentifierPath here?
|
||||||
|
|
||||||
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||||
@ -230,6 +232,16 @@ bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FunctionCallGraphBuilder::visit(Literal const& _literal)
|
||||||
|
{
|
||||||
|
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||||
|
functionReferenced(
|
||||||
|
dynamic_cast<FunctionDefinition const&>(*(*identifierPath)->annotation().referencedDeclaration)
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void FunctionCallGraphBuilder::enqueueCallable(CallableDeclaration const& _callable)
|
void FunctionCallGraphBuilder::enqueueCallable(CallableDeclaration const& _callable)
|
||||||
{
|
{
|
||||||
if (!m_graph.edges.count(&_callable))
|
if (!m_graph.edges.count(&_callable))
|
||||||
|
@ -74,6 +74,7 @@ private:
|
|||||||
bool visit(MemberAccess const& _memberAccess) override;
|
bool visit(MemberAccess const& _memberAccess) override;
|
||||||
bool visit(ModifierInvocation const& _modifierInvocation) override;
|
bool visit(ModifierInvocation const& _modifierInvocation) override;
|
||||||
bool visit(NewExpression const& _newExpression) override;
|
bool visit(NewExpression const& _newExpression) override;
|
||||||
|
bool visit(Literal const& _literal) override;
|
||||||
|
|
||||||
void enqueueCallable(CallableDeclaration const& _callable);
|
void enqueueCallable(CallableDeclaration const& _callable);
|
||||||
void processQueue();
|
void processQueue();
|
||||||
|
@ -3678,10 +3678,11 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
|||||||
|
|
||||||
void TypeChecker::endVisit(Literal const& _literal)
|
void TypeChecker::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
Type const* type = nullptr;
|
||||||
if (_literal.looksLikeAddress())
|
if (_literal.looksLikeAddress())
|
||||||
{
|
{
|
||||||
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
|
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
|
||||||
_literal.annotation().type = TypeProvider::address();
|
type = TypeProvider::address();
|
||||||
|
|
||||||
string msg;
|
string msg;
|
||||||
if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
|
if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
|
||||||
@ -3707,7 +3708,8 @@ void TypeChecker::endVisit(Literal const& _literal)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
|
Literal::SubDenomination const* subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix());
|
||||||
|
if (_literal.isHexNumber() && subDenomination && *subDenomination != Literal::SubDenomination::None)
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
5145_error,
|
5145_error,
|
||||||
_literal.location(),
|
_literal.location(),
|
||||||
@ -3715,20 +3717,83 @@ void TypeChecker::endVisit(Literal const& _literal)
|
|||||||
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_literal.subDenomination() == Literal::SubDenomination::Year)
|
if (subDenomination && *subDenomination == Literal::SubDenomination::Year)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4820_error,
|
4820_error,
|
||||||
_literal.location(),
|
_literal.location(),
|
||||||
"Using \"years\" as a unit denomination is deprecated."
|
"Using \"years\" as a unit denomination is deprecated."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_literal.annotation().type)
|
if (!type)
|
||||||
_literal.annotation().type = TypeProvider::forLiteral(_literal);
|
type = TypeProvider::forLiteral(_literal);
|
||||||
|
|
||||||
if (!_literal.annotation().type)
|
if (!type)
|
||||||
m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value.");
|
m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value.");
|
||||||
|
|
||||||
_literal.annotation().isPure = true;
|
// TODO at this point 'type' needs to be stored for code generation.
|
||||||
|
|
||||||
|
bool isPure = true;
|
||||||
|
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||||
|
{
|
||||||
|
Declaration const* declaration = (*identifierPath)->annotation().referencedDeclaration;
|
||||||
|
if (
|
||||||
|
!dynamic_cast<FunctionDefinition const*>(declaration) ||
|
||||||
|
!dynamic_cast<FunctionDefinition const*>(declaration)->isFree()
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
4438_error,
|
||||||
|
_literal.location(),
|
||||||
|
"The literal suffix needs to be a pre-defined suffix or a file-level function."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*declaration->type());
|
||||||
|
if (
|
||||||
|
dynamic_cast<RationalNumberType const*>(type) &&
|
||||||
|
dynamic_cast<RationalNumberType const*>(type)->isFractional()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||||
|
solAssert((mantissa && exponent) || (!mantissa && !exponent));
|
||||||
|
if (!mantissa)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
5503_error,
|
||||||
|
_literal.location(),
|
||||||
|
"This fractional number cannot be decomposed into a mantissa and decimal exponent "
|
||||||
|
"that fit the range of parameters of the suffix function."
|
||||||
|
);
|
||||||
|
else if (
|
||||||
|
functionType.parameterTypes().size() != 2 ||
|
||||||
|
!mantissa->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(0)) ||
|
||||||
|
!exponent->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(1))
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
4778_error,
|
||||||
|
_literal.location(),
|
||||||
|
"TODO Fractional number, types to do match."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (
|
||||||
|
functionType.parameterTypes().size() != 1 ||
|
||||||
|
!type->isImplicitlyConvertibleTo(*functionType.parameterTypes().front())
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
8838_error,
|
||||||
|
_literal.location(),
|
||||||
|
"The type of the literal cannot be converted to the parameter of the suffix function."
|
||||||
|
);
|
||||||
|
|
||||||
|
isPure = functionType.isPure();
|
||||||
|
if (functionType.returnParameterTypes().size() == 1)
|
||||||
|
type = functionType.returnParameterTypes().front();
|
||||||
|
else
|
||||||
|
type = TypeProvider::tuple(functionType.returnParameterTypes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_literal.annotation().isPure = isPure;
|
||||||
|
_literal.annotation().type = type;
|
||||||
_literal.annotation().isLValue = false;
|
_literal.annotation().isLValue = false;
|
||||||
_literal.annotation().isConstant = false;
|
_literal.annotation().isConstant = false;
|
||||||
}
|
}
|
||||||
|
@ -949,8 +949,10 @@ bool Literal::isHexNumber() const
|
|||||||
|
|
||||||
bool Literal::looksLikeAddress() const
|
bool Literal::looksLikeAddress() const
|
||||||
{
|
{
|
||||||
if (subDenomination() != SubDenomination::None)
|
// User suffixes are fine.
|
||||||
return false;
|
if (auto subDenomination = get_if<Literal::SubDenomination>(&suffix()))
|
||||||
|
if (*subDenomination != SubDenomination::None)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!isHexNumber())
|
if (!isHexNumber())
|
||||||
return false;
|
return false;
|
||||||
|
@ -2332,6 +2332,8 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
|
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
|
||||||
|
*
|
||||||
|
* It can have a suffix that can lead to a function call.
|
||||||
*/
|
*/
|
||||||
class Literal: public PrimaryExpression
|
class Literal: public PrimaryExpression
|
||||||
{
|
{
|
||||||
@ -2349,14 +2351,15 @@ public:
|
|||||||
Week = static_cast<int>(Token::SubWeek),
|
Week = static_cast<int>(Token::SubWeek),
|
||||||
Year = static_cast<int>(Token::SubYear)
|
Year = static_cast<int>(Token::SubYear)
|
||||||
};
|
};
|
||||||
|
using Suffix = std::variant<SubDenomination, ASTPointer<IdentifierPath>>;
|
||||||
Literal(
|
Literal(
|
||||||
int64_t _id,
|
int64_t _id,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
Token _token,
|
Token _token,
|
||||||
ASTPointer<ASTString> _value,
|
ASTPointer<ASTString> _value,
|
||||||
SubDenomination _sub = SubDenomination::None
|
Suffix _suffix = SubDenomination::None
|
||||||
):
|
):
|
||||||
PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_subDenomination(_sub) {}
|
PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_suffix(std::move(_suffix)) {}
|
||||||
void accept(ASTVisitor& _visitor) override;
|
void accept(ASTVisitor& _visitor) override;
|
||||||
void accept(ASTConstVisitor& _visitor) const override;
|
void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
@ -2366,7 +2369,8 @@ public:
|
|||||||
|
|
||||||
ASTString valueWithoutUnderscores() const;
|
ASTString valueWithoutUnderscores() const;
|
||||||
|
|
||||||
SubDenomination subDenomination() const { return m_subDenomination; }
|
//SubDenomination subDenomination() const { return m_suffix; }
|
||||||
|
Suffix const& suffix() const { return m_suffix; }
|
||||||
|
|
||||||
/// @returns true if this is a number with a hex prefix.
|
/// @returns true if this is a number with a hex prefix.
|
||||||
bool isHexNumber() const;
|
bool isHexNumber() const;
|
||||||
@ -2381,7 +2385,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Token m_token;
|
Token m_token;
|
||||||
ASTPointer<ASTString> m_value;
|
ASTPointer<ASTString> m_value;
|
||||||
SubDenomination m_subDenomination;
|
Suffix m_suffix;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -962,17 +962,15 @@ bool ASTJsonExporter::visit(Literal const& _node)
|
|||||||
Json::Value value{_node.value()};
|
Json::Value value{_node.value()};
|
||||||
if (!util::validateUTF8(_node.value()))
|
if (!util::validateUTF8(_node.value()))
|
||||||
value = Json::nullValue;
|
value = Json::nullValue;
|
||||||
Token subdenomination = Token(_node.subDenomination());
|
Json::Value subdenomination = Json::nullValue;
|
||||||
|
// if (auto subden = get_if<Literal::SubDenomination>(&_node.suffix()))
|
||||||
|
// subdenomination = Json::Value{TokenTraits::toString(*subden)};
|
||||||
|
// TODO suffix
|
||||||
std::vector<pair<string, Json::Value>> attributes = {
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
make_pair("kind", literalTokenKind(_node.token())),
|
make_pair("kind", literalTokenKind(_node.token())),
|
||||||
make_pair("value", value),
|
make_pair("value", value),
|
||||||
make_pair("hexValue", util::toHex(util::asBytes(_node.value()))),
|
make_pair("hexValue", util::toHex(util::asBytes(_node.value()))),
|
||||||
make_pair(
|
make_pair("subdenomination", subdenomination)
|
||||||
"subdenomination",
|
|
||||||
subdenomination == Token::Illegal ?
|
|
||||||
Json::nullValue :
|
|
||||||
Json::Value{TokenTraits::toString(subdenomination)}
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
appendExpressionAttributes(attributes, _node.annotation());
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
setJsonNode(_node, "Literal", std::move(attributes));
|
setJsonNode(_node, "Literal", std::move(attributes));
|
||||||
|
@ -1014,13 +1014,17 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
|
|||||||
|
|
||||||
void Literal::accept(ASTVisitor& _visitor)
|
void Literal::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
_visitor.visit(*this);
|
if (_visitor.visit(*this))
|
||||||
|
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||||
|
(*identifierPath)->accept(_visitor);
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Literal::accept(ASTConstVisitor& _visitor) const
|
void Literal::accept(ASTConstVisitor& _visitor) const
|
||||||
{
|
{
|
||||||
_visitor.visit(*this);
|
if (_visitor.visit(*this))
|
||||||
|
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||||
|
(*identifierPath)->accept(_visitor);
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +167,7 @@ public:
|
|||||||
|
|
||||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||||
/// not fit any type.
|
/// not fit any type.
|
||||||
|
/// Ignores user-defined suffixes.
|
||||||
static Type const* forLiteral(Literal const& _literal);
|
static Type const* forLiteral(Literal const& _literal);
|
||||||
static RationalNumberType const* rationalNumber(Literal const& _literal);
|
static RationalNumberType const* rationalNumber(Literal const& _literal);
|
||||||
|
|
||||||
|
@ -925,8 +925,10 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
|||||||
{
|
{
|
||||||
return make_tuple(false, rational(0));
|
return make_tuple(false, rational(0));
|
||||||
}
|
}
|
||||||
switch (_literal.subDenomination())
|
|
||||||
{
|
if (auto subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix()))
|
||||||
|
switch (*subDenomination)
|
||||||
|
{
|
||||||
case Literal::SubDenomination::None:
|
case Literal::SubDenomination::None:
|
||||||
case Literal::SubDenomination::Wei:
|
case Literal::SubDenomination::Wei:
|
||||||
case Literal::SubDenomination::Second:
|
case Literal::SubDenomination::Second:
|
||||||
@ -952,7 +954,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
|||||||
case Literal::SubDenomination::Year:
|
case Literal::SubDenomination::Year:
|
||||||
value *= bigint("31536000");
|
value *= bigint("31536000");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return make_tuple(true, value);
|
return make_tuple(true, value);
|
||||||
@ -1220,6 +1222,33 @@ FixedPointType const* RationalNumberType::fixedPointType() const
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pair<RationalNumberType const*, RationalNumberType const*> RationalNumberType::mantissaExponent() const
|
||||||
|
{
|
||||||
|
bool negative = (m_value < 0);
|
||||||
|
int exponent = 0;
|
||||||
|
rational value = abs(m_value); // We care about the sign later.
|
||||||
|
rational maxValue = negative ?
|
||||||
|
rational(bigint(1) << 255, 1):
|
||||||
|
rational((bigint(1) << 256) - 1, 1);
|
||||||
|
|
||||||
|
while (value.denominator() != 1)
|
||||||
|
{
|
||||||
|
value *= 10;
|
||||||
|
exponent--;
|
||||||
|
if (
|
||||||
|
value > rational((bigint(1) << 256) - 1) ||
|
||||||
|
value < rational(-(bigint(1) << 255)) ||
|
||||||
|
exponent < -255 // TODO sane vale?
|
||||||
|
)
|
||||||
|
return {nullptr, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
TypeProvider::rationalNumber(value),
|
||||||
|
TypeProvider::rationalNumber(-exponent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
StringLiteralType::StringLiteralType(Literal const& _literal):
|
StringLiteralType::StringLiteralType(Literal const& _literal):
|
||||||
m_value(_literal.value())
|
m_value(_literal.value())
|
||||||
{
|
{
|
||||||
|
@ -587,6 +587,9 @@ public:
|
|||||||
/// @returns true if the value is not an integer.
|
/// @returns true if the value is not an integer.
|
||||||
bool isFractional() const { return m_value.denominator() != 1; }
|
bool isFractional() const { return m_value.denominator() != 1; }
|
||||||
|
|
||||||
|
/// TODO document
|
||||||
|
std::pair<RationalNumberType const*, RationalNumberType const*> mantissaExponent() const;
|
||||||
|
|
||||||
/// @returns true if the value is negative.
|
/// @returns true if the value is negative.
|
||||||
bool isNegative() const { return m_value < 0; }
|
bool isNegative() const { return m_value < 0; }
|
||||||
|
|
||||||
|
@ -2270,19 +2270,62 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
void ExpressionCompiler::endVisit(Literal const& _literal)
|
void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||||
Type const* type = _literal.annotation().type;
|
|
||||||
|
|
||||||
switch (type->category())
|
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||||
{
|
{
|
||||||
case Type::Category::RationalNumber:
|
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||||
case Type::Category::Bool:
|
*(*identifierPath)->annotation().referencedDeclaration
|
||||||
case Type::Category::Address:
|
);
|
||||||
m_context << type->literalValue(&_literal);
|
FunctionType const& functionType = *function.functionType(true);
|
||||||
break;
|
|
||||||
case Type::Category::StringLiteral:
|
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
|
||||||
break; // will be done during conversion
|
|
||||||
default:
|
// TODO this is actually not always the right one.
|
||||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
auto type = TypeProvider::forLiteral(_literal);
|
||||||
|
|
||||||
|
if (dynamic_cast<RationalNumberType const*>(type) && dynamic_cast<RationalNumberType const*>(type)->isFractional())
|
||||||
|
{
|
||||||
|
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||||
|
m_context << mantissa->literalValue(nullptr);
|
||||||
|
utils().convertType(*mantissa, *functionType.parameterTypes().at(0));
|
||||||
|
m_context << exponent->literalValue(nullptr);
|
||||||
|
utils().convertType(*exponent, *functionType.parameterTypes().at(1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO reuse the code below
|
||||||
|
if (type->category() != Type::Category::StringLiteral)
|
||||||
|
m_context << type->literalValue(&_literal);
|
||||||
|
utils().convertType(*type, *functionType.parameterTypes().at(0));
|
||||||
|
}
|
||||||
|
m_context << m_context.functionEntryLabel(function).pushTag();
|
||||||
|
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
|
||||||
|
m_context << returnLabel;
|
||||||
|
|
||||||
|
// callee adds return parameters, but removes arguments and return label
|
||||||
|
m_context.adjustStackOffset(static_cast<int>(
|
||||||
|
CompilerUtils::sizeOnStack(functionType.returnParameterTypes()) -
|
||||||
|
CompilerUtils::sizeOnStack(functionType.parameterTypes()) -
|
||||||
|
1
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Type const* type = _literal.annotation().type;
|
||||||
|
|
||||||
|
|
||||||
|
switch (type->category())
|
||||||
|
{
|
||||||
|
case Type::Category::RationalNumber:
|
||||||
|
case Type::Category::Bool:
|
||||||
|
case Type::Category::Address:
|
||||||
|
m_context << type->literalValue(&_literal);
|
||||||
|
break;
|
||||||
|
case Type::Category::StringLiteral:
|
||||||
|
break; // will be done during conversion
|
||||||
|
default:
|
||||||
|
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2437,19 +2437,60 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
setLocation(_literal);
|
setLocation(_literal);
|
||||||
Type const& literalType = type(_literal);
|
|
||||||
|
|
||||||
switch (literalType.category())
|
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||||
{
|
{
|
||||||
case Type::Category::RationalNumber:
|
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||||
case Type::Category::Bool:
|
*(*identifierPath)->annotation().referencedDeclaration
|
||||||
case Type::Category::Address:
|
);
|
||||||
define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
FunctionType const& functionType = *function.functionType(true);
|
||||||
break;
|
|
||||||
case Type::Category::StringLiteral:
|
// TODO this is actually not always the right one.
|
||||||
break; // will be done during conversion
|
auto type = TypeProvider::forLiteral(_literal);
|
||||||
default:
|
|
||||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
vector<string> args;
|
||||||
|
if (dynamic_cast<RationalNumberType const*>(type) && dynamic_cast<RationalNumberType const*>(type)->isFractional())
|
||||||
|
{
|
||||||
|
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||||
|
IRVariable mantissaVar(m_context.newYulVariable(), *mantissa);
|
||||||
|
define(mantissaVar) << toCompactHexWithPrefix(mantissa->literalValue(&_literal)) << "\n";
|
||||||
|
args = convert(mantissaVar, *functionType.parameterTypes().at(0)).stackSlots();
|
||||||
|
|
||||||
|
IRVariable exponentVar(m_context.newYulVariable(), *exponent);
|
||||||
|
define(exponentVar) << toCompactHexWithPrefix(exponent->literalValue(&_literal)) << "\n";
|
||||||
|
args += convert(exponentVar, *functionType.parameterTypes().at(1)).stackSlots();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO reuse the code below
|
||||||
|
if (type->category() != Type::Category::StringLiteral)
|
||||||
|
{
|
||||||
|
IRVariable value(m_context.newYulVariable(), *type);
|
||||||
|
define(value) << toCompactHexWithPrefix(type->literalValue(&_literal)) << "\n";
|
||||||
|
args = convert(value, *functionType.parameterTypes().at(0)).stackSlots();
|
||||||
|
}
|
||||||
|
// TODO what about string?
|
||||||
|
}
|
||||||
|
define(_literal) <<
|
||||||
|
m_context.enqueueFunctionForCodeGeneration(function) <<
|
||||||
|
"(" + joinHumanReadable(args) + ")\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Type const& literalType = type(_literal);
|
||||||
|
|
||||||
|
switch (literalType.category())
|
||||||
|
{
|
||||||
|
case Type::Category::RationalNumber:
|
||||||
|
case Type::Category::Bool:
|
||||||
|
case Type::Category::Address:
|
||||||
|
define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
||||||
|
break;
|
||||||
|
case Type::Category::StringLiteral:
|
||||||
|
break; // will be done during conversion
|
||||||
|
default:
|
||||||
|
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1947,6 +1947,61 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTPointer<Expression> Parser::parseLiteral()
|
||||||
|
{
|
||||||
|
RecursionGuard recursionGuard(*this);
|
||||||
|
ASTNodeFactory nodeFactory(*this);
|
||||||
|
Token token = m_scanner->currentToken();
|
||||||
|
ASTPointer<ASTString> value = make_shared<string>(m_scanner->currentLiteral());
|
||||||
|
Literal::Suffix suffix = Literal::SubDenomination::None;
|
||||||
|
|
||||||
|
switch (token)
|
||||||
|
{
|
||||||
|
case Token::TrueLiteral:
|
||||||
|
case Token::FalseLiteral:
|
||||||
|
case Token::Number:
|
||||||
|
break;
|
||||||
|
case Token::StringLiteral:
|
||||||
|
case Token::UnicodeStringLiteral:
|
||||||
|
case Token::HexStringLiteral:
|
||||||
|
{
|
||||||
|
Token firstToken = token;
|
||||||
|
while (m_scanner->peekNextToken() == firstToken)
|
||||||
|
{
|
||||||
|
advance();
|
||||||
|
*value += m_scanner->currentLiteral();
|
||||||
|
}
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
advance();
|
||||||
|
if (m_scanner->currentToken() == Token::Illegal)
|
||||||
|
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
solAssert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
advance();
|
||||||
|
|
||||||
|
if (token == Token::Number && (
|
||||||
|
TokenTraits::isEtherSubdenomination(m_scanner->currentToken()) ||
|
||||||
|
TokenTraits::isTimeSubdenomination(m_scanner->currentToken())
|
||||||
|
))
|
||||||
|
{
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
suffix = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
else if (m_scanner->currentToken() == Token::Identifier)
|
||||||
|
{
|
||||||
|
auto identifierPath = parseIdentifierPath();
|
||||||
|
nodeFactory.setEndPositionFromNode(identifierPath);
|
||||||
|
suffix = move(identifierPath);
|
||||||
|
}
|
||||||
|
return nodeFactory.createNode<Literal>(token, move(value), move(suffix));
|
||||||
|
}
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parsePrimaryExpression()
|
ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||||
{
|
{
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
@ -1958,50 +2013,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
{
|
{
|
||||||
case Token::TrueLiteral:
|
case Token::TrueLiteral:
|
||||||
case Token::FalseLiteral:
|
case Token::FalseLiteral:
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
|
||||||
break;
|
|
||||||
case Token::Number:
|
case Token::Number:
|
||||||
if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()))
|
|
||||||
{
|
|
||||||
ASTPointer<ASTString> literal = getLiteralAndAdvance();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
|
||||||
advance();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
|
|
||||||
}
|
|
||||||
else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken()))
|
|
||||||
{
|
|
||||||
ASTPointer<ASTString> literal = getLiteralAndAdvance();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
|
||||||
advance();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Token::StringLiteral:
|
case Token::StringLiteral:
|
||||||
case Token::UnicodeStringLiteral:
|
case Token::UnicodeStringLiteral:
|
||||||
case Token::HexStringLiteral:
|
case Token::HexStringLiteral:
|
||||||
{
|
expression = parseLiteral();
|
||||||
string literal = m_scanner->currentLiteral();
|
|
||||||
Token firstToken = m_scanner->currentToken();
|
|
||||||
while (m_scanner->peekNextToken() == firstToken)
|
|
||||||
{
|
|
||||||
advance();
|
|
||||||
literal += m_scanner->currentLiteral();
|
|
||||||
}
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
advance();
|
|
||||||
if (m_scanner->currentToken() == Token::Illegal)
|
|
||||||
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Token::Identifier:
|
case Token::Identifier:
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
||||||
|
@ -159,6 +159,7 @@ private:
|
|||||||
ASTPointer<Expression> parseLeftHandSideExpression(
|
ASTPointer<Expression> parseLeftHandSideExpression(
|
||||||
ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
|
ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
|
||||||
);
|
);
|
||||||
|
ASTPointer<Expression> parseLiteral();
|
||||||
ASTPointer<Expression> parsePrimaryExpression();
|
ASTPointer<Expression> parsePrimaryExpression();
|
||||||
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
|
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
type Length is uint;
|
||||||
|
|
||||||
|
function km(uint meters) pure returns (Length) {
|
||||||
|
return Length.wrap(meters * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Float {
|
||||||
|
uint mantissa;
|
||||||
|
int exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(uint mantissa, int exponent) pure returns (Float memory) {
|
||||||
|
return Float(mantissa, exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
Length public length = 1000 km;
|
||||||
|
Float public factor = 1.23 f;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// length() -> 1000000
|
||||||
|
// factor() -> 0x7b, 2
|
Loading…
Reference in New Issue
Block a user