Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SyntaxError: Unexpected Identifier (Generators in ES6)

I came up with this simple experiment after reading the documentation on generators from MDN:

var nodes = {
    type: 'root',
    value: [
        { type: 'char', value: 'a' },
        { type: 'char', value: 'b' },
        { type: 'char', value: 'c' },
    ],
};

function* recursiveGenerator(node) {
    if (node.type === 'root') {
        node.value.forEach(function (subnode) {
            for (var suffix of recursiveGenerator(subnode)) {
                yield suffix;
            }
        });
    }

    else {
        yield node.value;
    }
}

for (generated of recursiveGenerator(nodes)) {
    console.log(generated);
}

Running it on node.js v0.11.9 with the --harmony flag set produces the following error:

alix@900X4C:~$ node --version
v0.11.9
alix@900X4C:~$ node --harmony test.js 

/home/alix/test.js:14
                yield suffix;
                      ^^^^^^
SyntaxError: Unexpected identifier

I also tried using for ... in ... and the let keyword instead of var, but without any success.

I don't understand what yield* does exactly, but if I use it within the for loop I get instead:

alix@900X4C:~$ node --harmony test.js 

/home/alix/test.js:14
                yield* suffix;
                ^
ReferenceError: yield is not defined

If I replace the yield in the for with console.log() it outputs a, b and c. What am I doing wrong?


Edit

Here's a minimalistic generator, showing that node.js knows what to do with generators:

function* alpha() {
    yield 'a';
    yield 'b';
    yield 'c';
}

for (var suffix of alpha()) {
    console.log(suffix);
}

Output:

alix@900X4C:~$ node --harmony y.js 
a
b
c

Solution (thanks @Andrew)

function* recursiveGenerator(node) {
    if (node.type === 'root') {
        for (var i = 0; i < node.value.length; ++i) {
            var subnode = node.value[i];

            for (var suffix of recursiveGenerator(subnode)) {
                yield suffix;
            }
        }
    }

    else {
        yield node.value;
    }
}

for (generated of recursiveGenerator(nodes)) {
    console.log(generated);
}
like image 770
Alix Axel Avatar asked Dec 30 '13 05:12

Alix Axel


People also ask

What causes the uncaught SyntaxError unexpected identifier error?

In this post, we looked at what causes the Uncaught SyntaxError: Unexpected identifier error and how to fix it. In most cases, it is just code that is not adhering to the JavaScript syntax rules or contains typos. Hopefully, this post has helped you with your code!

Why am I getting SyntaxError unexpected token import?

If you go an run it now as node server.js, you'll get an error SyntaxError: Unexpected token import. It happens because we don't "babelify" our node.js code on the fly yet.

How to use ES6 imports in Node JS?

As of now, Node.js doesn't support ES6 imports yet. However, you can use them today with the help of Babel. The following example is for the express.js server. 1. Install necessary packages 2. Tweak your server.js Here's an example server.js:

What is an uncaught SyntaxError in JavaScript?

What causes this error? Since JavaScript is not a compiled language, you can get syntax errors when running your code. One of these errors is the Uncaught SyntaxError: Unexpected identifier error. In this post, we'll look at what causes this, examples of it, and how to fix it. What causes this error? This error is mainly caused by three things.


2 Answers

Summarizing the comments: you can't use yield inside a regular function, so you can't use yield with forEach. Here an example of "generatorized" foreach:

function * foreach (arr, fn) {
  var i

  for (i = 0; i < arr.length; i++) {
    yield * fn(arr[i])
  }
}

function * gen (number) {
  yield number + 1
  yield number + 2
  yield number + 3
}

function * other () {
  yield * foreach([1, 2, 3], gen)
}

for (var i of other()) {
    console.log(i)
}

UPDATE Also the original problem can be solved quite elegantly using such a helper:

var nodes = {
  type: 'root',
  value: [
    { type: 'char', value: 'a' },
    { type: 'char', value: 'b' },
    { type: 'root', value: [
        { type: 'char', value: 'c' },
        { type: 'char', value: 'd' },
        { type: 'char', value: 'e' },
      ] 
    },
  ],
}

function * foreach (arr, fn) {
  var i

  for (i = 0; i < arr.length; i++) {
    yield * fn(arr[i])
  }
}

function * value (val) {
  yield val
}

function * recursiveGenerator(node) {
  yield * node.type === 'root' ?  foreach(node.value, recursiveGenerator) : value(node.value)
}

for (var generated of recursiveGenerator(nodes)) {
  console.log(generated);
}

So the generator itself becomes a one-liner!

like image 168
vkurchatkin Avatar answered Oct 21 '22 16:10

vkurchatkin


You've found your solution, but just for the record here is another example a little different that print the types of all nodes in the tree (I added some deepness and vars)

var nodes = {
    type: 'root',
    value: [
        { type: 'char', value: 'a' },
        { type: 'char', value: 'b' },
        { type: 'char', value: [{type: 'int', value: 'c'}] },
    ],
};

var flattenTree = function* (root) {
    yield root.type;
    var subvalues = root.value;
    for(var i in subvalues) {
        var gen = flattenTree(subvalues[i]);
        val = gen.next();
        while(!val.done) {
            if(val.value != undefined)
                yield val.value;
            val = gen.next();
        }
    }
}

var printTree = function() {
    console.log("begin tree");
    var generator = flattenTree(nodes);
    var next = generator.next();
    while(!next.done) {
        console.log(next);
        next = generator.next();
    }
    console.log("finish tree");
}

printTree();

Outputs:

~/workspace/tmp$ ../node/node --harmony test-gen.js 
begin tree
{ value: 'root', done: false }
{ value: 'char', done: false }
{ value: 'char', done: false }
{ value: 'char', done: false }
{ value: 'int', done: false }
finish tree
like image 3
nico Avatar answered Oct 21 '22 15:10

nico