; SPDX-License-Identifier: MIT


; union type operators

Import "stdops.rgr"
Import "JSON.rgr"

operators {

    cast _:Any  (arg:T target@(noeval):Any) {
        templates {
            go ( (e 1) )
        }
    }

    case             _@(newcontext):void           ( arg@(union):T item2@(define):boolean code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is bool ) /* union case for boolean */ {" nl I
                        "bool " (e 2) " = " (e 1) " as bool;" nl
                        (block 3)
                        nl i "}"
            )
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<bool>(" (e 1) ") ) /* union case for boolean */ {" nl I
                        "bool " (e 2) " = std::get<bool>(" (e 1) ");" nl
                        (block 3)
                        nl i "}"
            )
            es6 (
                (forkctx _ ) (def 2) "if( typeof(" (e 1) ") === 'boolean' ) /* union case for boolean */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is Boolean ) /* union case for boolean */ {" nl I
                        "val " (e 2) " : Boolean = " (e 1) " as Boolean;" nl
                        (block 3)
                        nl i "}"
            )
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", bool):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            )
            rust (
                (forkctx _ ) (def 2) "if let RJson::Bool(" (e 2) ") = &" (e 1) " {" nl I
                        "let " (e 2) " : bool = *" (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
        }
    }
    case             _@(newcontext):void           ( arg@(union):T item2@(define):int code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is int ) /* union case for int */ {" nl I
                        "int " (e 2) " = " (e 1) " as int;" nl
                        (block 3)
                        nl i "}"
            )
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<int>(" (e 1) ") ) /* union case for int */ {" nl I
                        "int " (e 2) " = std::get<int>(" (e 1) ");" nl
                        (block 3)
                        nl i "}"
            )
            es6 (
                (forkctx _ ) (def 2) "if( " "Number.isInteger ? Number.isInteger(" (e 1) ") : (function(v) { return typeof v === 'number' &&  isFinite(v) && Math.floor(v) === v; })(" (e 1) ")" " ) /* union case for int */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            php (
                (forkctx _ ) (def 2) "if( is_int(" (e 1) ") ) {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is Int ) /* union case for int */ {" nl I
                        "val " (e 2) " : Int = " (e 1) " as Int;" nl
                        (block 3)
                        nl i "}"
            )
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", int) and not isinstance(" (e 1) ", bool):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            )
            rust (
                (forkctx _ ) (def 2) "if let RJson::Int(" (e 2) ") = &" (e 1) " {" nl I
                        "let " (e 2) " : i64 = *" (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
        }
    }
    case             _@(newcontext):void           ( arg@(union):T item2@(define):double code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is double ) /* union case for double */ {" nl I
                        "double " (e 2) " = " (e 1) " as double;" nl
                        (block 3)
                        nl i "}"
            )
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<double>(" (e 1) ") ) /* union case for double */ {" nl I
                        "double " (e 2) " = std::get<double>(" (e 1) ");" nl
                        (block 3)
                        nl i "}"
            )
            es6 (
                (forkctx _ ) (def 2) "if ( (function(v) { return typeof v === 'number' &&  isFinite(v); })(" (e 1) "))" " /* union case for double */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            php (
                (forkctx _ ) (def 2) "if( is_double(" (e 1) ") ) {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is Double ) /* union case for double */ {" nl I
                        "val " (e 2) " : Double = " (e 1) " as Double;" nl
                        (block 3)
                        nl i "}"
            )
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", float):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            )
            rust (
                (forkctx _ ) (def 2) "if let RJson::Double(" (e 2) ") = &" (e 1) " {" nl I
                        "let " (e 2) " : f64 = *" (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
        }
    }

    case             _@(newcontext):void           ( arg@(union):T item2@(define):string code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is String ) /* union case for string */ {" nl I
                        "String " (e 2) " = " (e 1) " as String;" nl
                        (block 3)
                        nl i "}"
            )
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<std::string>(" (e 1) ") ) /* union case for string */ {" nl I
                        "std::string " (e 2) " = std::get<std::string>(" (e 1) ");" nl
                        (block 3)
                        nl i "}"
            )
            es6 (
                (forkctx _ ) (def 2) "if( typeof(" (e 1) ") === 'string' ) /* union case for string */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            php (
                (forkctx _ ) (def 2) "if( is_string(" (e 1) ") ) {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is String ) /* union case for string */ {" nl I
                        "val " (e 2) " : String = " (e 1) " as String;" nl
                        (block 3)
                        nl i "}"
            )
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", str):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            )
            rust (
                (forkctx _ ) (def 2) "if let RJson::Str(" (e 2) ") = &" (e 1) " {" nl I
                        "let " (e 2) " : String = " (e 2) ".clone();" nl
                        (block 3)
                        nl i "}"
            )
            ranger ( 'case ' (e 1) " " (e 2) ":" (typeof 2) " {" nl
                        I (block 3) i nl "}" nl
            )
        }
    }
    ; A member of a system union -- the JSON unions over JSONDataObject and
    ; JSONArrayObject -- is an ordinary object of the target language. It
    ; carries no shape tag, so the generic overload below, which narrows on
    ; __rg_kind / _rg_kind / .tag, can never be true for one. These two
    ; overloads keep the class test that a system union needs; the generic
    ; overload keeps the tag test that a shape case needs.
    ; Every `@serialize` deserializer emits `case item oo:JSONDataObject`,
    ; so without these the reader of an array of objects reads nothing.
    case             _@(newcontext):void           ( arg@(union):T item2@(define):JSONDataObject code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            ranger ( "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl
                        I (block 3) i nl "}" nl
            )
            php (
                (forkctx _ ) (def 2) "if( is_object(" (e 1) ") && get_class(" (e 1) ") == \"" (typeof 2) "\" ) /* union case */ {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            es6 (
                (forkctx _ ) (def 2) "if( " (e 1) " instanceof " (typeof 2) " ) /* union case */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )  
            go (
                (forkctx _ ) (def 2) "switch " (e 1) ".(type) {" nl I
                        "case " (typeof 2) ":" nl
                            I nl
                            "var " (e 2) " " (typeof 2) " = " (e 1) ".(" (typeof 2) ");" nl
                            ; Go rejects a declared-and-unused local, and an arm
                            ; that only tests the type never reads the binding.
                            "_ = " (e 2) ";" nl
                            (block 3)
                            i nl
                        nl i "}"
            ) 
            csharp (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ")" (e 1) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            swift3 (
                (forkctx _ ) (def 2) "if type(of: " (e 1) ") == " (typeof 2) ".self  {" nl I
                        "let " (e 2) " = " (e 1) " as! " (typeof 2)";" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            swift6 (
                (forkctx _ ) (def 2) "if type(of: " (e 1) ") == " (typeof 2) ".self  {" nl I
                        "let " (e 2) " = " (e 1) " as! " (typeof 2)";" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            java7 (
                (forkctx _ ) (def 2) "if( " (e 1) " instanceof " (typeof 2) ") {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ") " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        "val " (e 2) " : " (typeof 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", " (typeof 2) "):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            ) 
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<" (typeof 2) ">(" (e 1) ") ) {" nl I
                        (typeof 2) " " (e 2) " = std::get<" (typeof 2) ">(" (e 1) ");" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            scala (
                (forkctx _ ) (def 2) (e 1) " match {" nl
                I "case " (e 2) " : " (typeof 2) " => {" nl I
                            (block 3)
                        nl i "}" nl
                  "case _ => {} " nl i 
                  "}" nl
            ) 
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        (typeof 2) " " (e 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            rust (
                (forkctx _ ) (def 2) "if let " (typeof 1) "::" (typeof 2) "(" (e 2) ") = " (e 1) ".clone() { /* union case */" nl I
                        (block 3)
                        nl i "}"
            ) 
            
        }
    }
    case             _@(newcontext):void           ( arg@(union):T item2@(define):JSONArrayObject code:block )  {
        templates {
            llvm ( nl "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl I (block 3) nl i "}" nl )
            ; A union that holds a primitive is not sealable, so its Go type is
            ; interface{} and there is no tag struct: the generic overload below
            ; wrote `x.tag == interface{}_tag_string`, which is not even Go
            ; syntax. A type assertion is the narrowing here.
            go (
                (forkctx _ ) (def 2) "if " (e 2) ", __rg_ok := " (e 1) ".(" (typeof 2) "); __rg_ok { /* union case */" nl I
                        "_ = " (e 2) ";" nl
                        (block 3)
                        nl i "}"
            )
            ranger ( "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl
                        I (block 3) i nl "}" nl
            )
            php (
                (forkctx _ ) (def 2) "if( is_object(" (e 1) ") && get_class(" (e 1) ") == \"" (typeof 2) "\" ) /* union case */ {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            es6 (
                (forkctx _ ) (def 2) "if( " (e 1) " instanceof " (typeof 2) " ) /* union case */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )  
            go (
                (forkctx _ ) (def 2) "switch " (e 1) ".(type) {" nl I
                        "case " (typeof 2) ":" nl
                            I nl
                            "var " (e 2) " " (typeof 2) " = " (e 1) ".(" (typeof 2) ");" nl
                            ; Go rejects a declared-and-unused local, and an arm
                            ; that only tests the type never reads the binding.
                            "_ = " (e 2) ";" nl
                            (block 3)
                            i nl
                        nl i "}"
            ) 
            csharp (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ")" (e 1) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            swift3 (
                (forkctx _ ) (def 2) "if type(of: " (e 1) ") == " (typeof 2) ".self  {" nl I
                        "let " (e 2) " = " (e 1) " as! " (typeof 2)";" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            swift6 (
                (forkctx _ ) (def 2) "if type(of: " (e 1) ") == " (typeof 2) ".self  {" nl I
                        "let " (e 2) " = " (e 1) " as! " (typeof 2)";" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            java7 (
                (forkctx _ ) (def 2) "if( " (e 1) " instanceof " (typeof 2) ") {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ") " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        "val " (e 2) " : " (typeof 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            python (
                (forkctx _ ) (def 2) "if isinstance(" (e 1) ", " (typeof 2) "):" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            ) 
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<" (typeof 2) ">(" (e 1) ") ) {" nl I
                        (typeof 2) " " (e 2) " = std::get<" (typeof 2) ">(" (e 1) ");" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            scala (
                (forkctx _ ) (def 2) (e 1) " match {" nl
                I "case " (e 2) " : " (typeof 2) " => {" nl I
                            (block 3)
                        nl i "}" nl
                  "case _ => {} " nl i 
                  "}" nl
            ) 
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        (typeof 2) " " (e 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            rust (
                (forkctx _ ) (def 2) "if let " (typeof 1) "::" (typeof 2) "(" (e 2) ") = " (e 1) ".clone() { /* union case */" nl I
                        (block 3)
                        nl i "}"
            ) 
            
        }
    }
    case             _@(newcontext):void           ( arg@(union):T item@(define):T code:block )  {
        templates {
            ranger ( "case " (e 1) " " (e 2) ":" (typeof 2) " {" nl
                        I (block 3) i nl "}" nl
            )
            php (
                (forkctx _ ) (def 2) "if( is_object(" (e 1) ") && get_class(" (e 1) ") == \"" (typeof 2) "\" ) /* union case */ {" nl I
                        (e 2) " = " (e 1) ";" nl
                        (block 3)                        
                        nl i "}"
            )  
            es6 (
                ; S5 FlatTaggedObject: narrow on the stable kind tag. Sealable
                ; union members declare/set __rg_kind; a dual instanceof OR
                ; would defeat TypeScript control-flow narrowing.
                (forkctx _ ) (def 2) "if( " (e 1) " != null && " (e 1) ".__rg_kind === \"" (typeof 2) "\" ) /* union case */ {" nl I
                        "var " (e 2) " = " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )  
            go (
                ; S5 tagged struct: narrow on the tag, bind the variant pointer.
                (forkctx _ ) (def 2) "if " (e 1) ".tag == " (typeof 1) "_tag_" (rawtype 2) " { /* union case */" nl I
                        "var " (e 2) " " (typeof 2) " = " (e 1) "." (rawtype 2) ";" nl
                            ; Go rejects a declared-and-unused local, and an arm
                            ; that only tests the type never reads the binding.
                            "_ = " (e 2) ";" nl
                            (block 3)
                        nl i "}"
            ) 
            csharp (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ")" (e 1) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            swift3 (
                (forkctx _ ) (def 2) "if case let ." (rawtype 2) "(" (e 2) ") = " (e 1) " { /* union case */" nl I
                        (block 3)
                        nl i "}"
            ) 
            swift6 (
                (forkctx _ ) (def 2) "if case let ." (rawtype 2) "(" (e 2) ") = " (e 1) " { /* union case */" nl I
                        (block 3)
                        nl i "}"
            ) 
            java7 (
                (forkctx _ ) (def 2) "if( " (e 1) " instanceof " (typeof 2) ") {" nl I
                        (typeof 2) " " (e 2) " = (" (typeof 2) ") " (e 1) ";" nl
                        (block 3)
                        
                        nl i "}"
            )
            kotlin (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        "val " (e 2) " : " (typeof 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            python (
                ; S5 FlatTaggedObject: narrow on _rg_kind (single underscore —
                ; __rg_kind would be Python-mangled on the instance).
                (forkctx _ ) (def 2) "if " (e 1) " is not None and getattr(" (e 1) ", \"_rg_kind\", None) == \"" (typeof 2) "\":" nl I
                        (e 2) " = " (e 1) nl
                        (block 3)
                        nl i
            ) 
            cpp (
                (forkctx _ ) (def 2) "if( std::holds_alternative<" (typeof 2) ">(" (e 1) ") ) {" nl I
                        (typeof 2) " " (e 2) " = std::get<" (typeof 2) ">(" (e 1) ");" nl
                        (block 3)
                        
                        nl i "}"
            ) 
            scala (
                (forkctx _ ) (def 2) (e 1) " match {" nl
                I "case " (e 2) " : " (typeof 2) " => {" nl I
                            (block 3)
                        nl i "}" nl
                  "case _ => {} " nl i 
                  "}" nl
            ) 
            dart (
                (forkctx _ ) (def 2) "if( " (e 1) " is " (typeof 2) " ) /* union case */ {" nl I
                        (typeof 2) " " (e 2) " = " (e 1) " as " (typeof 2) ";" nl
                        (block 3)
                        nl i "}"
            ) 
            rust (
                (forkctx _ ) (def 2) "if let " (typeof 1) "::" (typeof 2) "(" (e 2) ") = &" (e 1) " { /* union case */" nl I
                        (block 3)
                        nl i "}"
            )

        }
    }
    ; `is v Shape.Case` — the kind test of `case` with the binding and the
    ; block taken away. `case v x:Shape.Case { … }` answers "is it this case?"
    ; and then hands the arm a narrowed `x`; an arm that only wants the answer
    ; pays for a binding it never reads. On Rust that binding costs a
    ; `.clone()` of the scrutinee (a refcount pair for every reference case),
    ; on Go a declared-and-discarded local, on ES6/Python a dead assignment.
    ; This is an expression, so it composes: `return (is self EvalValue.Number)`
    ; replaces the whole `case self n:… { return true } return false` shape.
    ; Every template below is the same discriminant test the `case` operator
    ; above already lowers to on that target — nothing new is invented, and no
    ; target evaluates (e 1) more than once.
    is _:boolean ( arg@(union):T item@(noeval):T ) {
        templates {
            ranger ( "(is " (e 1) " " (typeof 2) ")" )
            ; S5 FlatTaggedObject: the stable kind tag, no `var x = …` after it
            es6    ( "(" (e 1) " != null && " (e 1) ".__rg_kind === \"" (typeof 2) "\")" )
            ; single underscore — __rg_kind would be Python-mangled on the instance
            python ( "(" (e 1) " is not None and getattr(" (e 1) ", \"_rg_kind\", None) == \"" (typeof 2) "\")" )
            ; S5 tagged struct: the tag compare alone, no variant pointer bound
            go     ( "(" (e 1) ".tag == " (typeof 1) "_tag_" (rawtype 2) ")" )
            ; matches! reads the discriminant in place — no clone of the
            ; scrutinee, which is the whole point of this operator on Rust
            rust   ( "matches!(" (e 1) ", " (typeof 1) "::" (typeof 2) "(..))" )
            ; holds_alternative is already the bare index compare; the `case`
            ; template only added std::get on top of it
            cpp    ( "std::holds_alternative<" (typeof 2) ">(" (e 1) ")" )
            csharp ( "(" (e 1) " is " (typeof 2) ")" )
            kotlin ( "(" (e 1) " is " (typeof 2) ")" )
            dart   ( "(" (e 1) " is " (typeof 2) ")" )
            java7  ( "(" (e 1) " instanceof " (typeof 2) ")" )
            php    ( "(is_object(" (e 1) ") && get_class(" (e 1) ") == \"" (typeof 2) "\")" )
            scala  ( "(" (e 1) ".isInstanceOf[" (typeof 2) "])" )
            ; Swift's enum test is `if case`, a statement — an immediately
            ; applied closure is the only way to spell it as an expression.
            ; (e 1) is still named once, so it is still evaluated once.
            swift3 ( "({ () -> Bool in if case ." (rawtype 2) " = " (e 1) " { return true }; return false })()" )
            swift6 ( "({ () -> Bool in if case ." (rawtype 2) " = " (e 1) " { return true }; return false })()" )
        }
    }
    ; Reference identity: are these two names the same object? `==` means
    ; something different on every target — structural on Kotlin, a trait bound
    ; on Rust, pointer on C++ — so identity gets its own operator rather than a
    ; per-target reading of equality. Used by the generated equality of a shape's
    ; `@(reference)` cases (PLAN_SHAPES.md S4), and useful on its own.
    identical  _:boolean ( a:T b:T ) {
        templates {
            es6    ( "(" (e 1) " === " (e 2) ")" )
            python ( "(" (e 1) " is " (e 2) ")" )
            kotlin ( "(" (e 1) " === " (e 2) ")" )
            csharp ( "Object.ReferenceEquals(" (e 1) ", " (e 2) ")" )
            dart   ( "identical(" (e 1) ", " (e 2) ")" )
            swift3 ( "(" (e 1) " === " (e 2) ")" )
            swift6 ( "(" (e 1) " === " (e 2) ")" )
            scala  ( "(" (e 1) " eq " (e 2) ")" )
            php    ( "(" (e 1) " === " (e 2) ")" )
            rust   ( (e 1) ".rg_identical(&" (e 2) ")" )
            ; java7, go and cpp compare the handle itself: a reference, a
            ; pointer and a shared_ptr respectively
            * ( "(" (e 1) " == " (e 2) ")" )
        }
    }
    to   _:T ( to@(union noeval):T item:T) {
        templates {
            cpp.disabled ( "(r_create_union<" (typeof 1) ", " (typeof 2)">(" (e 2) "))" 
(create_polyfill 
'
template<typename T, typename S>
T r_create_union (S value) {
    T rv;
    rv = value;
    return rv;
};
'
)            
            )
            * ( (e 2) )
        }
    } 
}


operator type:[string:T] all {
    fn forEach:void (cb:(_:void (item:T index:string )) ) {
        def list (keys self)
        for list kk:string i {
            def value (unwrap (get self kk))
            cb( value kk )
        }
    } 
    fn forKeys:void (cb:(_:void (index:string))) {
        def list (keys self)
        for list it:string i {
            cb(it)
        }
    }      

    fn values:[T] () @doc('All values of the map') {
        def res:[T]
        def list (keys self)
        for list kk:string i {
            push res (unwrap (get self kk))
        }
        return res
    }

    fn map_length:int () @doc('Number of keys in the map') {
        def list (keys self)
        return (array_length list)
    }

    fn get_or:T (key:string fallback:T) @doc('Value at `key`, or `fallback` when absent') {
        if( has self key ) {
            return (unwrap (get self key))
        }
        return fallback
    }
}

; commented out native ops, because they are not compatible with async operators
operator type:[T] all {

    ; pure function or operator could be simply inlined...
    fn forEach@(pure):void (cb:(_:void (item:T index:int))) @doc('Call `fb` for each item in array') {
        for self it:T i {
            cb(it i)
        }
    }  

    fn has:boolean ( el:T) {
        def idx (indexOf el)
        return (idx >= 0)
    }

    fn map:[T] (cb:(_:T (item:T index:int))) {
        def len (array_length self)
        def res:[T] 
        for self it:T i {
            push res (cb(it i))
        }
        return res
    }  

    fn map:[S] (cb:(_:S (item:T index:int)) to@(noeval):[S] ) {
        def len (array_length self)
        def res:[S] 
        for self it:T i {
            push res (cb(it i))
        }
        return res
    }  

    fn filter:[T] (cb:(_:boolean (item:T index:int))) {
        def res:[T]
        for self it:T i {
            if( cb(it i) )  {
                push res it
            }
        }
        return res
    } 
    fn reduce@(weak):T (cb:(_:T (left:T right:T index:int)) initialValue:T) {
        def len (array_length self)
        def res:T initialValue
        if( len >= 1 ) {
            for self it:T i {
                res = ( cb ( res it i))
            }
        }        
        return res
    }     
    fn reduce@(weak):K (cb:(_:K (left:K right:T index:int)) initialValue:K) {
        def len (array_length self)
        def res initialValue
        if( len >= 1 ) {
            for self it:T i {
                res = ( cb ( res it i ))
            }
        }        
        return res
    }  

    fn groupBy:[T] ( cb:(_:string (item:T)) ) {
        def res:[T]
        def mapper:[string:boolean]
        for self it:T i {
            def key (cb(it))
            if( false == ( has mapper key ) ) {
                push res it
                set mapper key true
            } 
        }
        return res
    }

    fn clone:[T] () {
        def res:[T]
        for self it:T i {
            push res it
        }
        return res
    }


    ; some useful functions from scala
    fn find@(optional):T (cb:(_:boolean (item:T))) {
        def res@(optional):T
        for self it@(lives):T i {
            if( cb(it) )  {
                def res2@(optional):T 
                res2 = it
                return res2
            }
        }
        return res
    } 

    fn count:int (cb:(_:boolean (item:T))) {
        def res 0 
        for self it@(lives):T i {
            if( cb(it) )  {
                res = res + 1
            }
        }
        return res
    } 

    fn contains:boolean (cb:(_:boolean (item:T))) { 
        for self it@(lives):T i {
            if( cb(it) )  {
                return true
            }
        }
        return false
    } 

    fn any:boolean (cb:(_:boolean (item:T))) @doc('True when `cb` holds for at least one item') {
        for self it@(lives):T i {
            if( cb(it) )  {
                return true
            }
        }
        return false
    }

    fn all:boolean (cb:(_:boolean (item:T))) @doc('True when `cb` holds for every item (true when empty)') {
        for self it@(lives):T i {
            if( (cb(it)) == false )  {
                return false
            }
        }
        return true
    }

    fn slice:[T] (start:int end:int) @doc('Items from `start` up to but not including `end`') {
        def res:[T]
        def len (array_length self)
        def from start
        def to end
        if( from < 0 ) {
            from = 0
        }
        if( to > len ) {
            to = len
        }
        def i from
        while( i < to ) {
            push res (itemAt self i)
            i = i + 1
        }
        return res
    }


}

; immutable map type
trait Map @params( K T S ) {
    def elements@(weak):[K:T]    
}

trait Vector @params( T S ) {

    def start:int 0
    def cardinality 3
    def end:int 0
    def elements@(weak):[T]
    def parent@(weak):S

    ; inserting a new element inside the array
    ; you can re-use the arrays but the index ranges will change

    fn localCopy:S () {
        def obj (new S)
        obj.start = start
        obj.end = end
        obj.parent = this.parent
        obj.cardinality = cardinality
        obj.elements = elements
        return obj
    }

    fn set:S (idx:int item:T) {

        if( idx >= start ) {
            def res (this.localCopy())
            res.elements = (make _:[T] (res.cardinality) item)
            for elements e@(lives):T i {
                if ( (res.start + i) != idx ) {
                    set res.elements i e
                } {
                    set res.elements i item
                }
            }
            return res
        }
        def root (this.localCopy())
        def res root

        def p@(weak):S parent
        while( (!null? p) && (idx < p.start) ) {
            def newSlice@(lives) (p.localCopy())
            res.parent = newSlice
            res = newSlice
            if(!null? p.parent) {
                p = p.parent
            }
        }
        def newSlice@(lives) (p.localCopy())
        newSlice.elements = (make _:[T] (newSlice.cardinality) item)
        for p.elements e@(lives):T i {
            set newSlice.elements i e           
        }
        set newSlice.elements (idx - newSlice.start) item 
        res.parent = newSlice
        return root
    }    

    fn insert:S (idx:int item:T) {
        if( idx >= start ) {
            def res (this.localCopy())
            def use_card cardinality
            if( ( array_length res.elements) >= (use_card - 1) ) {
                use_card = ( array_length res.elements) + 1
            }
            res.elements = (make _:[T] (use_card) item)
            for elements e@(lives):T i {
                if ( res.start + i < idx ) {
                    set res.elements i e
                } {
                    if( idx == ( res.start + i )) {
                        set res.elements i item
                        set res.elements (i + 1) e
                    } {
                        set res.elements (i + 1) e
                    }                
                }
            }
            if( (idx - start) >= (array_length elements)) {
                set res.elements (idx - start) item
            }
            res.start = start
            res.end = end + 1
            res.cardinality = use_card
            return res
        }
        def root (this.localCopy())
        def res root
        res.start = start + 1
        res.end = end + 1        
        def p@(weak):S parent
        while( (!null? p) && (idx < p.start) ) {
            def newSlice@(lives) (p.localCopy())
            newSlice.start = newSlice.start + 1
            newSlice.end = newSlice.end + 1
            res.parent = newSlice
            res = newSlice
            if(!null? p.parent) {
                p = p.parent
            }
        }

        def newSlice@(lives) (p.localCopy())
        def use_card newSlice.cardinality
        if( ( array_length p.elements) >= (use_card - 1) ) {
            use_card = ( array_length p.elements) + 1
        }
        newSlice.elements = (make _:[T] (use_card) item)
        for p.elements e@(lives):T i {
            if ( newSlice.start + i < idx ) {
                set newSlice.elements i e
            } {
                if( idx == ( newSlice.start + i )) {
                    set newSlice.elements i item
                    set newSlice.elements (i + 1) e
                } {
                    set newSlice.elements (i + 1) e
                }                
            }            
        }
        
        newSlice.end = newSlice.end + 1
        newSlice.cardinality = use_card
        if ( (idx - newSlice.start) >= p.cardinality ) {
            newSlice.end = idx + 1
        }
        newSlice.parent = p.parent
        res.parent = newSlice
        return root
    }

    fn get:T (idx:int) {
        if( idx < 0 ) {
            ; should be runtime error here...
            return (itemAt elements 0)
        } 
        if( idx >= start && idx < end ) {
            return (itemAt elements (idx - start))
        }
        def p@(weak):S parent
        while( (!null? p) && (idx < p.start) ) {
            p = p.parent
        }
        if(!null? p ) {
            return (itemAt p.elements (idx - p.start))
        }
        return (itemAt elements 0)
    }    
    fn add:S (item:T) {
        if((end - start) >= cardinality) {
            def res (new S)
            res.start = this.end
            res.end = (this.end + 1)
            res.parent = this
            res.cardinality = (cardinality + 1)
            res.elements = (make _:[T] (res.cardinality) item)
            set res.elements 0 item
            return res
        }
        def res (new S)
        res.elements = (make _:[T] (cardinality) item)
        for elements e@(lives):T i {
            set res.elements i e
        }
        set res.elements (end - start) item
        res.parent = parent
        res.start = start
        res.end = end + 1
        res.cardinality = cardinality
        return res
    }

    fn count:int () {
        return end
    }

    ; TODO: create faster version which uses (make ) operator
    fn map:S (cb:(_:T (item:T)) ) {
        def res:S (new S)
        def cnt (this.count())
        def i 0
        while( i < cnt ) {
            def item (this.get(i))
            def new_value (cb(item))
            res = (res.add(new_value))
            i = i + 1
        }
        return res
    }     
}


operator type:Vector all {

;    TODO: strict type checking for this
;    fn for (iIndex@(ignore):T iteIndex@(ignore):int code:block ) (        
;        "def " (e 3) ":int 0" nl
;        "def loop_cnt (" (e 1) ".count())" nl
;        "while( " (e 3) "  < loop_cnt ) {" nl
;            I 
;            "def " (e 2) " (" (e 1) ".get(" (e 3) "))" nl
;            (block 4)
;            (e 3) " = " (e 3) " + 1" nl
;            i
;        "}" nl
;    )

    fn indexOf:int ( elem:T ) {
        def cnt (self.count())
        def i 0
        while( i < cnt ) {
            def item (itemAt self i)
            if(item == elem) {
                return i
            }
            i = i + 1
        }        
        return -1
    }
    fn clear:T (idx:int) { 
        ; def val (self.get(idx))
        return (new T)
    }     

    fn itemAt:T (idx:int) { 
        def val (self.get(idx))
        return val
    } 
    fn at:T (idx:int) { 
        def val (self.get(idx))
        return val
    } 
    fn push:S (item:T) {
        return (self.add(item))
    }
    fn last:T () {
        return (itemAt self ((self.count()) - 1) )
    }
    fn array_length:int () {
        return (self.count())
    }    
    fn length:int () {
        return (self.count())
    }    
    fn size:int () {
        return (self.count())
    }    
    fn forEach:void (cb:(_:void (item:T))) {
        def cnt (self.count())
        def i 0
        while( i < cnt ) {
            def item (itemAt self i)
            cb(item)
            i = i + 1
        }
    }

    fn forUntil:void (cb:(_:boolean (item:T))) {
        def cnt (self.count())
        def i 0
        while( i < cnt ) {
            def item (itemAt self i)
            if( (cb(item)) == false ) {
                return
            }
            i = i + 1
        }
    }

    fn map:S ( cb:(_:T (item:T)) ) {
        def res (new S) 
        def cnt (self.count())
        def i 0
        while( i < cnt ) {
            def item (itemAt self i)
            def value (cb(item))
            res = res.add(value)
            i = i + 1
        }
        return res
    }     

    ; TODO: faster version ....
    fn map:[K] (cb:(_:K (item:T)) to@(noeval):[K] ) {
        def res:[K] 
        def cnt (self.count())
        def i 0
        while( i < cnt ) {
            def item (itemAt self i)
            push res (cb(item))
            i = i + 1
        }
        return res
    }     


}

operator type:Map all {
    fn  keys:[K] () {
        return (keys self.elements)
    }
    fn  get@(optional):T (key:K) {
        return (get self.elements key)
    }
    fn  has:boolean (key:K) {
        return (has self.elements key)
    }
    fn  set:S (key:K value:T) {
        def c (new S)        
        def keys ( keys self.elements)
        for keys k:string i {
            if(k==key) {

            } {
                set c.elements k (unwrap (get self.elements k))
            }
        }
        set c.elements key value
        return c
    }
    fn  forEach@(weak):S ( cb:(_:void (item:T index:K)) ) {
        def keys (keys self.elements)
        for keys key:K i {
            cb( (unwrap (get self.elements key)) key )
        }
        return self
    }

}


