Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting around JavaScript's lack of goto WITHOUT additional flags

How could I emulate the goto programming construct in this case?

$.fn.hierarchy = function(info, ret) {
    if (info.constructor !== Object) {
        info = {children: info};
        goto label1; // Illegal JavaScript
    }

    if (!info.children) {
        info.children = [];
        goto label2; // Illegal JavaScript
    }

    label1:
    if (info.children.constructor !== Array)
        info.children = [info.children];

    label2:
    /*
    // Forget this code. It's irrelevant to my specific problem
    // (which is that JS doens't allow non-nested conditionals)
    //  and caused much confusion.
    if (!info.tagc)
        info.tagc = info.tag || 'div';
    */

I know I could implement exactly ONE of these gotos as an else clause:

$.fn.hierarchy = function(info, ret) {
    if (info.constructor !== Object) {
        info = {children: info};
        //goto label1;
    }

    else if (!info.children) {
        info.children = [];
        goto label2; // Illegal JavaScript
    }

    //label1:
    if (info.children.constructor !== Array)
        info.children = [info.children];

    label2:
    /*
    // Forget this code. It's irrelevant to my specific problem
    // (which is that JS doens't allow non-nested conditionals)
    //  and caused much confusion.
    if (!info.tagc)
        info.tagc = info.tag || 'div';
    */

Or:

$.fn.hierarchy = function(info, ret) {
    if (info.constructor !== Object) {
        info = {children: info};
        goto label1; // Illegal JavaScript
    }

    if (!info.children) {
        info.children = [];
        //goto label2;
    }

    else {
        label1:
        if (info.children.constructor !== Array)
            info.children = [info.children];
    }

    //label2:
    /*
    // Forget this code. It's irrelevant to my specific problem
    // (which is that JS doens't allow non-nested conditionals)
    //  and caused much confusion.
    if (!info.tagc)
        info.tagc = info.tag || 'div';
    */

But I want to have both gotos. And, no, I don't want additional flags.


EDIT:

@Luis Espinal: Your proposed solution doesn't work. If info is {children: 'a'}, your program fails to convert info.children to [a].

$.fn.hierarchy = function(info, ret) {
    if (info.constructor !== Object) {
        info = {children: info};
        // goto label1; // Illegal JavaScript
        // label1:
        if (info.children.constructor !== Array){
            info.children = [info.children];
        }
    }
    else if (!info.children) {
        info.children = [];
        // goto label2; // Illegal JavaScript
        // label2:
        /*
        // Wrong. This has to be placed outside all of this.
        if (!info.tagc)
        {
            info.tagc = info.tag || 'div';
        }
        */
    }
    /* the following code is missing:

    else {
        // Handles the case when info.constructor === Object
        // from the beginning
        // AND
        // info.children isn't an array
        if (info.children.constructor !== Array)
            info.children = [info.children];
    }
    */

EDIT: Some of you seemed to think the fourth conditional is relevant to my problem. The problem is actually that I cannot do the following:

If condition1 Then action1
If !condition1 && condition2 Then action2
If !condition2 && condition3 && regardlessOf(condition1) Then action3

Without using flags (temporary boolean variables).

Basically if condition1 is true, I don't have to test for condition2, and, if condition2 is true, I don't have to test for condition3. But, if condition1 && !condition2, I might have to test for condition3.

like image 848
pyon Avatar asked Jan 20 '23 08:01

pyon


2 Answers

Maybe change labels to functions, and gotos to setTimeout(functionname, 0)

For example, instead of:

label1:
    // do something
    goto label2

label2:
    // do something
    goto label1

try something like this:

function label1 () {
    // do something
    setTimeout(label2, 0);
    return; // may be needed if not at the end of function
}

function label2 () {
    // do something
    setTimeout(label1, 0);
    return; // may be needed if not at the end of function
}

(You have to use timeouts because first JavaScript doesn't optimize tail calls (yet) and second because you don't want to block the browser and timeout of 0 puts your callback at the end of the event loop)

like image 125
rsp Avatar answered Jan 24 '23 00:01

rsp


Unless my eyes are failing me or I'm missing an obscure side-effect, the first goto-ridden example is equivalent to the following if-else based version (bless 1960's structured programming constructs):

$.fn.hierarchy = function(info, ret) {
    if (info.constructor !== Object) {
        info = {children: info};
        // goto label1; // Illegal JavaScript
        // label1:
        if (info.children.constructor !== Array){
            info.children = [info.children];
        }
    }
    else if (!info.children) {
        info.children = [];
        // goto label2; // Illegal JavaScript
        // label2:
        if (!info.tagc)
        {
            info.tagc = info.tag || 'div';
        }
    }
like image 32
luis.espinal Avatar answered Jan 24 '23 00:01

luis.espinal