/**
 * Copyright (c) 2016-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */ import * as AlgebraicType from '../algebraic-type' ; import * as AlgebraicTypeUtils from '../algebraic-type-utils' ; import * as AlgebraicTypeUtilsForMatching from '../algebraic-type-utils-for-matching' ; import * as CPlusPlus from '../cplusplus' ; import * as Code from '../code' ; import * as Error from '../error' ; import * as FileWriter from '../file-writer' ; import * as Maybe from '../maybe' ; import * as ObjC from '../objc' ; import * as ObjCRenderer from '../objc-renderer' ; import * as StringUtils from '../string-utils' ; function matchingFileNameForAlgebraicType ( algebraicType ) { return algebraicType . name + 'TemplatedMatchingHelpers' ; } function matchBlockNameForSubtype ( subtype ) { return ( StringUtils . lowercased ( AlgebraicTypeUtils . subtypeNameFromSubtype ( subtype ) ) + 'MatchHandler' ) ; } function matcherFunctionBlockTypeForAttribute ( attribute ) { return ( ObjCRenderer . renderableTypeReferenceNestingSubsequentToken ( attribute . type . reference ) + attribute . name ) ; } function matcherFunctionParameterValueForSubtype ( subtype ) { return ( 'T(^' + matchBlockNameForSubtype ( subtype ) + ')(' + AlgebraicTypeUtils . attributesFromSubtype ( subtype ) . map ( matcherFunctionBlockTypeForAttribute ) . join ( ', ' ) + ')' ) ; } function matcherFunctionParameterNameForAlgebraicType ( algebraicType ) { return StringUtils . lowercased ( StringUtils . stringRemovingCapitalizedPrefix ( algebraicType . name ) ) ; } function matcherFunctionParameterForAlgebraicType ( algebraicType ) { return ( algebraicType . name + ' *' + matcherFunctionParameterNameForAlgebraicType ( algebraicType ) ) ; } function blockInvocationWrapper ( blockInvocation ) { return 'result = std::make_unique<T>(' + blockInvocation + ');' ; } function buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , soFar , subtype ) { return soFar . concat ( AlgebraicTypeUtilsForMatching . buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , subtype , blockInvocationWrapper ) ) ; } function functionParameterProviderWithAlgebraicTypeFirst ( algebraicType ) { return [ matcherFunctionParameterForAlgebraicType ( algebraicType ) ] . concat ( algebraicType . subtypes . map ( matcherFunctionParameterValueForSubtype ) ) ; } function functionParameterProviderWithAlgebraicTypeLast ( algebraicType ) { return algebraicType . subtypes . map ( matcherFunctionParameterValueForSubtype ) . concat ( matcherFunctionParameterForAlgebraicType ( algebraicType ) ) ; } function matcherFunctionCodeForAlgebraicType ( algebraicType , functionParameterProvider ) { const functionDeclaration = 'static T match(' + functionParameterProvider ( algebraicType ) . join ( ', ' ) + ') {' ; const matcherFunctionParameterName = matcherFunctionParameterNameForAlgebraicType ( algebraicType ) ; const assertionCode = 'NSCAssert(' + matcherFunctionParameterName + ' != nil, @"The ADT object ' + matcherFunctionParameterName + ' is nil");' ; const resultDeclaration = '__block std::unique_ptr<T> result;' ; const blockCode = algebraicType . subtypes . reduce ( ( soFar , subtype ) => buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , soFar , subtype ) , [ ] ) ; const keywordPartsForMatchInvocation = algebraicType . subtypes . reduce ( ( soFar , subtype , idx ) => AlgebraicTypeUtilsForMatching . buildKeywordPartsForInvokingMatchMethodForSubtype ( algebraicType , soFar , subtype , idx ) , [ ] ) ; const matchInvocationCode = '[' + matcherFunctionParameterName + ' ' + keywordPartsForMatchInvocation . join ( ' ' ) + '];' ; const returnCode = 'return *result;' ; const functionCode = [ assertionCode , resultDeclaration , '' ] . concat ( blockCode ) . concat ( matchInvocationCode ) . concat ( returnCode ) ; const endFunctionDeclaration = '}' ; return [ functionDeclaration ] . concat ( functionCode . map ( StringUtils . indent ( 2 ) ) ) . concat ( endFunctionDeclaration ) ; } function matcherFunctionShimCodeForAlgebraicType ( algebraicType ) { const functionDeclaration = 'static T match(' + functionParameterProviderWithAlgebraicTypeLast ( algebraicType ) . join ( ', ' ) + ') {' ; return [ functionDeclaration , `  return match(${ matcherFunctionParameterNameForAlgebraicType ( algebraicType ) }, ${ algebraicType . subtypes . map ( matchBlockNameForSubtype ) . join ( ', ' ) });` , '}' , ] ; } function templateForMatchingAlgebraicTypeWithCode ( code ) { return { templatedTypes : [ { type : CPlusPlus . TemplateType . Typename ( ) , value : 'T' } , ] , code : code } ; } function structForMatchingAlgebraicType ( algebraicType ) { return Code . Struct . ObjectiveCPlusPlusStruct ( { name : algebraicType . name + 'Matcher' , templates : [ templateForMatchingAlgebraicTypeWithCode ( [ ] ) ] , code : [ matcherFunctionCodeForAlgebraicType ( algebraicType , functionParameterProviderWithAlgebraicTypeFirst ) , matcherFunctionShimCodeForAlgebraicType ( algebraicType ) , ] } ) ; } function conditionallyAddToSpread < T > ( addIt , value ) { return addIt ? [ value ] : [ ] ; } function generateImports ( algebraicType , forBaseFile ) { return [ { file : 'Foundation.h' , isPublic : true , requiresCPlusPlus : false , library : 'Foundation' } , { file : algebraicType . name + '.h' , isPublic : true , requiresCPlusPlus : false , library : algebraicType . libraryName } , ... conditionallyAddToSpread ( ! forBaseFile , { file : matchingFileNameForAlgebraicType ( algebraicType ) + '.h' , isPublic : false , requiresCPlusPlus : false , library : null } ) , { file : 'memory' , isPublic : true , requiresCPlusPlus : true , library : null } , ] ; } function matchingFileForAlgebraicType ( algebraicType , forBaseFile ) { return { name : matchingFileNameForAlgebraicType ( algebraicType ) , type : Code . FileType . ObjectiveCPlusPlus , imports : generateImports ( algebraicType , forBaseFile ) , enumerations : [ ] , blockTypes : [ ] , comments : [ ] , forwardDeclarations : AlgebraicTypeUtilsForMatching . forwardDeclarationsForAlgebraicType ( algebraicType ) , staticConstants : [ ] , globalVariables : [ ] , functions : [ ] , classes : [ ] , diagnosticIgnores : [ ] , structs : [ structForMatchingAlgebraicType ( algebraicType ) ] , cppClasses : [ ] , namespaces : [ ] , macros : [ ] } ; } export function createAlgebraicTypePlugin ( ) { return { additionalFiles : function ( algebraicType ) : Code . File [ ] { return [ matchingFileForAlgebraicType ( algebraicType , false ) ] ; } , transformBaseFile : function ( algebraicType , baseFile ) : Code . File { baseFile . imports = baseFile . imports . concat ( generateImports ( algebraicType , true ) ) ; baseFile . structs . push ( structForMatchingAlgebraicType ( algebraicType ) ) ; return baseFile ; } , blockTypes : function ( algebraicType ) : ObjC . BlockType [ ] { return [ ] ; } , classMethods : function ( algebraicType ) : ObjC . Method [ ] { return [ ] ; } , enumerations : function ( algebraicType ) : ObjC . Enumeration [ ] { return [ ] ; } , transformFileRequest : function ( request ) : FileWriter . Request { return request ; } , fileType : function ( algebraicType ) : Code . FileType | null { return null ; } , forwardDeclarations : AlgebraicTypeUtilsForMatching . forwardDeclarationsForAlgebraicType , functions : function ( algebraicType ) : ObjC . Function [ ] { return [ ] ; } , headerComments : function ( algebraicType ) : ObjC . Comment [ ] { return [ ] ; } , implementedProtocols : function ( algebraicType ) : ObjC . ImplementedProtocol [ ] { return [ ] ; } , imports : function ( algebraicType ) : ObjC . Import [ ] { return [ ] ; } , instanceMethods : function ( algebraicType ) : ObjC . Method [ ] { return [ ] ; } , instanceVariables : function ( algebraicType ) : ObjC . InstanceVariable [ ] { return [ ] ; } , macros : function ( algebraicType ) : ObjC . Macro [ ] { return [ ] ; } , requiredIncludesToRun : [ 'TemplatedMatching' ] , staticConstants : function ( algebraicType ) : ObjC . Constant [ ] { return [ ] ; } , validationErrors : function ( algebraicType ) : Error . Error [ ] { return [ ] ; } , nullability : function ( algebraicType ) : ObjC . ClassNullability | null { return null ; } , subclassingRestricted : function ( algebraicType ) : boolean { return false ; } } ; }