0

I've been learning JavaScript for just about a month, and I'm trying to implement the Shunting Yard Algorithm

However, I seem to have a logic error somewhere (probably in the while loop) but I can't for the life of me figure it out.

var parser = function(inp){
    var outQueue=[];
    var opStack=[];

    Array.prototype.peek = function() {
        return this.slice(-1)[0];
};

    //tokenize
    var inArr=tokenize(inp);
 var top;
    var prec = {
        "^" : "right",
       "*" : "left",
        "/" : "left",
        "+" : "left",
    "-" : "left"
};

   var assoc = {
       "^" : 4,
       "*" : 3,
    "/" : 3,
    "+" : 2,
    "-" : 2
    };

    inArr.forEach(function(v) {
    //If the token is a number, then push it to the output queue
    if(v.match(/\d+/)) {
        outQueue.push(v);
    } 
    //If the token is an operator, o1, then:
    else if(v.match(/[+*-/^]/)) {
        if (opStack.peek()) {
        top = opStack.peek();
        //while there is an operator token o2, at the top of the operator stack and
        while(top.match(/[+*-/^]/) 
            //either o1 is left-associative and its precedence is less than or equal to that of o2,
            && ((assoc[v]==="left" && prec[v] <= prec[top])
                //or o1 is right associative, and has precedence less than that of o2,
                || (assoc[v]==="right" && prec[v] < prec[top]))) {
                    outQueue.push(opStack.pop());
                top = opStack.peek();
        }
    }
        //at the end of iteration push o1 onto the operator stack
        opStack.push(v);
    } 
    //If the token is a function token, then push it onto the stack.
    else if(v.match(/(sin|cos|tan)/)) {
        opStack.push(v);

    } 
    //If the token is a function argument separator 
    else if(v===",") {
        //Until the token at the top of the stack is a left parenthesis
        //pop operators off the stack onto the output queue.
        while(opStack.peek() != "(") {
            outQueue.push(opStack.pop());
        }
        /*if(opStack.length == 0){
            console.log("Mismatched parentheses");
            return;
        }*/
    } 
    //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
    else if(v==="(") {
        opStack.push(v);
    }
    //If the token is a right parenthesis (i.e. ")"):
    else if(v===")") {
        //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
        while(opStack.peek() != "(") {
            outQueue.push(opStack.pop());
        }
        /*if(opStack.length == 0){
            console.log("Unmatched parentheses");
            return;
        }*/
        //Pop the left parenthesis from the stack, but not onto the output queue.
        opStack.pop();

        //If the token at the top of the stack is a function token, pop it onto the output queue.
        if(opStack.peek().match(/(sin|cos|tan)/)) {
            outQueue.push(opStack.pop());
        }
    }
});

return outQueue.concat(opStack.reverse()).join(" ");
 };

function tokenize(arg) {
    return arg.split(" ");
}

console.log(parser("5 + 3 * 6 - ( 5 / 3 ) + 7"));

Correct output:

5 3 6 * + 5 3 / - 7 +

Actual output:

5 3 6 5 3 / 7 + - * +

[Please excuse the formatting; I'm on mobile]

2
  • There are many more functions then just sin tan and cos.
    – Bálint
    Commented Jan 19, 2017 at 9:09
  • Yes, @Balint, I'm just working with a specific use case, though
    – shalvah
    Commented Jan 19, 2017 at 9:17

2 Answers 2

2

You mixed up 2 variables

In the part where you check the precedence:

&& ((assoc[v]==="left" && prec[v] <= prec[top])
            //or o1 is right associative, and has precedence less than that of o2,
            || (assoc[v]==="right" && prec[v]

you check if assoc[v] is left or not. Assoc only holds numbers, so this'll be always false. Change the assocs to prec and vice-versa.

4
  • ARE YOU KIDDING ME??
    – shalvah
    Commented Jan 19, 2017 at 9:17
  • I guess this is what I get for being short on sleep. Thanks
    – shalvah
    Commented Jan 19, 2017 at 9:18
  • @shalvah This may not solve every problem, but this was the first thing I saw.
    – Bálint
    Commented Jan 19, 2017 at 9:19
  • Yes, there was another bug after changing that, but I fixed it easily. Thank you
    – shalvah
    Commented Jan 19, 2017 at 9:27
1

The mistake you did is to inverse precedence with associativy. The correct code should look like that:

var assoc = {
    "^" : "right",
    "*" : "left",
    "/" : "left",
    "+" : "left",
    "-" : "left"
};

var prec = {
   "^" : 4,
   "*" : 3,
   "/" : 3,
   "+" : 2,
   "-" : 2
};
1
  • Yeah :). I was rewriting the "while operator on stack" part which was not very clear to me. So I haven't seen your post until I post mine.
    – TrapII
    Commented Jan 19, 2017 at 10:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.