Commits

Alexander Tsepkov  committed b1b81d9

Improvements to classes, screw-ie8 option

  • Participants
  • Parent commits 95e47f5

Comments (0)

Files changed (5)

 			SomethingElse.method(self, var)
 			SomethingElse.anotherMethod(self)
 
-Notice that `Something` class has no `__init__` method. Like in Python, this method is optional for classes. If you omit it, an empty constructor will automatically get created for you by RapydScript (or when inheriting, the parent's constructor will be used). Also notice that we never inherited from SomethingElse class, yet we can invoke its methods. This brings us to the next point, the only real advantage of inheriting from another class (which you can't gain by calling the other classes method as shown above) is that the omitted methods are automatically copied from the parent. Admittedly, we might also care about instance() method, to have it work with the non-main parent, but we're already overwriting JavaScript's instanceof() method in stdlib, so feel free to tweak it further, if you have the need to. To summarize classes, assume they work the same way as in Python, plus a few bonus cases. The following, for example, are equivalent:
+Notice that `Something` class has no `__init__` method. Like in Python, this method is optional for classes. If you omit it, an empty constructor will automatically get created for you by RapydScript (or when inheriting, the parent's constructor will be used). Also notice that we never inherited from SomethingElse class, yet we can invoke its methods. This brings us to the next point, the only real advantage of inheriting from another class (which you can't gain by calling the other classes method as shown above) is that the omitted methods are automatically copied from the parent. Admittedly, we might also care about `isinstance()` method, to have it work with the non-main parent, which is equivalent to JavaScript's `instanceof` operator.
+
+**NOTE:** If you compile your code without `--screw-ie8` flag, the constructor will be executed every time you create a subclass of the given class. In most cases this will not hurt you, but in some cases you could see odd side-effects. For example, having a `print()` statement in the parent constructor will cause its contents to be output for every subclass, even if you do not explicitly create an instance. `--screw-ie8` option fixes this issue, at the expense of compatibility with Internet Explorer 6-8.
+
+To summarize classes, assume they work the same way as in Python, plus a few bonus cases. The following, for example, are equivalent:
 
 	class Aclass:
 		def __init__(self):
 ---------------------
 This section contains various topics which might be of interest to the programmer writing large projects using RapydScript, but might not be relevant to a programmer who is just getting started with RapydScript. The topics in this section focus on coding conventions to keep your code clean, optimizations, and additional libraries that come with RapydScript, as well as suggestions for writing your own libraries.
 
+### Browser Compatibility
+By default, RapydScript compiles your logic such that it will work on modern browsers running HTML5, as well as older browser like IE6-8. To do so, the compiler sometimes has to generate rather messy and inefficient output. As of September, 2013, less than 8% of the world has been found to be using IE8. If you know that your users will not be using older browsers, you can compile your logic with `--screw-ie8` option to generate cleaner, faster code.
+
 ### Code Conventions
 It's not hard to see that RapydScript is a cleaner language than JavaScript. However, like with all dynamically-typed languages (including Python), it's still easy to shoot yourself in the foot if you don't follow some sort of code conventions. Needless to say, they're called `conventions` for a reason, feel free to ignore them if you already have a set of conventions you follow or if you disagree with some.
 

File bin/rapydscript

     process.exit(0);
 }
 
+// configure settings for the output
+var BEAUTIFY = getOptions("p", true);
+var NO_MODULE_WRAPPER = getOptions("b", false);
+var NAMESPACE_IMPORTS = getOptions("n", false);
+var OMIT_BASELIB = getOptions("m", false);
+
+var OUTPUT_OPTIONS = {
+    beautify: BEAUTIFY ? true : false,
+    private_scope: NO_MODULE_WRAPPER ? false : true,
+    namespace_imports: NAMESPACE_IMPORTS ? true : false,
+    omit_baselib: OMIT_BASELIB ? true : false,
+    screw_old_browsers: ARGS.screw_ie8 ? true : false
+};
+
 if (ARGS.t || ARGS.test) {
     // run all tests and exit
     var assert = require("assert");
             return;
         }
         // generate output
-        var output = RapydScript.OutputStream({beautify: true});
-        ast.print(output);
+        // only a certain subset of flags we can set could produce successful unit tests, let's hardcode that subset
+        for (var i = 0; i < 2; i++) {
+            var title = i == 0 ? 'normal:\t\t' : 'screw-ie8:\t';
+            sys.print(title);
+            var output = RapydScript.OutputStream({
+                screw_old_browsers: i
+            });
+            ast.print(output);
 
-        // test that output performs correct JS operations
-//        sys.print(output);
-//            eval(output.toString());
-        var testcontent = "exports.run = function(){assert = require('assert');" + output.toString() + "};";
-        fs.writeFileSync(filepath + ".js", testcontent);
-        var testcase = require(filepath + ".js");
-        try {
-            testcase.run();
-        } catch (e) {
-            sys.print(file + ":\t" + e.stack + "\n\n");
+            // test that output performs correct JS operations
+    //        sys.print(output);
+    //            eval(output.toString());
+            var testcontent = "exports.run = function(){assert = require('assert');" + output.toString() + "};";
+            fs.writeFileSync(filepath + ".js", testcontent);
+            var testcase = require(filepath + ".js");
+            try {
+                testcase.run();
+            } catch (e) {
+                sys.print(file + ":\t" + e.stack + "\n\n");
+                fs.unlinkSync(filepath + ".js");
+                return;
+            }
+            sys.print(file + ":\ttest completed successfully\n");
             fs.unlinkSync(filepath + ".js");
-            return;
-        }
-        sys.print(file + ":\ttest completed successfully\n");
-        fs.unlinkSync(filepath + ".js");
+        };
     });
     process.exit(0);
 }
 
