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 210cf460b..f6eed4838 100644 --- a/hilti/toolchain/include/ast/builder/expression.h +++ b/hilti/toolchain/include/ast/builder/expression.h @@ -341,5 +341,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 46e0d96a3..7767843d6 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 986a955ef..5d364cb74 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 187 +%expect-rr 189 %{ @@ -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" @@ -234,7 +235,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 @@ -269,7 +270,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 %% @@ -785,6 +785,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 ']' @@ -814,12 +815,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()); } } @@ -848,14 +843,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 b2efe0846..de4f9c0ed 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 ecc9b1358..7f67f1540 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 937827179..0aeeaf83f 100644 --- a/hilti/toolchain/src/compiler/visitors/printer.cc +++ b/hilti/toolchain/src/compiler/visitors/printer.cc @@ -799,7 +799,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 7f808d0df..9a2b3b493 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 160 %{ @@ -238,7 +238,7 @@ 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 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 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 @@ -911,6 +911,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 ']' @@ -945,13 +946,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()); } } @@ -979,6 +973,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; + }