Log Call

Now that we have our basic tokens being parsed, it's time to tackle something a little more difficult.

Looking at our DSL again:

print hello

Taking a look at this we can immediately see that we want to print the thing after our print call.

As we haven't really defined language semantics, we're able to do that right now. Let's assume that we want to log everything on the same line as our print statement.

if (currentToken.type === TokenType.Log) {
  let nextNode = tokens[currentIndex++]

  const children: ASTNode[] = []

  // Process

  return {
    type: ASTNodeType.Log,
    children
  }
}

First, we set up nextNode to reference the next.. node :)

Then we'll define an array of child nodes - the nodes that we will actually be logging.

while (nextNode.type !== TokenType.LineBreak) {
  // Process

  nextNode = tokens[currentIndex]
}

Now, we're going to continuously loop over our current tokens. That is, until we find one that is a line break.

Once we hit our line break, we know that we're at the end of our log statement.

const next = process()

if (next) {
  children.push(next)
}

To get our actual child node, we need to now call process(). As we've already incremented currentIndex, the entire loop will run again (and potentially again and again.. depending on the complexity of the language that we end up) while iterating and generating our child tree.

Once we have this, we need to check it's not null, and then add it to our array of children.

This would be a perfect place to add some validation, what happens if the node we get back is null? We'll cover adding checks in a later section of the guide.

Final Code

if (currentToken.type === TokenType.Log) {
  let nextNode = tokens[currentIndex++]

  const children: ASTNode[] = []

  while (nextNode.type !== TokenType.LineBreak) {
    const next = process()

    if (next) {
      children.push(next)
    }

    nextNode = tokens[currentIndex]
  }

  return {
    type: ASTNodeType.Log,
    children
  }
}