iam-floyd 0.848.0 → 0.849.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,272 @@
1
+ using System;
2
+ using System.Collections;
3
+ using System.Collections.Generic;
4
+ using System.Globalization;
5
+ using System.Text;
6
+ using System.Text.RegularExpressions;
7
+
8
+ namespace IAM.Floyd;
9
+
10
+ /// <summary>
11
+ /// The semantics of JavaScript that the transpiled code relies on.
12
+ /// </summary>
13
+ internal static class Js
14
+ {
15
+ /// <summary>
16
+ /// Whether a value is a number.
17
+ /// </summary>
18
+ public static bool IsNumber(object value)
19
+ {
20
+ return value is double || value is int || value is long || value is float
21
+ || value is short || value is byte || value is decimal;
22
+ }
23
+
24
+ /// <summary>
25
+ /// Array.isArray: whether a value is a sequence, other than a string or a dictionary.
26
+ /// </summary>
27
+ public static bool IsArray(object value)
28
+ {
29
+ return value is IEnumerable && !(value is string) && !(value is IDictionary);
30
+ }
31
+
32
+ /// <summary>
33
+ /// A sequence as list.
34
+ /// </summary>
35
+ public static List<object> ToList(object value)
36
+ {
37
+ var result = new List<object>();
38
+ foreach (var item in (IEnumerable)value)
39
+ {
40
+ result.Add(item);
41
+ }
42
+ return result;
43
+ }
44
+
45
+ /// <summary>
46
+ /// The result of the typeof operator of JavaScript.
47
+ /// </summary>
48
+ public static string Typeof(object value)
49
+ {
50
+ if (value == null)
51
+ {
52
+ return "undefined";
53
+ }
54
+ if (value is bool)
55
+ {
56
+ return "boolean";
57
+ }
58
+ if (IsNumber(value))
59
+ {
60
+ return "number";
61
+ }
62
+ if (value is string)
63
+ {
64
+ return "string";
65
+ }
66
+ return "object";
67
+ }
68
+
69
+ /// <summary>
70
+ /// A value as JavaScript converts it to a string.
71
+ /// </summary>
72
+ public static string ToString(object value)
73
+ {
74
+ switch (value)
75
+ {
76
+ case null:
77
+ return "undefined";
78
+ case string text:
79
+ return text;
80
+ case bool flag:
81
+ return flag ? "true" : "false";
82
+ case double number:
83
+ return Number(number);
84
+ case float number:
85
+ return Number(number);
86
+ case decimal number:
87
+ return Number((double)number);
88
+ case IFormattable formattable when IsNumber(value):
89
+ return formattable.ToString(null, CultureInfo.InvariantCulture);
90
+ case IEnumerable items when !(value is IDictionary):
91
+ var result = new StringBuilder();
92
+ var first = true;
93
+ foreach (var item in items)
94
+ {
95
+ if (!first)
96
+ {
97
+ result.Append(',');
98
+ }
99
+ first = false;
100
+ if (item != null)
101
+ {
102
+ result.Append(ToString(item));
103
+ }
104
+ }
105
+ return result.ToString();
106
+ }
107
+ return value.ToString();
108
+ }
109
+
110
+ /// <summary>
111
+ /// A number as JavaScript formats it: the shortest digits that round-trip.
112
+ /// </summary>
113
+ public static string Number(double value)
114
+ {
115
+ if (double.IsNaN(value))
116
+ {
117
+ return "NaN";
118
+ }
119
+ if (double.IsInfinity(value))
120
+ {
121
+ return value > 0 ? "Infinity" : "-Infinity";
122
+ }
123
+ if (value == 0)
124
+ {
125
+ return "0";
126
+ }
127
+ if (value < 0)
128
+ {
129
+ return "-" + Number(-value);
130
+ }
131
+ // the shortest representation that parses back to the same value, e.g. 1.5E-07
132
+ var shortest = value.ToString("R", CultureInfo.InvariantCulture);
133
+ var e = shortest.IndexOf('E');
134
+ var mantissa = e < 0 ? shortest : shortest.Substring(0, e);
135
+ var exponent = e < 0 ? 0 : int.Parse(shortest.Substring(e + 1), CultureInfo.InvariantCulture);
136
+ var point = mantissa.IndexOf('.');
137
+ var digits = point < 0 ? mantissa : mantissa.Remove(point, 1);
138
+ // n is the position of the decimal point relative to the first digit
139
+ var n = (point < 0 ? mantissa.Length : point) + exponent;
140
+ while (digits.Length > 1 && digits[0] == '0')
141
+ {
142
+ digits = digits.Substring(1);
143
+ n--;
144
+ }
145
+ // trailing zeros are not significant
146
+ while (digits.Length > 1 && digits[digits.Length - 1] == '0')
147
+ {
148
+ digits = digits.Substring(0, digits.Length - 1);
149
+ }
150
+ var k = digits.Length;
151
+ var result = new StringBuilder();
152
+ if (k <= n && n <= 21)
153
+ {
154
+ result.Append(digits).Append('0', n - k);
155
+ }
156
+ else if (0 < n && n <= 21)
157
+ {
158
+ result.Append(digits, 0, n).Append('.').Append(digits.Substring(n));
159
+ }
160
+ else if (-6 < n && n <= 0)
161
+ {
162
+ result.Append("0.").Append('0', -n).Append(digits);
163
+ }
164
+ else
165
+ {
166
+ result.Append(digits[0]);
167
+ if (k > 1)
168
+ {
169
+ result.Append('.').Append(digits.Substring(1));
170
+ }
171
+ result.Append('e').Append(n - 1 >= 0 ? "+" : "-")
172
+ .Append(Math.Abs(n - 1).ToString(CultureInfo.InvariantCulture));
173
+ }
174
+ return result.ToString();
175
+ }
176
+
177
+ /// <summary>
178
+ /// String.prototype.substring: the indexes are clamped, and swapped if start is after end.
179
+ /// </summary>
180
+ public static string Substring(string value, int start)
181
+ {
182
+ return Substring(value, start, value.Length);
183
+ }
184
+
185
+ /// <summary>
186
+ /// String.prototype.substring: the indexes are clamped, and swapped if start is after end.
187
+ /// </summary>
188
+ public static string Substring(string value, int start, int end)
189
+ {
190
+ start = Math.Max(0, Math.Min(start, value.Length));
191
+ end = Math.Max(0, Math.Min(end, value.Length));
192
+ return start <= end
193
+ ? value.Substring(start, end - start)
194
+ : value.Substring(end, start - end);
195
+ }
196
+
197
+ /// <summary>
198
+ /// Array.prototype.filter.
199
+ /// </summary>
200
+ public static List<T> Filter<T>(List<T> list, Func<T, int, bool> predicate)
201
+ {
202
+ var result = new List<T>();
203
+ for (var i = 0; i < list.Count; i++)
204
+ {
205
+ if (predicate(list[i], i))
206
+ {
207
+ result.Add(list[i]);
208
+ }
209
+ }
210
+ return result;
211
+ }
212
+
213
+ /// <summary>
214
+ /// Array.prototype.map.
215
+ /// </summary>
216
+ public static List<TResult> Map<T, TResult>(List<T> list, Func<T, int, TResult> fn)
217
+ {
218
+ var result = new List<TResult>();
219
+ for (var i = 0; i < list.Count; i++)
220
+ {
221
+ result.Add(fn(list[i], i));
222
+ }
223
+ return result;
224
+ }
225
+
226
+ /// <summary>
227
+ /// Array.prototype.sort without compare function: sorts strings by UTF-16 code units.
228
+ /// </summary>
229
+ public static List<string> Sort(List<string> list)
230
+ {
231
+ list.Sort(string.CompareOrdinal);
232
+ return list;
233
+ }
234
+
235
+ /// <summary>
236
+ /// Date.prototype.toISOString. Dates of an unspecified kind are in UTC.
237
+ /// </summary>
238
+ public static string ToISOString(DateTime value)
239
+ {
240
+ if (value.Kind == DateTimeKind.Local)
241
+ {
242
+ value = value.ToUniversalTime();
243
+ }
244
+ return value.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
245
+ }
246
+
247
+ /// <summary>
248
+ /// A regular expression like new RegExp(pattern, flags), with the flags i, m and s.
249
+ /// </summary>
250
+ public static Regex RegExp(string pattern, string flags)
251
+ {
252
+ var options = RegexOptions.None;
253
+ foreach (var flag in flags)
254
+ {
255
+ switch (flag)
256
+ {
257
+ case 'i':
258
+ options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
259
+ break;
260
+ case 'm':
261
+ options |= RegexOptions.Multiline;
262
+ break;
263
+ case 's':
264
+ options |= RegexOptions.Singleline;
265
+ break;
266
+ default:
267
+ throw new ArgumentException("Unsupported regular expression flag: " + flag);
268
+ }
269
+ }
270
+ return new Regex(pattern, options);
271
+ }
272
+ }
@@ -0,0 +1,151 @@
1
+ using System;
2
+ using System.Collections;
3
+ using System.Globalization;
4
+ using System.Reflection;
5
+ using System.Text;
6
+
7
+ namespace IAM.Floyd;
8
+
9
+ /// <summary>
10
+ /// Serializes statements and their parts to JSON, as <c>JSON.stringify</c> of JavaScript.
11
+ /// </summary>
12
+ public static class Json
13
+ {
14
+ /// <summary>
15
+ /// The JSON of a value: dictionaries in the order of their keys, sequences, strings, numbers,
16
+ /// booleans, null, dates as ISO strings, and objects with a public <c>ToJSON()</c> method, such
17
+ /// as statements.
18
+ /// </summary>
19
+ /// <param name="value">The value</param>
20
+ /// <returns>The JSON, without whitespace</returns>
21
+ public static string Stringify(object value)
22
+ {
23
+ var result = new StringBuilder();
24
+ Write(result, value);
25
+ return result.ToString();
26
+ }
27
+
28
+ private static void Write(StringBuilder result, object value)
29
+ {
30
+ switch (value)
31
+ {
32
+ case null:
33
+ result.Append("null");
34
+ break;
35
+ case string text:
36
+ WriteString(result, text);
37
+ break;
38
+ case bool flag:
39
+ result.Append(flag ? "true" : "false");
40
+ break;
41
+ case double or float or decimal:
42
+ var number = Convert.ToDouble(value, CultureInfo.InvariantCulture);
43
+ result.Append(double.IsNaN(number) || double.IsInfinity(number) ? "null" : Js.Number(number));
44
+ break;
45
+ case IFormattable formattable when Js.IsNumber(value):
46
+ result.Append(formattable.ToString(null, CultureInfo.InvariantCulture));
47
+ break;
48
+ case DateTime date:
49
+ WriteString(result, Js.ToISOString(date));
50
+ break;
51
+ case IDictionary dictionary:
52
+ result.Append('{');
53
+ var first = true;
54
+ foreach (DictionaryEntry entry in dictionary)
55
+ {
56
+ if (!first)
57
+ {
58
+ result.Append(',');
59
+ }
60
+ first = false;
61
+ WriteString(result, Convert.ToString(entry.Key, CultureInfo.InvariantCulture));
62
+ result.Append(':');
63
+ Write(result, entry.Value);
64
+ }
65
+ result.Append('}');
66
+ break;
67
+ case IEnumerable items:
68
+ result.Append('[');
69
+ var firstItem = true;
70
+ foreach (var item in items)
71
+ {
72
+ if (!firstItem)
73
+ {
74
+ result.Append(',');
75
+ }
76
+ firstItem = false;
77
+ Write(result, item);
78
+ }
79
+ result.Append(']');
80
+ break;
81
+ default:
82
+ Write(result, ToJson(value));
83
+ break;
84
+ }
85
+ }
86
+
87
+ private static object ToJson(object value)
88
+ {
89
+ var method = value.GetType().GetMethod("ToJSON", Type.EmptyTypes);
90
+ if (method == null)
91
+ {
92
+ throw new ArgumentException("Cannot serialize " + value.GetType().FullName + " to JSON");
93
+ }
94
+ try
95
+ {
96
+ return method.Invoke(value, null);
97
+ }
98
+ catch (TargetInvocationException e) when (e.InnerException != null)
99
+ {
100
+ System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(e.InnerException).Throw();
101
+ throw;
102
+ }
103
+ }
104
+
105
+ private static void WriteString(StringBuilder result, string value)
106
+ {
107
+ result.Append('"');
108
+ for (var i = 0; i < value.Length; i++)
109
+ {
110
+ var c = value[i];
111
+ switch (c)
112
+ {
113
+ case '"':
114
+ result.Append("\\\"");
115
+ break;
116
+ case '\\':
117
+ result.Append("\\\\");
118
+ break;
119
+ case '\b':
120
+ result.Append("\\b");
121
+ break;
122
+ case '\f':
123
+ result.Append("\\f");
124
+ break;
125
+ case '\n':
126
+ result.Append("\\n");
127
+ break;
128
+ case '\r':
129
+ result.Append("\\r");
130
+ break;
131
+ case '\t':
132
+ result.Append("\\t");
133
+ break;
134
+ default:
135
+ var loneSurrogate = char.IsHighSurrogate(c)
136
+ ? i + 1 >= value.Length || !char.IsLowSurrogate(value[i + 1])
137
+ : char.IsLowSurrogate(c) && (i == 0 || !char.IsHighSurrogate(value[i - 1]));
138
+ if (c < 0x20 || loneSurrogate)
139
+ {
140
+ result.Append("\\u").Append(((int)c).ToString("x4", CultureInfo.InvariantCulture));
141
+ }
142
+ else
143
+ {
144
+ result.Append(c);
145
+ }
146
+ break;
147
+ }
148
+ }
149
+ result.Append('"');
150
+ }
151
+ }
package/eslint.config.mjs CHANGED
@@ -32,6 +32,7 @@ export default defineConfig(
32
32
  'tooter/**/*.js',
33
33
  'lib/generated/',
34
34
  'dist/',
35
+ 'java/target/',
35
36
  'docs/build/',
36
37
  ],
