Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "strucpp",
"version": "0.4.18",
"version": "0.4.19",
"description": "IEC 61131-3 Structured Text to C++ Compiler",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
336 changes: 60 additions & 276 deletions src/runtime/include/iec_date.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,303 +3,87 @@
// This file is part of the STruC++ Runtime Library and is covered by the
// STruC++ Runtime Library Exception. See COPYING.RUNTIME for details.
/**
* STruC++ Runtime - IEC Date Types
* STruC++ Runtime - IEC DATE Standard Functions
*
* This header provides value classes for IEC 61131-3 DATE and LDATE types.
* DATE represents calendar dates (stored as days since epoch 1970-01-01).
* LDATE is the IEC v3 long variant with the same precision.
* IEC 61131-3 standard functions on the DATE (and LDATE) calendar
* types. DATE / LDATE are stored as signed days since the Unix epoch
* (1970-01-01) in `IECVar<DATE_t>` — the same generic per-variable
* wrapper used for every other elementary type. Codegen emits DATE
* variables as `IEC_DATE` (the `IECVar<DATE_t>` alias) and the
* functions below take/return `IEC_DATE` so they're directly callable
* from generated POU code.
*
* Scope: only the standard arithmetic / comparison / round-trip
* functions. Calendar component accessors (YEAR, MONTH, DAY,
* DAY_OF_WEEK, DAY_OF_YEAR, …) are intentionally NOT here — those are
* OSCAT-style extensions and user libraries (OSCAT, codesys-v23
* stdlib imports, …) ship their own implementations. Providing them
* here would create overload ambiguity when a project imports such a
* library.
*
* Historical note: an earlier `DateValue<T>` + `IECDateVar<T>` value-
* class design lived here. Codegen never adopted it (DATE variables
* were always declared as `IEC_DATE`), so the parallel API was dead
* from generated code's perspective. Removed in favour of a single
* IECVar-based surface. See `iec_time.hpp` for the matching note on
* the TIME family.
*/

#pragma once

#include <cstdint>
#include "iec_types.hpp"
#include "iec_var.hpp"
#include "iec_traits.hpp"

