@@ -312,6 +312,29 @@ export abstract class FunctionPlugin implements FunctionPluginTypecheck<Function
312312 return ret
313313 }
314314
315+ /**
316+ * Builds syntactically-empty flags aligned with the expanded evaluated arguments.
317+ * When ranges are expanded, a single AST node may produce multiple evaluated values;
318+ * only nodes with `AstNodeType.EMPTY` are considered syntactically empty.
319+ */
320+ private buildSyntacticallyEmptyFlagsForExpandedArgs ( args : Ast [ ] , evaluatedArguments : [ InterpreterValue , boolean ] [ ] ) : boolean [ ] {
321+ const flags : boolean [ ] = [ ]
322+ let evalIdx = 0
323+ for ( const ast of args ) {
324+ const isEmpty = ast . type === AstNodeType . EMPTY
325+ if ( evalIdx < evaluatedArguments . length && evaluatedArguments [ evalIdx ] [ 1 ] ) {
326+ while ( evalIdx < evaluatedArguments . length && evaluatedArguments [ evalIdx ] [ 1 ] ) {
327+ flags . push ( isEmpty )
328+ evalIdx ++
329+ }
330+ } else {
331+ flags . push ( isEmpty )
332+ evalIdx ++
333+ }
334+ }
335+ return flags
336+ }
337+
315338 protected coerceScalarToNumberOrError = ( arg : InternalScalarValue ) : ExtendedNumber | CellError => this . arithmeticHelper . coerceScalarToNumberOrError ( arg )
316339
317340 protected coerceToType ( arg : InterpreterValue , coercedType : FunctionArgument , state : InterpreterState ) : Maybe < InterpreterValue | complex | RawNoErrorScalarValue > {
@@ -410,6 +433,9 @@ export abstract class FunctionPlugin implements FunctionPluginTypecheck<Function
410433 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
411434 const argumentValues : InterpreterValue [ ] = evaluatedArguments . map ( ( [ value , _ ] : [ InterpreterValue , boolean ] ) => value as InterpreterValue )
412435 const argumentIgnorableFlags = evaluatedArguments . map ( ( [ _ , ignorable ] ) => ignorable )
436+ const syntacticallyEmptyFlags = metadata . expandRanges
437+ ? this . buildSyntacticallyEmptyFlagsForExpandedArgs ( args , evaluatedArguments )
438+ : args . map ( ( ast ) => ast . type === AstNodeType . EMPTY )
413439 const argumentMetadata = this . buildMetadataForEachArgumentValue ( argumentValues . length , metadata )
414440 const isVectorizationOn = state . arraysFlag && ! metadata . vectorizationForbidden
415441
@@ -421,13 +447,13 @@ export abstract class FunctionPlugin implements FunctionPluginTypecheck<Function
421447
422448 if ( resultArrayHeight === 1 && resultArrayWidth === 1 ) {
423449 const vectorizedArguments = this . vectorizeAndBroadcastArgumentsIfNecessary ( isVectorizationOn , argumentValues , argumentMetadata , 0 , 0 )
424- return this . calculateSingleCellOfResultArray ( state , vectorizedArguments , argumentMetadata , argumentIgnorableFlags , functionImplementation , metadata . returnNumberType )
450+ return this . calculateSingleCellOfResultArray ( state , vectorizedArguments , argumentMetadata , argumentIgnorableFlags , syntacticallyEmptyFlags , functionImplementation , metadata . returnNumberType )
425451 }
426452
427453 const resultArray : InternalScalarValue [ ] [ ] = [ ...Array ( resultArrayHeight ) . keys ( ) ] . map ( row =>
428454 [ ...Array ( resultArrayWidth ) . keys ( ) ] . map ( col => {
429455 const vectorizedArguments = this . vectorizeAndBroadcastArgumentsIfNecessary ( isVectorizationOn , argumentValues , argumentMetadata , row , col )
430- const result = this . calculateSingleCellOfResultArray ( state , vectorizedArguments , argumentMetadata , argumentIgnorableFlags , functionImplementation , metadata . returnNumberType )
456+ const result = this . calculateSingleCellOfResultArray ( state , vectorizedArguments , argumentMetadata , argumentIgnorableFlags , syntacticallyEmptyFlags , functionImplementation , metadata . returnNumberType )
431457
432458 if ( result instanceof SimpleRangeValue ) {
433459 throw new Error ( 'Function returning array cannot be vectorized.' )
@@ -445,10 +471,11 @@ export abstract class FunctionPlugin implements FunctionPluginTypecheck<Function
445471 vectorizedArguments : Maybe < InterpreterValue > [ ] ,
446472 argumentsMetadata : FunctionArgument [ ] ,
447473 argumentIgnorableFlags : boolean [ ] ,
474+ syntacticallyEmptyFlags : boolean [ ] ,
448475 functionImplementation : ( ...arg : any ) => InterpreterValue ,
449476 returnNumberType : NumberType | undefined ,
450477 ) : RawInterpreterValue {
451- const coercedArguments = this . coerceArgumentsToRequiredTypes ( state , vectorizedArguments , argumentsMetadata , argumentIgnorableFlags )
478+ const coercedArguments = this . coerceArgumentsToRequiredTypes ( state , vectorizedArguments , argumentsMetadata , argumentIgnorableFlags , syntacticallyEmptyFlags )
452479
453480 if ( coercedArguments instanceof CellError ) {
454481 return coercedArguments
@@ -463,15 +490,17 @@ export abstract class FunctionPlugin implements FunctionPluginTypecheck<Function
463490 vectorizedArguments : Maybe < InterpreterValue > [ ] ,
464491 argumentsMetadata : FunctionArgument [ ] ,
465492 argumentIgnorableFlags : boolean [ ] ,
493+ syntacticallyEmptyFlags : boolean [ ] = [ ] ,
466494 ) : CellError | Maybe < InterpreterValue | complex | RawNoErrorScalarValue > [ ] {
467495 const coercedArguments : Maybe < InterpreterValue | complex | RawNoErrorScalarValue > [ ] = [ ]
468496
469497 for ( let i = 0 ; i < argumentsMetadata . length ; i ++ ) {
470498 const argumentMetadata = argumentsMetadata [ i ]
471499 const rawArg = vectorizedArguments [ i ]
500+ const isSyntacticallyEmpty = ! ! syntacticallyEmptyFlags [ i ]
472501 const argumentValue = rawArg === undefined
473502 ? argumentMetadata ?. defaultValue
474- : ( rawArg === EmptyValue && argumentMetadata ?. emptyAsDefault && argumentMetadata ?. defaultValue !== undefined )
503+ : ( isSyntacticallyEmpty && argumentMetadata ?. emptyAsDefault && argumentMetadata ?. defaultValue !== undefined )
475504 ? argumentMetadata . defaultValue
476505 : rawArg
477506
0 commit comments