# C# Templates
Templates:
    # Syntax
    EqualitySyntaxTemplate: &EqualitySyntaxTemplate !!js/function >
        (lhs, op, rhs) => {
            if (op.includes('!')) {
                return `${lhs} != ${rhs}`;
            }
            else {
                return `${lhs} == ${rhs}`;
            }
        }
    EosSyntaxTemplate: &EosSyntaxTemplate null
    EofTemplate: &EofSyntaxTemplate null
    # BSON Object Type templates
    CodeTypeTemplate: &CodeTypeTemplate null
    StringTypeTemplate: &StringTypeTemplate !!js/function >
        (str) => {
            let newStr = str;
            if (
              (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
              (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
              newStr = str.substr(1, str.length - 2);
            }
            return `"${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}"`;
        }
    RegexTypeTemplate: &RegexTypeTemplate !!js/function >
        (pattern, flags) => {
            flags = flags === '' ? '' : `(?${flags})`;
            // Double-quote stringify
            const str = flags + pattern;
            let newStr = str;
            if (
                (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
                (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
                    newStr = str.substr(1, str.length - 2);
            }
            return `new Regex("${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}")`;
        }
    BoolTypeTemplate: &BoolTypeTemplate null
    IntegerTypeTemplate: &IntegerTypeTemplate null
    DecimalTypeTemplate: &DecimalTypeTemplate null
    LongBasicTypeTemplate: &LongBasicTypeTemplate null
    HexTypeTemplate: &HexTypeTemplate null
    OctalTypeTemplate: &OctalTypeTemplate !!js/function >
        (literal) => {
            literal = literal.replace(/[oO]+/g, '0')
            return parseInt(literal, 8).toString()
        }
    NumericTypeTemplate: &NumericTypeTemplate null
    ArrayTypeTemplate: &ArrayTypeTemplate !!js/function >
        (literal, depth) => {
            const indent = '\n' + '    '.repeat(depth);
            const initialIndent = '\n' + '    '.repeat(depth-1);
            if (literal === '') {
              return 'new BsonArray()'
            }

            return `new BsonArray${initialIndent}{${literal}${initialIndent}}`;
        }
    ArrayTypeArgsTemplate: &ArrayTypeArgsTemplate !!js/function >
        (arg, depth, last) => {
            const indent = '\n' + '    '.repeat(depth);
            const arr = arg.split(', new').join(`, ${indent}new`)

            return last ? `${indent}${arr}` : `${indent}${arr},`;
        }
    NullTypeTemplate: &NullTypeTemplate !!js/function >
        () => {
            return 'BsonNull.Value';
        }
    UndefinedTypeTemplate: &UndefinedTypeTemplate !!js/function >
        () => {
            return 'BsonUndefined.Value';
        }
    ObjectTypeTemplate: &ObjectTypeTemplate !!js/function >
        (literal) => {
            if (literal === '') {
                return `new BsonDocument()`;
            }
            return literal;
        }
    ObjectTypeArgsTemplate: &ObjectTypeArgsTemplate !!js/function >
        (arg, depth) => {
            const indent = '\n' + '    '.repeat(depth);
            const initialIndent = '\n' + '    '.repeat(depth-1);
            const doubleStringify = (str) => {
                let newStr = str;
                if (
                  (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
                  (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
                    newStr = str.substr(1, str.length - 2);
                }
                return `"${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}"`;
            }

            if (arg.length === 1) {
              return `new BsonDocument(${doubleStringify(arg[0][0])}, ${arg[0][1]})`;
            }

            const pairs = arg.map((pair) => {
                return `${indent}{ ${doubleStringify(pair[0])}, ${pair[1]} }`;
            }).join(', ');

            return `new BsonDocument${initialIndent}{${pairs}${initialIndent}}`
        }
    # BSON Object Method templates
    CodeCodeTemplate: &CodeCodeTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Code`;
        }
    CodeCodeArgsTemplate: &CodeCodeArgsTemplate null
    CodeScopeTemplate: &CodeScopeTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Scope`;
        }
    CodeScopeArgsTemplate: &CodeScopeArgsTemplate null
    ObjectIdToStringTemplate: &ObjectIdToStringTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToString`;
        }
    ObjectIdToStringArgsTemplate: &ObjectIdToStringArgsTemplate null
    ObjectIdEqualsTemplate: &ObjectIdEqualsTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Equals`;
        }
    ObjectIdEqualsArgsTemplate: &ObjectIdEqualsArgsTemplate !!js/function >
        (lhs, arg) => {
            if (arg.indexOf('new') === 0) {
              arg = arg.replace(/new /g, '')
            }
            return `(new ${arg})`;
        }

    ObjectIdGetTimestampTemplate: &ObjectIdGetTimestampTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Timestamp`;
        }
    ObjectIdGetTimestampArgsTemplate: &ObjectIdGetTimestampArgsTemplate !!js/function >
        (lhs) => {
            return '';
        }
    BinaryValueTemplate: &BinaryValueTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToString`;
        }
    BinaryValueArgsTemplate: &BinaryValueArgsTemplate null
    BinaryLengthTemplate: &BinaryLengthTemplate !!js/function >
        () => {
          return '';
        }
    BinaryLengthArgsTemplate: &BinaryLengthArgsTemplate !!js/function >
        (lhs) => {
          return `${lhs}.Bytes.Length`;
        }
    BinaryToStringTemplate: &BinaryToStringTemplate !!js/function >
        (lhs) => {
          return `${lhs}.ToString`;
        }
    BinaryToStringArgsTemplate: &BinaryToStringArgsTemplate null
    BinarySubtypeTemplate: &BinarySubtypeTemplate !!js/function >
        (lhs) => {
          return `${lhs}.SubType`;
        }
    BinarySubtypeArgsTemplate: &BinarySubtypeArgsTemplate !!js/function >
        () => {
          return '';
        }
    DBRefGetDBTemplate: &DBRefGetDBTemplate !!js/function >
        (lhs) => {
          return `${lhs}.DatabaseName`;
        }
    DBRefGetDBArgsTemplate: &DBRefGetDBArgsTemplate !!js/function >
        () => {
          return '';
        }
    DBRefGetCollectionTemplate: &DBRefGetCollectionTemplate !!js/function >
        (lhs) => {
            return `${lhs}.CollectionName`;
        }
    DBRefGetCollectionArgsTemplate: &DBRefGetCollectionArgsTemplate !!js/function >
        () => {
          return '';
        }
    DBRefGetIdTemplate: &DBRefGetIdTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Id`;
        }
    DBRefGetIdArgsTemplate: &DBRefGetIdArgsTemplate !!js/function >
        () => {
          return '';
        }
    DBRefToStringTemplate: &DBRefToStringTemplate !!js/function >
        (lhs) => {
          return `${lhs}.ToString`;
        }
    DBRefToStringArgsTemplate: &DBRefToStringArgsTemplate null
    DoubleValueOfTemplate: &DoubleValueOfTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    DoubleValueOfArgsTemplate: &DoubleValueOfArgsTemplate !!js/function >
        (lhs) => {
            return '';
        }
    Int32ValueOfTemplate: &Int32ValueOfTemplate !!js/function >
        (lhs) => {
            return lhs;
        }
    Int32ValueOfArgsTemplate: &Int32ValueOfArgsTemplate !!js/function >
        (lhs) => {
            return '';
        }
    Int32ToStringTemplate: &Int32ToStringTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToString`;
        }
    Int32ToStringArgsTemplate: &Int32ToStringArgsTemplate null
    LongEqualsTemplate: &LongEqualsTemplate !!js/function >
        (lhs) => {
            return `${lhs} == `;
        }
    LongEqualsArgsTemplate: &LongEqualsArgsTemplate !!js/function >
        (lhs, arg) => {
              return `${arg}`;
        }
    LongToIntTemplate: &LongToIntTemplate !!js/function >
        (lhs) => {
            return `(int) ${lhs}`;
        }
    LongToIntArgsTemplate: &LongToIntArgsTemplate !!js/function >
        () => {
            return '';
        }
    LongValueOfTemplate: &LongValueOfTemplate !!js/function >
        (lhs) => {
            return `(int) ${lhs}`;
        }
    LongValueOfArgsTemplate: &LongValueOfArgsTemplate !!js/function >
        () => {
            return '';
        }
    LongToStringTemplate: &LongToStringTemplate !!js/function > # Also has emit method
        (lhs) => {
            return lhs;
        }
    LongToStringArgsTemplate: &LongToStringArgsTemplate null # Also has emit method
    LongToNumberTemplate: &LongToNumberTemplate !!js/function >
        (lhs) => {
            return `(double) ${lhs}`;
        }
    LongToNumberArgsTemplate: &LongToNumberArgsTemplate !!js/function >
        () => {
            return '';
        }
    LongAddTemplate: &LongAddTemplate !!js/function >
        (lhs) => {
            return `${lhs} +`;
        }
    LongAddArgsTemplate: &LongAddArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongSubtractTemplate: &LongSubtractTemplate !!js/function >
        (lhs) => {
            return `${lhs} -`;
        }
    LongSubtractArgsTemplate: &LongSubtractArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongMultiplyTemplate: &LongMultiplyTemplate !!js/function >
        (lhs) => {
            return `${lhs} *`;
        }
    LongMultiplyArgsTemplate: &LongMultiplyArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongDivTemplate: &LongDivTemplate !!js/function >
        (lhs) => {
            return `${lhs} /`;
        }
    LongDivArgsTemplate: &LongDivArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongModuloTemplate: &LongModuloTemplate !!js/function >
        (lhs) => {
            return `${lhs} %`;
        }
    LongModuloArgsTemplate: &LongModuloArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongAndTemplate: &LongAndTemplate !!js/function >
        (lhs) => {
            return `${lhs} &`;
        }
    LongAndArgsTemplate: &LongAndArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongOrTemplate: &LongOrTemplate !!js/function >
        (lhs) => {
            return `${lhs} |`;
        }
    LongOrArgsTemplate: &LongOrArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongXorTemplate: &LongXorTemplate !!js/function >
        (lhs) => {
            return `${lhs} ^`;
        }
    LongXorArgsTemplate: &LongXorArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongShiftLeftTemplate: &LongShiftLeftTemplate !!js/function >
        (lhs) => {
            return `${lhs} <<`;
        }
    LongShiftLeftArgsTemplate: &LongShiftLeftArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongShiftRightTemplate: &LongShiftRightTemplate !!js/function >
        (lhs) => {
            return `${lhs} >>`;
        }
    LongShiftRightArgsTemplate: &LongShiftRightArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongCompareTemplate: &LongCompareTemplate !!js/function >
        (lhs) => {
            return `${lhs} - `;
        }
    LongCompareArgsTemplate: &LongCompareArgsTemplate !!js/function >
        (lhs, arg) => {
            return arg;
        }
    LongIsOddTemplate: &LongIsOddTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    LongIsOddArgsTemplate: &LongIsOddArgsTemplate !!js/function >
        () => {
            return ' % 2 == 1';
        }
    LongIsZeroTemplate: &LongIsZeroTemplate !!js/function >
        (lhs) => {
            return `${lhs} == 0`;
        }
    LongIsZeroArgsTemplate: &LongIsZeroArgsTemplate !!js/function >
        () => {
            return '';
        }
    LongIsNegativeTemplate: &LongIsNegativeTemplate !!js/function >
        (lhs) => {
            return `${lhs} < 0`;
        }
    LongIsNegativeArgsTemplate: &LongIsNegativeArgsTemplate !!js/function >
        () => {
            return '';
        }
    LongNegateTemplate: &LongNegateTemplate !!js/function >
        () => {
            return '-';
        }
    LongNegateArgsTemplate: &LongNegateArgsTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    LongNotTemplate: &LongNotTemplate !!js/function >
        () => {
            return '~';
        }
    LongNotArgsTemplate: &LongNotArgsTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    LongNotEqualsTemplate: &LongNotEqualsTemplate !!js/function >
        (lhs) => {
            return `${lhs} !=`;
        }
    LongNotEqualsArgsTemplate: &LongNotEqualsArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongGreaterThanTemplate: &LongGreaterThanTemplate !!js/function >
        (lhs) => {
            return `${lhs} >`;
        }
    LongGreaterThanArgsTemplate: &LongGreaterThanArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongGreaterThanOrEqualTemplate: &LongGreaterThanOrEqualTemplate !!js/function >
        (lhs) => {
            return `${lhs} >=`;
        }
    LongGreaterThanOrEqualArgsTemplate: &LongGreaterThanOrEqualArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongLessThanTemplate: &LongLessThanTemplate !!js/function >
        (lhs) => {
            return `${lhs} <`;
        }
    LongLessThanArgsTemplate: &LongLessThanArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongLessThanOrEqualTemplate: &LongLessThanOrEqualTemplate !!js/function >
        (lhs) => {
            return `${lhs} <=`;
        }
    LongLessThanOrEqualArgsTemplate: &LongLessThanOrEqualArgsTemplate !!js/function >
        (lhs, arg) => {
            return ` ${arg}`;
        }
    LongFloatApproxTemplate: &LongFloatApproxTemplate !!js/function >
        (lhs) => {
            return `(float) ${lhs}`;
        }
    LongTopTemplate: &LongTopTemplate !!js/function >
        (lhs) => {
            return `${lhs} >> 32`;
        }
    LongBottomTemplate: &LongBottomTemplate !!js/function >
        (lhs) => {
           return `${lhs} & 0x0000ffff`;
        }
    TimestampToStringTemplate: &TimestampToStringTemplate !!js/function >
        (lhs) => {
          return `${lhs}.ToString`;
        }
    TimestampToStringArgsTemplate: &TimestampToStringArgsTemplate null
    TimestampEqualsTemplate: &TimestampEqualsTemplate !!js/function >
        (lhs) => {
          return `${lhs}.Equals`;
        }
    TimestampEqualsArgsTemplate: &TimestampEqualsArgsTemplate null
    TimestampGetLowBitsTemplate: &TimestampGetLowBitsTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToUniversalTime`;
        }
    TimestampGetLowBitsArgsTemplate: &TimestampGetLowBitsArgsTemplate null
    TimestampGetHighBitsTemplate: &TimestampGetHighBitsTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Increment`;
        }
    TimestampGetHighBitsArgsTemplate: &TimestampGetHighBitsArgsTemplate !!js/function >
        (lhs) => {
            return '';
        }
    TimestampTTemplate: &TimestampTTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToUniversalTime()`;
        }
    TimestampITemplate: &TimestampITemplate !!js/function >
        (lhs) => {
            return `${lhs}.Increment`;
        }
    TimestampCompareTemplate: &TimestampCompareTemplate !!js/function >
        (lhs) => {
            return `${lhs}.CompareTo`;
        }
    TimestampCompareArgsTemplate: &TimestampCompareArgsTemplate null
    TimestampNotEqualsTemplate: &TimestampNotEqualsTemplate !!js/function >
        (lhs) => {
            return `${lhs} != `;
        }
    TimestampNotEqualsArgsTemplate: &TimestampNotEqualsArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}`;
        }
    TimestampGreaterThanTemplate: &TimestampGreaterThanTemplate !!js/function >
        (lhs) => {
            return `${lhs} > `;
        }
    TimestampGreaterThanArgsTemplate: &TimestampGreaterThanArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}`;
        }
    TimestampGreaterThanOrEqualTemplate: &TimestampGreaterThanOrEqualTemplate !!js/function >
        (lhs) => {
            return `${lhs} >= `;
        }
    TimestampGreaterThanOrEqualArgsTemplate: &TimestampGreaterThanOrEqualArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}`;
        }
    TimestampLessThanTemplate: &TimestampLessThanTemplate !!js/function >
        (lhs) => {
            return `${lhs} < `;
        }
    TimestampLessThanArgsTemplate: &TimestampLessThanArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}`;
        }
    TimestampLessThanOrEqualTemplate: &TimestampLessThanOrEqualTemplate !!js/function >
        (lhs) => {
            return `${lhs} <= `;
        }
    TimestampLessThanOrEqualArgsTemplate: &TimestampLessThanOrEqualArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}`;
        }
    SymbolValueOfTemplate: &SymbolValueOfTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    SymbolValueOfArgsTemplate: &SymbolValueOfArgsTemplate !!js/function >
        () => {
            return '';
        }
    SymbolInspectTemplate: &SymbolInspectTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    SymbolInspectArgsTemplate: &SymbolInspectArgsTemplate !!js/function >
        () => {
            return '';
        }
    SymbolToStringTemplate: &SymbolToStringTemplate !!js/function >
        (lhs) => {
            return `${lhs}`;
        }
    SymbolToStringArgsTemplate: &SymbolToStringArgsTemplate !!js/function >
        () => {
            return '';
        }
    # Symbol Templates
    CodeSymbolTemplate: &CodeSymbolTemplate  !!js/function > # Also has process method
        () => {
            return 'BsonJavaScript';
        }
    CodeSymbolArgsTemplate: &CodeSymbolArgsTemplate !!js/function >
        (lhs, code, scope) => {
            // Double quote stringify
            let newStr = code === undefined ? '' : code;
            const str = newStr;
            if (
                (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
                (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
                newStr = str.substr(1, str.length - 2);
            }
            code = `"${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}"`;
            return (scope === undefined) ? `(${code})` : `WithScope(${code}, ${scope})`;
        }
    ObjectIdSymbolTemplate: &ObjectIdSymbolTemplate !!js/function >
        () => {
            return 'ObjectId';
        }
    ObjectIdSymbolArgsTemplate: &ObjectIdSymbolArgsTemplate !!js/function >
        (lhs, str) => {
            let newStr = str;
            if (
                (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
                (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
                newStr = str.substr(1, str.length - 2);
            }
            return `("${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}")`;
        }
    BinarySymbolTemplate: &BinarySymbolTemplate !!js/function >
        () => {
            return 'BsonBinaryData';
        }
    BinarySymbolArgsTemplate: &BinarySymbolArgsTemplate !!js/function >
        (lhs, bytes, type) => {
            const str = bytes;
            let newStr = str;
            if (
              (str.charAt(0) === '\'' && str.charAt(str.length - 1) === '\'') ||
              (str.charAt(0) === '"' && str.charAt(str.length - 1) === '"')) {
                newStr = str.substr(1, str.length - 2);
            }
            bytes = `"${newStr.replace(/\\([\s\S])|(")/g, '\\$1$2')}"`;

            if (type === null) {
                return `(System.Text.Encoding.ASCII.GetBytes(${bytes}))`;
            }
            return `(System.Text.Encoding.ASCII.GetBytes(${bytes}), ${type})`;
        }
    BinarySymbolSubtypeDefaultTemplate: &BinarySymbolSubtypeDefaultTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.Binary';
        }
    BinarySymbolSubtypeFunctionTemplate: &BinarySymbolSubtypeFunctionTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.Function';
        }
    BinarySymbolSubtypeByteArrayTemplate: &BinarySymbolSubtypeByteArrayTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.OldBinary';
        }
    BinarySymbolSubtypeUuidOldTemplate: &BinarySymbolSubtypeUuidOldTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.UuidLegacy';
        }
    BinarySymbolSubtypeUuidTemplate: &BinarySymbolSubtypeUuidTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.UuidStandard';
        }
    BinarySymbolSubtypeMd5Template: &BinarySymbolSubtypeMd5Template !!js/function >
        () => {
            return 'BsonBinarySubType.MD5';
        }
    BinarySymbolSubtypeUserDefinedTemplate: &BinarySymbolSubtypeUserDefinedTemplate !!js/function >
        () => {
            return 'BsonBinarySubType.UserDefined';
        }
    DBRefSymbolTemplate: &DBRefSymbolTemplate !!js/function >
        () => {
            return 'MongoDBRef';
        }
    DBRefSymbolArgsTemplate: &DBRefSymbolArgsTemplate null
    DoubleSymbolTemplate: &DoubleSymbolTemplate !!js/function >
        () => {
            return '';
        }
    DoubleSymbolArgsTemplate: &DoubleSymbolArgsTemplate !!js/function >
        (lhs, arg, type) => {
            arg = arg === undefined ? 0 : arg;

            if (type === '_hex') {
              return `Convert.ToDouble(${arg})`;
            }
            if (type === '_string') {
              return `Convert.ToDouble(${arg})`;
            }

            if (type === '_decimal') {
              return arg;
            }

            return `${Math.round(arg).toFixed(1)}`;
        }
    Int32SymbolTemplate: &Int32SymbolTemplate !!js/function >
        () => {
            return '';
        }
    Int32SymbolArgsTemplate: &Int32SymbolArgsTemplate !!js/function >
        (lhs, arg, type) => {
            arg = arg === undefined ? 0 : arg;

            if (type === '_hex' || type === '_decimal' || type === '_string') {
              return `Convert.ToInt32(${arg})`;
            }

            return arg;
        }
    LongSymbolTemplate: &LongSymbolTemplate !!js/function >
        () => {
            return '';
        }
    LongSymbolArgsTemplate: &LongSymbolArgsTemplate !!js/function > 
        (lhs, arg) => {
            if (arg.indexOf('\'') === 0 || arg.indexOf('"') === 0) {
              return `Convert.ToInt64(${arg})`;
            }
            return `${arg}L`;
        }
    LongSymbolMaxTemplate: &LongSymbolMaxTemplate !!js/function >
        () => {
            return 'Int64.MaxValue';
        }
    LongSymbolMaxArgsTemplate: &LongSymbolMaxArgsTemplate null
    LongSymbolMinTemplate: &LongSymbolMinTemplate !!js/function >
        () => {
            return 'Int64.MinValue';
        }
    LongSymbolMinArgsTemplate: &LongSymbolMinArgsTemplate null
    LongSymbolZeroTemplate: &LongSymbolZeroTemplate !!js/function >
        (lhs) => {
            return '0L';
        }
    LongSymbolZeroArgsTemplate: &LongSymbolZeroArgsTemplate null
    LongSymbolOneTemplate: &LongSymbolOneTemplate !!js/function >
        (lhs) => {
            return '1L';
        }
    LongSymbolOneArgsTemplate: &LongSymbolOneArgsTemplate null
    LongSymbolNegOneTemplate: &LongSymbolNegOneTemplate !!js/function >
        (lhs) => {
            return '-1L';
        }
    LongSymbolNegOneArgsTemplate: &LongSymbolNegOneArgsTemplate null
    LongSymbolFromBitsTemplate: &LongSymbolFromBitsTemplate !!js/function > # Also has process method
        () => {
            return '';
        }
    LongSymbolFromBitsArgsTemplate: &LongSymbolFromBitsArgsTemplate null
    LongSymbolFromIntTemplate: &LongSymbolFromIntTemplate !!js/function >
        () => {
            return '';
        }
    LongSymbolFromIntArgsTemplate: &LongSymbolFromIntArgsTemplate !!js/function >
        (lhs, arg) => {
            return `${arg}L`;
        }
    LongSymbolFromNumberTemplate: &LongSymbolFromNumberTemplate null
    LongSymbolFromNumberArgsTemplate: &LongSymbolFromNumberArgsTemplate null
    LongSymbolFromStringTemplate: &LongSymbolFromStringTemplate !!js/function >
        () => {
            return 'Convert.ToInt64';
        }
    LongSymbolFromStringArgsTemplate: &LongSymbolFromStringArgsTemplate !!js/function >
        (lhs, arg1, arg2) => {
          if (arg2) {
            return `(${arg1}, ${arg2})`
          }
          return `(${arg1}, 10)`
        }
    MinKeySymbolTemplate: &MinKeySymbolTemplate !!js/function > # Also has emit method
        () => {
            return 'BsonMinKey';
        }
    MinKeySymbolArgsTemplate: &MinKeySymbolArgsTemplate !!js/function > # Also has emit method
        () => {
            return '.Value';
        }
    MaxKeySymbolTemplate: &MaxKeySymbolTemplate !!js/function > # Also has emit method
        () => {
            return 'BsonMaxKey';
        }
    MaxKeySymbolArgsTemplate: &MaxKeySymbolArgsTemplate !!js/function > # Also has emit method
        () => {
            return '.Value';
        }
    TimestampSymbolTemplate: &TimestampSymbolTemplate !!js/function >
        () => {
            return 'BsonTimestamp';
        }
    TimestampSymbolArgsTemplate: &TimestampSymbolArgsTemplate !!js/function >
        (lhs, arg1, arg2) => {
            if (typeof arg1 === 'undefined') {
                return '(0, 0)'
            }
            return `(${arg1}, ${arg2})`;
        }
    SymbolSymbolTemplate: &SymbolSymbolTemplate null # Has emit method 
    SymbolSymbolArgsTemplate: &SymbolSymbolArgsTemplate null # Has emit method
    BSONRegExpSymbolTemplate: &BSONRegExpSymbolTemplate !!js/function >
        () => {
            return 'BsonRegularExpression';
        }
    BSONRegExpSymbolArgsTemplate: &BSONRegExpSymbolArgsTemplate !!js/function >
        (lhs, pattern, flags) => {
            return `(${pattern}${flags ? ', ' + flags : ''})`;
        }
    Decimal128SymbolTemplate: &Decimal128SymbolTemplate null
    Decimal128SymbolArgsTemplate: &Decimal128SymbolArgsTemplate null
    Decimal128SymbolFromStringTemplate: &Decimal128SymbolFromStringTemplate !!js/function >
        (lhs) => {
            return `${lhs}.Parse`;
        }
    Decimal128SymbolFromStringArgsTemplate: &Decimal128SymbolFromStringArgsTemplate !!js/function > 
        (lhs, arg) => {
            return `(${arg})`;
        }
    Decimal128ToStringTemplate: &Decimal128ToStringTemplate !!js/function >
        (lhs) => {
            return `${lhs}.ToString`;
        }
    Decimal128ToStringArgsTemplate: &Decimal128ToStringArgsTemplate null
    # BSON Util Templates
    ObjectIdCreateFromHexStringTemplate: &ObjectIdCreateFromHexStringTemplate !!js/function >
        (lhs) => {
            return `new ${lhs}`;
        }
    ObjectIdCreateFromHexStringArgsTemplate: &ObjectIdCreateFromHexStringArgsTemplate null
    ObjectIdCreateFromTimeTemplate: &ObjectIdCreateFromTimeTemplate !!js/function > # Also has emit method
        (lhs) => {
            return `new ${lhs}.GenerateNewId`;
        }
    ObjectIdCreateFromTimeArgsTemplate: &ObjectIdCreateFromTimeArgsTemplate !!js/function >
        (lhs, arg) => {
            if (!Number.isNaN(parseInt(arg, 10))) {
              return `(Convert.ToInt32(${arg}))`;
            }
            return `(${arg})`;
        }
    ObjectIdIsValidTemplate: &ObjectIdIsValidTemplate !!js/function >
        (lhs) => {
            return `new ${lhs}`;
        }
    ObjectIdIsValidArgsTemplate: &ObjectIdIsValidArgsTemplate null
    # JS Symbol Templates
    ObjectSymbolTemplate: &ObjectSymbolTemplate null
    ObjectSymbolArgsTemplate: &ObjectSymbolArgsTemplate null
    ObjectSymbolCreateTemplate: &ObjectSymbolCreateTemplate !!js/function >
        () => {
            return '';
        }
    ObjectSymbolCreateArgsTemplate: &ObjectSymbolCreateArgsTemplate !!js/function >
        (lhs, arg) => {
            return arg;
        }
    NumberSymbolTemplate: &NumberSymbolTemplate !!js/function >
        () => {
            return '';
        }
    NumberSymbolArgsTemplate: &NumberSymbolArgsTemplate !!js/function >
        (lhs, arg, type) => {
            arg = arg === undefined ? 0 : arg;

            if (type === '_string') {
              if (arg.indexOf('.') >= 0) {
                return `float.Parse(${arg})`
              }
              return `int.Parse(${arg})`;
            }

            return `(int) ${arg}`;
        }
    DateSymbolTemplate: &DateSymbolTemplate !!js/function > # Also has emit method
        () => {
            return 'Datetime';
        }
    DateSymbolArgsTemplate: &DateSymbolArgsTemplate null # Has emit method
    DateSymbolNowTemplate: &DateSymbolNowTemplate null # Has emit method
    DateSymbolNowArgsTemplate: &DateSymbolNowArgsTemplate null # Has emit method
    RegExpSymbolTemplate: &RegExpSymbolTemplate !!js/function > # Also has process method
        () => {
            return 'Regex';
        }
    RegExpSymbolArgsTemplate: &RegExpSymbolArgsTemplate null
    ImportTemplate: &ImportTemplate !!js/function >
        (args) => {
            const universal = ['using MongoDB.Bson;', 'using MongoDB.Driver;'];
            const all = universal.concat(Object.values(args));
            return all.join('\n');
        }
    0ImportTemplate: &0ImportTemplate null
    1ImportTemplate: &1ImportTemplate null
    2ImportTemplate: &2ImportTemplate null
    3ImportTemplate: &3ImportTemplate null
    4ImportTemplate: &4ImportTemplate null
    5ImportTemplate: &5ImportTemplate null
    6ImportTemplate: &6ImportTemplate null
    7ImportTemplate: &7ImportTemplate null
    8ImportTemplate: &8ImportTemplate !!js/function >
        () => {
            return 'using System.Text.RegularExpressions;';
        }
    9ImportTemplate: &9ImportTemplate null
    10ImportTemplate: &10ImportTemplate null
    11ImportTemplate: &11ImportTemplate null
    12ImportTemplate: &12ImportTemplate null
    100ImportTemplate: &100ImportTemplate null
    101ImportTemplate: &101ImportTemplate null
    102ImportTemplate: &102ImportTemplate null
    103ImportTemplate: &103ImportTemplate null
    104ImportTemplate: &104ImportTemplate null
    105ImportTemplate: &105ImportTemplate null
    106ImportTemplate: &106ImportTemplate null
    107ImportTemplate: &107ImportTemplate null
    108ImportTemplate: &108ImportTemplate null
    109ImportTemplate: &109ImportTemplate null
    110ImportTemplate: &110ImportTemplate null
    111ImportTemplate: &111ImportTemplate null
    112ImportTemplate: &112ImportTemplate null
    113ImportTemplate: &113ImportTemplate null
    114ImportTemplate: &114ImportTemplate null
    200ImportTemplate: &200ImportTemplate !!js/function >
        () => {
            return 'using System;';
        }
    201ImportTemplate: &201ImportTemplate null
    300ImportTemplate: &300ImportTemplate null
    301ImportTemplate: &301ImportTemplate null
    302ImportTemplate: &302ImportTemplate null
    303ImportTemplate: &303ImportTemplate null
    304ImportTemplate: &304ImportTemplate null
    305ImportTemplate: &305ImportTemplate null
    306ImportTemplate: &306ImportTemplate null