namespace strucpp {

template<typename StorageType>
class DateValue {
public:
using storage_type = StorageType;

constexpr DateValue() noexcept : days_since_epoch_(0) {}
constexpr explicit DateValue(StorageType days) noexcept : days_since_epoch_(days) {}

static constexpr DateValue from_days(int64_t days) noexcept {
return DateValue(static_cast<StorageType>(days));
}

static constexpr DateValue from_ymd(int year, int month, int day) noexcept {
int a = (14 - month) / 12;
int y = year + 4800 - a;
int m = month + 12 * a - 3;
int jdn = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
constexpr int UNIX_EPOCH_JDN = 2440588;
return DateValue(static_cast<StorageType>(jdn - UNIX_EPOCH_JDN));
}

constexpr StorageType to_days() const noexcept {
return days_since_epoch_;
}

void to_ymd(int& year, int& month, int& day) const noexcept {
constexpr int UNIX_EPOCH_JDN = 2440588;
int jdn = static_cast<int>(days_since_epoch_) + UNIX_EPOCH_JDN;

int a = jdn + 32044;
int b = (4 * a + 3) / 146097;
int c = a - (146097 * b) / 4;
int d = (4 * c + 3) / 1461;
int e = c - (1461 * d) / 4;
int m = (5 * e + 2) / 153;

day = e - (153 * m + 2) / 5 + 1;
month = m + 3 - 12 * (m / 10);
year = 100 * b + d - 4800 + m / 10;
}

int year() const noexcept {
int y = 0, m = 0, d = 0;
to_ymd(y, m, d);
return y;
}

int month() const noexcept {
int y = 0, m = 0, d = 0;
to_ymd(y, m, d);
return m;
}

int day() const noexcept {
int y = 0, m = 0, d = 0;
to_ymd(y, m, d);
return d;
}

constexpr int day_of_week() const noexcept {
return static_cast<int>((days_since_epoch_ + 4) % 7);
}

int day_of_year() const noexcept {
int y = 0, m = 0, d = 0;
to_ymd(y, m, d);
DateValue jan1 = from_ymd(y, 1, 1);
return static_cast<int>(days_since_epoch_ - jan1.days_since_epoch_) + 1;
}

constexpr operator StorageType() const noexcept { return days_since_epoch_; }

constexpr DateValue operator+(int64_t days) const noexcept {
return DateValue(days_since_epoch_ + static_cast<StorageType>(days));
}

constexpr DateValue operator-(int64_t days) const noexcept {
return DateValue(days_since_epoch_ - static_cast<StorageType>(days));
}

constexpr int64_t operator-(const DateValue& other) const noexcept {
return days_since_epoch_ - other.days_since_epoch_;
}

DateValue& operator+=(int64_t days) noexcept {
days_since_epoch_ += static_cast<StorageType>(days);
return *this;
}

DateValue& operator-=(int64_t days) noexcept {
days_since_epoch_ -= static_cast<StorageType>(days);
return *this;
}

constexpr bool operator==(const DateValue& other) const noexcept {
return days_since_epoch_ == other.days_since_epoch_;
}

constexpr bool operator!=(const DateValue& other) const noexcept {
return days_since_epoch_ != other.days_since_epoch_;
}

constexpr bool operator<(const DateValue& other) const noexcept {
return days_since_epoch_ < other.days_since_epoch_;
}

constexpr bool operator<=(const DateValue& other) const noexcept {
return days_since_epoch_ <= other.days_since_epoch_;
}

constexpr bool operator>(const DateValue& other) const noexcept {
return days_since_epoch_ > other.days_since_epoch_;
}

constexpr bool operator>=(const DateValue& other) const noexcept {
return days_since_epoch_ >= other.days_since_epoch_;
}

private:
StorageType days_since_epoch_;
};

using IEC_DATE_Value = DateValue<DATE_t>;
using IEC_LDATE_Value = DateValue<LDATE_t>;

template<typename T>
class IECDateVar {
public:
using value_type = T;

IECDateVar() noexcept : value_{}, forced_{false}, forced_value_{} {}
explicit IECDateVar(T v) noexcept : value_{v}, forced_{false}, forced_value_{} {}
IECDateVar(const IECDateVar&) = default;
IECDateVar(IECDateVar&&) = default;
IECDateVar& operator=(const IECDateVar&) = default;
IECDateVar& operator=(IECDateVar&&) = default;

T get() const noexcept {
return forced_ ? forced_value_ : value_;
}

void set(T v) noexcept {
value_ = v;
}

T get_underlying() const noexcept {
return value_;
}

void force(T v) noexcept {
forced_ = true;
forced_value_ = v;
}

void unforce() noexcept {
forced_ = false;
}

bool is_forced() const noexcept {
return forced_;
}

T get_forced_value() const noexcept {
return forced_value_;
}

operator T() const noexcept {
return get();
}

IECDateVar& operator=(T v) noexcept {
set(v);
return *this;
}

IECDateVar& operator+=(int64_t days) noexcept {
set(get() + days);
return *this;
}

IECDateVar& operator-=(int64_t days) noexcept {
set(get() - days);
return *this;
}

private:
T value_;
bool forced_;
T forced_value_;
};

using IEC_DATE_Var = IECDateVar<IEC_DATE_Value>;
using IEC_LDATE_Var = IECDateVar<IEC_LDATE_Value>;

inline constexpr IEC_DATE_Value DATE_FROM_YMD(int year, int month, int day) noexcept {
return IEC_DATE_Value::from_ymd(year, month, day);
// ---------------------------------------------------------------------------
// Construction helpers
// ---------------------------------------------------------------------------
// `DATE_FROM_YMD(2024, 3, 15)` converts a Gregorian (year, month, day)
// triple into a DATE by way of the Julian Day Number. Branch-free
// integer arithmetic.
inline IEC_DATE DATE_FROM_YMD(int year, int month, int day) noexcept {
const int a = (14 - month) / 12;
const int y = year + 4800 - a;
const int m = month + 12 * a - 3;
const int jdn = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
constexpr int UNIX_EPOCH_JDN = 2440588;
return IEC_DATE(static_cast<DATE_t>(jdn - UNIX_EPOCH_JDN));
}

inline constexpr IEC_LDATE_Value LDATE_FROM_YMD(int year, int month, int day) noexcept {
return IEC_LDATE_Value::from_ymd(year, month, day);
inline IEC_DATE DATE_FROM_DAYS(int64_t days) noexcept {
return IEC_DATE(static_cast<DATE_t>(days));
}

inline constexpr IEC_DATE_Value DATE_FROM_DAYS(int64_t days) noexcept {
return IEC_DATE_Value::from_days(days);
inline int64_t DATE_TO_DAYS(IEC_DATE d) noexcept {
return iec_unwrap(d);
}

inline constexpr IEC_LDATE_Value LDATE_FROM_DAYS(int64_t days) noexcept {
return IEC_LDATE_Value::from_days(days);
// ---------------------------------------------------------------------------
// Arithmetic
// ---------------------------------------------------------------------------
inline IEC_DATE ADD_DATE(IEC_DATE d, int64_t days) noexcept {
return IEC_DATE(iec_unwrap(d) + static_cast<DATE_t>(days));
}

template<typename T>
inline constexpr int64_t DATE_TO_DAYS(const DateValue<T>& d) noexcept {
return d.to_days();
inline IEC_DATE SUB_DATE(IEC_DATE d, int64_t days) noexcept {
return IEC_DATE(iec_unwrap(d) - static_cast<DATE_t>(days));
}

template<typename T>
inline int YEAR(const DateValue<T>& d) noexcept {
return d.year();
inline int64_t DIFF_DATE(IEC_DATE a, IEC_DATE b) noexcept {
return iec_unwrap(a) - iec_unwrap(b);
}

template<typename T>
inline int MONTH(const DateValue<T>& d) noexcept {
return d.month();
}

template<typename T>
inline int DAY(const DateValue<T>& d) noexcept {
return d.day();
}

template<typename T>
inline constexpr int DAY_OF_WEEK(const DateValue<T>& d) noexcept {
return d.day_of_week();
}

template<typename T>
inline int DAY_OF_YEAR(const DateValue<T>& d) noexcept {
return d.day_of_year();
}

template<typename T>
inline constexpr DateValue<T> ADD_DATE(const DateValue<T>& d, int64_t days) noexcept {
return d + days;
}

template<typename T>
inline constexpr DateValue<T> SUB_DATE(const DateValue<T>& d, int64_t days) noexcept {
return d - days;
}

template<typename T>
inline constexpr int64_t DIFF_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a - b;
}

template<typename T>
inline constexpr bool GT_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a > b;
}

template<typename T>
inline constexpr bool GE_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a >= b;
}

template<typename T>
inline constexpr bool EQ_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a == b;
}

template<typename T>
inline constexpr bool LE_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a <= b;
}

template<typename T>
inline constexpr bool LT_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a < b;
}

template<typename T>
inline constexpr bool NE_DATE(const DateValue<T>& a, const DateValue<T>& b) noexcept {
return a != b;
}
// ---------------------------------------------------------------------------
// Comparison
// ---------------------------------------------------------------------------
inline bool GT_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) > iec_unwrap(b); }
inline bool GE_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) >= iec_unwrap(b); }
inline bool EQ_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) == iec_unwrap(b); }
inline bool NE_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) != iec_unwrap(b); }
inline bool LE_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) <= iec_unwrap(b); }
inline bool LT_DATE(IEC_DATE a, IEC_DATE b) noexcept { return iec_unwrap(a) < iec_unwrap(b); }

} // namespace strucpp
Loading
Loading