diff --git a/demo/index.js b/demo/index.js index 9599878..195db63 100644 --- a/demo/index.js +++ b/demo/index.js @@ -118,11 +118,6 @@ window.onload = function() { var beginTime, elapsedTime; code = getCode(editor).trim(); - - while (/^\([\s\S]+\)$/.test(code)) { - code = code.slice(1, -1); - } - beginTime = now(); result = eval.call(null, SCScript.compile(code)); diff --git a/demo/parse.js b/demo/parse.js index 52e2f66..8a5eda0 100644 --- a/demo/parse.js +++ b/demo/parse.js @@ -14,9 +14,6 @@ $(function() { } function update(code, mode) { - while (/^\([\s\S]+\)$/.test(code)) { - code = code.slice(1, -1); - } if (prev[0] !== code || prev[1] !== mode) { try { switch (mode) { diff --git a/package.json b/package.json index 14a9894..60cc448 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scscript", - "version": "0.0.67", + "version": "0.0.68", "author": "Nao Yonamine ", "homepage": "http://mohayonao.github.io/SCScript/", "bugs": "https://github.com/mohayonao/SCScript/issues", diff --git a/src/sc/lang/compiler/codegen/block-expr.js b/src/sc/lang/compiler/codegen/block-expr.js new file mode 100644 index 0000000..b3ee262 --- /dev/null +++ b/src/sc/lang/compiler/codegen/block-expr.js @@ -0,0 +1,15 @@ +(function(sc) { + "use strict"; + + require("./codegen"); + + var CodeGen = sc.lang.compiler.CodeGen; + + CodeGen.addGenerateMethod("BlockExpression", function(node) { + var body = this.withFunction([], function() { + return this.generateStatements(node.body); + }); + + return [ "(", body, ")()" ]; + }); +})(sc); diff --git a/src/sc/lang/compiler/codegen/block-expr_test.js b/src/sc/lang/compiler/codegen/block-expr_test.js new file mode 100644 index 0000000..4fca026 --- /dev/null +++ b/src/sc/lang/compiler/codegen/block-expr_test.js @@ -0,0 +1,46 @@ +(function() { + "use strict"; + + require("./"); + + var Syntax = sc.lang.compiler.Syntax; + var Token = sc.lang.compiler.Token; + + describe("sc.lang.compiler.CodeGen", function() { + describe("BlockExpression", function() { + sc.test.codegen().each([ + { + code: "(var a = 10; a)", + expected: "(function() { var $a; $a = $.Integer(10); return $a; })()", + ast: { + type: Syntax.BlockExpression, + body: [ + { + type: Syntax.VariableDeclaration, + kind: "var", + declarations: [ + { + type: Syntax.VariableDeclarator, + id: { + type: Syntax.Identifier, + name: "a" + }, + init: { + type: Syntax.Literal, + value: "10", + valueType: Token.IntegerLiteral + } + } + ] + }, + { + type: Syntax.Identifier, + name: "a" + } + ] + } + }, + ]); + }); + }); +})(); diff --git a/src/sc/lang/compiler/codegen/codegen.js b/src/sc/lang/compiler/codegen/codegen.js index 4f87162..ffa1ae4 100644 --- a/src/sc/lang/compiler/codegen/codegen.js +++ b/src/sc/lang/compiler/codegen/codegen.js @@ -105,6 +105,20 @@ return result; }; + CodeGen.prototype.generateStatements = function(elements) { + var lastIndex = elements.length - 1; + + return elements.map(function(item, i) { + var stmt = this.generate(item); + + if (i === lastIndex) { + stmt = [ "return ", stmt ]; + } + + return [ stmt, ";" ]; + }, this); + }; + CodeGen.prototype.useTemporaryVariable = function(func) { var result; var tempName = "_ref" + this.state.tempVarId; diff --git a/src/sc/lang/compiler/codegen/codegen_test.js b/src/sc/lang/compiler/codegen/codegen_test.js index 2ed4108..fd2f637 100644 --- a/src/sc/lang/compiler/codegen/codegen_test.js +++ b/src/sc/lang/compiler/codegen/codegen_test.js @@ -79,6 +79,14 @@ test = toCompiledString(test); expect(test).to.equal("[]"); }); + it("generateStatements", function() { + var codegen = new CodeGen(); + + var test; + test = codegen.generateStatements([ "a", "b", "c" ]); + test = toCompiledString(test); + expect(test).to.equal("$a;$b;return $c;"); + }); it("useTemporaryVariable", function() { var codegen = new CodeGen(); diff --git a/src/sc/lang/compiler/codegen/index.js b/src/sc/lang/compiler/codegen/index.js index f936c26..7565a12 100644 --- a/src/sc/lang/compiler/codegen/index.js +++ b/src/sc/lang/compiler/codegen/index.js @@ -1,6 +1,7 @@ require("./codegen"); require("./assignment-expr"); require("./binop-expr"); +require("./block-expr"); require("./call-expr"); require("./envir-expr"); require("./event-expr"); diff --git a/src/sc/lang/compiler/codegen/program.js b/src/sc/lang/compiler/codegen/program.js index e2feacc..ce474ae 100644 --- a/src/sc/lang/compiler/codegen/program.js +++ b/src/sc/lang/compiler/codegen/program.js @@ -11,7 +11,7 @@ } var body = this.withFunction([ "" ], function() { // "" compiled as $ - return generateStatements(this, node.body); + return this.generateStatements(node.body); }); var result = [ "(", body, ")" ]; @@ -22,18 +22,4 @@ return result; }); - - function generateStatements(that, elements) { - var lastIndex = elements.length - 1; - - return elements.map(function(item, i) { - var stmt = that.generate(item); - - if (i === lastIndex) { - stmt = [ "return ", stmt ]; - } - - return [ stmt, ";" ]; - }); - } })(sc); diff --git a/src/sc/lang/compiler/compiler.js b/src/sc/lang/compiler/compiler.js index 0f6222c..8187ad5 100644 --- a/src/sc/lang/compiler/compiler.js +++ b/src/sc/lang/compiler/compiler.js @@ -23,6 +23,7 @@ }, Syntax: { AssignmentExpression: "AssignmentExpression", + BlockExpression: "BlockExpression", BinaryExpression: "BinaryExpression", CallExpression: "CallExpression", FunctionExpression: "FunctionExpression", diff --git a/src/sc/lang/compiler/node/node.js b/src/sc/lang/compiler/node/node.js index d56d511..f21fc41 100644 --- a/src/sc/lang/compiler/node/node.js +++ b/src/sc/lang/compiler/node/node.js @@ -30,6 +30,12 @@ } return node; }, + createBlockExpression: function(body) { + return { + type: Syntax.BlockExpression, + body: body + }; + }, createCallExpression: function(callee, method, args, stamp) { return { type: Syntax.CallExpression, diff --git a/src/sc/lang/compiler/parser/block-expr.js b/src/sc/lang/compiler/parser/block-expr.js new file mode 100644 index 0000000..2e29453 --- /dev/null +++ b/src/sc/lang/compiler/parser/block-expr.js @@ -0,0 +1,24 @@ +(function(sc) { + "use strict"; + + require("./parser"); + + var Node = sc.lang.compiler.Node; + var Parser = sc.lang.compiler.Parser; + + Parser.addParseMethod("BlockExpression", function() { + var marker = this.createMarker(); + + this.expect("("); + + var expr = this.withScope(function() { + return Node.createBlockExpression( + this.parseFunctionBody() + ); + }); + + this.expect(")"); + + return marker.update().apply(expr); + }); +})(sc); diff --git a/src/sc/lang/compiler/parser/block-expr_test.js b/src/sc/lang/compiler/parser/block-expr_test.js new file mode 100644 index 0000000..8204194 --- /dev/null +++ b/src/sc/lang/compiler/parser/block-expr_test.js @@ -0,0 +1,69 @@ +(function() { + "use strict"; + + require("./"); + + var Syntax = sc.lang.compiler.Syntax; + var Token = sc.lang.compiler.Token; + var Message = sc.lang.compiler.Message; + var strlib = sc.libs.strlib; + + describe("sc.lang.compiler.Parser", function() { + describe("parseBlockExpression", function() { + sc.test.compile(this.title).each({ + "(var a = 10;)": sc.test.OK, + "var a = 10;": strlib.format(Message.UnexpectedKeyword, "var"), + }); + sc.test.parse(this.title).each({ + "(var a = 10;)": { + type: Syntax.BlockExpression, + body: [ + { + type: Syntax.VariableDeclaration, + kind: "var", + declarations: [ + { + type: Syntax.VariableDeclarator, + id: { + type: Syntax.Identifier, + name: "a", + range: [ 5, 6 ], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 6 } + } + }, + init: { + type: Syntax.Literal, + value: "10", + valueType: Token.IntegerLiteral, + range: [ 9, 11 ], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 11 } + } + }, + range: [ 5, 11 ], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 11 } + } + } + ], + range: [ 1, 11 ], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 11 } + }, + } + ], + range: [ 0, 13 ], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 13 } + } + } + }); + }); + }); +})(); diff --git a/src/sc/lang/compiler/parser/index.js b/src/sc/lang/compiler/parser/index.js index 25fc685..d6593cb 100644 --- a/src/sc/lang/compiler/parser/index.js +++ b/src/sc/lang/compiler/parser/index.js @@ -1,6 +1,7 @@ require("./parser"); require("./assignment-expr"); require("./binop-expr"); +require("./block-expr"); require("./braces"); require("./call-expr"); require("./envir-expr"); diff --git a/src/sc/lang/compiler/parser/parentheses.js b/src/sc/lang/compiler/parser/parentheses.js index 15a9825..a3b197b 100644 --- a/src/sc/lang/compiler/parser/parentheses.js +++ b/src/sc/lang/compiler/parser/parentheses.js @@ -43,6 +43,11 @@ return this.parseEventExpression(); }; } + if (this.match("var")) { + return function() { + return this.parseBlockExpression(); + }; + } if (this.match("..")) { return function() { return this.parseSeriesExpression(); diff --git a/src/sc/lang/compiler/parser/parentheses_test.js b/src/sc/lang/compiler/parser/parentheses_test.js index b881278..590a6a6 100644 --- a/src/sc/lang/compiler/parser/parentheses_test.js +++ b/src/sc/lang/compiler/parser/parentheses_test.js @@ -20,8 +20,8 @@ "(0;1..10)": sc.test.OK, // SeriesExpression "(1;2)": sc.test.OK, // Expressions "(1)": sc.test.OK, // Expression + "(var a;)": sc.test.OK, // BlockExpression "[1]": strlib.format(Message.UnexpectedToken, "["), - "(var a;)": Message.UnexpectedKeyword, }); sc.test.parse(this.title).each({ "()": { diff --git a/src/sc/lang/compiler/parser/program.js b/src/sc/lang/compiler/parser/program.js index d880cb9..82c3367 100644 --- a/src/sc/lang/compiler/parser/program.js +++ b/src/sc/lang/compiler/parser/program.js @@ -3,6 +3,7 @@ require("./parser"); + var Syntax = sc.lang.compiler.Syntax; var Node = sc.lang.compiler.Node; var Parser = sc.lang.compiler.Parser; @@ -15,6 +16,14 @@ ); }); + if (hasSingleBlockExpression(node)) { + node.body = node.body[0].body; + } + return marker.update().apply(node); }); + + function hasSingleBlockExpression(node) { + return node.body.length === 1 && node.body[0].type === Syntax.BlockExpression; + } })(sc); diff --git a/src/sc/lang/compiler/parser/program_test.js b/src/sc/lang/compiler/parser/program_test.js index a9e7add..7d88e01 100644 --- a/src/sc/lang/compiler/parser/program_test.js +++ b/src/sc/lang/compiler/parser/program_test.js @@ -70,6 +70,44 @@ start: { line: 1, column: 0 }, end: { line: 1, column: 6 }, } + }, + "(var a;)": { + type: Syntax.Program, + body: [ + { + type: Syntax.VariableDeclaration, + declarations: [ + { + type: Syntax.VariableDeclarator, + id: { + type: Syntax.Identifier, + name: "a", + range: [ 5, 6 ], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 6 }, + } + }, + range: [ 5, 6 ], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 6 }, + } + } + ], + kind: "var", + range: [ 1, 6 ], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 6 }, + } + } + ], + range: [ 0, 8 ], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 8 }, + } } }); }); diff --git a/src/sc/node-cli/repl.js b/src/sc/node-cli/repl.js index 3850a7c..6048f4d 100644 --- a/src/sc/node-cli/repl.js +++ b/src/sc/node-cli/repl.js @@ -18,9 +18,6 @@ function toString(obj) { var replOptions = { prompt: "scsc> ", eval: function(input, context, filename, callback) { - while (/^\([\s\S]+\)$/.test(input)) { - input = input.slice(1, -1).trim(); - } try { var js = SCScript.compile(input, { loc: true, range: true }); var result;