Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Codewars Tiny Three-Pass Compiler JS heap out of memory

I was doing the Tiny Three-Pass Compiler on codewars and I keep getting a error on node v.10 saying `

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed – JavaScript heap
out of memory Aborted (core dumped) <— Last few GCs —>

[19:0x5578df6e1f80] 6093 ms: Mark-sweep 580.2 (592.5) -> 580.2
(584.5) MB, 1501.0 / 0.0 ms (average mu = 0.282, current mu = 0.000)
last resort GC in old space requested [19:0x5578df6e1f80] 6285 ms:
Mark-sweep 580.2 (584.5) -> 580.2 (584.5) MB, 191.9 / 0.0 ms (average
mu = 0.243, current mu = 0.000) last resort GC in old space requested

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<— JS stacktrace —>

==== JS stack trace =========================================

0: ExitFrame [pc: 0x3dbb90e5be1d] Security context: 0x3dd3dcdf3419 <JSObject>
1: /* anonymous */ [0x3dd3dcdcb241] [/home/codewarrior/node/test.js:~48]

[pc=0x3dbb90efc9ce](this=0x3d354a868c89 ,program=0x1344e3ade909 <String[47]: [ x y z ] ( 23x

  • 5y – 3z ) / (1 + 3 + 22)>)
    2: /
    anonymous */ [0x3d354a8463c9] [/home/codewarrior/node/test.js:214] [bytecode=0x3dd3dcdce859
    offset=90](this=0x3d354…`

and on node v.8 I get this error:
`

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed – JavaScript heap out of memory
Aborted (core dumped)
`

The code that I am currently using is:

function isNumber (token){
  return !isNaN(token);
}
function isOperator(token){
  return "*/+-".indexOf(token) !== -1;
}

function Compiler () {};

Compiler.prototype.compile = function (program) {
  return this.pass3(this.pass2(this.pass1(program)));
};

Compiler.prototype.tokenize = function (program) {
  // Turn a program string into an array of tokens.  Each token
  // is either '[', ']', '(', ')', '+', '-', '*', '/', a variable
  // name or a number (as a string)
  var regex = /\s*([-+*/\(\)\[\]]|[A-Za-z]+|[0-9]+)\s*/g;
  return program.replace(regex, ":$1").substring(1).split(':').map( function (tok) {
    return isNaN(tok) ? tok : tok|0;
  });
};

Compiler.prototype.pass1 = function (program) {
  var tokens = this.tokenize(program);
  
  function getNextToken(){
    token = tokens.shift();
  }
  function precedenceIsNotGreater(o1, o2){
    var precedences = {
      '/' : 4,
      '*' : 3,
      '+' : 2,
      '-' : 1,
    }
    return precedences[o1] <= precedences[o2];
  }
  
  var token ;
  var outputQueue = [];
  var operatorStack = [];
  var args = [];
  
  do{
    getNextToken()
    if(token === '['){
      for(getNextToken(); token !== ']'; getNextToken()){
        args.push(token);
      }
    }else if(isNumber(token) || args.includes(token)){
      outputQueue.push(token);
    }else if(isOperator(token)){
      var o1 = token;
      for(var o2 = operatorStack[operatorStack.length-1]; operatorStack.length && isOperator(o2) && precedenceIsNotGreater(o1, o2); o2 = operatorStack[operatorStack.length -1]){
        outputQueue.push(token);
      }
      operatorStack.push(o1);
    }else if(token === '('){
      operatorStack.push(token);
    } else if(token === ')'){
      for(var nextOperator = operatorStack[operatorStack.length -1]; operatorStack.length && nextOperator !== '('; nextOperator = operatorStack[operatorStack.length-1]){
        outputQueue.push(operatorStack.pop())
      }
      operatorStack.pop()
    }
  } while(tokens.length);
  
  while(operatorStack.length){
    outputQueue.push(operatorStack.pop())
  }
  
  var output;
  
  function getNextOutput(){
    output = outputQueue.pop();
  }
  
  function buildAst(outputQueue) {
    getNextOutput();
    var node = {};
    
    if(isNumber(output)){
      node.op = 'imm';
      node.n = output;
    } else if(args.includes(output)){
      node.op = 'arg';
      node.n = args.indexOf(output);
    } else if(isOperator(output)){
      node.op = output;
      var b = buildAst(outputQueue);
      var a = buildAst(outputQueue);
      node.a = a;
      node.b = b;
    }
    return node;
  }
  return buildAst(outputQueue);
};

Compiler.prototype.pass2 = function (ast) {
  function reduceTree(ast) {
    if(ast.op === 'imm' || ast.op === 'arg') {
      return ast;
    }
    ast.a = reduceTree(ast.a);
    ast.b = reduceTree(ast.b);
    
    if(ast.a.op === 'imm' && ast.b.op === 'imm'){
     var n = Function("return " + ''+ast.a.n+ast.op+ast.b.n)();
      return{ op: 'imm', n: n}
    }
    return ast;
  }
  return reduceTree(ast)
};

Compiler.prototype.pass3 = function (ast) {
  var operatorMap = {
    '+' : 'AD',
    '-' : 'SU',
    '*' : 'MU',
    '/' : 'DI',
  }
  
  var operationDepths = {};
  var maxDepth = -Infinity;
  
  function markDepth (ast, depth = 0){
    if(ast.a && ast.b){
      maxDepth = Math.max(maxDepth, depth);
      if(!operationDepths[depth]){
        operationDepths[depth] = [];
      }
      operationDepths[depth].push(ast);
      markDepth(ast.a, depth+1);
      markDepth(ast.b, depth+1);
    }
  }
  markDepth(ast);
  var asm = [];
  
  var currentDepth = maxDepth;
  while(currentDepth >= 0){
    currentDepthOperations = operationDepths[currentDepth];
    while(currentDepthOperations.length){
     var currentOperation = currentDepthOperations.shift();
      
      if(currentOperation.b.op === 'imm'){
        asm.push('IM ' + currentOperation.b.n);
      }else if(currentOperation.b.op === 'arg'){
        asm.push('AR ' + currentOperation.b.n)
      }else{
        asm.push('PO')
      }
      
      asm.push('SW')
      
      if(currentOperation.a.aop === 'imm'){
        asm.push('IM ' + currentOperation.a.n)
      }else if(currentOperation.a.op === 'arg') {
        asm.push('AR ' + currentOperation.a.n)
      }else{
        asm.push('PO')
      }
      
      asm.push(operatorMap[currentOperation.op]);
      
      asm.push('PU')
    }
    currentDepth--;
  }
  return asm;
};

If anyone can help that would be great!
Thanks in advance.

>Solution :

The cause of the heap overflow is that you have an infinite loop here:

      for(var o2 = operatorStack[operatorStack.length-1]; operatorStack.length && isOperator(o2) && precedenceIsNotGreater(o1, o2); o2 = operatorStack[operatorStack.length -1]){
        if (count-- < 0) throw "too many iterations second for";
        outputQueue.push(token);
      }

If this loop enters with its loop condition being true, then that loop condition will always remain true. This is because it does not pop anything from that stack. Eventually the ever growing outputQueue will take up all available heap memory.

The correction is to replace this:

    outputQueue.push(token);

with:

    outputQueue.push(operatorStack.pop());

Disclaimer: I didn’t check for any other problems in this rather large code block. This only addresses the error you got and its cause.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading