Skip to content

Instantly share code, notes, and snippets.

@lorenzoongithub
Created May 14, 2015 20:46
Show Gist options
  • Save lorenzoongithub/25aed56399b4952547be to your computer and use it in GitHub Desktop.
Save lorenzoongithub/25aed56399b4952547be to your computer and use it in GitHub Desktop.
structured.js
//
// http://khan.github.io/structuredjs/
//
// structured.js is a Javascript library that provides a simple interface
// for checking the structure of Javascript code, backed by the abstract syntax tree
// generated by Esprima.
//
// This gist was originally derived from
// https://github.com/Khan/structuredjs/blob/master/tests.js
//
load('http://khan.github.io/structuredjs/external/underscore.min.js');
load('http://khan.github.io/structuredjs/external/esprima.js');
load('http://khan.github.io/structuredjs/structured.js');
// Accepts string and matches
if (Structured.match("var draw = function() {}","function() { var draw = function() {};}") == false) throw ""
// Accepts string and doesn't match it
if (Structured.match("var drow = function() {}","function() { var draw = function() {};}") ) throw ""
// Empty structure matches empty string.
if (Structured.match("",function () {}) == false) throw ""
// Basic if-statement structure matches.
if (Structured.match("if (y > 30 && x > 13) {x += y;}",function () {
if (_) {}
}) == false) throw ""
// Using a named function is allowable as well.
if (Structured.match("if (y > 30 && x > 13) {x += y;}",function foo() {
if (_) {}
}) == false) throw ""
// Basic if-else statement structure matches.
if (Structured.match("if (y > 30 && x > 13) {x += y;} else { y += 2;}",function () {
if (_) {} else {}
}) == false) throw ""
// Basic if, else-if, else statement structure matches.
if (Structured.match("if (y > 30 && x > 13) {x += y;} \t else if(x <10) {y -= 20;} else { y += 2;}",function () {
if (_) {} else if (_) {} else {}
}) == false) throw ""
// Basic for-statement structure matches.
if (Structured.match("for (var a = 0; a < 10; a += 1) { a -= 2;}",function () {
for (_; _; _) {}
}) == false) throw ""
// Basic variable declaration + initialization matches.
if (Structured.match("var a = 30;",function () {
var _ = _;
}) == false) throw ""
// Basic function assignment into var matches.
if (Structured.match("var test = function() {return 3+2;}",function () {
var _ = function() {};
}) == false) throw ""
// Basic standalone function declaration matches.
if (Structured.match("function foo() {return x+2;}",function () {
function _() {}
}) == false) throw ""
// No-parameter call to function matches.
if (Structured.match("rect();",function () {
rect();
}) == false) throw ""
// Parameterized call to no-param function structure matches.
if (Structured.match("rect(3);",function () {
rect();
}) == false) throw ""
// Fully specified parameter call to function matches.
if (Structured.match("rect(30, 40, 10, 11);",function () {
rect(30, 40, 10, 11);
}) == false) throw ""
// Parameters with wildcards call to function matches.
if (Structured.match("rect(30, 40, 10, 11);",function () {
rect(30, _, _, 11);
}) == false) throw ""
// Parameters with all wildcards call to function matches.
if (Structured.match("rect(30, 40);",function () {
rect(_, _);
}) == false) throw ""
// Extra params to function matches.
if (Structured.match("rect(30, 40, 30);",function () {
rect(_, _);
}) == false) throw ""
// Wildcard array matches [,]
if (Structured.match("[_]",function () {
[,];
}) == false) throw ""
// Empty array matches [,]
if (Structured.match("[,]",function () {
[];
}) == false) throw ""
// If-else does not match only an if.
if (Structured.match("if (y > 30 && x > 13) {x += y;}",function () {
if (_) {
_;
} else {}
}) ) throw ""
// If, else if, else structure does not match only if else.
if (Structured.match("if(y > 30 && x > 13) {x += y;} else {y += 2;}",function () {
if (_) {} else if (_) {} else {}
}) ) throw ""
// Variable declaration + init does not match just declaration.
if (Structured.match("var a;",function () {
var _ = _;
}) ) throw ""
// For-statement does not match a while loop.
if (Structured.match("while(true) { a -= 2;}",function () {
for (_; _; _) {}
}) ) throw ""
// Basic function declaration does not match basic var declaration
if (Structured.match("var test = 3+5",function () {
var _ = function() {};
}) ) throw ""
// Function declaration doesn't match function assignment into var
if (Structured.match("var test = function foo() {return 3+2;}",function () {
function _() {}
}) ) throw ""
// Call to function does not match differently-named function.
if (Structured.match("rect();",function () {
ellipse();
}) ) throw ""
// Fully specified parameter call to function identifies mismatch.
if (Structured.match("rect(300, 400, 100, 110);",function () {
rect(30, 40, 10, 11);
}) ) throw ""
// Too few parameters does not match.
if (Structured.match("rect(30);",function () {
rect(30, 40);
}) ) throw ""
// Parameters with wildcards call to function identifies mismatch.
if (Structured.match("rect(60, 40, 10, 11);",function () {
rect(30, _, _, 11);
}) ) throw ""
// Wildcard params do not match no-params for function call.
if (Structured.match("rect();",function () {
rect(_, _);
}) ) throw ""
// Parameters with too few wildcards call to function mismatches.
if (Structured.match("rect(30, 40);",function () {
rect(_, _, _);
}) ) throw ""
// Extra else-if statement correctly allowed though not specified.
if (Structured.match("if (y > 30 && x > 13) {x += y;} \t else if (x <10) {y -= 20;} else { y += 2;}",function () {
if (_) {} else {}
}) == false) throw ""
// Correctly matching for-loop condition.
if (Structured.match(" \t var x = 30; \n \t for (var i = 0; i < 10; j += 1) { \n \t if (y > 30 && x > 13) {x += y;} \n \t console.log(x); \n \t }",function () {
for (; _ < 10; _ += 1) {
if (_) {
}
}
}) == false) throw ""
// Correctly not matching for-loop condition.
if (Structured.match(" \t var x = 30; \n \t for (var i = 0; i < 20; i += 1) { \n \t if (y > 30 && x > 13) {x += y;} \n \t console.log(x); \n \t }",function () {
for (; _ < 10; _ += 1) {
if (_) {
}
}
}) ) throw ""
// Nested if with distractions matches.
if (Structured.match(" \t var x = 30; \n \t if (x > 10) { \n \t if (y > 30 && x > 13) {x += y;} \n \t console.log(x); \n \t } \n ",function () {
if (_) {
if (_) {
}
}
}) == false) throw ""
// More complex nested if with distraction matches.
if (Structured.match(" \t var x = 30; \n \t if (x > 10) { \n \t for (var a = 0; a < 2; a += 1) { \n \t if (y > 30 && x > 13) {x += y;} \n } \t console.log(x); \n \t } \n ",function () {
if (_) {
if (_) {
}
}
}) == false) throw ""
// Non-nested if does not match.
if (Structured.match(" \t var x = 30; \n \t if (x > 10) { \n \t while (y > 30 && x > 13) {x += y;} \n \t console.log(x); \n \t } if (y > 30 && x > 13) {x += y;} \n ",function () {
if (_) {
if (_) {
}
}
}) ) throw ""
// Draw with wildcard rect call matches.
if (Structured.match(" \t var draw = function() { \n \t rect(x, y, 30, 30); \n \t }; \n ",function () {
var draw = function() {
rect(_, _, _, _);
};
}) == false) throw ""
// Draw with wildcard rect call and distractions matches.
if (Structured.match(" \t var draw = function() { \n \t 20; \n \t rect(); \n \t }; \n ",function () {
var draw = function() {
rect();
};
}) == false) throw ""
// Larger drawing test successful.
if (Structured.match(" \t var draw = function() { \n \t var a = 20; \n \t var b = 40; \n \t var c = 10; \n \t if (a > 10 && b > 30) { \n \t a -= 3; \n \t var d = 2; \n \t if (d > 1 && c > 3) { \n \t d = 2; \n \t c = a / 2; \n \t b = 20; \n \t d = b; \n \t a = c % 2; \n \t rect(); \n \t ellipse(); \n \t } d += 10; c *= d; rect(3, c, d, a); \n \t ellipse(a, a, c, d); rect(); f += 3; \n \t } rect(); \n \t var b = 10; \n \t ellipse(); \n \t }; \n ",function () {
var draw = function() {
var _ = 10;
if (_) {
var _ = _;
if (_) {
_ = _ / 2;
_ = _ % 2;
rect();
}
}
rect();
ellipse();
};
}) == false) throw ""
// Back-to-back function calls match.
if (Structured.match("rect(); ellipse();",function () {
rect();
ellipse()
}) == false) throw ""
// Back-to-back function calls do not match only the first.
if (Structured.match("rect();",function () {
rect();
ellipse()
}) ) throw ""
// Back-to-back vars matched.
if (Structured.match("var a = 0; var b = 1; var c = 30;",function () {
var _ = 0;
var _ = 1;
var _ = _;
}) == false) throw ""
// Partial match does not suffice.
if (Structured.match("var a = 0; var b = 1;",function () {
var _ = 0;
var _ = 1;
var _ = _;
}) ) throw ""
// Multi-function call separated by statements matches.
if (Structured.match(" \t var draw = function() { \n \t var a = 20; \n \t rect(); \n \t var b = 10; \n \t ellipse(); \n \t var c = 30; \n \t var d = 3; \n \t c = a + b; \n \t }; \n ",function () {
var draw = function() {
rect();
ellipse();
var _ = 3;
};
}) == false) throw ""
// Always false varCallback causes failure.
if (Structured.match("var x = 10; var y = 20;",function () {
var _ = $a;
},{varCallbacks: {$a: function (obj) {
return false;
}}}) ) throw ""
// One always false varCallback of two causes failure.
if (Structured.match("var x = 10; var y = 20; var k = 42;",function () {
var _ = $a;
var _ = $b;
},{varCallbacks: {$a: function (obj) {
return false;
}, $b: function (obj) {
return true;
}}}) ) throw ""
// Always true varCallback with no match does not match.
if (Structured.match("var x = 10; var y = 20;",function () {
var z = $a;
},{varCallbacks: {$a: function (obj) {
return true;
}}}) ) throw ""
// Two single-matching var callbacks still need ordering.
if (Structured.match("var x = 10; var y = 20; var z = 40;",function () {
var _ = $a;
var _ = $b;
},{varCallbacks: {$a: function (obj) {
return obj.type === "Literal" && obj.value > 11;
}, $b: function (obj) {
return obj.type === "Literal" && obj.value < 11;
}}}) ) throw ""
// Returning failure object will be false
if (Structured.match("var x = 10; var y = 20",function () {
var $a = _;
},{varCallbacks: {$a: function (obj) {
return {
"failure": "Nothing can match $a!"
};
}}}) ) throw ""
// Returning failure object is false
if (Structured.match("var x = 10; var y = 20; var c = 0;",function () {
var $a = $b;
},{varCallbacks: {$a: function (obj) {
return true;
}, $b: function (obj) {
if (obj.value > 30) {
return true;
}
return {
"failure": "Make sure the value is big"
}
}}}) ) throw ""
// Unary - operator folded on number literals.
if (Structured.match("var x = -5;",function () {
var x = $num;
},{varCallbacks: {$num: function (num) {
return num && num.value && num.value < 0;
}}}) == false) throw ""
// Unary + operator folded on number literals.
if (Structured.match("var x = +5;",function () {
var x = $num;
},{varCallbacks: {$num: function (num) {
return num && num.value && num.value > -10;
}}}) == false) throw ""
// Unary + - operators work on non-literals.
if (Structured.match("var y = 10; var x = +y; x = -y;",function () {
var x = +$var;
x = -$var;
}) == false) throw ""
// Basic variable match works.
if (Structured.match("var x = 10; x -= 1;",function () {
var $a = 10;
$a -= 1;
}) == false) throw ""
// Basic variable no-match works.
if (Structured.match("var x = 10; y -= 1;",function () {
var $a = 10;
$a -= 1;
}) ) throw ""
// Basic multiple-option variable match works.
if (Structured.match("var x = 10; var y = 10; var t = 3; var c = 1; c = 3; t += 2; y += 2;",function () {
var $a = 10;
var $b = _;
$b += 2;
$a += 2;
}) == false) throw ""
// Basic multiple-option variable no-match works.
if (Structured.match("var x = 10; var y = 10; var t = 3; var c = 1; c = 3; y += 2;",function () {
var $a = _;
var $b = _;
$b = 3 + 2;
$a += 2;
}) ) throw ""
// More ambiguous multi-variable non-matching works.
if (Structured.match(" \n \t var r, s, t, u, v, w, x, y, z; \n \t r = 1; s = 1; t = 1; u = 1; v = 1; w = 1; x = 1; y = 1; z = 1; \n \t x += 3; \n \t ",function () {
var $a, $b, $c, $d;
$a = 1;
$b = 1;
$c = 1;
$d = 1;
$a += 3;
}) ) throw ""
// Complex vars with small mismatch breaks.
if (Structured.match(" \n \t var a = 10, b = 20, z = 3, y = 1; \n \t var bar = function(x) {return x + 3;}; \n \t var foo = function(x) {return x + 3;}; \n \t var draw = function() { \n \t var t = z + y; \n \t foo(t); \n \t foo(3, 'eagle', t, 10); \n \t test(z); \n \t foo(z); \n \t z = 'eaglee'.length; \n \t } \n \t ",function () {
var $a = _;
var $d = function() {}
var draw = function() {
var $b = $a + _;
$d(_, $e, $b, _);
$d($a);
$a = $e.length;
}
}) ) throw ""
// Downward expression ordering works.
if (Structured.match("var x = 5; while(true) { var y = 6; }",function () {
var x = 5;
var y = 6;
}) == false) throw ""
// Single function
if (Structured.match("var a = 1","function(){ var $v = 1; }",{varCallbacks: function ($v) {
return $v.name === "a";
}}) == false) throw ""
// List of functions
if (Structured.match("var a = 2","function(){ var _ = $x; }",{varCallbacks: [function ($x) {
return $x.value === 2;
}]}) == false) throw ""
// Single constraint
if (Structured.match("size()","function(){ $f() }",{varCallbacks: {variables: ["$f"], fn: function (func) {
return func.name === "size";
}}}) == false) throw ""
// List of Constraints
if (Structured.match("a = b + 10","function(){ a = $fun + $add }",{varCallbacks: [{variables: ["$fun"], fn: function (variable) {
return variable.name === "b";
}}, {variables: ["$add"], fn: function (num) {
return num.value > 5;
}}]}) == false) throw ""
// List of Constraints
if (Structured.match("a = b + 10","function(){ a = $fun + $add }",{varCallbacks: [function ($fun){
return $fun.name == "b";
}, {variables: ["$add"], fn: function (num) {
return num.value > 5;
}}]}) == false) throw ""
console.log('all done')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment