Toolmaking
With
ASTs

Will Klein

Toolmaking

 

My Dad

Toolmaker

Tools

 

Source: https://en.wikipedia.org/wiki/Die_(manufacturing)

Manufacturing Die

Source: http://studentshop.pratt.duke.edu/lab+space

Milling Machines

Developer

Software

Tools

function addTen(value) {
  var sum = 0;

  return value + 10
}

if (someCondition) {
  if (someOtherCondition) {
    added = addTen(someValue);
  }
}

This Code...

function addTen(value) {
  return value + 10;
}

if (someCondition &&
    someOtherCondition) {
  added = addTen(someValue);
}

should really be

Code Review

  1. Reviewer: comprehend code
  2. Reviewer: leave comments
  3. Submitter: review comments
  4. Submitter: fix code (maybe)
  5. Submitter: push changes

Manual Process

Tools

 

Linting

 

function addTen(value) {
  var sum = 0;

  return value + 10
}

no-unused-vars

semi

ESLint Rules

if (someCondition) {
  if (someOtherCondition) {
    added = addTen(someValue);
  }
}

???

ESLint Rules

Toolmaking

 

Toolmaking
With
ASTs

Abstract

Syntax
Trees

Wizardry

Wizardry?

IfStatement

if (someCondition) {}
if (someCondition) {}

IfStatement

{
  "type": "Program",
  "body": [
    {
      "type": "IfStatement",
      "test": {
        "type": "Identifier",
        "name": "someCondition"
      },
      "consequent": {
        "type": "BlockStatement",
        "body": []
      },
      "alternate": null
    }
  ]
}

Nested IfStatement

if (someCondition) {
  if (someOtherCondition) {
    added = addTen(someValue);
  }
}

Nested IfStatement

Nested IfStatement

{
  "type": "IfStatement",
    "test": {
      "type": "Identifier",
      "name": "someCondition"
    },
    "consequent": {
      "type": "BlockStatement",
      "body": [
        {
          "type": "IfStatement",
          "test": {
            "type": "Identifier",
            "name": "someOtherCondition"
          },
          "consequent": {
            "type": "BlockStatement",
            "body": [ ... ] } } ] } }
  1. Builds an AST of your code
  2. Traverses the tree
  3. For each node, run rules

ESLint: Under the Hood

  1. Builds an AST of your code
  2. Traverses the tree
  3. For each node, run rules
  4. Rules are built-in, or plugins

ESLint: Under the Hood

ESLint: Rule boilerplate

module.exports = function(context) {
  return {






  };
};

ESLint: Rule boilerplate

module.exports = function(context) {
  return {
    "IfStatement": function(node) {




    }
  };
};

ESLint: Rule boilerplate

module.exports = function(context) {
  return {
    "IfStatement": function(node) {
      if (/* test some things */) {
        context.report(node,
            "Unexpected bad code.");
      }
    }
  };
};

Nested IfStatement

ESLint: context

module.exports = function(context) {
  return {
    "IfStatement": function(node) {
      var ancestors = context.getAncestors(),
          parent = ancestors.pop(),
          grandparent = ancestors.pop();

      if (/* test some things */) {
        context.report(node,
            "Unexpected bad code.");
      }
    }
  };
};

Directly nested CASE

if (someCondition)
  if (someOtherCondition) {}

directly nested TEST

if (someCondition)
  if (someOtherCondition) {}
var ancestors = context.getAncestors(),
    parent = ancestors.pop(),
    grandparent = ancestors.pop();

if (parent.type === "IfStatement") {
  context.report(node, "Unexpected bad code.");
}

block Nested CASE

if (someCondition) {
  if (someOtherCondition) {}
}

block Nested Test

var ancestors = context.getAncestors(),
    parent = ancestors.pop(),
    grandparent = ancestors.pop();

if (parent.type === "IfStatement" ||
    (parent.type === "BlockStatement" &&
     grandparent.type === "IfStatement")) {
  context.report(node, "Unexpected bad code.");
}
if (someCondition) {
  if (someOtherCondition) {}
}

Siblings test

var ancestors = context.getAncestors(),
    parent = ancestors.pop(),
    grandparent = ancestors.pop();

if (parent.type === "IfStatement" ||
    (parent.type === "BlockStatement" &&
     parent.body.length === 1 &&
     grandparent.type === "IfStatement")) {
  context.report(node, "Unexpected bad code.");
}
if (someCondition) {
  doSomething();
  if (someOtherCondition) {}
}

consequent test

var ancestors = context.getAncestors(),
    parent = ancestors.pop(),
    grandparent = ancestors.pop();

if (parent.type === "IfStatement" ||
    (parent.type === "BlockStatement" &&
     parent.body.length === 1 &&
     grandparent.type === "IfStatement" &&
     parent === grandparent.consequent)) {
  context.report(node, "Unexpected bad code.");
}
if (someCondition) {
  doSomething();
} else {
  if (someOtherCondition) {}
}

no-nested-if

module.exports = function(context) {
  return {
    "IfStatement": function(node) {
      var ancestors = context.getAncestors(),
          parent = ancestors.pop(),
          grandparent = ancestors.pop();
        
      if (parent.type === "IfStatement" ||
          (parent.type === "BlockStatement" &&
           parent.body.length === 1 &&
           grandparent.type === "IfStatement" &&
           parent === grandparent.consequent)) {
        context.report(node, "Unexpected bad code.");
      }
    }
  };
};

Code

Funny thing...

Toolmaker

Source: https://en.wikipedia.org/wiki/Numerical_control

CNC Machines

Source: https://github.com/grbl/grbl/wiki/G-Code-Examples

G-Code

( RUN IN VISE ON PARALLELS )
(Z OFFSET: TOP OF MATERIAL WITH )
( 0.375" MATERIAL ABOVE VISE JAWS )
(X0,Y0,Z0= Center, Center, Top)
(STOCK ORIGIN = X0. Y0. Z.01)
(MATERIAL TYPE= ALUMINUM inch - 6061)
(MATERIAL SIZE= X1.75 Y1.75 Z.5)
(TOOL= 1/4 2-FLUTE HSS END MILL)

Funny thing...

Developer

Fin

[Archive] Toolmaking with ASTs - Draft Version

By Will Klein

[Archive] Toolmaking with ASTs - Draft Version

A potential conference talk, Fri Jun 26, 2015

  • 1,938