diff --git a/esprima-to-ast.js b/esprima-to-ast.js index 0c2a0a384ed0be35ce7b7f920eb02123211cc651..e86aaf8bca7157a1b97dab3f066cef025c927f27 100644 --- a/esprima-to-ast.js +++ b/esprima-to-ast.js @@ -3,6 +3,9 @@ //https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API$revision/707671 function esprimaToAST(prog, sourceText) { + + var contextStrictMode = false; + var toList = function (array) { var r = {type: "list", tag: "[]"}; for (var i = array.length - 1; i >= 0; i--) { @@ -37,6 +40,29 @@ function esprimaToAST(prog, sourceText) { return toOption(funcTr, (arr.length > 0 ? arr[0] : null)); } + var isDirectivePrologue = function(stat) { + return stat.type === "ExpressionStatement" && + stat.expression.type === "Literal" && + (typeof stat.expression.value) === "string"; + } + + var isUseStrictDirective = function(stat) { + return isDirectivePrologue(stat) && + /^["']use strict["']$/.test(stat.expression.raw); + } + + var getDirectivePrologue = function(statArr) { + // statArr.takeWhile(isDirectivePrologue) + return statArr.filter(function(stat) { + this.ok = this.ok && isDirectivePrologue(stat); + return this.ok; + }, { ok: true }); + }; + + var bodyIsStrict = function(statArr) { + return getDirectivePrologue(statArr).some(isUseStrictDirective); + } + /*** Miscellaneous Helper Nodes ***/ // The JSCert AST special-cases this instance of option @@ -60,8 +86,7 @@ function esprimaToAST(prog, sourceText) { }; var r = {loc: toLoc(prog.loc), type: "prog"}; r.tag = "Coq_prog_intro"; - // TODO deal with strictness - r.strictness = false; + r.strictness = contextStrictMode = bodyIsStrict(prog.body); r.elements = toList(prog.body.map(trStatAsElement)); return r; }; @@ -92,16 +117,20 @@ function esprimaToAST(prog, sourceText) { } var loc = toLoc(stat.loc); + var prog = { loc: loc, type: "prog", tag: "Coq_prog_intro" }; + + var oldContextStrictMode = contextStrictMode; + contextStrictMode = contextStrictMode || bodyIsStrict(stat.body); + prog.strictness = contextStrictMode; + prog.elements = toList(stat.body.map(trStatAsElement)); + contextStrictMode = oldContextStrictMode; + var source = ""; if (sourceText && stat.range) { // Esprima's range includes the { } source = sourceText.slice(stat.range[0]+1, stat.range[1]-1); } - // TODO strictness - var prog = {loc: loc, type: "prog", tag: "Coq_prog_intro", - strictness: false, - elements: toList(stat.body.map(trStatAsElement))}; return {loc: loc, type: "funcbody", tag: "Coq_funcbody_intro", prog: prog, source: source}; }; diff --git a/generator/TODO b/generator/TODO index cf9af5b7287b338783217b8d51bc45c0ae3067b9..b17dd0c8bb4f779692df71bff62568a7aa2f91d8 100644 --- a/generator/TODO +++ b/generator/TODO @@ -41,12 +41,6 @@ NEXT - in JsInterpreter let append = strappend (* hack for compatibility, to do cleanup *) -[thomas, biggest piece, ask if anything problematic] -- update the code that translates esprima syntax to recognize - "use strict" directives, which come as first statement of - a body, in hte form of a raw expression of raw type "use script". - (look on the web for code that already does this for esprima). - [alan or thomas] - figure out what to do for Parser_syntax Parser_main diff --git a/test/parser.js b/test/parser.js index 11e69c2a3c0c3b050e7196081a4668434099f443..1d845420432598e400ae95402df05206a005ddf4 100644 --- a/test/parser.js +++ b/test/parser.js @@ -9,9 +9,6 @@ var assert = require('chai').assert; var esprima = require('esprima'); var esprimaToAST = require('../esprima-to-ast.js'); -var test262path = fs.readlinkSync(__dirname + '/test262'); -var tests = []; - /* Tests whether a given test is negative. * Returns: a string if type of failure specified, true, or false */ @@ -203,6 +200,78 @@ function typecheckAST(ast) { return typecheck("prog", ast); } +describe("Custom testcases", function() { + it("Extracts function body strings", function() { + var source = +`function f() { + // body line 1 +a()};`; + + var prog = esprima.parse(source, {loc: true, range: true}); + var ast = esprimaToAST.esprimaToAST(prog, source); + + var sourceBody = /{([^]*)}/.exec(source)[1]; + assert.strictEqual(sourceBody, ast.elements.head.body.source); + + }); + + [ { source: `"use strict";` , strict: true } + , { source: `'use strict';` , strict: true } + , { source: `"not strict"; "use strict";` , strict: true } + , { source: `""; 0; "use strict";` , strict: false } + , { source: `"use\\u0020strict";` , strict: false } + ].forEach(function (test) { + it("Correctly parses that `" + test.source + "` is " + (test.strict ? " " : "not ") + "strict mode code.", function() { + var est = esprima.parse(test.source, {loc: true, range: true}); + var ast = esprimaToAST.esprimaToAST(est, test.source); + assert.strictEqual(test.strict, ast.strictness); + }); + }); + + var isFunction = function(stat) { + return stat.tag === "Coq_element_func_decl" + || (stat.tag === "Coq_element_stat" && + stat.stat.tag === "Coq_stat_expr" && + stat.stat.expr.tag === "Coq_expr_function"); + }; + + + var getFunctionStrictness = function(stat) { + if (stat.tag === "Coq_element_func_decl") { + return stat.body.prog.strictness; + } else if (stat.tag === "Coq_element_stat") { + return stat.stat.expr.body.prog.strictness; + } else { + assert.fail(); + } + }; + + [ { source: `"use strict"; function x() {}` , strict: [true] } + , { source: `'use strict'; (function() {})` , strict: [true] } + , { source: `function x() {"use strict";}` , strict: [true] } + , { source: `(function() {"use strict";})` , strict: [true] } + , { source: `(function(){}); function x(){"use strict";};` , strict: [false, true] } + ].forEach(function (test) { + it("Correctly parses that `" + test.source + "` is " + (test.strict ? " " : "not ") + "strict mode code.", function() { + var est = esprima.parse(test.source, {loc: true, range: true}); + var ast = esprimaToAST.esprimaToAST(est, test.source); + + var listElem = ast.elements; + var index = 0; + while (listElem.tag === "::") { + if (isFunction(listElem.head)) { + assert.strictEqual(test.strict[index], getFunctionStrictness(listElem.head)); + index++; + } + listElem = listElem.tail; + } + }); + }); +}); + +var test262path = fs.readlinkSync(__dirname + '/test262'); +var tests = []; + walk(test262path) .pipe(filter.obj(file => file.stats.isFile() && file.path.endsWith(".js"))) .on('readable', function() { @@ -257,19 +326,3 @@ walk(test262path) run(); }); - -describe("Custom testcases", function() { - it("Extracts function body strings", function() { - var source = -`function f() { - // body line 1 -a()};`; - - var prog = esprima.parse(source, {loc: true, range: true}); - var ast = esprimaToAST.esprimaToAST(prog, source); - - var sourceBody = /{([^]*)}/.exec(source)[1]; - assert.strictEqual(sourceBody, ast.elements.head.body.source); - - }); -});