37
38
  },
@@ -0,0 +1,3 @@
1
+ module udondan.github.io/iam-floyd/go/iamfloyd
2
+
3
+ go 1.21
@@ -0,0 +1,25 @@
1
+ package iamfloyd
2
+
3
+ // String returns a pointer to a string, for the parameters of the API
4
+ func String(value string) *string {
5
+ return &value
6
+ }
7
+
8
+ // Strings returns a pointer to a list of pointers to strings, for the parameters of the API
9
+ func Strings(values ...string) *[]*string {
10
+ list := make([]*string, len(values))
11
+ for i := range values {
12
+ list[i] = &values[i]
13
+ }
14
+ return &list
15
+ }
16
+
17
+ // Number returns a pointer to a number, for the parameters of the API
18
+ func Number(value float64) *float64 {
19
+ return &value
20
+ }
21
+
22
+ // Bool returns a pointer to a boolean, for the parameters of the API
23
+ func Bool(value bool) *bool {
24
+ return &value
25
+ }
@@ -0,0 +1,219 @@
1
+ package js
2
+
3
+ import (
4
+ "sort"
5
+ "strconv"
6
+ )
7
+
8
+ // Entry is an entry of a Record or a Map
9
+ type Entry[K comparable, V any] struct {
10
+ Key K
11
+ Value V
12
+ }
13
+
14
+ // Record is an object of JavaScript used as dictionary: the keys that are array indexes come
15
+ // first in ascending order, then the other keys in the order they were added.
16
+ type Record[V any] struct {
17
+ keys []string
18
+ values map[string]V
19
+ }
20
+
21
+ // NewRecord returns an empty record
22
+ func NewRecord[V any]() *Record[V] {
23
+ return &Record[V]{values: map[string]V{}}
24
+ }
25
+
26
+ // RecordOf returns a record with the given entries
27
+ func RecordOf[V any](entries ...Entry[string, V]) *Record[V] {
28
+ record := NewRecord[V]()
29
+ for _, entry := range entries {
30
+ record.Set(entry.Key, entry.Value)
31
+ }
32
+ return record
33
+ }
34
+
35
+ // Get returns the value of a key, or the zero value if the key is missing
36
+ func (r *Record[V]) Get(key string) V {
37
+ return r.values[key]
38
+ }
39
+
40
+ // GetOr returns the value of a key, or the fallback if the key is missing
41
+ func (r *Record[V]) GetOr(key string, fallback V) V {
42
+ if value, ok := r.values[key]; ok {
43
+ return value
44
+ }
45
+ return fallback
46
+ }
47
+
48
+ // Set adds or replaces the value of a key
49
+ func (r *Record[V]) Set(key string, value V) {
50
+ if _, ok := r.values[key]; !ok {
51
+ r.keys = append(r.keys, key)
52
+ }
53
+ r.values[key] = value
54
+ }
55
+
56
+ // Has returns whether the record has a key
57
+ func (r *Record[V]) Has(key string) bool {
58
+ _, ok := r.values[key]
59
+ return ok
60
+ }
61
+
62
+ // Len returns the number of keys
63
+ func (r *Record[V]) Len() int {
64
+ return len(r.keys)
65
+ }
66
+
67
+ func arrayIndex(key string) (uint64, bool) {
68
+ if key == "" || (len(key) > 1 && key[0] == '0') {
69
+ return 0, false
70
+ }
71
+ index, err := strconv.ParseUint(key, 10, 32)
72
+ return index, err == nil && index < 4294967295
73
+ }
74
+
75
+ // Keys returns the keys in the order of JavaScript
76
+ func (r *Record[V]) Keys() []string {
77
+ var indexes []string
78
+ var others []string
79
+ for _, key := range r.keys {
80
+ if _, ok := arrayIndex(key); ok {
81
+ indexes = append(indexes, key)
82
+ } else {
83
+ others = append(others, key)
84
+ }
85
+ }
86
+ if len(indexes) == 0 {
87
+ return others
88
+ }
89
+ sort.Slice(indexes, func(i, j int) bool {
90
+ a, _ := arrayIndex(indexes[i])
91
+ b, _ := arrayIndex(indexes[j])
92
+ return a < b
93
+ })
94
+ return append(indexes, others...)
95
+ }
96
+
97
+ // Values returns the values in the order of the keys
98
+ func (r *Record[V]) Values() []V {
99
+ keys := r.Keys()
100
+ values := make([]V, len(keys))
101
+ for i, key := range keys {
102
+ values[i] = r.values[key]
103
+ }
104
+ return values
105
+ }
106
+
107
+ // Entries returns the entries in the order of the keys
108
+ func (r *Record[V]) Entries() []Entry[string, V] {
109
+ keys := r.Keys()
110
+ entries := make([]Entry[string, V], len(keys))
111
+ for i, key := range keys {
112
+ entries[i] = Entry[string, V]{key, r.values[key]}
113
+ }
114
+ return entries
115
+ }
116
+
117
+ // MarshalJSON writes the record like JSON.stringify
118
+ func (r *Record[V]) MarshalJSON() ([]byte, error) {
119
+ return []byte(Stringify(r)), nil
120
+ }
121
+
122
+ func (r *Record[V]) writeJSON(writer *jsonWriter) {
123
+ writer.out.WriteByte('{')
124
+ for i, key := range r.Keys() {
125
+ if i > 0 {
126
+ writer.out.WriteByte(',')
127
+ }
128
+ writer.writeString(key)
129
+ writer.out.WriteByte(':')
130
+ writer.write(r.values[key])
131
+ }
132
+ writer.out.WriteByte('}')
133
+ }
134
+
135
+ // Map is a Map of JavaScript: the keys are in the order they were added
136
+ type Map[K comparable, V any] struct {
137
+ keys []K
138
+ values map[K]V
139
+ }
140
+
141
+ // NewMap returns an empty map
142
+ func NewMap[K comparable, V any]() *Map[K, V] {
143
+ return &Map[K, V]{values: map[K]V{}}
144
+ }
145
+
146
+ // Get returns the value of a key, or the zero value if the key is missing
147
+ func (m *Map[K, V]) Get(key K) V {
148
+ return m.values[key]
149
+ }
150
+
151
+ // GetOr returns the value of a key, or the fallback if the key is missing
152
+ func (m *Map[K, V]) GetOr(key K, fallback V) V {
153
+ if value, ok := m.values[key]; ok {
154
+ return value
155
+ }
156
+ return fallback
157
+ }
158
+
159
+ // Set adds or replaces the value of a key
160
+ func (m *Map[K, V]) Set(key K, value V) {
161
+ if _, ok := m.values[key]; !ok {
162
+ m.keys = append(m.keys, key)
163
+ }
164
+ m.values[key] = value
165
+ }
166
+
167
+ // Has returns whether the map has a key
168
+ func (m *Map[K, V]) Has(key K) bool {
169
+ _, ok := m.values[key]
170
+ return ok
171
+ }
172
+
173
+ // Len returns the number of keys
174
+ func (m *Map[K, V]) Len() int {
175
+ return len(m.keys)
176
+ }
177
+
178
+ // Entries returns the entries in the order they were added
179
+ func (m *Map[K, V]) Entries() []Entry[K, V] {
180
+ entries := make([]Entry[K, V], len(m.keys))
181
+ for i, key := range m.keys {
182
+ entries[i] = Entry[K, V]{key, m.values[key]}
183
+ }
184
+ return entries
185
+ }
186
+
187
+ // Set is a Set of JavaScript: the values are in the order they were added
188
+ type Set[T comparable] struct {
189
+ values []T
190
+ index map[T]bool
191
+ }
192
+
193
+ // NewSet returns an empty set
194
+ func NewSet[T comparable]() *Set[T] {
195
+ return &Set[T]{index: map[T]bool{}}
196
+ }
197
+
198
+ // Add adds a value, unless the set has it
199
+ func (s *Set[T]) Add(value T) {
200
+ if !s.index[value] {
201
+ s.index[value] = true
202
+ s.values = append(s.values, value)
203
+ }
204
+ }
205
+
206
+ // Has returns whether the set has a value
207
+ func (s *Set[T]) Has(value T) bool {
208
+ return s.index[value]
209
+ }
210
+
211
+ // Len returns the number of values
212
+ func (s *Set[T]) Len() int {
213
+ return len(s.values)
214
+ }
215
+
216
+ // Values returns the values in the order they were added
217
+ func (s *Set[T]) Values() []T {
218
+ return append([]T(nil), s.values...)
219
+ }