Hugging a syntax tree

We are hiring JavaScript developers for Shoutem and Five agency 

 

Random text

 

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin urna odio, aliquam vulputate faucibus id, elementum lobortis felis. Mauris urna dolor, placerat ac sagittis quis.

Random code

 
var bla = function(foo, bar) {
    return foo * bar;
}

console.log("we all know the answer " 
    + bla(7,6));
g = 1;
f = 2;
var h = 1, i;
g = 1;
d = 2;
var h = 1, i;
diff --git a/example1.js b/ex3.js
index 2b85dd4..e92c113 100644
--- a/example1.js
+++ b/ex3.js
@@ -1,3 +1,3 @@
 g = 1;
-f = 2;
+d = 2;
 var h = 1, i;

diff - compare files line by line

 

displays the changes made in a standard format, such that both humans and machines can understand the changes and apply them

 
diff --git a/example1.js b/ex3.js
index 2b85dd4..e92c113 100644
--- a/example1.js
+++ b/ex3.js
@@ -1,3 +1,3 @@
 g = 1;
-f = 2;
+d = 2;
 var h = 1, i;
g = 1;
f = 2;
var h = 1, i;
g = 1;
d = 2;
var h = 1, i;

patch - apply a diff file to an original

 

example.js

example.diff

modified example.js

patch [options] [originalfile [patchfile]]
diff --git a/source_replacer.js b/bla
index b002aba..cd9b732 100644
--- a/source_replacer.js                                                                                                                                                      [0/317]
+++ b/bla
@@ -1,30 +1,30 @@
-'use strict';
+"use strict"

 function transformToStringIndex(originalSource, line, column) {
-    var startLine = line - 1,
-        lineStart = 0,
-        lineStartIndexes = originalSource.split('\n').map(function (line) {
-            return line.length;
-        });
+        var startLine = line - 1,
+                lineStart = 0,
+                lineStartIndexes = originalSource.split('\n').map(function(line) {
+                        return line.length;
+                });

-    if (startLine > 0) {
-        lineStart = lineStartIndexes.slice(0, startLine).reduce(function (a, b) {
-            return a + b;
-        }) + startLine;
-    }
+        if (startLine > 0) {
+                lineStart = lineStartIndexes.slice(0, startLine).reduce(function(a, b) {
+                        return a + b;
+                }) + startLine;
+        }

-    return lineStart + column;
+        return lineStart + column;
 }

 function replaceOnLocations(target, source, originalSource) {
-    var loc1 = transformToStringIndex(originalSource, source.start.line, source.start.column),
-        loc2 = transformToStringIndex(originalSource, source.end.line, source.end.column),
-        loc3 = transformToStringIndex(target.source, target.start.line, target.start.column),
-        loc4 = transformToStringIndex(target.source, target.end.line, target.end.column);
+        var loc1 = transformToStringIndex(originalSource, source.start.line, source.start.column),
+                loc2 = transformToStringIndex(originalSource, source.end.line, source.end.column),
+                loc3 = transformToStringIndex(target.source, target.start.line, target.start.column),
+                loc4 = transformToStringIndex(target.source, target.end.line, target.end.column);

-    return originalSource.substring(0, loc1) + target.source.substring(loc3, loc4) + originalSource.substring(loc2);
+        return originalSource.substring(0, loc1) + target.source.substring(loc3, loc4) + originalSource.substring(loc2);
 }

 module.exports = {
-    patchSource: replaceOnLocations
+        patchSource: replaceOnLocations
 };

How browsers read our JavaScript

 
g = 1;
f = 2;
var h = 1, i;
	
g = 1;


f=2;var h=1,i;
	

Code formatting does not matter

 
{
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "AssignmentExpression",
                "operator": "=",
                "left": {
                    "type": "Identifier",
                    "name": "g"
                },
                "right": {
                    "type": "Literal",
                    "value": 1,
                    "raw": "1"
                }
            }
        },
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "AssignmentExpression",
                "operator": "=",
                "left": {
                    "type": "Identifier",
                    "name": "f"
                },
                "right": {
                    "type": "Literal",
                    "value": 2,
                    "raw": "2"
                }
            }
        },
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "h"
                    },
                    
                    ...

Abstract Syntax Tree (AST)

 
  • non-ambiguous representation of our JavaScript code
  • traversable tree-structured representation of a context-free grammar 
 

Useful properties to us:

 

AST Parsers in JavaScript

 

AST specification

 

astii diff

 
JavaScript source file1          JavaScript source file2  
         +                                +               
         |                                |               
         |                                |               
         |                                |               
         v                                v               
abstract syntax tree             abstract syntax tree     
         +                                +               
         |                                |               
         |                                |               
         |                                |               
         v                                v               
generated JS source file         generated JS source file2
                    +              +                      
                    |              |                      
                    |     ++++     |                      
                    +---> diff <---+                      
                          ----                             

compare AST-neutral representations of two JavaScript files line by line

 
Index: undefined
===================================================================
--- undefined
+++ undefined
@@ -1,3 +1,3 @@
 g = 1;
-f = 2;
+d = 2;
 var h = 1, i;
\ No newline at end of file
g = 1;
f = 2;
var h = 1, i;
g = 1;
d = 2;


var h = 1,
    i;

astii diff - AST to Javascript code generation

 

astii patch

 

apply an astii-generated diff file to an original in an AST-aware way, preserving original formatting

 
original JavaScript source
         +
         |
         |
         |
         v
abstract syntax tree
         +
         |
         |
         |
         v
generated JS source file
         +
         |
         |
         <---+ patch <---+ AST-aware patchfile
         |
         |
         v
patched JavaScript source
                           
patched JavaScript source            original JavaScript source
            +                                     +
            |                                     |
            |                                     |
            |                                     |
            v                                     v
abstract syntax tree of patched     abstract syntax tree of original
            +                                     +
            |                                     |
            |                                     |
            |                                     |
            +---------->Compare and swap<---------+

Preserving the original formatting

 

Parallel traverse and compare

 

Swap code

 
         ...
         },
         "type": "BlockStatement",
         "body": [
             {
                 "loc": {
                     "start": {
                         "line": 5,
                         "column": 4
                     },
                     "end": {
                         "line": 5,
                         "column": 17
                     }
                 },
                 "type": "VariableDeclaration",
                 "declarations": [
                     {
                         "loc": {
                             "start": {
                                 "line": 5,
                                 "column": 8
                             },
                             "end": {
                                 "line": 5,
                                 "column": 16
                             }
                         },
                         "type": "VariableDeclarator",
                       ...
         ...
         },
         "type": "BlockStatement",
         "body": [
             {
                 "loc": {
                     "start": {
                         "line": 5,
                         "column": 4
                     },
                     "end": {
                         "line": 7,
                         "column": 12
                     }
                 },
                 "type": "VariableDeclaration",
                 "declarations": [
                     {
                         "loc": {
                             "start": {
                                 "line": 5,
                                 "column": 8
                             },
                             "end": {
                                 "line": 5,
                                 "column": 16
                             }
                         },
                         "type": "VariableDeclarator",
                       ...

The future

 
  • AST-aware git blame
  • diff and patch support for multiple files
  • pluggable into git
  • ideas?
 
Made with Slides.com