Created
May 14, 2015 20:46
-
-
Save lorenzoongithub/25aed56399b4952547be to your computer and use it in GitHub Desktop.
structured.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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