PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
str_utils.hpp
Go to the documentation of this file.
1#pragma once
2#include <limits>
3#include <string>
4#include <type_traits>
5#include <vector>
6
7#include <pineforge/na.hpp>
9
10namespace pineforge {
11
12// One argument of str_format_values: a number, which its placeholder's style
13// renders, or a text, which is inserted as it is. A bool is its text, "true"
14// or "false"; a signed integer's na is the number na.
16 enum class Kind { Number, Text };
18 double number = 0.0;
19 std::string text;
20
21 template <class T, std::enable_if_t<std::is_arithmetic_v<T>
22 && !std::is_same_v<T, bool>, int> = 0>
23 StrFormatValue(T value) : kind(Kind::Number), number(static_cast<double>(value)) {
24 if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
25 if (value == std::numeric_limits<T>::min()) number = na<double>();
26 }
27 }
28 StrFormatValue(bool value) : kind(Kind::Text), text(value ? "true" : "false") {}
29 StrFormatValue(const std::string& value) : kind(Kind::Text), text(value) {}
30 StrFormatValue(const char* value) : kind(Kind::Text), text(value) {}
31};
32
33// The external-string argument keeps text as a retained slice. In particular,
34// building the format argument list never copies an accumulated text payload.
36 enum class Kind { Number, Text };
38 double number = 0.0;
40 template <class T, std::enable_if_t<std::is_arithmetic_v<T>
41 && !std::is_same_v<T, bool>, int> = 0>
42 ScriptStrFormatValue(T value) : kind(Kind::Number), number(static_cast<double>(value)) {
43 if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
44 if (value == std::numeric_limits<T>::min()) number = na<double>();
45 }
46 }
47 ScriptStrFormatValue(bool value) : kind(Kind::Text), text(value ? "true" : "false") {}
48 ScriptStrFormatValue(ScriptString value) : kind(Kind::Text), text(std::move(value)) {}
49 ScriptStrFormatValue(const char* value) : kind(Kind::Text), text(value) {}
50};
51
52// The source language's MessageFormat subset: `{i}` inserts argument i, and
53// a number argument is rendered by `{i}` as the grouped "#,###.###" pattern
54// and by `{i,number,<style>}` in the style -- none (the same pattern),
55// `integer`, `percent` (x100, no fraction), `currency` ("$", two decimals,
56// grouped) or a decimal pattern (see str_tostring). A text argument is
57// inserted as it is whatever the placeholder's type and style. Text between
58// single quotes is literal and `''` is one quote. A placeholder whose index
59// is not a number, names no argument, or (for a number) has a type other
60// than `number` is kept as written; an unclosed `{` keeps the rest.
61std::string str_format_values(const std::string& fmt,
62 const std::vector<StrFormatValue>& args);
63
64// str_format_values with every argument a text: `{i}` and
65// `{i,number,<style>}` insert args[i] as it is, and quoting and the kept
66// placeholders follow the same rules. A number reaches it already rendered,
67// by str_tostring.
68std::string str_format(const std::string& fmt,
69 const std::vector<std::string>& args);
70
71std::string str_format_time(long long timestamp_ms,
72 const std::string& format,
73 const std::string& timezone);
74
75std::string str_match(const std::string& source,
76 const std::string& regex_pattern);
77
78std::vector<std::string> str_split(const std::string& source,
79 const std::string& separator);
80
81// A number's text, from its shortest round-trip decimal spelling, every
82// scaling and rounding (half-up) done on those digits. NaN is "NaN", the
83// infinities "Infinity" and "-Infinity", and a value that rounds to zero has
84// no sign. format_mode:
85// "" up to ten fraction digits, trailing zeros dropped
86// (0.1234567890123 -> "0.123456789", 1234.5 -> "1234.5")
87// "percent" up to two fraction digits and a "%": the value is not scaled
88// (1.005 -> "1.01%")
89// "volume" up to two fraction digits of the value in thousands,
90// millions, billions or trillions with a "K", "M", "B" or "T",
91// none below a thousand (1005 -> "1.01K", 12.34 -> "12")
92// "mintick" the value rounded to `mintick` (> 0) with its decimals kept
93// (1.2 at 0.01 -> "1.20"); a non-positive tick is the default
94// otherwise a decimal pattern: '0' a required and '#' an optional
95// fraction digit, ',' before the point groups by thousands,
96// '%' scales by 100 (0.1234 at "#.##%" -> "12.34%")
97std::string str_tostring(double value,
98 const std::string& format_mode = "",
99 double mintick = 0.0);
100
102 const std::vector<ScriptStrFormatValue>&);
103ScriptString script_str_format(const ScriptString&, const std::vector<ScriptString>&);
105 const ScriptString& format_mode = {}, double mintick = 0.0);
106
107// Constrain the source argument so existing calls with C string literals still
108// resolve to their std::string ABI. A ScriptString source selects this path
109// without converting a literal to a different owning representation first.
110template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
111inline ScriptString str_format_values(Text&& format, const std::vector<ScriptStrFormatValue>& args) {
112 return script_str_format_values(format, args);
113}
114template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
115inline ScriptString str_format(Text&& format, const std::vector<ScriptString>& args) {
116 return script_str_format(format, args);
117}
118template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
119inline ScriptStringParts str_split(Text&& source, const ScriptString& separator) {
120 return ScriptStringParts(std::forward<Text>(source), separator);
121}
122template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
123inline ScriptStringParts str_split(Text&& source, std::string_view separator) {
124 auto& store = source.store() ? *source.store() : ScriptStringScope::current();
125 return ScriptStringParts(std::forward<Text>(source), ScriptString(store, separator));
126}
127template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
128inline ScriptStringParts str_split(Text&& source, const char* separator) {
129 return str_split(std::forward<Text>(source), std::string_view(separator));
130}
131template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
132inline ScriptStringParts str_split(Text&& source, const std::string& separator) {
133 return str_split(std::forward<Text>(source), std::string_view(separator));
134}
135
136// Deprecated `pine_*` spellings: exact inline forwards kept for generated code
137// and the source adapter. New code calls the neutral names above.
138
139inline std::string pine_str_format(const std::string& fmt,
140 const std::vector<std::string>& args) {
141 return str_format(fmt, args);
142}
143
144inline std::string pine_str_format_time(long long timestamp_ms,
145 const std::string& format,
146 const std::string& timezone) {
147 return str_format_time(timestamp_ms, format, timezone);
148}
149
150inline std::string pine_str_match(const std::string& source,
151 const std::string& regex_pattern) {
152 return str_match(source, regex_pattern);
153}
154
155inline std::vector<std::string> pine_str_split(const std::string& source,
156 const std::string& separator) {
157 return str_split(source, separator);
158}
159
160inline std::string pine_str_tostring(double value,
161 const std::string& format_mode = "",
162 double mintick = 0.0) {
163 return str_tostring(value, format_mode, mintick);
164}
165
166template <class Text, std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
167inline ScriptString pine_str_format(Text&& format, const std::vector<ScriptString>& args) {
168 return script_str_format(format, args);
169}
170template <class Text, class Separator,
171 std::enable_if_t<std::is_same_v<std::decay_t<Text>, ScriptString>, int> = 0>
172inline ScriptStringParts pine_str_split(Text&& source, Separator&& separator) {
173 return str_split(std::forward<Text>(source), std::forward<Separator>(separator));
174}
176 const ScriptString& format = {}, double mintick = 0.0) {
177 return str_tostring(store, value, format, mintick);
178}
179
180} // namespace pineforge
static detail::o1::Store & current()
std::string str_format(const std::string &fmt, const std::vector< std::string > &args)
ScriptString script_str_format_values(const ScriptString &, const std::vector< ScriptStrFormatValue > &)
std::string str_tostring(double value, const std::string &format_mode="", double mintick=0.0)
std::vector< std::string > pine_str_split(const std::string &source, const std::string &separator)
double na< double >()
Definition na.hpp:64
std::vector< std::string > str_split(const std::string &source, const std::string &separator)
ScriptString script_str_format(const ScriptString &, const std::vector< ScriptString > &)
std::string str_format_values(const std::string &fmt, const std::vector< StrFormatValue > &args)
std::string pine_str_format(const std::string &fmt, const std::vector< std::string > &args)
std::string pine_str_tostring(double value, const std::string &format_mode="", double mintick=0.0)
std::string pine_str_format_time(long long timestamp_ms, const std::string &format, const std::string &timezone)
std::string str_match(const std::string &source, const std::string &regex_pattern)
std::string str_format_time(long long timestamp_ms, const std::string &format, const std::string &timezone)
std::string pine_str_match(const std::string &source, const std::string &regex_pattern)
ScriptStrFormatValue(ScriptString value)
Definition str_utils.hpp:48
ScriptStrFormatValue(const char *value)
Definition str_utils.hpp:49
StrFormatValue(const std::string &value)
Definition str_utils.hpp:29
StrFormatValue(const char *value)
Definition str_utils.hpp:30