From 480bbe45b42e3b35812d0540bed926c68d145cb0 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Sat, 3 Dec 2022 07:32:33 +0100 Subject: [PATCH 01/23] Fix bug in cast from `real` to `interval`. --- hilti/toolchain/src/compiler/codegen/operators.cc | 2 +- tests/hilti/types/interval/ops.hlt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index 5e2865375..092886264 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -371,7 +371,7 @@ struct Visitor : hilti::visitor::PreOrder { /// Real result_t operator()(const operator_::real::CastToInterval& n) { - return fmt("::hilti::rt::Interval(%f, hilti::rt::Interval::NanosecondTag())", op0(n)); + return fmt("::hilti::rt::Interval(%f, hilti::rt::Interval::SecondTag())", op0(n)); } result_t operator()(const operator_::real::CastToTime& n) { return fmt("::hilti::rt::Time(%f, hilti::rt::Time::SecondTag())", op0(n)); diff --git a/tests/hilti/types/interval/ops.hlt b/tests/hilti/types/interval/ops.hlt index 21ed494b9..8d73a56cd 100644 --- a/tests/hilti/types/interval/ops.hlt +++ b/tests/hilti/types/interval/ops.hlt @@ -28,5 +28,6 @@ assert i1 >= i1; assert i2 <= i2; assert cast(5) == interval(5.0); +assert cast(5.0) == interval(5.0); } From 57a568207962ae5bb395326e73e0549323482102 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Sat, 3 Dec 2022 06:59:35 +0100 Subject: [PATCH 02/23] Support arbitrary expression as argument to `interval(...)` and `interval_ns(...)`. So far we used constructor expressions like `interval(...)` to create *constants* of the given type, and for that we required that the argument was a single atomic value. The result was that these constructor expressions were really more like literals for the corresponding types than "normal" expressions. While that's useful internally, it's confusing for the user. This commits is beginning to change that, using `interval`/`interval_NS` as the first instances that now supports arbitrary expressions as its argument. In follow-up commits, we'll port other constructor expressions over to that new syntax. (This also unifies HILTI & Spicy to each provide both of those interval constructors.) To make this change without loosing a way to create actual constants (which we need at some places), we implement this in two stages: the parser initially turns such `interval(...)` expressions into call operators[1] that the types now define for each desired constructor. We then extend the normalizer to look at all the relevant call operators: if they are just receiving a constant value as their argument, then it substitutes them with a plain ``Ctor`` node (``ctor::Interval`` in our cases here). That check for "if they just receive a constant value" isn't quite trivial in general. In our case, where we just want to go back to what we had, it's not that difficult, but more generally it would be nice if we had a constant folder that could compute results even for more complex, but constant, expressions. As that's a piece that our AST infrastructure is still missing, this commit starts going that route by adding a new "constant folder" visitor. Right now, it's covers only the simple cases we need here, but it's something we can extend going forward. Addresses #1123. [1] These call operators are a bit special because they actually don't have anything to call: in `interval(42)` the `interval` isn't any kind of actual value; we just need it for overload resolution. To make that work, we use a `expression::Member` as the first operand, which models a predefined ID. Normally `Member` is just for field lookups in, e.g., structs, but it works just as well elsewhere. --- 3rdparty/SafeInt | 2 +- doc/autogen/types/interval.rst | 22 ++++++ hilti/toolchain/CMakeLists.txt | 1 + .../include/ast/builder/expression.h | 5 ++ .../toolchain/include/ast/operators/common.h | 37 ++++++++++ .../include/ast/operators/interval.h | 15 ++++ .../include/compiler/detail/visitors.h | 27 +++++++ .../src/compiler/codegen/operators.cc | 25 +++++++ hilti/toolchain/src/compiler/parser/parser.yy | 20 ++---- .../toolchain/src/compiler/parser/scanner.ll | 1 + .../src/compiler/visitors/constant-folder.cc | 70 +++++++++++++++++++ .../src/compiler/visitors/normalizer.cc | 63 +++++++++++++++++ .../src/compiler/visitors/printer.cc | 2 +- spicy/toolchain/src/compiler/parser/parser.yy | 16 ++--- .../output | 2 +- tests/hilti/types/interval/ops.hlt | 7 ++ 16 files changed, 290 insertions(+), 25 deletions(-) create mode 100644 hilti/toolchain/src/compiler/visitors/constant-folder.cc diff --git a/3rdparty/SafeInt b/3rdparty/SafeInt index a5408ba2c..d0178e3c3 160000 --- a/3rdparty/SafeInt +++ b/3rdparty/SafeInt @@ -1 +1 @@ -Subproject commit a5408ba2c025ec99a7afb6ccca907d5a263242a5 +Subproject commit d0178e3c3128b57041d6b0676028ef99c95b4f13 diff --git a/doc/autogen/types/interval.rst b/doc/autogen/types/interval.rst index a26d6395a..0f0820ff9 100644 --- a/doc/autogen/types/interval.rst +++ b/doc/autogen/types/interval.rst @@ -10,6 +10,28 @@ .. rubric:: Operators +.. spicy:operator:: interval::Call interval interval(int) + + Creates an interval interpreting the argument as number of seconds. + +.. spicy:operator:: interval::Call interval interval(real) + + Creates an interval interpreting the argument as number of seconds. + +.. spicy:operator:: interval::Call interval interval(uint) + + Creates an interval interpreting the argument as number of seconds. + +.. spicy:operator:: interval::Call interval interval_ns(int) + + Creates an interval interpreting the argument as number of + nanoseconds. + +.. spicy:operator:: interval::Call interval interval_ns(uint) + + Creates an interval interpreting the argument as number of + nanoseconds. + .. spicy:operator:: interval::Difference interval t:interval op:- t:interval Returns the difference of the intervals. diff --git a/hilti/toolchain/CMakeLists.txt b/hilti/toolchain/CMakeLists.txt index 94db253ef..2fa826416 100644 --- a/hilti/toolchain/CMakeLists.txt +++ b/hilti/toolchain/CMakeLists.txt @@ -83,6 +83,7 @@ set(SOURCES src/compiler/plugin.cc src/compiler/unit.cc src/compiler/visitors/coercer.cc + src/compiler/visitors/constant-folder.cc src/compiler/visitors/normalizer.cc src/compiler/visitors/printer.cc src/compiler/visitors/renderer.cc diff --git a/hilti/toolchain/include/ast/builder/expression.h b/hilti/toolchain/include/ast/builder/expression.h index 26803a2c6..e00c6f97c 100644 --- a/hilti/toolchain/include/ast/builder/expression.h +++ b/hilti/toolchain/include/ast/builder/expression.h @@ -348,5 +348,10 @@ inline auto port(Expression port, Expression protocol, const Meta& m = Meta()) { std::vector{std::move(port), std::move(protocol)}, m); } +inline Expression namedCtor(const std::string& name, const std::vector& args, Meta m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Call, + {expression::Member(ID(name)), expression::Ctor(hilti::ctor::Tuple(args))}, + std::move(m)); +} } // namespace hilti::builder diff --git a/hilti/toolchain/include/ast/operators/common.h b/hilti/toolchain/include/ast/operators/common.h index afa6bf4af..27cdd99e0 100644 --- a/hilti/toolchain/include/ast/operators/common.h +++ b/hilti/toolchain/include/ast/operators/common.h @@ -279,6 +279,43 @@ private: \ __END_OPERATOR_CUSTOM +/** Defines a constructor-style call operator introduced by a keyword. */ +#define BEGIN_KEYWORD_CTOR(ns, cls, kw, result_, doc_) \ + __BEGIN_OPERATOR_CUSTOM(ns, Call, cls) \ + \ + const auto& signature() const { \ + static hilti::operator_::Signature _signature = {.result = result_, .args = parameters(), .doc = doc_}; \ + return _signature; \ + } \ + \ + const std::vector& operands() const { \ + static std::vector _operands = {{{}, hilti::type::Member(kw)}, \ + {{}, hilti::type::OperandList(signature().args)}}; \ + return _operands; \ + } \ + \ + std::string doc() const { return signature().doc; } \ + \ + hilti::Type result(const hilti::node::Range& ops) const { \ + return *hilti::operator_::type(signature().result, ops, ops); \ + } \ + \ + bool isLhs() const { return signature().lhs; } \ + hilti::operator_::Priority priority() const { return signature().priority; } \ + \ + void validate(const hilti::expression::ResolvedOperator& /* i */, hilti::operator_::position_t /* p */) const {} + + +#define END_KEYWORD_CTOR __END_OPERATOR_CUSTOM + +/** Shortcut to define a constructor-style call operator introduced by a keyword using a single argument. */ +#define STANDARD_KEYWORD_CTOR(ns, cls, kw, result_, ty_op, doc_) \ + BEGIN_KEYWORD_CTOR(ns, cls, kw, result_, doc_) \ + \ + std::vector parameters() const { return {{"op", ty_op}}; } \ + \ + END_KEYWORD_CTOR + /** * No-op to have the auto-generated code pick up on an operator that's * fully defined separately. diff --git a/hilti/toolchain/include/ast/operators/interval.h b/hilti/toolchain/include/ast/operators/interval.h index c4e815fdd..3be88f164 100644 --- a/hilti/toolchain/include/ast/operators/interval.h +++ b/hilti/toolchain/include/ast/operators/interval.h @@ -27,6 +27,21 @@ STANDARD_OPERATOR_2x(interval, MultipleUnsignedInteger, Multiple, type::Interval STANDARD_OPERATOR_2x(interval, MultipleReal, Multiple, type::Interval(), type::Interval(), type::Real(), "Multiplies the interval with the given factor."); +STANDARD_KEYWORD_CTOR(interval, CtorSignedIntegerNs, "interval_ns", type::Interval(), + type::SignedInteger(type::Wildcard()), + "Creates an interval interpreting the argument as number of nanoseconds."); +STANDARD_KEYWORD_CTOR(interval, CtorSignedIntegerSecs, "interval", type::Interval(), + type::SignedInteger(type::Wildcard()), + "Creates an interval interpreting the argument as number of seconds."); +STANDARD_KEYWORD_CTOR(interval, CtorUnsignedIntegerNs, "interval_ns", type::Interval(), + type::UnsignedInteger(type::Wildcard()), + "Creates an interval interpreting the argument as number of nanoseconds."); +STANDARD_KEYWORD_CTOR(interval, CtorUnsignedIntegerSecs, "interval", type::Interval(), + type::UnsignedInteger(type::Wildcard()), + "Creates an interval interpreting the argument as number of seconds."); +STANDARD_KEYWORD_CTOR(interval, CtorRealSecs, "interval", type::Interval(), type::Real(), + "Creates an interval interpreting the argument as number of seconds."); + BEGIN_METHOD(interval, Seconds) const auto& signature() const { static auto _signature = diff --git a/hilti/toolchain/include/compiler/detail/visitors.h b/hilti/toolchain/include/compiler/detail/visitors.h index 2a5d1716b..3e9aeeb54 100644 --- a/hilti/toolchain/include/compiler/detail/visitors.h +++ b/hilti/toolchain/include/compiler/detail/visitors.h @@ -58,6 +58,33 @@ std::string renderOperatorInstance(const expression::ResolvedOperator& o); void renderNode(const Node& n, std::ostream& out, bool include_scopes = false); void renderNode(const Node& n, logging::DebugStream stream, bool include_scopes = false); +/** + * Folds an expression into a constant value if that's possible. Note that the + * current implementation is very, very basic, and covers just a few cases. If + * the function returns an error, that does not necessarily mean that the + * expression is not represeneting a constant value, but only that we aren't + * able to compute it. + */ +Result foldConstant(const Expression& expr); + +/** + * Folds an expression intoa constant value of a specific type, if that's + * possible. This behaves like the non-templated version of `foldConstant()` + * but adds a check that the resulting ``Ctor`` is of the expected type. If + * not, it will fail. + */ +template +Result foldConstant(const Expression& expr) { + auto ctor = foldConstant(expr); + if ( ! ctor ) + return ctor.error(); + + if ( auto ctor_ = ctor->tryAs() ) + return *ctor_; + else + return result::Error("unexpected type"); +} + namespace ast { /** Implements the corresponding functionality for the default HILTI compiler plugin. */ void buildScopes(const std::shared_ptr& ctx, Node* root, Unit* unit); diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index 092886264..b1aa07f85 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -305,6 +305,31 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::interval::Sum& n) { return binary(n, "+"); } result_t operator()(const operator_::interval::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::interval::CtorSignedIntegerSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Interval(%s, hilti::rt::Interval::SecondTag())", args[0]); + } + + result_t operator()(const operator_::interval::CtorSignedIntegerNs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Interval(%s, hilti::rt::Interval::NanosecondTag())", args[0]); + } + + result_t operator()(const operator_::interval::CtorUnsignedIntegerSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Interval(%s, hilti::rt::Interval::SecondTag())", args[0]); + } + + result_t operator()(const operator_::interval::CtorUnsignedIntegerNs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Interval(%s, hilti::rt::Interval::NanosecondTag())", args[0]); + } + + result_t operator()(const operator_::interval::CtorRealSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Interval(%f, hilti::rt::Interval::SecondTag())", args[0]); + } + // List result_t operator()(const operator_::list::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } result_t operator()(const operator_::list::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index 883c0d9d6..aec88c8d0 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -34,7 +34,7 @@ namespace hilti { namespace detail { class Parser; } } %glr-parser %expect 115 -%expect-rr 189 +%expect-rr 191 %{ @@ -165,6 +165,7 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token INOUT "inout" %token INT "int" %token INTERVAL "interval" +%token INTERVAL_NS "interval_ns" %token IOSRC "iosrc" %token ITERATOR "iterator" %token CONST_ITERATOR "const_iterator" @@ -235,7 +236,7 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %type > struct_fields union_fields opt_union_fields %type base_type_no_attrs base_type type function_type tuple_type struct_type enum_type union_type %type ctor tuple struct_ list regexp map set -%type expr tuple_elem tuple_expr member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type expr tuple_elem tuple_expr member_expr ctor_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g %type > opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_type_arguments %type > opt_func_default_expr %type function_with_body method_with_body hook_with_body function_without_body @@ -270,7 +271,6 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %type , std::vector>> global_scope_items %type const_real -%type const_sint %type const_uint %% @@ -787,6 +787,7 @@ expr_e : BEGIN_ '(' expr ')' { $$ = hilti::expression::Unres | expr_f { $$ = std::move($1); } expr_f : ctor { $$ = hilti::expression::Ctor(std::move($1), __loc__); } + | ctor_expr { $$ = std::move($1); } | PORT '(' expr ',' expr ')' { $$ = hilti::builder::port(std::move($3), std::move($5), __loc__); } | '-' expr_g { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SignNeg, {std::move($2)}, __loc__); } | '[' expr FOR local_id IN expr ']' @@ -816,12 +817,6 @@ ctor : CBOOL { $$ = hilti::ctor::Bool($1, __ | CADDRESS { $$ = hilti::ctor::Address(hilti::ctor::Address::Value($1), __loc__); } | CADDRESS '/' CUINTEGER { $$ = hilti::ctor::Network(hilti::ctor::Network::Value($1, $3), __loc__); } | CPORT { $$ = hilti::ctor::Port(hilti::ctor::Port::Value($1), __loc__); } - | INTERVAL '(' const_real ')' { try { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3, hilti::rt::Interval::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value()); error(@$, e.what()); } - } - | INTERVAL '(' const_sint ')' { try { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3, hilti::rt::Interval::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value()); error(@$, e.what()); } - } | TIME '(' const_real ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } } @@ -850,14 +845,13 @@ ctor : CBOOL { $$ = hilti::ctor::Bool($1, __ | tuple { $$ = std::move($1); } ; +ctor_expr : INTERVAL '(' expr ')' { $$ = builder::namedCtor("interval", { std::move($3) }, __loc__); } + | INTERVAL_NS '(' expr ')' { $$ = builder::namedCtor("interval_ns", { std::move($3) }, __loc__); } + const_real : CUREAL { $$ = $1; } | '+' CUREAL { $$ = $2; } | '-' CUREAL { $$ = -$2; } -const_sint : CUINTEGER { $$ = check_int64_range($1, true, __loc__); } - | '+' CUINTEGER { $$ = check_int64_range($2, true, __loc__); } - | '-' CUINTEGER { $$ = -check_int64_range($2, false, __loc__); } - const_uint : CUINTEGER { $$ = $1; } | '+' CUINTEGER { $$ = $2; } diff --git a/hilti/toolchain/src/compiler/parser/scanner.ll b/hilti/toolchain/src/compiler/parser/scanner.ll index 575754eb8..479a13c74 100644 --- a/hilti/toolchain/src/compiler/parser/scanner.ll +++ b/hilti/toolchain/src/compiler/parser/scanner.ll @@ -117,6 +117,7 @@ init return token::INIT; inout return token::INOUT; int return token::INT; interval return token::INTERVAL; +interval_ns return token::INTERVAL_NS; iterator return token::ITERATOR; list return token::LIST; local return token::LOCAL; diff --git a/hilti/toolchain/src/compiler/visitors/constant-folder.cc b/hilti/toolchain/src/compiler/visitors/constant-folder.cc new file mode 100644 index 000000000..9f6a16dee --- /dev/null +++ b/hilti/toolchain/src/compiler/visitors/constant-folder.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace { + +// For now, this is only a very basic constant folder that only covers cases we +// need to turn type constructor expressions coming with a single argument into +// ctor expressions. +struct VisitorConstantFolder : public visitor::PreOrder { + // Helper to cast an uint64 to int64, with range check. + int64_t to_int64(uint64_t x, position_t& p) { + if ( x > std::numeric_limits::max() ) + logger().error("signed integer value out of range 2", p.node.location()); + + return static_cast(x); + } + + result_t operator()(const expression::Ctor& n, position_t p) { return n.ctor(); } + + result_t operator()(const operator_::signed_integer::SignNeg& n, position_t p) { + if ( auto op = detail::foldConstant(n.op1()) ) { + if ( op->value() >= 0 ) { + if ( op->value() < -std::numeric_limits::min() ) { + logger().error("signed integer value out of range X", p.node.location()); + return {}; + } + } + else { + if ( -op->value() > std::numeric_limits::max() ) { + logger().error("signed integer value out of range Y", p.node.location()); + return {}; + } + } + + return ctor::SignedInteger(-op->value(), op->width(), op->meta()); + } + else + return {}; + } + + result_t operator()(const operator_::real::SignNeg& n, position_t p) { + if ( auto op = detail::foldConstant(n.op1()) ) + return ctor::Real(-op->value(), op->meta()); + else + return {}; + } +}; + +} // anonymous namespace + +Result detail::foldConstant(const Expression& expr) { + auto v = VisitorConstantFolder(); + + if ( auto ctor = v.dispatch(expr) ) + return *ctor; + else + return result::Error("not a foldable constant expression"); +} diff --git a/hilti/toolchain/src/compiler/visitors/normalizer.cc b/hilti/toolchain/src/compiler/visitors/normalizer.cc index 8bbcc89b4..57e569800 100644 --- a/hilti/toolchain/src/compiler/visitors/normalizer.cc +++ b/hilti/toolchain/src/compiler/visitors/normalizer.cc @@ -1,5 +1,7 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +#include +#include #include #include #include @@ -27,6 +29,12 @@ struct VisitorNormalizer : public visitor::PreOrder { util::fmt("[%s] %s -> expression %s (%s)", old.typename_(), old, nexpr, old.location())); } + // Log debug message recording resolving a ctor. + void logChange(const Node& old, const Ctor& nexpr) { + HILTI_DEBUG(logging::debug::Normalizer, + util::fmt("[%s] %s -> ctor %s (%s)", old.typename_(), old, nexpr, old.location())); + } + // Log debug message recording resolving a statement. void logChange(const Node& old, const Statement& nstmt) { HILTI_DEBUG(logging::debug::Normalizer, @@ -44,6 +52,31 @@ struct VisitorNormalizer : public visitor::PreOrder { HILTI_DEBUG(logging::debug::Normalizer, util::fmt("%s -> %s (%s)", old, nattr, old.location())); } + auto callArgument(const expression::ResolvedOperatorBase& o, int i) { + auto ctor = o.op1().as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return ctor.as().value()[i]; + } + + // Helper to replace an type constructor expression that receives a + // constant argument with a corresponding ctor expression. + template + void tryReplaceCtorExpression(const Operator& op, position_t p, std::function cb) { + if ( auto ctor = detail::foldConstant(callArgument(op, 0)) ) { + try { + auto i = cb(*ctor); + logChange(p.node, i); + p.node = expression::Ctor(std::move(i)); + modified = true; + } catch ( hilti::rt::RuntimeError& e ) { + p.node.addError(e.what()); + } + } + } + void operator()(const declaration::Function& u, position_t p) { if ( u.linkage() == declaration::Linkage::Struct ) { // Link method implementations to their parent type. @@ -125,6 +158,36 @@ struct VisitorNormalizer : public visitor::PreOrder { } } + void operator()(const operator_::interval::CtorSignedIntegerSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::SecondTag())); + }); + } + + void operator()(const operator_::interval::CtorUnsignedIntegerSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::SecondTag())); + }); + } + + void operator()(const operator_::interval::CtorSignedIntegerNs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::NanosecondTag())); + }); + } + + void operator()(const operator_::interval::CtorUnsignedIntegerNs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::NanosecondTag())); + }); + } + + void operator()(const operator_::interval::CtorRealSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::SecondTag())); + }); + } + void operator()(const statement::If& n, position_t p) { if ( n.init() && ! n.condition() ) { auto cond = expression::UnresolvedID(n.init()->id()); diff --git a/hilti/toolchain/src/compiler/visitors/printer.cc b/hilti/toolchain/src/compiler/visitors/printer.cc index a90a3d406..aef145c86 100644 --- a/hilti/toolchain/src/compiler/visitors/printer.cc +++ b/hilti/toolchain/src/compiler/visitors/printer.cc @@ -800,7 +800,7 @@ struct Visitor : visitor::PreOrder { void operator()(const type::Interval& n) { out << const_(n) << "interval"; } - void operator()(const type::Member& n) { out << const_(n) << ""; } + void operator()(const type::Member& n) { out << const_(n) << n.id(); } void operator()(const type::Network& n) { out << const_(n) << "net"; } diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index 33779996d..43e848499 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -34,7 +34,7 @@ namespace spicy { namespace detail { class Parser; } } %verbose %glr-parser -%expect 131 +%expect 129 %expect-rr 164 %{ @@ -240,7 +240,8 @@ static int _field_width = 0; %type > struct_fields %type base_type_no_ref base_type type tuple_type struct_type enum_type unit_type bitfield_type reference_type %type ctor tuple struct_ regexp list vector map set -%type expr tuple_elem tuple_expr member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type expr tuple_elem member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type expr tuple_elem tuple_expr member_expr ctor_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g %type > opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_unit_field_args opt_unit_field_sinks %type > opt_else_block %type > opt_init_expression opt_unit_field_condition unit_field_repeat opt_unit_field_repeat opt_unit_switch_expr @@ -915,6 +916,7 @@ expr_e : CAST type_param_begin type type_param_end '(' expr ')' { $$ = | expr_f { $$ = std::move($1); } expr_f : ctor { $$ = hilti::expression::Ctor(std::move($1), __loc__); } + | ctor_expr { $$ = std::move($1); } | PORT '(' expr ',' expr ')' { $$ = hilti::builder::port(std::move($3), std::move($5), __loc__); } | '-' expr_g { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SignNeg, {std::move($2)}, __loc__); } | '[' expr FOR local_id IN expr ']' @@ -949,13 +951,6 @@ ctor : CADDRESS { $$ = hilti::ctor::Address(hil | '+' CUINTEGER { $$ = hilti::ctor::SignedInteger(check_int64_range($2, true, __loc__), 64, __loc__); } | '-' CUINTEGER { $$ = hilti::ctor::SignedInteger(-check_int64_range($2, false, __loc__), 64, __loc__); } | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } - | INTERVAL '(' const_real ')' { try { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3, hilti::rt::Interval::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value()); error(@$, e.what()); } - } - | INTERVAL '(' const_sint ')' { try { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3, hilti::rt::Interval::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value()); error(@$, e.what()); } - } - | INTERVAL_NS '(' const_sint ')' { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3, hilti::rt::Interval::NanosecondTag()), __loc__); } | TIME '(' const_real ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } } @@ -983,6 +978,9 @@ ctor : CADDRESS { $$ = hilti::ctor::Address(hil | vector { $$ = std::move($1); } ; +ctor_expr : INTERVAL '(' expr ')' { $$ = hilti::builder::namedCtor("interval", { std::move($3) }, __loc__); } + | INTERVAL_NS '(' expr ')' { $$ = hilti::builder::namedCtor("interval_ns", { std::move($3) }, __loc__); } + const_real : CUREAL { $$ = $1; } | '+' CUREAL { $$ = $2; } | '-' CUREAL { $$ = -$2; } diff --git a/tests/Baseline/spicy.types.interval.ctor-out-of-range/output b/tests/Baseline/spicy.types.interval.ctor-out-of-range/output index 028b73376..78c4be0c9 100644 --- a/tests/Baseline/spicy.types.interval.ctor-out-of-range/output +++ b/tests/Baseline/spicy.types.interval.ctor-out-of-range/output @@ -1,4 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/ctor-out-of-range.spicy:7:12: value cannot be represented as an interval [error] <...>/ctor-out-of-range.spicy:8:12: value cannot be represented as an interval -[error] spicyc: parse error +[error] spicyc: aborting after errors diff --git a/tests/hilti/types/interval/ops.hlt b/tests/hilti/types/interval/ops.hlt index 8d73a56cd..fab5b0a25 100644 --- a/tests/hilti/types/interval/ops.hlt +++ b/tests/hilti/types/interval/ops.hlt @@ -7,6 +7,8 @@ import hilti; global i1 = interval(2.5); global i2 = interval(1.5); +global i3 = interval(10 + 15); +global i4 = interval(10.0 + 15.0); hilti::print(i1); hilti::print(i2); @@ -30,4 +32,9 @@ assert i2 <= i2; assert cast(5) == interval(5.0); assert cast(5.0) == interval(5.0); +assert i3 == interval(25.0); +assert i3 == i4; + +assert interval_ns(2500000000) == i1; + } From 54b22f83164c124c2d0ee89359871f1b1f1b6976 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Thu, 8 Dec 2022 17:51:50 +0100 Subject: [PATCH 03/23] Port more type constructors to new call operators. This moves `time/time_ns/stream/error/intN/uintN` over to the new call operator model that we introduced for `interval`. One HILTI-side syntax change: HILTI now also uses `intN(x)` and `uintN(x)` instead of `int`/`uint`. This is to avoid having to introduce two separate set of operators. This change slightly changes timing for when exactly some errors are caught, which required tweaking a couple of tests a bit. --- doc/autogen/types/error.rst | 6 + doc/autogen/types/integer.rst | 64 +++++++ doc/autogen/types/stream.rst | 4 + doc/autogen/types/time.rst | 20 +++ hilti/toolchain/include/ast/operators/error.h | 2 + .../include/ast/operators/signed-integer.h | 17 ++ .../toolchain/include/ast/operators/stream.h | 3 + hilti/toolchain/include/ast/operators/time.h | 11 ++ .../include/ast/operators/unsigned-integer.h | 18 ++ .../src/compiler/codegen/operators.cc | 118 +++++++++++++ hilti/toolchain/src/compiler/parser/parser.yy | 60 ++++--- .../toolchain/src/compiler/parser/scanner.ll | 9 + .../src/compiler/visitors/normalizer.cc | 158 +++++++++++++++++- spicy/toolchain/src/compiler/parser/parser.yy | 86 +++++----- .../spicy.types.integer.ctor-fail-10/output | 5 + .../spicy.types.integer.ctor-fail-2/output | 2 +- .../spicy.types.integer.ctor-fail-3/output | 4 +- .../spicy.types.integer.ctor-fail-4/output | 4 +- .../spicy.types.integer.ctor-fail-5/output | 4 +- .../spicy.types.integer.ctor-fail-6/output | 4 +- .../spicy.types.integer.ctor-fail-8/output | 5 +- .../spicy.types.integer.ctor-fail-9/output | 3 + .../spicy.types.integer.ctor-fail/output | 3 - .../output | 2 +- tests/hilti/types/integer/cast.hlt | 2 +- .../hilti/types/integer/operators-signed.hlt | 12 ++ .../types/integer/operators-unsigned.hlt | 12 ++ tests/hilti/types/result/error.hlt | 3 + tests/hilti/types/stream/ops.hlt | 4 + tests/hilti/types/time/ops.hlt | 5 + tests/spicy/types/integer/ctor-fail.spicy | 18 +- 31 files changed, 569 insertions(+), 99 deletions(-) create mode 100644 tests/Baseline/spicy.types.integer.ctor-fail-10/output create mode 100644 tests/Baseline/spicy.types.integer.ctor-fail-9/output diff --git a/doc/autogen/types/error.rst b/doc/autogen/types/error.rst index d9be01083..6a328c307 100644 --- a/doc/autogen/types/error.rst +++ b/doc/autogen/types/error.rst @@ -4,3 +4,9 @@ Retrieves the textual description associated with the error. +.. rubric:: Operators + +.. spicy:operator:: error::Call error error(string) + + Creates an error with the given message. + diff --git a/doc/autogen/types/integer.rst b/doc/autogen/types/integer.rst index a2630bfcb..385371a0c 100644 --- a/doc/autogen/types/integer.rst +++ b/doc/autogen/types/integer.rst @@ -12,6 +12,70 @@ Computes the bit-wise 'xor' of the two integers. +.. spicy:operator:: integer::Call int<16> int16(int) + + Creates a 16-bit signed integer value. + +.. spicy:operator:: integer::Call int<16> int16(uint) + + Creates a 16-bit signed integer value. + +.. spicy:operator:: integer::Call int<32> int32(int) + + Creates a 32-bit signed integer value. + +.. spicy:operator:: integer::Call int<32> int32(uint) + + Creates a 32-bit signed integer value. + +.. spicy:operator:: integer::Call int<64> int64(int) + + Creates a 64-bit signed integer value. + +.. spicy:operator:: integer::Call int<64> int64(uint) + + Creates a 64-bit signed integer value. + +.. spicy:operator:: integer::Call int<8> int8(int) + + Creates a 8-bit signed integer value. + +.. spicy:operator:: integer::Call int<8> int8(uint) + + Creates a 8-bit signed integer value. + +.. spicy:operator:: integer::Call uint<16> uint16(int) + + Creates a 16-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<16> uint16(uint) + + Creates a 16-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<32> uint32(int) + + Creates a 32-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<32> uint32(uint) + + Creates a 32-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<64> uint64(int) + + Creates a 64-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<64> uint64(uint) + + Creates a 64-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<8> uint8(int) + + Creates a 8-bit unsigned integer value. + +.. spicy:operator:: integer::Call uint<8> uint8(uint) + + Creates a 8-bit unsigned integer value. + .. spicy:operator:: integer::Cast bool cast(int) Converts the value to a boolean by comparing against zero diff --git a/doc/autogen/types/stream.rst b/doc/autogen/types/stream.rst index 5cd610e49..a6312a8b8 100644 --- a/doc/autogen/types/stream.rst +++ b/doc/autogen/types/stream.rst @@ -38,6 +38,10 @@ Returns an iterator to the beginning of the container's content. +.. spicy:operator:: stream::Call stream stream(bytes) + + Creates a stream instance preinitialized with the given data. + .. spicy:operator:: stream::End end() Returns an iterator to the end of the container's content. diff --git a/doc/autogen/types/time.rst b/doc/autogen/types/time.rst index 777bbe309..83bb94ddb 100644 --- a/doc/autogen/types/time.rst +++ b/doc/autogen/types/time.rst @@ -12,6 +12,26 @@ .. rubric:: Operators +.. spicy:operator:: time::Call time time(int) + + Creates an time interpreting the argument as number of seconds. + +.. spicy:operator:: time::Call time time(real) + + Creates an time interpreting the argument as number of seconds. + +.. spicy:operator:: time::Call time time(uint) + + Creates an time interpreting the argument as number of seconds. + +.. spicy:operator:: time::Call time time_ns(int) + + Creates an time interpreting the argument as number of nanoseconds. + +.. spicy:operator:: time::Call time time_ns(uint) + + Creates an time interpreting the argument as number of nanoseconds. + .. spicy:operator:: time::Difference interval t:time op:- t:time Returns the difference of the times. diff --git a/hilti/toolchain/include/ast/operators/error.h b/hilti/toolchain/include/ast/operators/error.h index fc0acd536..56570e7c9 100644 --- a/hilti/toolchain/include/ast/operators/error.h +++ b/hilti/toolchain/include/ast/operators/error.h @@ -7,6 +7,8 @@ namespace hilti::operator_ { // NOLINT(modernize-concat-nested-namespaces) +STANDARD_KEYWORD_CTOR(error, Ctor, "error", type::Error(), type::String(), "Creates an error with the given message."); + BEGIN_METHOD(error, Description) const auto& signature() const { static auto _signature = Signature{.self = type::Error(), diff --git a/hilti/toolchain/include/ast/operators/signed-integer.h b/hilti/toolchain/include/ast/operators/signed-integer.h index a08bf17ee..a5230f660 100644 --- a/hilti/toolchain/include/ast/operators/signed-integer.h +++ b/hilti/toolchain/include/ast/operators/signed-integer.h @@ -114,4 +114,21 @@ STANDARD_OPERATOR_2x(signed_integer, CastToInterval, Cast, type::Interval(), typ STANDARD_OPERATOR_2x(signed_integer, CastToBool, Cast, type::Bool(), type::SignedInteger(type::Wildcard()), type::Type_(type::Bool()), "Converts the value to a boolean by comparing against zero"); +STANDARD_KEYWORD_CTOR(signed_integer, CtorSigned8, "int8", type::SignedInteger(8), + type::SignedInteger(type::Wildcard()), "Creates a 8-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorSigned16, "int16", type::SignedInteger(16), + type::SignedInteger(type::Wildcard()), "Creates a 16-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorSigned32, "int32", type::SignedInteger(32), + type::SignedInteger(type::Wildcard()), "Creates a 32-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorSigned64, "int64", type::SignedInteger(64), + type::SignedInteger(type::Wildcard()), "Creates a 64-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorUnsigned8, "int8", type::SignedInteger(8), + type::UnsignedInteger(type::Wildcard()), "Creates a 8-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorUnsigned16, "int16", type::SignedInteger(16), + type::UnsignedInteger(type::Wildcard()), "Creates a 16-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorUnsigned32, "int32", type::SignedInteger(32), + type::UnsignedInteger(type::Wildcard()), "Creates a 32-bit signed integer value."); +STANDARD_KEYWORD_CTOR(signed_integer, CtorUnsigned64, "int64", type::SignedInteger(64), + type::UnsignedInteger(type::Wildcard()), "Creates a 64-bit signed integer value."); + } // namespace hilti::operator_ diff --git a/hilti/toolchain/include/ast/operators/stream.h b/hilti/toolchain/include/ast/operators/stream.h index 8f764b5aa..4e0e517cb 100644 --- a/hilti/toolchain/include/ast/operators/stream.h +++ b/hilti/toolchain/include/ast/operators/stream.h @@ -55,6 +55,9 @@ STANDARD_OPERATOR_2(stream::iterator, Sum, type::stream::Iterator(), type::const STANDARD_OPERATOR_2(stream::iterator, SumAssign, type::stream::Iterator(), type::stream::Iterator(), type::UnsignedInteger(64), "Advances the iterator by the given number of stream.") +STANDARD_KEYWORD_CTOR(stream, Ctor, "stream", type::Stream(), type::constant(type::Bytes()), + "Creates a stream instance preinitialized with the given data."); + BEGIN_METHOD(stream::iterator, Offset) const auto& signature() const { static auto _signature = Signature{.self = type::constant(type::stream::Iterator()), diff --git a/hilti/toolchain/include/ast/operators/time.h b/hilti/toolchain/include/ast/operators/time.h index ebf27c1c6..036a8b387 100644 --- a/hilti/toolchain/include/ast/operators/time.h +++ b/hilti/toolchain/include/ast/operators/time.h @@ -26,6 +26,17 @@ STANDARD_OPERATOR_2(time, GreaterEqual, type::Bool(), type::Time(), type::Time() STANDARD_OPERATOR_2(time, Lower, type::Bool(), type::Time(), type::Time(), "Compares the times."); STANDARD_OPERATOR_2(time, LowerEqual, type::Bool(), type::Time(), type::Time(), "Compares the times."); +STANDARD_KEYWORD_CTOR(time, CtorSignedIntegerNs, "time_ns", type::Time(), type::SignedInteger(type::Wildcard()), + "Creates an time interpreting the argument as number of nanoseconds."); +STANDARD_KEYWORD_CTOR(time, CtorSignedIntegerSecs, "time", type::Time(), type::SignedInteger(type::Wildcard()), + "Creates an time interpreting the argument as number of seconds."); +STANDARD_KEYWORD_CTOR(time, CtorUnsignedIntegerNs, "time_ns", type::Time(), type::UnsignedInteger(type::Wildcard()), + "Creates an time interpreting the argument as number of nanoseconds."); +STANDARD_KEYWORD_CTOR(time, CtorUnsignedIntegerSecs, "time", type::Time(), type::UnsignedInteger(type::Wildcard()), + "Creates an time interpreting the argument as number of seconds."); +STANDARD_KEYWORD_CTOR(time, CtorRealSecs, "time", type::Time(), type::Real(), + "Creates an time interpreting the argument as number of seconds."); + BEGIN_METHOD(time, Seconds) const auto& signature() const { static auto _signature = diff --git a/hilti/toolchain/include/ast/operators/unsigned-integer.h b/hilti/toolchain/include/ast/operators/unsigned-integer.h index f4067bb4f..5773d5f72 100644 --- a/hilti/toolchain/include/ast/operators/unsigned-integer.h +++ b/hilti/toolchain/include/ast/operators/unsigned-integer.h @@ -127,4 +127,22 @@ STANDARD_OPERATOR_2x(unsigned_integer, CastToInterval, Cast, type::Interval(), t type::Type_(type::Interval()), "Interprets the value as number of seconds."); STANDARD_OPERATOR_2x(unsigned_integer, CastToBool, Cast, type::Bool(), type::SignedInteger(type::Wildcard()), type::Type_(type::Bool()), "Converts the value to a boolean by comparing against zero"); + +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorSigned8, "uint8", type::UnsignedInteger(8), + type::SignedInteger(type::Wildcard()), "Creates a 8-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorSigned16, "uint16", type::UnsignedInteger(16), + type::SignedInteger(type::Wildcard()), "Creates a 16-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorSigned32, "uint32", type::UnsignedInteger(32), + type::SignedInteger(type::Wildcard()), "Creates a 32-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorSigned64, "uint64", type::UnsignedInteger(64), + type::SignedInteger(type::Wildcard()), "Creates a 64-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorUnsigned8, "uint8", type::UnsignedInteger(8), + type::UnsignedInteger(type::Wildcard()), "Creates a 8-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorUnsigned16, "uint16", type::UnsignedInteger(16), + type::UnsignedInteger(type::Wildcard()), "Creates a 16-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorUnsigned32, "uint32", type::UnsignedInteger(32), + type::UnsignedInteger(type::Wildcard()), "Creates a 32-bit unsigned integer value."); +STANDARD_KEYWORD_CTOR(unsigned_integer, CtorUnsigned64, "uint64", type::UnsignedInteger(64), + type::UnsignedInteger(type::Wildcard()), "Creates a 64-bit unsigned integer value."); + } // namespace hilti::operator_ diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index b1aa07f85..fd4434ebe 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -274,7 +274,15 @@ struct Visitor : hilti::visitor::PreOrder { return fmt("::hilti::rt::enum_::has_label(%s, %s)", op0(n), cg->typeInfo(n.op0().type())); } + // Error + + result_t operator()(const operator_::error::Ctor& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::result::Error(%s)", args[0]); + } + // Exception + result_t operator()(const operator_::exception::Ctor& n) { std::string type; @@ -651,6 +659,11 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::stream::SumAssignView& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } result_t operator()(const operator_::stream::SumAssignBytes& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::stream::Ctor& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Stream(%s)", args[0]); + } + result_t operator()(const operator_::stream::Freeze& n) { auto [self, args] = methodArguments(n); return fmt("%s.freeze()", self); @@ -874,6 +887,46 @@ struct Visitor : hilti::visitor::PreOrder { return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); } + result_t operator()(const operator_::signed_integer::CtorSigned8& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorSigned16& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorSigned32& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorSigned64& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorUnsigned8& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorUnsigned16& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorUnsigned32& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::signed_integer::CtorUnsigned64& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + // Time result_t operator()(const operator_::time::DifferenceInterval& n) { return binary(n, "-"); } @@ -888,6 +941,31 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::time::SumInterval& n) { return binary(n, "+"); } result_t operator()(const operator_::time::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::time::CtorSignedIntegerSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Time(%s, hilti::rt::Time::SecondTag())", args[0]); + } + + result_t operator()(const operator_::time::CtorSignedIntegerNs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Time(%s, hilti::rt::Time::NanosecondTag())", args[0]); + } + + result_t operator()(const operator_::time::CtorUnsignedIntegerSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Time(%s, hilti::rt::Time::SecondTag())", args[0]); + } + + result_t operator()(const operator_::time::CtorUnsignedIntegerNs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Time(%s, hilti::rt::Time::NanosecondTag())", args[0]); + } + + result_t operator()(const operator_::time::CtorRealSecs& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("::hilti::rt::Time(%f, hilti::rt::Time::SecondTag())", args[0]); + } + // Tuple result_t operator()(const operator_::tuple::CustomAssign& n) { @@ -980,6 +1058,46 @@ struct Visitor : hilti::visitor::PreOrder { return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); } + result_t operator()(const operator_::unsigned_integer::CtorSigned8& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorSigned16& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorSigned32& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorSigned64& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorUnsigned8& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorUnsigned16& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorUnsigned32& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + + result_t operator()(const operator_::unsigned_integer::CtorUnsigned64& n) { + auto args = tupleArguments(n, n.op1()); + return fmt("static_cast(%s)", args[0]); + } + // Vector result_t operator()(const operator_::vector::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } result_t operator()(const operator_::vector::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index aec88c8d0..8356ec002 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -33,8 +33,8 @@ namespace hilti { namespace detail { class Parser; } } %verbose %glr-parser -%expect 115 -%expect-rr 191 +%expect 113 +%expect-rr 205 %{ @@ -164,6 +164,10 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token INIT "init" %token INOUT "inout" %token INT "int" +%token INT16 "int16" +%token INT32 "int32" +%token INT64 "int64" +%token INT8 "int8" %token INTERVAL "interval" %token INTERVAL_NS "interval_ns" %token IOSRC "iosrc" @@ -209,6 +213,7 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token STRUCT "struct" %token SWITCH "switch" %token TIME "time" +%token TIME_NS "time_ns" %token TIMER "timer" %token TIMERMGR "timer_mgr" %token TIMESASSIGN "*=" @@ -219,6 +224,10 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token TYPE "type" %token TYPEINFO "typeinfo" %token UINT "uint" +%token UINT16 "uint16" +%token UINT32 "uint32" +%token UINT64 "uint64" +%token UINT8 "uint8" %token UNION "union" %token UNPACK "unpack" %token UNSET "unset" @@ -270,9 +279,6 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %type , std::vector>> global_scope_items -%type const_real -%type const_uint - %% %start module; @@ -808,35 +814,22 @@ member_expr : local_id { $$ = hilti::expression::Membe ctor : CBOOL { $$ = hilti::ctor::Bool($1, __loc__); } | CBYTES { $$ = hilti::ctor::Bytes(std::move($1), __loc__); } | CSTRING { $$ = hilti::ctor::String($1, __loc__); } - | const_real { $$ = hilti::ctor::Real($1, __loc__); } | CUINTEGER { $$ = hilti::ctor::UnsignedInteger($1, 64, __loc__); } | '+' CUINTEGER { $$ = hilti::ctor::SignedInteger(check_int64_range($2, true, __loc__), 64, __loc__); } | '-' CUINTEGER { $$ = hilti::ctor::SignedInteger(-check_int64_range($2, false, __loc__), 64, __loc__); } + | CUREAL { $$ = hilti::ctor::Real($1, __loc__); } + | '+' CUREAL { $$ = hilti::ctor::Real($2, __loc__);; } + | '-' CUREAL { $$ = hilti::ctor::Real(-$2, __loc__);; } | CNULL { $$ = hilti::ctor::Null(__loc__); } | CADDRESS { $$ = hilti::ctor::Address(hilti::ctor::Address::Value($1), __loc__); } | CADDRESS '/' CUINTEGER { $$ = hilti::ctor::Network(hilti::ctor::Network::Value($1, $3), __loc__); } | CPORT { $$ = hilti::ctor::Port(hilti::ctor::Port::Value($1), __loc__); } - | TIME '(' const_real ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } - } - | TIME '(' const_uint ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } - } - | STREAM '(' CBYTES ')' { $$ = hilti::ctor::Stream(std::move($3), __loc__); } - | ERROR '(' CSTRING ')' { $$ = hilti::ctor::Error(std::move($3), __loc__); } | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } | DEFAULT type_param_begin type type_param_end '(' opt_exprs ')' { $$ = hilti::ctor::Default(std::move($3), std::move($6), __loc__); } - | UINT '<' CUINTEGER '>' '(' CUINTEGER ')' - { $$ = hilti::ctor::UnsignedInteger($6, $3, __loc__); } - | INT '<' CUINTEGER '>' '(' CUINTEGER ')' - { $$ = hilti::ctor::SignedInteger(check_int64_range($6, true, __loc__), $3, __loc__); } - | INT '<' CUINTEGER '>' '(' '-' CUINTEGER ')' - { $$ = hilti::ctor::SignedInteger(-check_int64_range($7, false, __loc__), $3, __loc__); } - | list { $$ = std::move($1); } | map { $$ = std::move($1); } | regexp { $$ = std::move($1); } @@ -845,16 +838,21 @@ ctor : CBOOL { $$ = hilti::ctor::Bool($1, __ | tuple { $$ = std::move($1); } ; -ctor_expr : INTERVAL '(' expr ')' { $$ = builder::namedCtor("interval", { std::move($3) }, __loc__); } - | INTERVAL_NS '(' expr ')' { $$ = builder::namedCtor("interval_ns", { std::move($3) }, __loc__); } - -const_real : CUREAL { $$ = $1; } - | '+' CUREAL { $$ = $2; } - | '-' CUREAL { $$ = -$2; } - -const_uint : CUINTEGER { $$ = $1; } - | '+' CUINTEGER { $$ = $2; } - +ctor_expr : INTERVAL '(' expr ')' { $$ = hilti::builder::namedCtor("interval", { std::move($3) }, __loc__); } + | INTERVAL_NS '(' expr ')' { $$ = hilti::builder::namedCtor("interval_ns", { std::move($3) }, __loc__); } + | TIME '(' expr ')' { $$ = hilti::builder::namedCtor("time", { std::move($3) }, __loc__); } + | TIME_NS '(' expr ')' { $$ = hilti::builder::namedCtor("time_ns", { std::move($3) }, __loc__); } + | STREAM '(' expr ')' { $$ = hilti::builder::namedCtor("stream", { std::move($3) }, __loc__); } + | ERROR '(' expr ')' { $$ = hilti::builder::namedCtor("error", { std::move($3) }, __loc__); } + | INT8 '(' expr ')' { $$ = hilti::builder::namedCtor("int8", { std::move($3) }, __loc__); } + | INT16 '(' expr ')' { $$ = hilti::builder::namedCtor("int16", { std::move($3) }, __loc__); } + | INT32 '(' expr ')' { $$ = hilti::builder::namedCtor("int32", { std::move($3) }, __loc__); } + | INT64 '(' expr ')' { $$ = hilti::builder::namedCtor("int64", { std::move($3) }, __loc__); } + | UINT8 '(' expr ')' { $$ = hilti::builder::namedCtor("uint8", { std::move($3) }, __loc__); } + | UINT16 '(' expr ')' { $$ = hilti::builder::namedCtor("uint16", { std::move($3) }, __loc__); } + | UINT32 '(' expr ')' { $$ = hilti::builder::namedCtor("uint32", { std::move($3) }, __loc__); } + | UINT64 '(' expr ')' { $$ = hilti::builder::namedCtor("uint64", { std::move($3) }, __loc__); } + ; tuple : '(' opt_tuple_elems1 ')' { $$ = hilti::ctor::Tuple(std::move($2), __loc__); } diff --git a/hilti/toolchain/src/compiler/parser/scanner.ll b/hilti/toolchain/src/compiler/parser/scanner.ll index 479a13c74..205068a7c 100644 --- a/hilti/toolchain/src/compiler/parser/scanner.ll +++ b/hilti/toolchain/src/compiler/parser/scanner.ll @@ -112,6 +112,10 @@ hook return token::HOOK; if return token::IF; import return token::IMPORT; in return token::IN; +int16 return token::INT16; +int32 return token::INT32; +int64 return token::INT64; +int8 return token::INT8; !in return token::NOT_IN; init return token::INIT; inout return token::INOUT; @@ -145,11 +149,16 @@ struct return token::STRUCT; switch return token::SWITCH; throw return token::THROW; time return token::TIME; +time_ns return token::TIME_NS; try return token::TRY; tuple return token::TUPLE; type return token::TYPE; typeinfo return token::TYPEINFO; uint return token::UINT; +uint16 return token::UINT16; +uint32 return token::UINT32; +uint64 return token::UINT64; +uint8 return token::UINT8; union return token::UNION; unpack return token::UNPACK; unset return token::UNSET; diff --git a/hilti/toolchain/src/compiler/visitors/normalizer.cc b/hilti/toolchain/src/compiler/visitors/normalizer.cc index 57e569800..6e5a8862f 100644 --- a/hilti/toolchain/src/compiler/visitors/normalizer.cc +++ b/hilti/toolchain/src/compiler/visitors/normalizer.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -69,14 +70,33 @@ struct VisitorNormalizer : public visitor::PreOrder { try { auto i = cb(*ctor); logChange(p.node, i); - p.node = expression::Ctor(std::move(i)); + p.node = expression::Ctor(std::move(i), p.node.location()); modified = true; } catch ( hilti::rt::RuntimeError& e ) { - p.node.addError(e.what()); + logger().error(e.what(), p.node.location()); } } } + // Helper to cast an uint64 to int64, with range check. + int64_t to_int64(uint64_t x, position_t& p) { + if ( x > std::numeric_limits::max() ) + logger().error("signed integer value out of range", p.node.location()); + + return static_cast(x); + } + + // Overload that doesn't need to do any checking. + int64_t to_int64(int64_t x, position_t& p) { return x; } + + // Helper to cast an int64 to uint64, with range check. + uint64_t to_uint64(int64_t x, position_t& p) { + if ( x < 0 ) + logger().error("unsigned integer value out of range", p.node.location()); + + return static_cast(x); + } + void operator()(const declaration::Function& u, position_t p) { if ( u.linkage() == declaration::Linkage::Struct ) { // Link method implementations to their parent type. @@ -158,6 +178,10 @@ struct VisitorNormalizer : public visitor::PreOrder { } } + void operator()(const operator_::error::Ctor& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { return ctor::Error(ctor.value()); }); + } + void operator()(const operator_::interval::CtorSignedIntegerSecs& op, position_t p) { tryReplaceCtorExpression(op, p, [](const auto& ctor) { return ctor::Interval(ctor::Interval::Value(ctor.value(), hilti::rt::Interval::SecondTag())); @@ -188,6 +212,136 @@ struct VisitorNormalizer : public visitor::PreOrder { }); } + void operator()(const operator_::signed_integer::CtorSigned8& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 8); + }); + } + + void operator()(const operator_::signed_integer::CtorSigned16& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 16); + }); + } + + void operator()(const operator_::signed_integer::CtorSigned32& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 32); + }); + } + + void operator()(const operator_::signed_integer::CtorSigned64& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 64); + }); + } + + void operator()(const operator_::signed_integer::CtorUnsigned8& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 8); + }); + } + + void operator()(const operator_::signed_integer::CtorUnsigned16& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 16); + }); + } + + void operator()(const operator_::signed_integer::CtorUnsigned32& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 32); + }); + } + + void operator()(const operator_::signed_integer::CtorUnsigned64& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::SignedInteger(to_int64(ctor.value(), p), 64); + }); + } + + void operator()(const operator_::time::CtorSignedIntegerSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Time(ctor::Time::Value(ctor.value(), hilti::rt::Time::SecondTag())); + }); + } + + void operator()(const operator_::time::CtorUnsignedIntegerSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Time(ctor::Time::Value(ctor.value(), hilti::rt::Time::SecondTag())); + }); + } + + void operator()(const operator_::stream::Ctor& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { return ctor::Stream(ctor.value()); }); + } + + void operator()(const operator_::time::CtorSignedIntegerNs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Time(ctor::Time::Value(ctor.value(), hilti::rt::Time::NanosecondTag())); + }); + } + + void operator()(const operator_::time::CtorUnsignedIntegerNs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Time(ctor::Time::Value(ctor.value(), hilti::rt::Time::NanosecondTag())); + }); + } + + void operator()(const operator_::time::CtorRealSecs& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::Time(ctor::Time::Value(ctor.value(), hilti::rt::Time::SecondTag())); + }); + } + + void operator()(const operator_::unsigned_integer::CtorSigned8& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::UnsignedInteger(to_uint64(ctor.value(), p), 8); + }); + } + + void operator()(const operator_::unsigned_integer::CtorSigned16& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::UnsignedInteger(to_uint64(ctor.value(), p), 16); + }); + } + + void operator()(const operator_::unsigned_integer::CtorSigned32& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::UnsignedInteger(to_uint64(ctor.value(), p), 32); + }); + } + + void operator()(const operator_::unsigned_integer::CtorSigned64& op, position_t p) { + tryReplaceCtorExpression(op, p, [this, &p](const auto& ctor) { + return ctor::UnsignedInteger(to_uint64(ctor.value(), p), 64); + }); + } + + void operator()(const operator_::unsigned_integer::CtorUnsigned8& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::UnsignedInteger(ctor.value(), 8); + }); + } + + void operator()(const operator_::unsigned_integer::CtorUnsigned16& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::UnsignedInteger(ctor.value(), 16); + }); + } + + void operator()(const operator_::unsigned_integer::CtorUnsigned32& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::UnsignedInteger(ctor.value(), 32); + }); + } + + void operator()(const operator_::unsigned_integer::CtorUnsigned64& op, position_t p) { + tryReplaceCtorExpression(op, p, [](const auto& ctor) { + return ctor::UnsignedInteger(ctor.value(), 64); + }); + } + void operator()(const statement::If& n, position_t p) { if ( n.init() && ! n.condition() ) { auto cond = expression::UnresolvedID(n.init()->id()); diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index 43e848499..7fd0c0588 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -34,7 +34,7 @@ namespace spicy { namespace detail { class Parser; } } %verbose %glr-parser -%expect 129 +%expect 126 %expect-rr 164 %{ @@ -239,8 +239,7 @@ static int _field_width = 0; %type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl hook_decl struct_field %type > struct_fields %type base_type_no_ref base_type type tuple_type struct_type enum_type unit_type bitfield_type reference_type -%type ctor tuple struct_ regexp list vector map set -%type expr tuple_elem member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type ctor tuple struct_ regexp list vector map set unit_field_ctor %type expr tuple_elem tuple_expr member_expr ctor_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g %type > opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_unit_field_args opt_unit_field_sinks %type > opt_else_block @@ -274,11 +273,6 @@ static int _field_width = 0; %type , std::vector>> global_scope_items -%type const_real -%type const_uint -%type const_sint - - // Spicy-only %type > opt_unit_field_id %type opt_unit_field_engine opt_hook_engine @@ -289,6 +283,9 @@ static int _field_width = 0; %type unit_switch_case %type > unit_switch_cases +%type const_sint +%type const_uint + %% // Magic states sent by the scanner to provide two separate entry points. @@ -653,7 +650,7 @@ enum_label : local_id { $$ = hilti::type::enum_::Labe | local_id '=' CUINTEGER { $$ = hilti::type::enum_::Label(std::move($1), $3, __loc__); } ; -bitfield_type : BITFIELD '(' const_uint ')' +bitfield_type : BITFIELD '(' CUINTEGER ')' { _field_width = $3; } '{' opt_bitfield_bits '}' { $$ = spicy::type::Bitfield($3, $7, __loc__); } @@ -669,9 +666,9 @@ bitfield_bits | bitfield_bits_spec { $$ = std::vector(); $$.push_back(std::move($1)); } bitfield_bits_spec - : local_id ':' const_uint DOTDOT const_uint opt_attributes ';' + : local_id ':' CUINTEGER DOTDOT CUINTEGER opt_attributes ';' { $$ = spicy::type::bitfield::Bits(std::move($1), $3, $5, _field_width, std::move($6), __loc__); } - | local_id ':' const_uint opt_attributes ';' + | local_id ':' CUINTEGER opt_attributes ';' { $$ = spicy::type::bitfield::Bits(std::move($1), $3, $3, _field_width, std::move($4), __loc__); } /* --- Begin of Spicy units --- */ @@ -716,7 +713,7 @@ unit_field : opt_unit_field_id opt_unit_field_engine base_type opt_unit_fiel $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($3), std::move($2), {}, std::move($4), std::move($7), std::move($5), std::move($6), std::move($8), __loc__); } - | opt_unit_field_id opt_unit_field_engine ctor opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks + | opt_unit_field_id opt_unit_field_engine unit_field_ctor opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($3), std::move($2), {}, std::move($4), std::move($7), std::move($5), std::move($6), std::move($8), __loc__); } | opt_unit_field_id opt_unit_field_engine scoped_id opt_unit_field_args opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks @@ -725,6 +722,25 @@ unit_field : opt_unit_field_id opt_unit_field_engine base_type opt_unit_fiel | opt_unit_field_id opt_unit_field_engine '(' unit_field_in_container ')' opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($4), std::move($2), {}, std::move($6), std::move($9), std::move($7), std::move($8), std::move($10), __loc__); } + +const_sint : CUINTEGER { $$ = check_int64_range($1, true, __loc__); } + | '+' CUINTEGER { $$ = check_int64_range($2, true, __loc__); } + | '-' CUINTEGER { $$ = -check_int64_range($2, false, __loc__); } + +const_uint : CUINTEGER { $$ = $1; } + | '+' CUINTEGER { $$ = $2; } + +unit_field_ctor + : ctor + | UINT8 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 8, __loc__); } + | UINT16 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 16, __loc__); } + | UINT32 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 32, __loc__); } + | UINT64 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 64, __loc__); } + | INT8 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 8, __loc__); } + | INT16 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 16, __loc__); } + | INT32 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 32, __loc__); } + | INT64 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 64, __loc__); } + unit_field_in_container : ctor opt_unit_field_args opt_attributes { $$ = spicy::type::unit::item::UnresolvedField({}, std::move($1), {}, std::move($2), {}, {}, std::move($3), {}, {}, __loc__); } @@ -945,29 +961,14 @@ ctor : CADDRESS { $$ = hilti::ctor::Address(hil | CPORT { $$ = hilti::ctor::Port(hilti::ctor::Port::Value($1), __loc__); } | CNULL { $$ = hilti::ctor::Null(__loc__); } | CSTRING { $$ = hilti::ctor::String($1, __loc__); } - - | const_real { $$ = hilti::ctor::Real($1, __loc__); } | CUINTEGER { $$ = hilti::ctor::UnsignedInteger($1, 64, __loc__); } | '+' CUINTEGER { $$ = hilti::ctor::SignedInteger(check_int64_range($2, true, __loc__), 64, __loc__); } | '-' CUINTEGER { $$ = hilti::ctor::SignedInteger(-check_int64_range($2, false, __loc__), 64, __loc__); } - | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } - | TIME '(' const_real ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } - } - | TIME '(' const_uint ')' { try { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::SecondTag()), __loc__); } - catch ( hilti::rt::OutOfRange& e ) { $$ = hilti::ctor::Time(hilti::ctor::Time::Value()); error(@$, e.what()); } - } - | TIME_NS '(' const_uint ')' { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3, hilti::rt::Time::NanosecondTag()), __loc__); } - | STREAM '(' CBYTES ')' { $$ = hilti::ctor::Stream(std::move($3), __loc__); } + | CUREAL { $$ = hilti::ctor::Real($1, __loc__); } + | '+' CUREAL { $$ = hilti::ctor::Real($2, __loc__);; } + | '-' CUREAL { $$ = hilti::ctor::Real(-$2, __loc__);; } - | UINT8 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 8, __loc__); } - | UINT16 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 16, __loc__); } - | UINT32 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 32, __loc__); } - | UINT64 '(' const_uint ')' { $$ = hilti::ctor::UnsignedInteger($3, 64, __loc__); } - | INT8 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 8, __loc__); } - | INT16 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 16, __loc__); } - | INT32 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 32, __loc__); } - | INT64 '(' const_sint ')' { $$ = hilti::ctor::SignedInteger($3, 64, __loc__); } + | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } | list { $$ = std::move($1); } | map { $$ = std::move($1); } @@ -980,17 +981,18 @@ ctor : CADDRESS { $$ = hilti::ctor::Address(hil ctor_expr : INTERVAL '(' expr ')' { $$ = hilti::builder::namedCtor("interval", { std::move($3) }, __loc__); } | INTERVAL_NS '(' expr ')' { $$ = hilti::builder::namedCtor("interval_ns", { std::move($3) }, __loc__); } - -const_real : CUREAL { $$ = $1; } - | '+' CUREAL { $$ = $2; } - | '-' CUREAL { $$ = -$2; } - -const_sint : CUINTEGER { $$ = check_int64_range($1, true, __loc__); } - | '+' CUINTEGER { $$ = check_int64_range($2, true, __loc__); } - | '-' CUINTEGER { $$ = -check_int64_range($2, false, __loc__); } - -const_uint : CUINTEGER { $$ = $1; } - | '+' CUINTEGER { $$ = $2; } + | TIME '(' expr ')' { $$ = hilti::builder::namedCtor("time", { std::move($3) }, __loc__); } + | TIME_NS '(' expr ')' { $$ = hilti::builder::namedCtor("time_ns", { std::move($3) }, __loc__); } + | STREAM '(' expr ')' { $$ = hilti::builder::namedCtor("stream", { std::move($3) }, __loc__); } + | INT8 '(' expr ')' { $$ = hilti::builder::namedCtor("int8", { std::move($3) }, __loc__); } + | INT16 '(' expr ')' { $$ = hilti::builder::namedCtor("int16", { std::move($3) }, __loc__); } + | INT32 '(' expr ')' { $$ = hilti::builder::namedCtor("int32", { std::move($3) }, __loc__); } + | INT64 '(' expr ')' { $$ = hilti::builder::namedCtor("int64", { std::move($3) }, __loc__); } + | UINT8 '(' expr ')' { $$ = hilti::builder::namedCtor("uint8", { std::move($3) }, __loc__); } + | UINT16 '(' expr ')' { $$ = hilti::builder::namedCtor("uint16", { std::move($3) }, __loc__); } + | UINT32 '(' expr ')' { $$ = hilti::builder::namedCtor("uint32", { std::move($3) }, __loc__); } + | UINT64 '(' expr ')' { $$ = hilti::builder::namedCtor("uint64", { std::move($3) }, __loc__); } + ; tuple : '(' opt_tuple_elems1 ')' { $$ = hilti::ctor::Tuple(std::move($2), __loc__); } | TUPLE '(' opt_exprs ')' { $$ = hilti::ctor::Tuple(std::move($3), __loc__); } diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-10/output b/tests/Baseline/spicy.types.integer.ctor-fail-10/output new file mode 100644 index 000000000..880fbf4f8 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-10/output @@ -0,0 +1,5 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/ctor-fail.spicy:3:16: integer value out of range for type +[error] <...>/ctor-fail.spicy:4:17: integer value out of range for type +[error] <...>/ctor-fail.spicy:5:17: integer value out of range for type +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-2/output b/tests/Baseline/spicy.types.integer.ctor-fail-2/output index ca4ed0d35..3a42ba3a4 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-2/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-2/output @@ -5,5 +5,5 @@ [error] <...>/ctor-fail.spicy:1:38: invalid character [error] <...>/ctor-fail.spicy:1:38: invalid character [error] <...>/ctor-fail.spicy:1:38: invalid character -[error] <...>/ctor-fail.spicy:1:38: syntax error, unexpected ')', expecting unsigned integer value or '+' +[error] <...>/ctor-fail.spicy:1:38: syntax error, unexpected ')' [error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-3/output b/tests/Baseline/spicy.types.integer.ctor-fail-3/output index 68a9492d5..3b67c73dd 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-3/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-3/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.spicy:1:34: syntax error, unexpected '-', expecting unsigned integer value or '+' -[error] spicyc: parse error +[error] <...>/ctor-fail.spicy:1:28: unsigned integer value out of range +[error] spicyc: errors encountered during normalizing diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-4/output b/tests/Baseline/spicy.types.integer.ctor-fail-4/output index f4e27962c..240ab6989 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-4/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-4/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.spicy:1:36: syntax error, unexpected '-', expecting unsigned integer value or '+' -[error] spicyc: parse error +[error] <...>/ctor-fail.spicy:1:29: unsigned integer value out of range +[error] spicyc: errors encountered during normalizing diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-5/output b/tests/Baseline/spicy.types.integer.ctor-fail-5/output index 68a9492d5..2a2afe307 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-5/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-5/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.spicy:1:34: syntax error, unexpected '-', expecting unsigned integer value or '+' -[error] spicyc: parse error +[error] <...>/ctor-fail.spicy:1:27: unsigned integer value out of range +[error] spicyc: errors encountered during normalizing diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-6/output b/tests/Baseline/spicy.types.integer.ctor-fail-6/output index f4e27962c..240ab6989 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-6/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-6/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.spicy:1:36: syntax error, unexpected '-', expecting unsigned integer value or '+' -[error] spicyc: parse error +[error] <...>/ctor-fail.spicy:1:29: unsigned integer value out of range +[error] spicyc: errors encountered during normalizing diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-8/output b/tests/Baseline/spicy.types.integer.ctor-fail-8/output index 2cc7999b6..273a428d1 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail-8/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail-8/output @@ -1,4 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.spicy:5:23: signed integer value out of range -[error] <...>/ctor-fail.spicy:6:23: signed integer value out of range -[error] spicyc: parse error +[error] <...>/ctor-fail.spicy:4:17: signed integer value out of range +[error] spicyc: errors encountered during normalizing diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-9/output b/tests/Baseline/spicy.types.integer.ctor-fail-9/output new file mode 100644 index 000000000..610d73d4e --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-9/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/ctor-fail.spicy:3:23: signed integer value out of range +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail/output b/tests/Baseline/spicy.types.integer.ctor-fail/output index 6d7d2f8db..0e493c71c 100644 --- a/tests/Baseline/spicy.types.integer.ctor-fail/output +++ b/tests/Baseline/spicy.types.integer.ctor-fail/output @@ -5,7 +5,4 @@ [error] <...>/ctor-fail.spicy:13:16: integer value out of range for type [error] <...>/ctor-fail.spicy:14:17: integer value out of range for type [error] <...>/ctor-fail.spicy:15:17: integer value out of range for type -[error] <...>/ctor-fail.spicy:18:16: integer value out of range for type -[error] <...>/ctor-fail.spicy:19:17: integer value out of range for type -[error] <...>/ctor-fail.spicy:20:17: integer value out of range for type [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.interval.ctor-out-of-range/output b/tests/Baseline/spicy.types.interval.ctor-out-of-range/output index 78c4be0c9..9806eadb9 100644 --- a/tests/Baseline/spicy.types.interval.ctor-out-of-range/output +++ b/tests/Baseline/spicy.types.interval.ctor-out-of-range/output @@ -1,4 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/ctor-out-of-range.spicy:7:12: value cannot be represented as an interval [error] <...>/ctor-out-of-range.spicy:8:12: value cannot be represented as an interval -[error] spicyc: aborting after errors +[error] spicyc: errors encountered during normalizing diff --git a/tests/hilti/types/integer/cast.hlt b/tests/hilti/types/integer/cast.hlt index 1cc7603a3..bf262d50b 100644 --- a/tests/hilti/types/integer/cast.hlt +++ b/tests/hilti/types/integer/cast.hlt @@ -12,7 +12,7 @@ i = cast>(j); assert j == 32; assert i == 32; -global auto x = int<8>(5); +global auto x = int8(5); global int<8> y = 5; assert x == y; } diff --git a/tests/hilti/types/integer/operators-signed.hlt b/tests/hilti/types/integer/operators-signed.hlt index 2914829c3..71fea36b4 100644 --- a/tests/hilti/types/integer/operators-signed.hlt +++ b/tests/hilti/types/integer/operators-signed.hlt @@ -53,4 +53,16 @@ assert i == -30; i /= 3; assert i == -10; +global uint<64> u = 10; +assert int8(20) == int8(2 * u); +assert int16(20) == int16(2 * u); +assert int32(20) == int32(2 * u); +assert int64(20) == int64(2 * u); + +global int<64> s = 10; +assert int8(20) == int8(2 * s); +assert int16(20) == int16(2 * s); +assert int32(20) == int32(2 * s); +assert int64(20) == int64(2 * s); + } diff --git a/tests/hilti/types/integer/operators-unsigned.hlt b/tests/hilti/types/integer/operators-unsigned.hlt index a9be9d642..01d466ec5 100644 --- a/tests/hilti/types/integer/operators-unsigned.hlt +++ b/tests/hilti/types/integer/operators-unsigned.hlt @@ -58,4 +58,16 @@ assert i == 30; i /= 3; assert i == 10; +global uint<64> u = 10; +assert uint8(20) == uint8(2 * u); +assert uint16(20) == uint16(2 * u); +assert uint32(20) == uint32(2 * u); +assert uint64(20) == uint64(2 * u); + +global uint<64> s = 10; +assert uint8(20) == uint8(2 * s); +assert uint16(20) == uint16(2 * s); +assert uint32(20) == uint32(2 * s); +assert uint64(20) == uint64(2 * s); + } diff --git a/tests/hilti/types/result/error.hlt b/tests/hilti/types/result/error.hlt index 9bb33f0f5..7d893c6c3 100644 --- a/tests/hilti/types/result/error.hlt +++ b/tests/hilti/types/result/error.hlt @@ -10,4 +10,7 @@ global auto e = error("test"); hilti::print(e); hilti::print(e.description()); +global s = " 2"; +assert error("test 2").description() == error("test" + s).description(); + } diff --git a/tests/hilti/types/stream/ops.hlt b/tests/hilti/types/stream/ops.hlt index 4c70592c4..a2bb15d68 100644 --- a/tests/hilti/types/stream/ops.hlt +++ b/tests/hilti/types/stream/ops.hlt @@ -55,4 +55,8 @@ function void p(stream x) { global stream x2 = stream(b"abc"); p(x2); +global auto q = b"def"; +global auto qq = stream(b"abc" + q); +assert |qq| == 6 && qq.starts_with(b"abcdef"); + } diff --git a/tests/hilti/types/time/ops.hlt b/tests/hilti/types/time/ops.hlt index 5331738bb..0be8d6da1 100644 --- a/tests/hilti/types/time/ops.hlt +++ b/tests/hilti/types/time/ops.hlt @@ -12,6 +12,7 @@ global i2 = interval(-110.5); hilti::print(t1); assert t1.seconds() == 1295415110.5; assert t1.nanoseconds() == cast>(1295415110.5 * 1e9); +assert time_ns(1295415110500000000) == t1; hilti::print(t1+i); hilti::print(time(1295418710)); @@ -31,4 +32,8 @@ assert t1 <= t1; assert cast