-var BEAUTIFY = getOptions("p", true);
-var NO_MODULE_WRAPPER = getOptions("b", false);
-var NAMESPACE_IMPORTS = getOptions("n", false);
-var OMIT_BASELIB = getOptions("m", false);
-
-var OUTPUT_OPTIONS = {
-    beautify: BEAUTIFY ? true : false,
-    private_scope: NO_MODULE_WRAPPER ? false : true,
-    namespace_imports: NAMESPACE_IMPORTS ? true : false,
-    omit_baselib: OMIT_BASELIB ? true : false
-};
-
-if (BEAUTIFY)
-    RapydScript.merge(OUTPUT_OPTIONS, BEAUTIFY);
+//if (BEAUTIFY);
+//    RapydScript.merge(OUTPUT_OPTIONS, BEAUTIFY);
 
 if (ARGS.comments) {
     if (/^\//.test(ARGS.comments)) {

File lib/output.js

         preserve_line : false,
         namespace_imports: false,
         omit_baselib  : false,
-        private_scope : true
+        private_scope : true,
+        screw_old_browsers: false
     }, true);
 
     var indentation = 0;
                     ]
                 });
             },
+            "extends": function() {
+                // regular:
+                //
+                // function _$rapyd$_extends(child, parent) {
+                //  child.prototype = new parent;
+                //  child.prototype.constructor = child;
+                //  _$rapyd$_rebindAll(child.prototype);
+                // }
+                //
+                // screw_old_browsers:
+                //
+                // function _$rapyd$_extends(child, parent) {
+                //  child.prototype = Object.create(parent.prototype)
+                //  child.prototype.constructor = child;
+                // }
+                if (options.screw_old_browsers) {
+                    return func_gen("_$rapyd$_extends", {
+                        args: ['child', 'parent'],
+                        body: [
+                            ['child.prototype', '=', 'Object.create', {call: ['parent.prototype']}],
+                            ['child.prototype.constructor', '=', 'child']
+                        ]
+                    });
+                } else {
+                    return func_gen("_$rapyd$_extends", {
+                        args: ['child', 'parent'],
+                        body: [
+                            ['child.prototype', '=', 'new', 'parent'],
+                            ['child.prototype.constructor', '=', 'child'],
+                            ['_$rapyd$_rebindAll', {call: ['child.prototype']}]
+                        ]
+                    });
+                }
+            },
             "in": function(){
                 //function _$rapyd$_in(val, arr){
                 //  if (arr instanceof Array) return arr.indexOf(val) != -1;
             });
             print_bracketed(stmt, output, true);
             output.semicolon();
+            output.newline();
             decorate(stmt, output, self.name.name + ".prototype.");
         };
 
             output.with_block(function(){
                 bind_methods(self.bound, output);
             });
-//            output.with_block(function(){
-//                if (self.parent) {
-//                    output.indent();
-//                    self.parent.print(output);
-//                    output.print(".prototype.constructor.call(this)");
-//                    output.semicolon();
-//                    output.newline();
-//                }
-//            });
         }
+        output.newline();
 
         // inheritance
         if (self.parent) {
-            output.newline();
-            class_def();
-            output.print("new");
-            output.space();
-            self.parent.print(output);
-            output.print("()");
+            output.indent();
+            output.print('_$rapyd$_extends');
+            output.with_parens(function(){
+                self.name.print(output);
+                output.comma();
+                self.parent.print(output);
+            });
             output.semicolon();
             output.newline();
-            class_def("constructor");
-            self.name.print(output);
-            output.semicolon();
-            
-            // rebind prototype
-            output.newline();
-            output.indent();
-            output.print('_$rapyd$_rebindAll');
-            output.with_parens(function(){
-                self.name.print(output);
-                output.print('.prototype');
-            });
-            output.semicolon();
         }
 
         // actual methods
                     class_fun_def(stmt);
                 }
             } else if (stmt instanceof AST_Class) {
-                output.newline();
                 output.indent();
                 self.name.print(output);
                 output.print(".");
                 stmt.print(output);
+                output.newline();
             }
         });
         decorate(self, output);

File lib/parse.js

     'Image': {},
     'RegExp': {},
     'Error': {},
-    'Object': {static: ['getOwnPropertyNames', 'keys']},
+    'Object': {static: ['getOwnPropertyNames', 'keys', 'create']},
     'String': {static: ['fromCharCode']},
     'Array': {static: ['isArray', 'from', 'of']},
     'Number': {static: ['isFinite', 'isNaN']},
                 return finalize_import(import_());
 
               case "class":
+                BASELIB["extends"] = true;
                 BASELIB["bind"] = true;
                 BASELIB["rebind_all"] = true;
                 return class_();

File package.json

   "description": "RapydScript to JavaScript compiler",
   "homepage": "http://rapydscript.pyjeon.com",
   "main": "tools/node.js",
-  "version": "0.1.91",
+  "version": "0.1.92",
   "engines": {
     "node": ">=0.4.0"
   },