Coding Journal

Jordan Vidrine

Daily Coding Journal

An experienced business owner + musician with a demonstrated history of management in the Energy sector and an insatiable appetite for learning new and complex skills. Transitioning from an ownership role & seeking employment in Web Development as a Jr Full Stack Developer.

July 13th 2019

So Hurricane Barry is making its way to land. Im posted up at my parents house in case the power back home goes out. I’ve been reading a bit of SICP which apparently is a computer programmer’s/CS Major’s dream book. I’ve also been going through MIT lectures on the book and I find it very interesting.

The language it will be focused on is a dialect of Lisp called Scheme. This all comes recommended to my by a friend who’s has his degree in CS. I think it’s a good reccomendation as the book is going through the basic building blocks of what programs are, of what interpreters are, and what is actually going on behind the scenes when we write programs. I love this type of stuff, taking it all apart is so interesting to me.

Anyway! That being said, I felt it necessary to also go back to the Regex section of Eloquent Javascript. Why? Because in it is a section on writing a parsing function that interprets an INI file using regex statements. Here is the example the book gives.

function parseINI(string) {
  // Start with an object to hold the top-level fields
  let result = {};
  let section = result;
  string.split(/\r?\n/).forEach(line => {
    let match;
    if (match = line.match(/^(\w+)=(.*)$/)) {
      section[match[1]] = match[2];
    } else if (match = line.match(/^\[(.*)\]$/)) {
      section = result[match[1]] = {};
    } else if (!/^\s*(;.*)?$/.test(line)) {
      throw new Error("Line '" + line + "' is not valid.");
    }
  });
  return result;
}

What’s happening is here the parseINI function is being fed a string. It splits that string up with any new line or return with /\r?\n/ as a regex passed to the split method on a string.

It then goes through each line and tests it to see if it a line that either declares a new section with [newSectionText] or if it defines a key in the current section it is on with something like newKey=newKeyText

So what the above code does to a .INI file like this:

name=Vasilis
[address]
city=Tessaloniki

Would be to turn it into an object like this:

{name: "Vasilis", address: {city: "Tessaloniki"}}

A couple of interesting things to note for me were the defining of a variable inside of an if statement. if (match = line.match(/^(\w+)=(.*)$/)) will evaluate the object that is assigned to match immediately. If nothing gets assigned to it, the if statement moves on.

Another interesting thing is that on the first else if statement, we re-assign the section variable to a new empty object we are defining in result at the same time we are assigning it. This allows us to edit the current section we are in on the next .forEach line. I would have thought we would have used recursion for this, but since we are calling section as a reference to an object in result. Any edits we make to that section are also made in result.

EJS Ch 12 ‘A Programming Language’

In this chapter we go through a process of how a program is parsed. It is very similar to the first chapter of SICP that I am going through and this lines up perfectly with it. I put the following code from the book into a google code snippet and ran through the debugger line by line to see what was going on. It was pretty complex, but it made sense to me. Very cool!

What thw following code gives back is a Syntax Tree of the program you pass to it.

// PARSE EXPRESSION
function parseExpression(program) {
  program = skipSpace(program);
  let match, expr;
  if (match = /^"([^"]*)"/.exec(program)) {
    expr = {type: "value", value: match[1]};
  } else if (match = /^\d+\b/.exec(program)) {
    expr = {type: "value", value: Number(match[0])};
  } else if (match = /^[^\s(),#"]+/.exec(program)) {
    expr = {type: "word", name: match[0]};
  } else {
    throw new SyntaxError("Unexpected syntax: " + program);
  }

  return parseApply(expr, program.slice(match[0].length));
}

// SKIP SPACE
function skipSpace(string) {
  let first = string.search(/\S/);
  if (first == -1) return "";
  return string.slice(first);
}

// PARSE APPLY
function parseApply(expr, program) {
  program = skipSpace(program);
  if (program[0] != "(") {
    return {expr: expr, rest: program};
  }

  program = skipSpace(program.slice(1));
  expr = {type: "apply", operator: expr, args: []};
  while (program[0] != ")") {
    let arg = parseExpression(program);
    expr.args.push(arg.expr);
    program = skipSpace(arg.rest);
    if (program[0] == ",") {
      program = skipSpace(program.slice(1));
    } else if (program[0] != ")") {
      throw new SyntaxError("Expected ',' or ')'");
    }
  }
  return parseApply(expr, program.slice(1));
}

function parse(program) {
  debugger;
  let {expr, rest} = parseExpression(program);
  if (skipSpace(rest).length > 0) {
    throw new SyntaxError("Unexpected text after program");
  }
  return expr;
}

console.log(parse("+(a, 10)"));

// → {type: "apply",
//    operator: {type: "word", name: "+"},
//    args: [{type: "word", name: "a"},
//           {type: "value", value: 10}]}

Obviously, way more needs to happen in order for code to be ‘run’. We need to evaluate this syntax tree and figure out what it is trying to tell us. That we will cover in the next section of the chapter.

Back to blog...