2
2
// SPDX-License-Identifier: Apache-2.0
3
3
package com.amazon.ion.impl.macro
4
4
5
- import com.amazon.ion.IonException
6
- import com.amazon.ion.SymbolToken
5
+ import com.amazon.ion.*
7
6
import com.amazon.ion.impl._Private_RecyclingStack
8
7
import com.amazon.ion.impl._Private_Utils.newSymbolToken
9
8
import com.amazon.ion.impl.macro.Expression.*
10
9
import java.io.ByteArrayOutputStream
11
10
import java.math.BigDecimal
11
+ import java.math.BigInteger
12
12
13
13
/* *
14
14
* Evaluates an EExpression from a List of [EExpressionBodyExpression] and the [TemplateBodyExpression]s
@@ -209,6 +209,119 @@ class MacroEvaluator {
209
209
}
210
210
}
211
211
212
+ private object MakeTimestampExpander : Expander {
213
+ private fun readOptionalIntArg (
214
+ signatureIndex : Int ,
215
+ expansionInfo : ExpansionInfo ,
216
+ macroEvaluator : MacroEvaluator
217
+ ): Int? {
218
+ if (expansionInfo.i == expansionInfo.endExclusive) return null
219
+ val parameterName = SystemMacro .MakeTimestamp .signature[signatureIndex].variableName
220
+ val arg = readZeroOrOneExpandedArgumentValues(expansionInfo, macroEvaluator, parameterName)
221
+ return arg?.let {
222
+ it as ? IntValue ? : throw IonException (" $parameterName must be an integer" )
223
+ it.longValue.toInt()
224
+ }
225
+ }
226
+
227
+ override fun nextExpression (expansionInfo : ExpansionInfo , macroEvaluator : MacroEvaluator ): Expression {
228
+ val year = readExactlyOneExpandedArgumentValue(expansionInfo, macroEvaluator, SystemMacro .MakeTimestamp .signature[0 ].variableName)
229
+ .let { it as ? IntValue ? : throw IonException (" year must be an integer" ) }
230
+ .longValue.toInt()
231
+ val month = readOptionalIntArg(1 , expansionInfo, macroEvaluator)
232
+ val day = readOptionalIntArg(2 , expansionInfo, macroEvaluator)
233
+ val hour = readOptionalIntArg(3 , expansionInfo, macroEvaluator)
234
+ val minute = readOptionalIntArg(4 , expansionInfo, macroEvaluator)
235
+ val second = if (expansionInfo.i == expansionInfo.endExclusive) {
236
+ null
237
+ } else when (val arg = readZeroOrOneExpandedArgumentValues(expansionInfo, macroEvaluator, SystemMacro .MakeTimestamp .signature[5 ].variableName)) {
238
+ null -> null
239
+ is DecimalValue -> arg.value
240
+ is IntValue -> arg.longValue.toBigDecimal()
241
+ else -> throw IonException (" second must be a decimal" )
242
+ }
243
+ val offsetMinutes = readOptionalIntArg(6 , expansionInfo, macroEvaluator)
244
+
245
+ try {
246
+ val ts = if (second != null ) {
247
+ month ? : throw IonException (" make_timestamp: month is required when second is present" )
248
+ day ? : throw IonException (" make_timestamp: day is required when second is present" )
249
+ hour ? : throw IonException (" make_timestamp: hour is required when second is present" )
250
+ minute ? : throw IonException (" make_timestamp: minute is required when second is present" )
251
+ Timestamp .forSecond(year, month, day, hour, minute, second, offsetMinutes)
252
+ } else if (minute != null ) {
253
+ month ? : throw IonException (" make_timestamp: month is required when minute is present" )
254
+ day ? : throw IonException (" make_timestamp: day is required when minute is present" )
255
+ hour ? : throw IonException (" make_timestamp: hour is required when minute is present" )
256
+ Timestamp .forMinute(year, month, day, hour, minute, offsetMinutes)
257
+ } else if (hour != null ) {
258
+ throw IonException (" make_timestamp: minute is required when hour is present" )
259
+ } else {
260
+ if (offsetMinutes != null ) throw IonException (" make_timestamp: offset_minutes is prohibited when hours and minute are not present" )
261
+ if (day != null ) {
262
+ month ? : throw IonException (" make_timestamp: month is required when day is present" )
263
+ Timestamp .forDay(year, month, day)
264
+ } else if (month != null ) {
265
+ Timestamp .forMonth(year, month)
266
+ } else {
267
+ Timestamp .forYear(year)
268
+ }
269
+ }
270
+ return TimestampValue (value = ts)
271
+ } catch (e: IllegalArgumentException ) {
272
+ throw IonException (e.message)
273
+ }
274
+ }
275
+ }
276
+
277
+ private object SumExpander : Expander {
278
+ override fun nextExpression (expansionInfo : ExpansionInfo , macroEvaluator : MacroEvaluator ): Expression {
279
+ val a = readExactlyOneExpandedArgumentValue(expansionInfo, macroEvaluator, " a" )
280
+ val b = readExactlyOneExpandedArgumentValue(expansionInfo, macroEvaluator, " b" )
281
+ if (a !is IntValue || b !is IntValue ) throw IonException (" operands of sum must be integers" )
282
+ // TODO: Use LongIntValue when possible.
283
+ return BigIntValue (value = a.bigIntegerValue + b.bigIntegerValue)
284
+ }
285
+ }
286
+
287
+ private object DeltaExpander : Expander {
288
+ override fun nextExpression (expansionInfo : ExpansionInfo , macroEvaluator : MacroEvaluator ): Expression {
289
+ // TODO: Optimize to use Long and only fallback to BigInteger if needed.
290
+ // TODO: Optimize for lazy evaluation
291
+ if (expansionInfo.additionalState == null ) {
292
+ val position = expansionInfo.i
293
+ var runningTotal = BigInteger .ZERO
294
+ val values = ArrayDeque <BigInteger >()
295
+ readExpandedArgumentValues(expansionInfo, macroEvaluator) {
296
+ when (it) {
297
+ is IntValue -> {
298
+ runningTotal + = it.bigIntegerValue
299
+ values + = runningTotal
300
+ }
301
+ is DataModelValue -> throw IonException (" Invalid argument type for 'delta': ${it.type} " )
302
+ is FieldName -> TODO (" Unreachable. We shouldn't be able to get here without first encountering a StructValue." )
303
+ }
304
+ true // continue expansion
305
+ }
306
+
307
+ if (values.isEmpty()) {
308
+ // Return fake, empty expression group
309
+ return ExpressionGroup (position, position)
310
+ }
311
+
312
+ expansionInfo.additionalState = values
313
+ expansionInfo.i = position
314
+ }
315
+
316
+ val valueQueue = expansionInfo.additionalState as ArrayDeque <BigInteger >
317
+ val nextValue = valueQueue.removeFirst()
318
+ if (valueQueue.isEmpty()) {
319
+ expansionInfo.i = expansionInfo.endExclusive
320
+ }
321
+ return BigIntValue (value = nextValue)
322
+ }
323
+ }
324
+
212
325
private enum class IfExpander (private val minInclusive : Int , private val maxExclusive : Int ) : Expander {
213
326
IF_NONE (0 , 1 ),
214
327
IF_SOME (1 , - 1 ),
@@ -254,11 +367,10 @@ class MacroEvaluator {
254
367
}
255
368
nExpression.value.intValueExact()
256
369
}
257
- else -> throw IonException (" The first argument of repeat must be a positive integer" )
370
+ else -> throw IonException (" The first argument of repeat must be a non-negative integer" )
258
371
}
259
- if (iterationsRemaining <= 0 ) {
260
- // TODO: Confirm https://github.com/amazon-ion/ion-docs/issues/350
261
- throw IonException (" The first argument of repeat must be a positive integer" )
372
+ if (iterationsRemaining < 0 ) {
373
+ throw IonException (" The first argument of repeat must be a non-negative integer" )
262
374
}
263
375
// Decrement because we're starting the first iteration right away.
264
376
iterationsRemaining--
@@ -267,21 +379,27 @@ class MacroEvaluator {
267
379
}
268
380
269
381
override fun nextExpression (expansionInfo : ExpansionInfo , macroEvaluator : MacroEvaluator ): Expression {
270
- val repeatsRemaining = expansionInfo.additionalState as ? Int
382
+ val repeatsRemainingAfterTheCurrentOne = expansionInfo.additionalState as ? Int
271
383
? : init (expansionInfo, macroEvaluator)
272
384
385
+ if (repeatsRemainingAfterTheCurrentOne < 0 ) {
386
+ expansionInfo.nextSourceExpression()
387
+ return ExpressionGroup (0 , 0 )
388
+ }
389
+
273
390
val repeatedExpressionIndex = expansionInfo.i
274
391
val next = readFirstExpandedArgumentValue(expansionInfo, macroEvaluator)
275
- next ? : throw IonException ( " repeat macro requires at least one value for value parameter " )
276
- if (repeatsRemaining > 0 ) {
277
- expansionInfo.additionalState = repeatsRemaining - 1
392
+ next ? : return ExpressionGroup ( 0 , 0 )
393
+ if (repeatsRemainingAfterTheCurrentOne > 0 ) {
394
+ expansionInfo.additionalState = repeatsRemainingAfterTheCurrentOne - 1
278
395
expansionInfo.i = repeatedExpressionIndex
279
396
}
280
397
return next
281
398
}
282
399
}
283
400
284
401
private object MakeFieldExpander : Expander {
402
+ // This is wrong!
285
403
override fun nextExpression (expansionInfo : ExpansionInfo , macroEvaluator : MacroEvaluator ): Expression {
286
404
/* *
287
405
* Uses [ExpansionInfo.additionalState] to track whether the expansion is on the field name or value.
@@ -317,7 +435,10 @@ class MacroEvaluator {
317
435
MakeSymbol (MakeSymbolExpander ),
318
436
MakeBlob (MakeBlobExpander ),
319
437
MakeDecimal (MakeDecimalExpander ),
438
+ MakeTimestamp (MakeTimestampExpander ),
320
439
MakeField (MakeFieldExpander ),
440
+ Sum (SumExpander ),
441
+ Delta (DeltaExpander ),
321
442
IfNone (IfExpander .IF_NONE ),
322
443
IfSome (IfExpander .IF_SOME ),
323
444
IfSingle (IfExpander .IF_SINGLE ),
@@ -328,23 +449,28 @@ class MacroEvaluator {
328
449
companion object {
329
450
@JvmStatic
330
451
fun forSystemMacro (macro : SystemMacro ): ExpansionKind {
331
- return if (macro.body != null ) {
332
- TemplateBody
333
- } else when (macro) {
452
+ return when (macro) {
334
453
SystemMacro .None -> Values // "none" takes no args, so we can treat it as an empty "values" expansion
335
454
SystemMacro .Values -> Values
336
455
SystemMacro .Annotate -> Annotate
337
456
SystemMacro .MakeString -> MakeString
338
457
SystemMacro .MakeSymbol -> MakeSymbol
339
458
SystemMacro .MakeBlob -> MakeBlob
340
459
SystemMacro .MakeDecimal -> MakeDecimal
460
+ SystemMacro .MakeTimestamp -> MakeTimestamp
341
461
SystemMacro .IfNone -> IfNone
342
462
SystemMacro .IfSome -> IfSome
343
463
SystemMacro .IfSingle -> IfSingle
344
464
SystemMacro .IfMulti -> IfMulti
345
465
SystemMacro .Repeat -> Repeat
346
466
SystemMacro .MakeField -> MakeField
347
- else -> throw IllegalStateException (" Unreachable. All other macros have a template body." )
467
+ SystemMacro .Sum -> Sum
468
+ SystemMacro .Delta -> Delta
469
+ else -> if (macro.body != null ) {
470
+ TemplateBody
471
+ } else {
472
+ TODO (" System macro ${macro.macroName} needs either a template body or a hard-coded expander." )
473
+ }
348
474
}
349
475
}
350
476
}
0 commit comments