瀏覽代碼

Merge branch 'master' into ast-interpreter

Conflicts:
	bin/amberc
	js/IDE.deploy.js
	js/IDE.js
	js/Kernel-Methods.deploy.js
	js/Kernel-Methods.js
	js/Kernel-Objects.deploy.js
	js/Kernel-Objects.js
	js/Kernel-Tests.deploy.js
	js/Kernel-Tests.js
	js/SUnit.deploy.js
	js/SUnit.js
	js/boot.js
	js/parser.js
	js/parser.pegjs
Nicolas Petton 12 年之前
父節點
當前提交
3f6a804415
共有 31 個文件被更改,包括 5279 次插入3907 次删除
  1. 10 350
      bin/amberc
  2. 761 0
      bin/amberc.js
  3. 353 0
      bin/amberc.sh
  4. 337 359
      js/IDE.deploy.js
  5. 337 359
      js/IDE.js
  6. 82 0
      js/Kernel-Collections.deploy.js
  7. 102 0
      js/Kernel-Collections.js
  8. 379 304
      js/Kernel-Methods.deploy.js
  9. 408 308
      js/Kernel-Methods.js
  10. 332 322
      js/Kernel-Objects.deploy.js
  11. 320 310
      js/Kernel-Objects.js
  12. 324 304
      js/Kernel-Tests.deploy.js
  13. 326 301
      js/Kernel-Tests.js
  14. 233 234
      js/SUnit.deploy.js
  15. 235 236
      js/SUnit.js
  16. 4 5
      js/boot.js
  17. 0 0
      js/lib/peg-0.6.2.min.js
  18. 7 0
      js/lib/peg-0.7.0.min.js
  19. 280 233
      js/parser.js
  20. 4 22
      js/parser.pegjs
  21. 1 1
      repl/Makefile
  22. 1 1
      repl/REPL.js
  23. 322 228
      repl/amber.js
  24. 6 6
      server/FileServer.st
  25. 8 8
      server/server.js
  26. 1 3
      st/IDE.st
  27. 37 0
      st/Kernel-Collections.st
  28. 53 7
      st/Kernel-Methods.st
  29. 5 1
      st/Kernel-Tests.st
  30. 9 3
      st/Makefile
  31. 2 2
      st/SUnit.st

+ 10 - 350
bin/amberc

@@ -1,353 +1,13 @@
-#!/bin/bash
-#
-# This is a "compiler" for Amber code. Run without arguments for help.
-#
-# Get Amber root directory from the location of this script so that
-# we can find the st and js directories etc.
+#!/usr/bin/env node
 
-# Earlier we used this but it does not work on Mac
-# Amber=$(readlink -f `dirname ${0}`/..)
-TARGET=`dirname ${0}`/..
-pushd . >/dev/null
-cd $TARGET
-AMBER="`\pwd -P`"
-popd >/dev/null
+var path = require('path');
+var amberc = require('./amberc.js');
 
-function usage {
-	cat <<ENDOFHELP
-Usage: $0 [-l lib1,lib2...] [-i file] [-m class] [-M file]
-          [-o] [-O|-A] [-d] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]
-
-   Will compile Amber files - either separately or into a runnable complete
-   program. If no .st files are listed only a linking stage is performed.
-   Files listed will be handled using these rules:
-
-   *.js
-     Files are linked (concatenated) in listed order.
-     If not found we look in $AMBER/js
-
-   *.st
-     Files are compiled into .js files before concatenated.
-     If not found we look in $AMBER/st.
-
-     NOTE: Each file is currently considered to be a fileout of a single class
-     category of the same name as the file!
-
-   If no Program is specified each given .st file will be compiled into
-   a .js file. Otherwise a <Program>.js file is linked together based on
-   the options:
-
-  -l library1,library2
-     Additionally add listed libraries (no spaces or .js) in listed order.
-
-  -i file
-     Add library initializer <file> instead of default $AMBER/js/init.js 
-
-  -m class
-     Add at end a call to #main in class <class>. 
-
-  -M file
-     Add at end javascript file <file> acting as main.
-        
-  -o
-     Optimize each js file using the Google closure compiler.
-     Using Closure compiler found at ~/compiler.jar    
-
-  -O
-     Optimize final <Program>.js using the Google closure compiler.
-     Using Closure compiler found at ~/compiler.jar
-
-  -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS
-
-  -d
-     Additionally export code for deploy - stripped from source etc.
-     Uses suffix ".deploy.js" in addition to any explicit given suffic using -s.
-
-  -s suffix
-     Add <suffix> to compiled js files so that File.st is compiled into
-     File.<suffix>.js.
-
-  -S suffix
-     Use <suffix> for all libraries accessed using -L or -l. This makes it possible
-     to have multiple flavors of Amber and libraries in the same place.
-
-
-     Example invocations:
-
-     Just compile Kernel-Objects.st to Kernel-Objects.js:
-
-        amberc Kernel-Objects.st
-
-     Compile Hello.st to Hello.js and create complete program called
-     Program.js and adding a call to class method #main in class Hello:
-
-        amberc -m Hello Hello.st Program
-
-     Compile two .st files into corresponding .js files,
-     and link with specific myboot.js, myKernel.js, myinit.js
-     and main.js and create complete program called Program.js:
-
-        amberc -M main.js myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program
-
-ENDOFHELP
-	exit 1;
-}
-
-# Check we at least got one argument
-if [ -z $1 ] ; then
-   usage
-fi
-
-# Define our predefined library combinations
-KERNEL="boot Kernel-Objects Kernel-Classes Kernel-Methods Kernel-Collections Kernel-Exceptions Kernel-Transcript Kernel-Announcements"
-COMPILER="$KERNEL parser Compiler Compiler-Exceptions Compiler-Core Compiler-AST Compiler-IR Compiler-Inlining Compiler-Semantic"
-
-# Predefined initializer
-INITIALIZER="$AMBER/js/init.js"
-
-# Default values
-ENV=
-INIT=$INITIALIZER
-MAIN=
-MAINFILE=
-BASE=$KERNEL
-LOAD=
-CLOSUREOPTS=
-# Ok, bad coding practice but hey, who would use such a suffix?
-SUFFIX=no-silly-suffix
-SUFFIXUSED=
-DEPLOY=false
-NODECOMPILE=nodecompile
-
-# Read options and shift them away
-while getopts "l:i:m:M:oOAds:S:h?" o; do
-case "$o" in
-   l) LOAD=$OPTARG;;
-   i) INIT=$OPTARG;;
-   m) MAIN=$OPTARG;;
-   M) MAINFILE=$OPTARG;;
-   o) CLOSURE=true
-      CLOSUREPARTS=true;;
-   O) CLOSURE=true
-      CLOSUREFULL=true;;
-   A) CLOSURE=true
-      CLOSUREOPTS="$CLOSUREOPTS --compilation_level ADVANCED_OPTIMIZATIONS"
-      CLOSUREFULL=true;;
-   d) DEPLOY=true;;
-   s) SUFFIX=$OPTARG
-      SUFFIXUSED=$SUFFIX;;
-   S) LOADSUFFIX=$OPTARG
-      SUFFIXUSED=$SUFFIX;;
-   h) usage;;
-   [?])  usage;;
-   esac
-done
-shift $(($OPTIND - 1))
-
-# Check for Closure compiler and Java
-if [ ! -z $CLOSURE ]; then
-  java > /dev/null
-  if [ $? -eq 0 ]; then 
-    if [ ! -f ~/compiler.jar ]; then
-      echo "Can not find Closure compiler at ~/compiler.jar"
-      exit 1
-    fi
-  else
-   echo "java is not installed and is needed for -O, -A or -o (Closure compiler)."
-   exit 1
-  fi
-fi
-
-# Function for looking up listed js files
-function resolvejs {
-  FNAME="$1$LOADSUFFIX.js"
-  if [ -f $FNAME ]; then
-    RESOLVED="$FNAME" 
-  else
-    if [ -f $AMBER/js/$FNAME ]; then
-      RESOLVED="$AMBER/js/$FNAME"
-    else
-      echo "Javascript file not found: $FNAME"
-      exit 1
-    fi
-  fi
-}
-
-# Resolve listed libraries in $BASE deparated by spaces
-for FILE in $BASE
-do
-   resolvejs $FILE
-   TOBASE="$TOBASE $RESOLVED"
-done
-
-# Resolve listed libraries in $LOAD separated by ,
-LOAD=${LOAD//,/\ }
-for FILE in $LOAD
-do
-   resolvejs $FILE
-   TOLOAD="$TOLOAD $RESOLVED"
-done
-
-# Resolve COMPILER
-for FILE in $COMPILER
-do
-   resolvejs $FILE
-   TOCOMPILER="$TOCOMPILER $RESOLVED"
-done
-
-# Add supplied libraries we have not already loaded (they are already resolved)
-#for FILE in $EXTRA
-#do
-#   resolvejs $FILE
-#   TOEXTRA="$TOEXTRA $RESOLVED"
-#done
-
-TOCOMPILER="$TOCOMPILER$TOLOAD"
-
-# Resolve init and nodecompile
-THEREST="init $AMBER/bin/$NODECOMPILE"
-for FILE in $THEREST
-do
-   resolvejs $FILE
-   TOCOMPILER="$TOCOMPILER $RESOLVED"
-done
-
-# Add supplied libraries
-LIBS="$TOBASE $TOLOAD"
-
-# Get a unique tempdir and make it get auto removed on exit
-TMPDIR=`mktemp -d amberc.XXXXXX 2>>/dev/null` ||\
-    TMPDIR=/tmp/amberc.$$.`date +%s` && mkdir -p $TMPDIR
-trap "rm -rf $TMPDIR" EXIT
-
-
-# --------------------------------------------------
-# Collect libraries and Smalltalk files looking
-# both locally and in $AMBER/js and $AMBER/st 
-# --------------------------------------------------
-PROGRAM=
-until [ "$*" = "" ]
-do
-  case $1 in
-     *.st)
-        CATEGORY=`basename $1 .st`
-        if [ -f "$1" ]; then
-           COMPILE="$COMPILE $1 $CATEGORY"
-           COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js"
-        else
-           if [ -f $AMBER/st/$1 ]; then
-             COMPILE="$COMPILE $AMBER/st/$1 $CATEGORY"
-             COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js"
-           else
-             echo "Amber file not found: $1"
-             exit 1
-           fi
-        fi
-        ;;
-
-     *.js)
-        resolvejs $1
-	LIBS="$LIBS $RESOLVED" 
-        ;;
-      *)
-        # Will end up being the last non js/st argument
-        PROGRAM=$1
-        ;;
-  esac
-  shift
-done
-
-# --------------------------------------------------
-# Actual compilation phase of collected .st files
-# --------------------------------------------------
-
-# Create compiler dynamically
-cat $TOCOMPILER > $TMPDIR/compiler.js
- 
-# Compile all collected .st files to .js
-echo "Loading libraries$TOCOMPILER and compiling ..."
-node $TMPDIR/compiler.js $DEPLOY $SUFFIX $COMPILE
-
-# Verify all .js files corresponding to .st files were created, otherwise exit
-IFS=" "
-for FILE in $COMPILED
-do
-  if [ ! -f "$FILE" ]; then
-    echo "Failed compilation of $FILE, exiting."
-    exit 1
-  fi 
-done
-
-if [ ! -z $CLOSUREPARTS ]; then
-  echo "Compiling all js files using Google closure compiler."
-
-  ALLJSFILES="$COMPILED $LIBS"
-  for FILE in $ALLJSFILES
-  do
-    mv $FILE $FILE.original
-    java -jar ~/compiler.jar $CLOSUREOPTS --js $FILE.original --js_output_file $FILE
-    rm $FILE.original
-  done
-fi
-
-
-if [ -z $PROGRAM ]; then
-  echo "Done."
-  exit 0
-fi
-
-# --------------------------------------------------
-# Now we start composing resulting javascript file.
-# --------------------------------------------------
-
-# Add collected libraries to libs.js file.
-if [ ! -z "$LIBS" ]; then
-  echo "Adding libraries $LIBS ..."
-  cat $LIBS > $TMPDIR/libs.js
-  LIBS=$TMPDIR/libs.js
-fi
-
-echo "Adding Amber code$COMPILED ..."
-
-# Check for init file
-if [ ! -z "$INIT" ]; then
-   if [ -f "$INIT" ]; then
-      echo "Adding initializer $INIT ..."
-   else
-      echo "Can not find init file $INIT, exiting."
-      exit 1
-   fi 
-fi
-
-# Check for adding main
-if [ ! -z "$MAIN" ]; then
-  echo "Adding call to $MAIN class >> main ..."
-  echo "smalltalk.$MAIN._main()" > $TMPDIR/main.js
-  MAIN=$TMPDIR/main.js
-fi
-
-# Check for adding main file
-if [ ! -z "$MAINFILE" ]; then
-   if [ -f "$MAINFILE" ]; then
-      echo "Adding main as $MAINFILE ..."
-   else
-      echo "Can not find main file $MAINFILE, exiting."
-      exit 1
-   fi 
-   MAIN=$MAINFILE
-fi
-
-# And finally concatenate Program.js
-echo "Writing $PROGRAM.js ..."
-cat $LIBS $COMPILED $INIT $MAIN > $PROGRAM.js
-echo "Done."
-
-
-if [ ! -z $CLOSUREFULL ]; then
-  echo "Compiling $PROGRAM.js file using Google closure compiler."
-  mv $PROGRAM.js $PROGRAM.js.original
-  java -jar ~/compiler.jar $CLOSUREOPTS --js $PROGRAM.js.original --js_output_file $PROGRAM.js
-  rm $PROGRAM.js.original
-  echo "Done."
-fi
+// Get Amber root directory from the location of this script so that
+// we can find the st and js directories etc.
+var amber_dir = path.normalize(path.join(path.dirname(process.argv[1]), '..'));
+// Get default location of compiler.jar
+var closure_jar = path.resolve(path.join(process.env['HOME'], 'compiler.jar'));
 
+var compiler = new amberc.Compiler(amber_dir, closure_jar);
+compiler.main();

+ 761 - 0
bin/amberc.js

@@ -0,0 +1,761 @@
+/**
+ * This is a "compiler" for Amber code.
+ * Put the following code into compiler.js:
+ *     var amberc = require('amberc');
+ *     var compiler = new amberc.Compiler('path/to/amber', ['/optional/path/to/compiler.jar]);
+ *     compiler.main();
+ *
+ * Execute 'node compiler.js' without arguments or with -h / --help for help.
+ */
+
+/**
+ * Map the async filter function onto array and evaluate callback, once all have finished.
+ * Taken from: http://howtonode.org/control-flow-part-iii
+ */
+function async_map(array, filter, callback) {
+	if (0 === array.length) {
+		callback(null, null);
+		return;
+	}
+	var counter = array.length;
+	var new_array = [];
+	array.forEach(function (item, index) {
+		filter(item, function (err, result) {
+			if (err) { callback(err); return; }
+			new_array[index] = result;
+			counter--;
+			if (counter === 0) {
+				callback(null, new_array);
+			}
+		});
+	});
+}
+
+
+/**
+ * Always evaluates the callback parameter.
+ * Used by Combo blocks to always call the next function,
+ * even if all of the other functions did not run.
+ */
+function always_resolve(callback) {
+	callback();
+}
+
+
+/**
+ * Combine several async functions and evaluate callback once all of them have finished.
+ * Taken from: http://howtonode.org/control-flow
+ */
+function Combo(callback) {
+  this.callback = callback;
+  this.items = 0;
+  this.results = [];
+}
+
+Combo.prototype = {
+  add: function () {
+    var self = this,
+        id = this.items;
+    this.items++;
+    return function () {
+      self.check(id, arguments);
+    };
+  },
+  check: function (id, arguments) {
+    this.results[id] = Array.prototype.slice.call(arguments);
+    this.items--;
+    if (this.items == 0) {
+      this.callback.apply(this, this.results);
+    }
+  }
+};
+
+var path = require('path'),
+	util = require('util'),
+	fs = require('fs'),
+	exec = require('child_process').exec;
+
+/**
+ * AmberC constructor function.
+ * amber_dir: points to the location of an amber installation
+ * closure_jar: location of compiler.jar (can be left undefined)
+ */
+function AmberC(amber_dir, closure_jar) {
+	this.amber_dir = amber_dir;
+	this.closure_jar = closure_jar;
+	this.kernel_libraries = ['boot', 'Kernel-Objects', 'Kernel-Classes', 'Kernel-Methods',
+	                         'Kernel-Collections', 'Kernel-Exceptions', 'Kernel-Transcript',
+	                         'Kernel-Announcements'];
+	this.compiler_libraries = this.kernel_libraries.concat(['parser', 'Compiler', 'Compiler-Exceptions']);
+	                          //, 'Compiler-Core', 'Compiler-AST', 'Compiler-IR', 'Compiler-Inlining', 'Compiler-Semantic'];
+}
+
+
+/**
+ * Default values.
+ */
+var createDefaults = function(amber_dir, finished_callback){
+	return {
+		'smalltalk': {}, // the evaluated compiler will be stored in this variable (see create_compiler)
+		'load': [],
+		'init': path.join(amber_dir, 'js', 'init.js'),
+		'main': undefined,
+		'mainfile': undefined,
+		'closure': false,
+		'closure_parts': false,
+		'closure_full': false,
+		'closure_options': '',
+		'suffix': '',
+		'loadsuffix': '',
+		'suffix_used': '',
+		'deploy': false,
+		'libraries': [],
+		'compile': [],
+		'compiled_categories': [],
+		'compiled': [],
+		'program': undefined,
+		'finished_callback': finished_callback
+	};
+};
+
+
+/**
+ * Main function for executing the compiler.
+ */
+AmberC.prototype.main = function(parameters, finished_callback) {
+	console.time('Compile Time');
+	var options = parameters || process.argv.slice(2);
+
+	if (1 > options.length) {
+		this.usage();
+	} else {
+		this.defaults = createDefaults(this.amber_dir, finished_callback);
+		this.handle_options(options);
+	}
+};
+
+
+/**
+ * Process given program options and update defaults values.
+ * Followed by check_for_closure_compiler() and then collect_files().
+ */
+AmberC.prototype.handle_options = function(optionsArray) {
+	var stFiles = [];
+	var jsFiles = [];
+	var programName = [];
+	var currentItem = optionsArray.shift();
+	var defaults = this.defaults;
+
+	while(undefined !== currentItem) {
+		switch(currentItem) {
+			case '-l':
+				defaults.load.push.apply(defaults.load, optionsArray.shift().split(','));
+				break;
+			case '-i':
+				defaults.init = optionsArray.shift();
+				break;
+			case '-m':
+				defaults.main = optionsArray.shift();
+				break;
+			case '-M':
+				defaults.mainfile = optionsArray.shift();
+				break;
+			case '-o':
+				defaults.closure = true;
+				defaults.closure_parts = true;
+				break;
+			case '-O':
+				defaults.closure = true;
+				defaults.closure_full = true;
+				break;
+			case '-A':
+				defaults.closure = true;
+				defaults.closure_options = defaults.closure_options + ' --compilation_level ADVANCED_OPTIMIZATIONS';
+				defaults.closure_full = true;
+				break;
+			case '-d':
+				defaults.deploy = true;
+				break;
+			case '-s':
+				defaults.suffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-S':
+				defaults.loadsuffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-h':
+			case '--help':
+			case '?':
+				this.usage();
+				break;
+			default:
+				var fileSuffix = path.extname(currentItem);
+				switch (fileSuffix) {
+					case '.st':
+						stFiles.push(currentItem);
+						break;
+					case '.js':
+						jsFiles.push(currentItem);
+						break;
+					default:
+						// Will end up being the last non js/st argument
+						programName.push(currentItem);
+						break;
+				};
+		};
+		currentItem = optionsArray.shift();
+	}
+
+	if(1 < programName.length) {
+		throw new Error('More than one name for ProgramName given: ' + programName);
+	} else {
+		defaults.program = programName[0];
+	}
+
+	var self = this;
+	this.check_for_closure_compiler(function(){
+		self.collect_files(stFiles, jsFiles)
+	});
+};
+
+
+/**
+ * Print usage options and exit.
+ */
+AmberC.prototype.usage = function() {
+	console.log('Usage: amberc [-l lib1,lib2...] [-i init_file] [-m main_class] [-M main_file]');
+	console.log('          [-o] [-O|-A] [-d] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]');
+	console.log('');
+	console.log('   amberc compiles Amber files - either separately or into a complete runnable');
+	console.log('   program. If no .st files are listed only a linking stage is performed.');
+	console.log('   Files listed will be handled using the following rules:');
+	console.log('');
+	console.log('   *.js');
+	console.log('     Files are linked (concatenated) in listed order.');
+	console.log('     If not found we look in $AMBER/js/');
+	console.log('');
+	console.log('   *.st');
+	console.log('     Files are compiled into .js files before concatenation.');
+	console.log('     If not found we look in $AMBER/st/.');
+	console.log('');
+	console.log('     NOTE: Each .st file is currently considered to be a fileout of a single class');
+	console.log('     category of the same name as the file!');
+	console.log('');
+	console.log('   If no <Program> is specified each given .st file will be compiled into');
+	console.log('   a matching .js file. Otherwise a <Program>.js file is linked together based on');
+	console.log('   the given options:');
+	console.log('  -l library1,library2');
+	console.log('     Add listed JavaScript libraries in listed order.');
+	console.log('     Libraries are not separated by spaces or end with .js.');
+	console.log('');
+	console.log('  -i init_file');
+	console.log('     Add library initializer <init_file> instead of default $AMBER/js/init.js ');
+	console.log('');
+	console.log('  -m main_class');
+	console.log('     Add a call to the class method main_class>>main at the end of <Program>.');
+	console.log('');
+	console.log('  -M main_file');
+	console.log('     Add <main_file> at the end of <Program.js> acting as #main.');
+	console.log('');
+	console.log('  -o');
+	console.log('     Optimize each .js file using the Google closure compiler.');
+	console.log('     Using Closure compiler found at ~/compiler.jar');
+	console.log('');
+	console.log('  -O');
+	console.log('     Optimize final <Program>.js using the Google closure compiler.');
+	console.log('     Using Closure compiler found at ~/compiler.jar');
+	console.log('');
+	console.log('  -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS');
+	console.log('');
+	console.log('  -d');
+	console.log('     Additionally export code for deploy - stripped from source etc.');
+	console.log('     Uses suffix ".deploy.js" in addition to any explicit suffic set by -s.');
+	console.log('');
+	console.log('  -s suffix');
+	console.log('     Add <suffix> to compiled .js files. File.st is then compiled into');
+	console.log('     File.<suffix>.js.');
+	console.log('');
+	console.log('  -S suffix');
+	console.log('     Use <suffix> for all libraries accessed using -l. This makes it possible');
+	console.log('     to have multiple flavors of Amber and libraries in the same place.');
+	console.log('');
+	console.log('');
+	console.log('     Example invocations:');
+	console.log('');
+	console.log('     Just compile Kernel-Objects.st to Kernel-Objects.js:');
+	console.log('');
+	console.log('        amberc Kernel-Objects.st');
+	console.log('');
+	console.log('     Compile Hello.st to Hello.js and create complete program called Program.js.');
+	console.log('     Additionally add a call to the class method Hello>>main:');
+	console.log('');
+	console.log('        amberc -m Hello Hello.st Program');
+	console.log('');
+	console.log('     Compile Cat1.st and Cat2.st files into corresponding .js files.');
+	console.log('     Link them with myboot.js and myKernel.js and add myinit.js as custom');
+	console.log('     initializer file. Add main.js last which contains the startup code');
+	console.log('      and merge everything into a complete program named Program.js:');
+	console.log('');
+	console.log('        amberc -M main.js -i myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program');
+
+	process.exit();
+};
+
+
+/**
+ * Checks if the java executable exists and afterwards,
+ * if compiler.jar exists at the path stored in this.closure_jar.
+ * All closure related entries are set to false upon failure.
+ *
+ * callback gets called in any case.
+ */
+AmberC.prototype.check_for_closure_compiler = function(callback) {
+	var defaults = this.defaults;
+	var self = this;
+	if (defaults.closure) {
+		exec('which java', function(error, stdout, stderr) {
+			// stdout contains path to java executable
+			if (null !== error) {
+				console.warn('java is not installed but is needed for -O, -A or -o (Closure compiler).');
+				defaults.closure = false;
+				defaults.closure_parts = false;
+				defaults.closure_full = false;
+				callback();
+				return;
+			}
+			path.exists(self.closure_jar, function(exists) {
+				if (!exists) {
+					console.warn('Can not find Closure compiler at: ' + self.closure_jar);
+					defaults.closure = false;
+					defaults.closure_parts = false;
+					defaults.closure_full = false;
+					callback();
+					return;
+				}
+			});
+		});
+	} else {
+		callback();
+	}
+};
+
+
+/**
+ * Check if the file given as parameter exists in the local directory or in $AMBER/js/.
+ * '.js' is appended first.
+ *
+ * @param filename name of a file without '.js' prefix
+ * @param callback gets called on success with path to .js file as parameter
+ */
+AmberC.prototype.resolve_js = function(filename, callback) {
+	var baseName = path.basename(filename, '.js');
+	var jsFile = baseName + this.defaults.loadsuffix + '.js';
+	var amberJsFile = path.join(this.amber_dir, 'js', jsFile);
+	console.log('Resolving: ' + jsFile);
+	path.exists(jsFile, function(exists) {
+		if (exists) {
+			callback(jsFile);
+		} else {
+			path.exists(amberJsFile, function(exists) {
+				if (exists) {
+					callback(amberJsFile);
+				} else {
+					throw(new Error('JavaScript file not found: ' + jsFile));
+				}
+			});
+		}
+	});
+};
+
+
+/**
+ * Collect libraries and Smalltalk files looking
+ * both locally and in $AMBER/js and $AMBER/st.
+ * Followed by resolve_libraries().
+ */
+AmberC.prototype.collect_files = function(stFiles, jsFiles) {
+	var self = this;
+	var collected_files = new Combo(function() {
+		self.resolve_libraries();
+	});
+	if (0 !== stFiles.length) {
+		self.collect_st_files(stFiles, collected_files.add());
+	}
+	if (0 !== jsFiles.length) {
+		self.collect_js_files(jsFiles, collected_files.add());
+	}
+};
+
+
+/**
+ * Resolve st files given by stFiles and add them to defaults.compile.
+ * Respective categories get added to defaults.compile_categories.
+ * callback is evaluated afterwards.
+ */
+AmberC.prototype.collect_st_files = function(stFiles, callback) {
+	var defaults = this.defaults;
+	var self = this;
+	var collected_st_files = new Combo(function() {
+		Array.prototype.slice.call(arguments).forEach(function(data) {
+			var stFile = data[0];
+			var stCategory = data[1];
+			defaults.compile.push(stFile);
+			defaults.compiled_categories.push(stCategory);
+			defaults.compiled.push(stCategory + defaults.suffix_used + '.js');
+		});
+		callback();
+	});
+
+	stFiles.forEach(function(stFile) {
+		var _callback = collected_st_files.add();
+		console.log('Checking: ' + stFile);
+		var category = path.basename(stFile, '.st');
+		var amberStFile = path.join(self.amber_dir, 'st', stFile);
+		path.exists(stFile, function(exists) {
+			if (exists) {
+				_callback(stFile, category);
+			} else {
+				path.exists(amberStFile, function(exists) {
+					if (exists) {
+						_callback(amberStFile, category);
+					} else {
+						throw(new Error('Smalltalk file not found: ' + amberStFile));
+					}
+				});
+			}
+		});
+	});
+};
+
+
+/**
+ * Resolve js files given by jsFiles and add them to defaults.libraries.
+ * callback is evaluated afterwards.
+ */
+AmberC.prototype.collect_js_files = function(jsFiles, callback) {
+	var self = this;
+	var collected_js_files = new Combo(function() {
+		Array.prototype.slice.call(arguments).forEach(function(file) {
+			self.defaults.libraries.push(file[0]);
+		});
+		callback();
+	});
+
+	jsFiles.forEach(function(jsFile) {
+		self.resolve_js(jsFile, collected_js_files.add());
+	});
+};
+
+
+/**
+ * Resolve kernel and compiler files.
+ * Followed by resolve_init().
+ */
+AmberC.prototype.resolve_libraries = function() {
+	// Resolve libraries listed in this.kernel_libraries
+	var self = this;
+	var all_resolved = new Combo(function(resolved_library_files, resolved_compiler_files) {
+		self.resolve_init(resolved_compiler_files[0]);
+	});
+	this.resolve_kernel(all_resolved.add());
+	this.resolve_compiler(all_resolved.add());
+};
+
+
+/**
+ * Resolve .js files needed by kernel
+ * callback is evaluated afterwards.
+ */
+AmberC.prototype.resolve_kernel = function(callback) {
+	var self = this;
+	var kernel_files = this.kernel_libraries.concat(this.defaults.load);
+	var kernel_resolved = new Combo(function() {
+		Array.prototype.slice.call(arguments).forEach(function(file) {
+			if (undefined !== file[0]) {
+				self.defaults.libraries.push(file[0]);
+			}
+		});
+		callback(null);
+	});
+
+	kernel_files.forEach(function(file) {
+		self.resolve_js(file, kernel_resolved.add());
+	});
+
+	always_resolve(kernel_resolved.add());
+};
+
+
+/**
+ * Resolve .js files needed by compiler.
+ * callback is evaluated afterwards with resolved files as argument.
+ */
+AmberC.prototype.resolve_compiler = function(callback) {
+	// Resolve compiler libraries
+	var compiler_files = this.compiler_libraries.concat(this.defaults.load);
+	var compiler_resolved = new Combo(function() {
+		var compilerFiles = [];
+		Array.prototype.slice.call(arguments).forEach(function(file) {
+			if (undefined !== file[0]) {
+				compilerFiles.push(file[0]);
+			}
+		});
+		callback(compilerFiles);
+	});
+	var self = this
+	compiler_files.forEach(function(file) {
+		self.resolve_js(file, compiler_resolved.add());
+	});
+
+	always_resolve(compiler_resolved.add());
+};
+
+
+/**
+ * Resolves default.init and adds it to compilerFiles.
+ * Followed by create_compiler().
+ */
+AmberC.prototype.resolve_init = function(compilerFiles) {
+	// check and add init.js
+	var initFile = this.defaults.init;
+	if ('.js' !== path.extname(initFile)) {
+		initFile = this.resolve_js(initFile);
+		this.defaults.init = initFile;
+	}
+	compilerFiles.push(initFile);
+
+	this.create_compiler(compilerFiles);
+};
+
+
+/**
+ * Read all .js files needed by compiler and eval() them.
+ * The finished Compiler gets stored in defaults.smalltalk.
+ * Followed by compile().
+ */
+AmberC.prototype.create_compiler = function(compilerFilesArray) {
+	var self = this;
+	var compiler_files = new Combo(function() {
+		var content = '(function() {';
+		Array.prototype.slice.call(arguments).forEach(function(data) {
+			// data is an array where index 0 is the error code and index 1 contains the data
+			content += data[1];
+		});
+		content = content + 'return smalltalk;})();';
+		self.defaults.smalltalk = eval(content);
+		console.log('Compiler loaded');
+
+		self.compile();
+	});
+
+	compilerFilesArray.forEach(function(file) {
+		console.log('Loading file: ' + file);
+		fs.readFile(file, compiler_files.add());
+	});
+};
+
+
+/**
+ * Compile all given .st files by importing them.
+ * Followed by category_export().
+ */
+AmberC.prototype.compile = function() {
+	console.log('Compiling collected .st files')
+	// import .st files
+	var self = this;
+	var imports = new Combo(function() {
+		Array.prototype.slice.call(arguments).forEach(function(code) {
+			if (undefined !== code[0]) {
+				// get element 0 of code since all return values are stored inside an array by Combo
+				self.defaults.smalltalk.Importer._new()._import_(code[0]._stream());
+			}
+		});
+		self.category_export();
+	});
+
+	this.defaults.compile.forEach(function(stFile) {
+		var callback = imports.add();
+		if (/\.st/.test(stFile)) {
+			console.log('Importing: ' + stFile);
+			fs.readFile(stFile, 'utf8', function(err, data) {
+				if (!err)
+					callback(data);
+				else
+					throw new Error('Could not import: ' + stFile);
+			});
+		}
+	});
+	always_resolve(imports.add());
+};
+
+
+/**
+ * Export compiled categories to JavaScript files.
+ * Followed by verify().
+ */
+AmberC.prototype.category_export = function() {
+	var defaults = this.defaults;
+	var self = this;
+	// export categories as .js
+	async_map(defaults.compiled_categories, function(category, callback) {
+		var jsFile = category + defaults.suffix_used + '.js';
+		var jsFileDeploy = category + defaults.suffix_used + '.deploy.js';
+		console.log('Exporting ' + (defaults.deploy ? '(debug + deploy)' : '(debug)')
+			+ ' category ' + category + ' as ' + jsFile
+			+ (defaults.deploy ? ' and ' + jsFileDeploy : ''));
+		fs.writeFile(jsFile, defaults.smalltalk.Exporter._new()._exportPackage_(category), function(err) {
+			if (defaults.deploy) {
+				fs.writeFile(jsFileDeploy, defaults.smalltalk.StrippedExporter._new()._exportPackage_(category), callback);
+			} else {
+				callback(null, null);
+			}
+		});
+	}, function(err, result){
+		self.verify();
+	});
+};
+
+
+/**
+ * Verify if all .st files have been compiled.
+ * Followed by compose_js_files() and optimize().
+ */
+AmberC.prototype.verify = function() {
+	console.log('Verifying if all .st files were compiled');
+	var self = this;
+	async_map(this.defaults.compiled, function(file, callback) {
+			path.exists(file, function(exists) {
+				if (exists)
+					callback(null, null);
+				else
+					throw(new Error('Compilation failed of: ' + file));
+			});
+		}, function(err, result) {
+			self.compose_js_files();
+	});
+};
+
+
+/**
+ * Synchronous function.
+ * Concatenates compiled JavaScript files into one file in the correct order.
+ * The name of the produced file is given by defaults.program (set by the last commandline option).
+ */
+AmberC.prototype.compose_js_files = function() {
+	var defaults = this.defaults;
+	if (undefined === defaults.program) {
+		return;
+	}
+	var program_files = [];
+
+	if (0 !== defaults.libraries.length) {
+		console.log('Collecting libraries: ' + defaults.libraries);
+		program_files.push.apply(program_files, defaults.libraries);
+	}
+
+	if (0 !== defaults.compiled.length) {
+		console.log('Collecting compiled files: ' + defaults.compiled);
+		program_files.push.apply(program_files, defaults.compiled);
+	}
+
+	if (undefined !== defaults.init) {
+		console.log('Adding initializer ' + defaults.init);
+		program_files.push(defaults.init);
+	}
+
+	console.log('Writing program file: %s.js', defaults.program);
+
+	var fileStream = fs.createWriteStream(defaults.program + defaults.suffix_used + '.js');
+	fileStream.on('error', function(error) {
+		fileStream.end();
+		console.log(error);
+	});
+	var self = this;
+	fileStream.on('close', function(){
+		self.optimize();
+	});
+
+	program_files.forEach(function(file) {
+		if(path.existsSync(file)) {
+			console.log('Adding : ' + file);
+			console.log(fileStream.write(fs.readFileSync(file)));
+		} else {
+			fileStream.end();
+			throw(new Error('Can not find file ' + file));
+		}
+	});
+	if (undefined !== defaults.main) {
+		console.log('Adding call to: %s>>main', defaults.main);
+		fileStream.write('smalltalk.' + defaults.main + '._main()');
+	}
+
+	if (undefined !== defaults.mainfile && path.existsSync(defaults.mainfile)) {
+		console.log('Adding main file: ' + defaults.mainfile);
+		fileStream.write(fs.readFileSync(defaults.mainfile));
+	}
+
+	console.log('Done.');
+	fileStream.end();
+};
+
+
+/**
+ * Optimize created JavaScript files with Google Closure compiler depending
+ * on the flags: defaults.closure_parts, defaults.closure_full.
+ */
+AmberC.prototype.optimize = function() {
+	var defaults = this.defaults;
+	var self = this;
+	var optimization_done = new Combo(function() {
+		console.timeEnd('Compile Time');
+		if (undefined !== defaults.finished_callback) {
+			defaults.finished_callback();
+		}
+	});
+
+	if (defaults.closure_parts) {
+		console.log('Compiling all js files using Google closure compiler.');
+		var allJsFiles = defaults.compiled.concat(defaults.libraries);
+		allJsFiles.forEach(function(file) {
+			var minifiedName = path.basename(file, '.js') + '.min.js';
+			self.closure_compile(file, minifiedName, optimization_done.add());
+		});
+	}
+	if (defaults.closure_full) {
+		console.log('Compiling ' + defaults.program + '.js file using Google closure compiler.');
+		self.closure_compile(defaults.program + '.js', defaults.program + '.min.js', optimization_done.add());
+	}
+
+	always_resolve(optimization_done.add());
+};
+
+
+/**
+ * Compile sourceFile into minifiedFile with Google Closure compiler.
+ * callback gets executed once finished.
+ */
+AmberC.prototype.closure_compile = function(sourceFile, minifiedFile, callback) {
+	// exec is asynchronous
+	var self = this;
+	exec(
+		'java -jar ' +
+		self.closure_jar + ' ' +
+		self.defaults.closure_options +
+		' --js '+ sourceFile +
+		' --js_output_file '+ minifiedFile,
+		function (error, stdout, stderr) {
+			if (error) {
+				console.log(stderr);
+			} else {
+				console.log(stdout);
+				console.log('Minified: '+ minifiedFile);
+			}
+			callback();
+		}
+	);
+};
+
+module.exports.Compiler = AmberC;
+module.exports.Combo = Combo;
+module.exports.map = async_map;

+ 353 - 0
bin/amberc.sh

@@ -0,0 +1,353 @@
+#!/bin/bash
+#
+# This is a "compiler" for Amber code. Run without arguments for help.
+#
+# Get Amber root directory from the location of this script so that
+# we can find the st and js directories etc.
+
+# Earlier we used this but it does not work on Mac
+# Amber=$(readlink -f `dirname ${0}`/..)
+TARGET=`dirname ${0}`/..
+pushd . >/dev/null
+cd $TARGET
+AMBER="`\pwd -P`"
+popd >/dev/null
+
+function usage {
+	cat <<ENDOFHELP
+Usage: $0 [-l lib1,lib2...] [-i file] [-m class] [-M file]
+          [-o] [-O|-A] [-d] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]
+
+   Will compile Amber files - either separately or into a runnable complete
+   program. If no .st files are listed only a linking stage is performed.
+   Files listed will be handled using these rules:
+
+   *.js
+     Files are linked (concatenated) in listed order.
+     If not found we look in $AMBER/js
+
+   *.st
+     Files are compiled into .js files before concatenated.
+     If not found we look in $AMBER/st.
+
+     NOTE: Each file is currently considered to be a fileout of a single class
+     category of the same name as the file!
+
+   If no Program is specified each given .st file will be compiled into
+   a .js file. Otherwise a <Program>.js file is linked together based on
+   the options:
+
+  -l library1,library2
+     Additionally add listed libraries (no spaces or .js) in listed order.
+
+  -i file
+     Add library initializer <file> instead of default $AMBER/js/init.js 
+
+  -m class
+     Add at end a call to #main in class <class>. 
+
+  -M file
+     Add at end javascript file <file> acting as main.
+        
+  -o
+     Optimize each js file using the Google closure compiler.
+     Using Closure compiler found at ~/compiler.jar    
+
+  -O
+     Optimize final <Program>.js using the Google closure compiler.
+     Using Closure compiler found at ~/compiler.jar
+
+  -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS
+
+  -d
+     Additionally export code for deploy - stripped from source etc.
+     Uses suffix ".deploy.js" in addition to any explicit given suffic using -s.
+
+  -s suffix
+     Add <suffix> to compiled js files so that File.st is compiled into
+     File.<suffix>.js.
+
+  -S suffix
+     Use <suffix> for all libraries accessed using -L or -l. This makes it possible
+     to have multiple flavors of Amber and libraries in the same place.
+
+
+     Example invocations:
+
+     Just compile Kernel-Objects.st to Kernel-Objects.js:
+
+        amberc Kernel-Objects.st
+
+     Compile Hello.st to Hello.js and create complete program called
+     Program.js and adding a call to class method #main in class Hello:
+
+        amberc -m Hello Hello.st Program
+
+     Compile two .st files into corresponding .js files,
+     and link with specific myboot.js, myKernel.js, myinit.js
+     and main.js and create complete program called Program.js:
+
+        amberc -M main.js myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program
+
+ENDOFHELP
+	exit 1;
+}
+
+# Check we at least got one argument
+if [ -z $1 ] ; then
+   usage
+fi
+
+# Define our predefined library combinations
+KERNEL="boot Kernel-Objects Kernel-Classes Kernel-Methods Kernel-Collections Kernel-Exceptions Kernel-Transcript Kernel-Announcements"
+COMPILER="$KERNEL parser Compiler Compiler-Exceptions"
+
+# Predefined initializer
+INITIALIZER="$AMBER/js/init.js"
+
+# Default values
+ENV=
+INIT=$INITIALIZER
+MAIN=
+MAINFILE=
+BASE=$KERNEL
+LOAD=
+CLOSUREOPTS=
+# Ok, bad coding practice but hey, who would use such a suffix?
+SUFFIX=no-silly-suffix
+SUFFIXUSED=
+DEPLOY=false
+NODECOMPILE=nodecompile
+
+# Read options and shift them away
+while getopts "l:i:m:M:oOAds:S:h?" o; do
+case "$o" in
+   l) LOAD=$OPTARG;;
+   i) INIT=$OPTARG;;
+   m) MAIN=$OPTARG;;
+   M) MAINFILE=$OPTARG;;
+   o) CLOSURE=true
+      CLOSUREPARTS=true;;
+   O) CLOSURE=true
+      CLOSUREFULL=true;;
+   A) CLOSURE=true
+      CLOSUREOPTS="$CLOSUREOPTS --compilation_level ADVANCED_OPTIMIZATIONS"
+      CLOSUREFULL=true;;
+   d) DEPLOY=true;;
+   s) SUFFIX=$OPTARG
+      SUFFIXUSED=$SUFFIX;;
+   S) LOADSUFFIX=$OPTARG
+      SUFFIXUSED=$SUFFIX;;
+   h) usage;;
+   [?])  usage;;
+   esac
+done
+shift $(($OPTIND - 1))
+
+# Check for Closure compiler and Java
+if [ ! -z $CLOSURE ]; then
+  java > /dev/null
+  if [ $? -eq 0 ]; then 
+    if [ ! -f ~/compiler.jar ]; then
+      echo "Can not find Closure compiler at ~/compiler.jar"
+      exit 1
+    fi
+  else
+   echo "java is not installed and is needed for -O, -A or -o (Closure compiler)."
+   exit 1
+  fi
+fi
+
+# Function for looking up listed js files
+function resolvejs {
+  FNAME="$1$LOADSUFFIX.js"
+  if [ -f $FNAME ]; then
+    RESOLVED="$FNAME" 
+  else
+    if [ -f $AMBER/js/$FNAME ]; then
+      RESOLVED="$AMBER/js/$FNAME"
+    else
+      echo "Javascript file not found: $FNAME"
+      exit 1
+    fi
+  fi
+}
+
+# Resolve listed libraries in $BASE deparated by spaces
+for FILE in $BASE
+do
+   resolvejs $FILE
+   TOBASE="$TOBASE $RESOLVED"
+done
+
+# Resolve listed libraries in $LOAD separated by ,
+LOAD=${LOAD//,/\ }
+for FILE in $LOAD
+do
+   resolvejs $FILE
+   TOLOAD="$TOLOAD $RESOLVED"
+done
+
+# Resolve COMPILER
+for FILE in $COMPILER
+do
+   resolvejs $FILE
+   TOCOMPILER="$TOCOMPILER $RESOLVED"
+done
+
+# Add supplied libraries we have not already loaded (they are already resolved)
+#for FILE in $EXTRA
+#do
+#   resolvejs $FILE
+#   TOEXTRA="$TOEXTRA $RESOLVED"
+#done
+
+TOCOMPILER="$TOCOMPILER$TOLOAD"
+
+# Resolve init and nodecompile
+THEREST="init $AMBER/bin/$NODECOMPILE"
+for FILE in $THEREST
+do
+   resolvejs $FILE
+   TOCOMPILER="$TOCOMPILER $RESOLVED"
+done
+
+# Add supplied libraries
+LIBS="$TOBASE $TOLOAD"
+
+# Get a unique tempdir and make it get auto removed on exit
+TMPDIR=`mktemp -d amberc.XXXXXX 2>>/dev/null` ||\
+    TMPDIR=/tmp/amberc.$$.`date +%s` && mkdir -p $TMPDIR
+trap "rm -rf $TMPDIR" EXIT
+
+
+# --------------------------------------------------
+# Collect libraries and Smalltalk files looking
+# both locally and in $AMBER/js and $AMBER/st 
+# --------------------------------------------------
+PROGRAM=
+until [ "$*" = "" ]
+do
+  case $1 in
+     *.st)
+        CATEGORY=`basename $1 .st`
+        if [ -f "$1" ]; then
+           COMPILE="$COMPILE $1 $CATEGORY"
+           COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js"
+        else
+           if [ -f $AMBER/st/$1 ]; then
+             COMPILE="$COMPILE $AMBER/st/$1 $CATEGORY"
+             COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js"
+           else
+             echo "Amber file not found: $1"
+             exit 1
+           fi
+        fi
+        ;;
+
+     *.js)
+        resolvejs $1
+	LIBS="$LIBS $RESOLVED" 
+        ;;
+      *)
+        # Will end up being the last non js/st argument
+        PROGRAM=$1
+        ;;
+  esac
+  shift
+done
+
+# --------------------------------------------------
+# Actual compilation phase of collected .st files
+# --------------------------------------------------
+
+# Create compiler dynamically
+cat $TOCOMPILER > $TMPDIR/compiler.js
+ 
+# Compile all collected .st files to .js
+echo "Loading libraries$TOCOMPILER and compiling ..."
+node $TMPDIR/compiler.js $DEPLOY $SUFFIX $COMPILE
+
+# Verify all .js files corresponding to .st files were created, otherwise exit
+IFS=" "
+for FILE in $COMPILED
+do
+  if [ ! -f "$FILE" ]; then
+    echo "Failed compilation of $FILE, exiting."
+    exit 1
+  fi 
+done
+
+if [ ! -z $CLOSUREPARTS ]; then
+  echo "Compiling all js files using Google closure compiler."
+
+  ALLJSFILES="$COMPILED $LIBS"
+  for FILE in $ALLJSFILES
+  do
+    mv $FILE $FILE.original
+    java -jar ~/compiler.jar $CLOSUREOPTS --js $FILE.original --js_output_file $FILE
+    rm $FILE.original
+  done
+fi
+
+
+if [ -z $PROGRAM ]; then
+  echo "Done."
+  exit 0
+fi
+
+# --------------------------------------------------
+# Now we start composing resulting javascript file.
+# --------------------------------------------------
+
+# Add collected libraries to libs.js file.
+if [ ! -z "$LIBS" ]; then
+  echo "Adding libraries $LIBS ..."
+  cat $LIBS > $TMPDIR/libs.js
+  LIBS=$TMPDIR/libs.js
+fi
+
+echo "Adding Amber code$COMPILED ..."
+
+# Check for init file
+if [ ! -z "$INIT" ]; then
+   if [ -f "$INIT" ]; then
+      echo "Adding initializer $INIT ..."
+   else
+      echo "Can not find init file $INIT, exiting."
+      exit 1
+   fi 
+fi
+
+# Check for adding main
+if [ ! -z "$MAIN" ]; then
+  echo "Adding call to $MAIN class >> main ..."
+  echo "smalltalk.$MAIN._main()" > $TMPDIR/main.js
+  MAIN=$TMPDIR/main.js
+fi
+
+# Check for adding main file
+if [ ! -z "$MAINFILE" ]; then
+   if [ -f "$MAINFILE" ]; then
+      echo "Adding main as $MAINFILE ..."
+   else
+      echo "Can not find main file $MAINFILE, exiting."
+      exit 1
+   fi 
+   MAIN=$MAINFILE
+fi
+
+# And finally concatenate Program.js
+echo "Writing $PROGRAM.js ..."
+cat $LIBS $COMPILED $INIT $MAIN > $PROGRAM.js
+echo "Done."
+
+
+if [ ! -z $CLOSUREFULL ]; then
+  echo "Compiling $PROGRAM.js file using Google closure compiler."
+  mv $PROGRAM.js $PROGRAM.js.original
+  java -jar ~/compiler.jar $CLOSUREOPTS --js $PROGRAM.js.original --js_output_file $PROGRAM.js
+  rm $PROGRAM.js.original
+  echo "Done."
+fi
+

文件差異過大導致無法顯示
+ 337 - 359
js/IDE.deploy.js


文件差異過大導致無法顯示
+ 337 - 359
js/IDE.js


+ 82 - 0
js/Kernel-Collections.deploy.js

@@ -3428,6 +3428,88 @@ smalltalk.Set);
 
 
 
+smalltalk.addClass('Queue', smalltalk.Object, ['read', 'readIndex', 'write'], 'Kernel-Collections');
+smalltalk.addMethod(
+"_back_",
+smalltalk.method({
+selector: "back:",
+fn: function (anObject){
+var self=this;
+smalltalk.send(self["@write"],"_add_",[anObject]);
+return self}
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_front",
+smalltalk.method({
+selector: "front",
+fn: function (){
+var self=this;
+var $1;
+$1=smalltalk.send(self,"_frontIfAbsent_",[(function(){
+return smalltalk.send(self,"_error_",["Cannot read from empty Queue."]);
+})]);
+return $1;
+}
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_frontIfAbsent_",
+smalltalk.method({
+selector: "frontIfAbsent:",
+fn: function (aBlock){
+var self=this;
+var $1,$2,$3;
+var $early={};
+try {
+var result;
+result=smalltalk.send(self["@read"],"_at_ifAbsent_",[self["@readIndex"],(function(){
+$1=smalltalk.send(self["@write"],"_isEmpty",[]);
+if(smalltalk.assert($1)){
+$2=smalltalk.send(self["@readIndex"],"__gt",[(1)]);
+if(smalltalk.assert($2)){
+self["@read"]=[];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+};
+$3=smalltalk.send(aBlock,"_value",[]);
+throw $early=[$3];
+};
+self["@read"]=self["@write"];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+self["@write"];
+return smalltalk.send(self["@read"],"_first",[]);
+})]);
+smalltalk.send(self["@read"],"_at_put_",[self["@readIndex"],nil]);
+self["@readIndex"]=smalltalk.send(self["@readIndex"],"__plus",[(1)]);
+return result;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+fn: function (){
+var self=this;
+self["@read"]=[];
+self["@readIndex"]=(1);
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+return self}
+}),
+smalltalk.Queue);
+
+
+
 smalltalk.addClass('RegularExpression', smalltalk.Object, [], 'Kernel-Collections');
 smalltalk.addMethod(
 "_compile_",

+ 102 - 0
js/Kernel-Collections.js

@@ -4629,6 +4629,108 @@ smalltalk.Set);
 
 
 
+smalltalk.addClass('Queue', smalltalk.Object, ['read', 'readIndex', 'write'], 'Kernel-Collections');
+smalltalk.addMethod(
+"_back_",
+smalltalk.method({
+selector: "back:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+smalltalk.send(self["@write"],"_add_",[anObject]);
+return self},
+args: ["anObject"],
+source: "back: anObject\x0a\x09write add: anObject\x0a",
+messageSends: ["add:"],
+referencedClasses: []
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_front",
+smalltalk.method({
+selector: "front",
+category: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=smalltalk.send(self,"_frontIfAbsent_",[(function(){
+return smalltalk.send(self,"_error_",["Cannot read from empty Queue."]);
+})]);
+return $1;
+},
+args: [],
+source: "front\x0a    ^self frontIfAbsent: [ self error: 'Cannot read from empty Queue.']\x0a",
+messageSends: ["frontIfAbsent:", "error:"],
+referencedClasses: []
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_frontIfAbsent_",
+smalltalk.method({
+selector: "frontIfAbsent:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+var $1,$2,$3;
+var $early={};
+try {
+var result;
+result=smalltalk.send(self["@read"],"_at_ifAbsent_",[self["@readIndex"],(function(){
+$1=smalltalk.send(self["@write"],"_isEmpty",[]);
+if(smalltalk.assert($1)){
+$2=smalltalk.send(self["@readIndex"],"__gt",[(1)]);
+if(smalltalk.assert($2)){
+self["@read"]=[];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+};
+$3=smalltalk.send(aBlock,"_value",[]);
+throw $early=[$3];
+};
+self["@read"]=self["@write"];
+self["@read"];
+self["@readIndex"]=(1);
+self["@readIndex"];
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+self["@write"];
+return smalltalk.send(self["@read"],"_first",[]);
+})]);
+smalltalk.send(self["@read"],"_at_put_",[self["@readIndex"],nil]);
+self["@readIndex"]=smalltalk.send(self["@readIndex"],"__plus",[(1)]);
+return result;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+},
+args: ["aBlock"],
+source: "frontIfAbsent: aBlock\x0a\x09| result |\x0a\x09result := read at: readIndex ifAbsent: [\x0a\x09\x09write isEmpty ifTrue: [\x0a\x09\x09\x09readIndex > 1 ifTrue: [ read := #(). readIndex := 1 ].\x0a\x09\x09\x09^aBlock value ].\x0a    \x09read := write.\x0a    \x09readIndex := 1.\x0a    \x09write := OrderedCollection new.\x0a    \x09read first ].\x0a    read at: readIndex put: nil.\x0a    readIndex := readIndex + 1.\x0a    ^result\x0a",
+messageSends: ["at:ifAbsent:", "ifTrue:", ">", "value", "isEmpty", "new", "first", "at:put:", "+"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.Queue);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+category: 'initialization',
+fn: function (){
+var self=this;
+self["@read"]=[];
+self["@readIndex"]=(1);
+self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+return self},
+args: [],
+source: "initialize\x0a\x09read := #().\x0a    readIndex := 1.\x0a    write := OrderedCollection new",
+messageSends: ["new"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.Queue);
+
+
+
 smalltalk.addClass('RegularExpression', smalltalk.Object, [], 'Kernel-Collections');
 smalltalk.addMethod(
 "_compile_",

文件差異過大導致無法顯示
+ 379 - 304
js/Kernel-Methods.deploy.js


文件差異過大導致無法顯示
+ 408 - 308
js/Kernel-Methods.js


文件差異過大導致無法顯示
+ 332 - 322
js/Kernel-Objects.deploy.js


文件差異過大導致無法顯示
+ 320 - 310
js/Kernel-Objects.js


文件差異過大導致無法顯示
+ 324 - 304
js/Kernel-Tests.deploy.js


文件差異過大導致無法顯示
+ 326 - 301
js/Kernel-Tests.js


+ 233 - 234
js/SUnit.deploy.js

@@ -6,8 +6,8 @@ smalltalk.method({
 selector: "result",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@result"];
-}, self, "result", [], smalltalk.ResultAnnouncement)}
+return self["@result"];
+}
 }),
 smalltalk.ResultAnnouncement);
 
@@ -17,8 +17,8 @@ smalltalk.method({
 selector: "result:",
 fn: function (aTestResult){
 var self=this;
-return smalltalk.withContext(function($ctx) { self["@result"]=aTestResult;
-return self}, self, "result:", [aTestResult], smalltalk.ResultAnnouncement)}
+self["@result"]=aTestResult;
+return self}
 }),
 smalltalk.ResultAnnouncement);
 
@@ -29,10 +29,11 @@ smalltalk.addMethod(
 "_assert_",
 smalltalk.method({
 selector: "assert:",
-fn: function (aBoolean){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_description_(aBoolean,"Assertion failed");
-return self}, self, "assert:", [aBoolean], smalltalk.TestCase)}
+fn: function (aBoolean) {
+    var self = this;
+    smalltalk.send(self, "_assert_description_", [aBoolean, "Assertion failed"]);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -40,12 +41,13 @@ smalltalk.addMethod(
 "_assert_description_",
 smalltalk.method({
 selector: "assert:description:",
-fn: function (aBoolean,aString){
-var self=this;
-return smalltalk.withContext(function($ctx) { if(! smalltalk.assert(aBoolean)){
-_st(self)._signalFailure_(aString);
-};
-return self}, self, "assert:description:", [aBoolean,aString], smalltalk.TestCase)}
+fn: function (aBoolean, aString) {
+    var self = this;
+    if (!smalltalk.assert(aBoolean)) {
+        smalltalk.send(self, "_signalFailure_", [aString]);
+    }
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -53,12 +55,12 @@ smalltalk.addMethod(
 "_assert_equals_",
 smalltalk.method({
 selector: "assert:equals:",
-fn: function (expected,actual){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(self)._assert_description_(_st(expected).__eq(actual),_st(_st(_st("Expected: ").__comma(_st(expected)._asString())).__comma(" but was: ")).__comma(_st(actual)._asString()));
-return $1;
-}, self, "assert:equals:", [expected,actual], smalltalk.TestCase)}
+fn: function (expected, actual) {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(self, "_assert_description_", [smalltalk.send(expected, "__eq", [actual]), smalltalk.send(smalltalk.send(smalltalk.send("Expected: ", "__comma", [smalltalk.send(expected, "_asString", [])]), "__comma", [" but was: "]), "__comma", [smalltalk.send(actual, "_asString", [])])]);
+    return $1;
+}
 }),
 smalltalk.TestCase);
 
@@ -66,10 +68,11 @@ smalltalk.addMethod(
 "_deny_",
 smalltalk.method({
 selector: "deny:",
-fn: function (aBoolean){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st(aBoolean)._not());
-return self}, self, "deny:", [aBoolean], smalltalk.TestCase)}
+fn: function (aBoolean) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(aBoolean, "_not", [])]);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -79,8 +82,8 @@ smalltalk.method({
 selector: "performTest",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._perform_(_st(self)._selector());
-return self}, self, "performTest", [], smalltalk.TestCase)}
+smalltalk.send(self,"_perform_",[smalltalk.send(self,"_selector",[])]);
+return self}
 }),
 smalltalk.TestCase);
 
@@ -90,13 +93,13 @@ smalltalk.method({
 selector: "runCase",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st((function(){
-_st(self)._setUp();
-return _st(self)._performTest();
-}))._ensure_((function(){
-return _st(self)._tearDown();
-}));
-return self}, self, "runCase", [], smalltalk.TestCase)}
+smalltalk.send((function(){
+smalltalk.send(self,"_setUp",[]);
+return smalltalk.send(self,"_performTest",[]);
+}),"_ensure_",[(function(){
+return smalltalk.send(self,"_tearDown",[]);
+})]);
+return self}
 }),
 smalltalk.TestCase);
 
@@ -104,10 +107,10 @@ smalltalk.addMethod(
 "_selector",
 smalltalk.method({
 selector: "selector",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@testSelector"];
-}, self, "selector", [], smalltalk.TestCase)}
+fn: function () {
+    var self = this;
+    return self['@testSelector'];
+}
 }),
 smalltalk.TestCase);
 
@@ -115,10 +118,11 @@ smalltalk.addMethod(
 "_setTestSelector_",
 smalltalk.method({
 selector: "setTestSelector:",
-fn: function (aSelector){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@testSelector"]=aSelector;
-return self}, self, "setTestSelector:", [aSelector], smalltalk.TestCase)}
+fn: function (aSelector) {
+    var self = this;
+    self['@testSelector'] = aSelector;
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -126,9 +130,10 @@ smalltalk.addMethod(
 "_setUp",
 smalltalk.method({
 selector: "setUp",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self}, self, "setUp", [], smalltalk.TestCase)}
+fn: function () {
+    var self = this;
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -136,10 +141,11 @@ smalltalk.addMethod(
 "_should_",
 smalltalk.method({
 selector: "should:",
-fn: function (aBlock){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st(aBlock)._value());
-return self}, self, "should:", [aBlock], smalltalk.TestCase)}
+fn: function (aBlock) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(aBlock, "_value", [])]);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -147,15 +153,11 @@ smalltalk.addMethod(
 "_should_raise_",
 smalltalk.method({
 selector: "should:raise:",
-fn: function (aBlock,anExceptionClass){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st((function(){
-_st(aBlock)._value();
-return false;
-}))._on_do_(anExceptionClass,(function(ex){
-return true;
-})));
-return self}, self, "should:raise:", [aBlock,anExceptionClass], smalltalk.TestCase)}
+fn: function (aBlock, anExceptionClass) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(function () {smalltalk.send(aBlock, "_value", []);return false;}, "_on_do_", [anExceptionClass, function (ex) {return true;}])]);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -163,15 +165,11 @@ smalltalk.addMethod(
 "_shouldnt_raise_",
 smalltalk.method({
 selector: "shouldnt:raise:",
-fn: function (aBlock,anExceptionClass){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st((function(){
-_st(aBlock)._value();
-return true;
-}))._on_do_(anExceptionClass,(function(ex){
-return false;
-})));
-return self}, self, "shouldnt:raise:", [aBlock,anExceptionClass], smalltalk.TestCase)}
+fn: function (aBlock, anExceptionClass) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(function () {smalltalk.send(aBlock, "_value", []);return true;}, "_on_do_", [anExceptionClass, function (ex) {return false;}])]);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -179,13 +177,14 @@ smalltalk.addMethod(
 "_signalFailure_",
 smalltalk.method({
 selector: "signalFailure:",
-fn: function (aString){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1,$2;
-$1=_st((smalltalk.TestFailure || TestFailure))._new();
-_st($1)._messageText_(aString);
-$2=_st($1)._signal();
-return self}, self, "signalFailure:", [aString], smalltalk.TestCase)}
+fn: function (aString) {
+    var self = this;
+    var $1, $2;
+    $1 = smalltalk.send(smalltalk.TestFailure || TestFailure, "_new", []);
+    smalltalk.send($1, "_messageText_", [aString]);
+    $2 = smalltalk.send($1, "_signal", []);
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -193,9 +192,10 @@ smalltalk.addMethod(
 "_tearDown",
 smalltalk.method({
 selector: "tearDown",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self}, self, "tearDown", [], smalltalk.TestCase)}
+fn: function () {
+    var self = this;
+    return self;
+}
 }),
 smalltalk.TestCase);
 
@@ -204,17 +204,17 @@ smalltalk.addMethod(
 "_allTestSelectors",
 smalltalk.method({
 selector: "allTestSelectors",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-var selectors;
-selectors=_st(self)._testSelectors();
-$1=_st(self)._shouldInheritSelectors();
-if(smalltalk.assert($1)){
-_st(selectors)._addAll_(_st(_st(self)._superclass())._allTestSelectors());
-};
-return selectors;
-}, self, "allTestSelectors", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    var $1;
+    var selectors;
+    selectors = smalltalk.send(self, "_testSelectors", []);
+    $1 = smalltalk.send(self, "_shouldInheritSelectors", []);
+    if (smalltalk.assert($1)) {
+        smalltalk.send(selectors, "_addAll_", [smalltalk.send(smalltalk.send(self, "_superclass", []), "_allTestSelectors", [])]);
+    }
+    return selectors;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -222,14 +222,12 @@ smalltalk.addMethod(
 "_buildSuite",
 smalltalk.method({
 selector: "buildSuite",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(self)._allTestSelectors())._collect_((function(each){
-return _st(self)._selector_(each);
-}));
-return $1;
-}, self, "buildSuite", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(self, "_allTestSelectors", []), "_collect_", [function (each) {return smalltalk.send(self, "_selector_", [each]);}]);
+    return $1;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -237,12 +235,12 @@ smalltalk.addMethod(
 "_isAbstract",
 smalltalk.method({
 selector: "isAbstract",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(self)._name()).__eq("TestCase");
-return $1;
-}, self, "isAbstract", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(self, "_name", []), "__eq", ["TestCase"]);
+    return $1;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -250,10 +248,10 @@ smalltalk.addMethod(
 "_lookupHierarchyRoot",
 smalltalk.method({
 selector: "lookupHierarchyRoot",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return (smalltalk.TestCase || TestCase);
-}, self, "lookupHierarchyRoot", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    return smalltalk.TestCase || TestCase;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -261,15 +259,15 @@ smalltalk.addMethod(
 "_selector_",
 smalltalk.method({
 selector: "selector:",
-fn: function (aSelector){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$3,$1;
-$2=_st(self)._new();
-_st($2)._setTestSelector_(aSelector);
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
-}, self, "selector:", [aSelector], smalltalk.TestCase.klass)}
+fn: function (aSelector) {
+    var self = this;
+    var $2, $3, $1;
+    $2 = smalltalk.send(self, "_new", []);
+    smalltalk.send($2, "_setTestSelector_", [aSelector]);
+    $3 = smalltalk.send($2, "_yourself", []);
+    $1 = $3;
+    return $1;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -277,12 +275,12 @@ smalltalk.addMethod(
 "_shouldInheritSelectors",
 smalltalk.method({
 selector: "shouldInheritSelectors",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(self).__tild_eq(_st(self)._lookupHierarchyRoot());
-return $1;
-}, self, "shouldInheritSelectors", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(self, "_~_eq", [smalltalk.send(self, "_lookupHierarchyRoot", [])]);
+    return $1;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -290,14 +288,12 @@ smalltalk.addMethod(
 "_testSelectors",
 smalltalk.method({
 selector: "testSelectors",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(_st(self)._methodDictionary())._keys())._select_((function(each){
-return _st(each)._match_("^test");
-}));
-return $1;
-}, self, "testSelectors", [], smalltalk.TestCase.klass)}
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(smalltalk.send(self, "_methodDictionary", []), "_keys", []), "_select_", [function (each) {return smalltalk.send(each, "_match_", ["^test"]);}]);
+    return $1;
+}
 }),
 smalltalk.TestCase.klass);
 
@@ -310,10 +306,11 @@ smalltalk.addMethod(
 "_addError_",
 smalltalk.method({
 selector: "addError:",
-fn: function (anError){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(_st(self)._errors())._add_(anError);
-return self}, self, "addError:", [anError], smalltalk.TestResult)}
+fn: function (anError) {
+    var self = this;
+    smalltalk.send(smalltalk.send(self, "_errors", []), "_add_", [anError]);
+    return self;
+}
 }),
 smalltalk.TestResult);
 
@@ -321,10 +318,11 @@ smalltalk.addMethod(
 "_addFailure_",
 smalltalk.method({
 selector: "addFailure:",
-fn: function (aFailure){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(_st(self)._failures())._add_(aFailure);
-return self}, self, "addFailure:", [aFailure], smalltalk.TestResult)}
+fn: function (aFailure) {
+    var self = this;
+    smalltalk.send(smalltalk.send(self, "_failures", []), "_add_", [aFailure]);
+    return self;
+}
 }),
 smalltalk.TestResult);
 
@@ -332,10 +330,10 @@ smalltalk.addMethod(
 "_errors",
 smalltalk.method({
 selector: "errors",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@errors"];
-}, self, "errors", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    return self['@errors'];
+}
 }),
 smalltalk.TestResult);
 
@@ -343,10 +341,10 @@ smalltalk.addMethod(
 "_failures",
 smalltalk.method({
 selector: "failures",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@failures"];
-}, self, "failures", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    return self['@failures'];
+}
 }),
 smalltalk.TestResult);
 
@@ -354,10 +352,11 @@ smalltalk.addMethod(
 "_increaseRuns",
 smalltalk.method({
 selector: "increaseRuns",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@runs"]=_st(self["@runs"]).__plus((1));
-return self}, self, "increaseRuns", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    self['@runs'] = smalltalk.send(self['@runs'], "__plus", [1]);
+    return self;
+}
 }),
 smalltalk.TestResult);
 
@@ -365,15 +364,16 @@ smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
 selector: "initialize",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
-self["@timestamp"]=_st((smalltalk.Date || Date))._now();
-self["@runs"]=(0);
-self["@errors"]=_st((smalltalk.Array || Array))._new();
-self["@failures"]=_st((smalltalk.Array || Array))._new();
-self["@total"]=(0);
-return self}, self, "initialize", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    smalltalk.send(self, "_initialize", [], smalltalk.Object);
+    self['@timestamp'] = smalltalk.send(smalltalk.Date || Date, "_now", []);
+    self['@runs'] = 0;
+    self['@errors'] = smalltalk.send(smalltalk.Array || Array, "_new", []);
+    self['@failures'] = smalltalk.send(smalltalk.Array || Array, "_new", []);
+    self['@total'] = 0;
+    return self;
+}
 }),
 smalltalk.TestResult);
 
@@ -383,13 +383,13 @@ smalltalk.method({
 selector: "nextRunDo:",
 fn: function (aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$1;
-$2=_st(_st(self)._runs()).__eq_eq(_st(self)._total());
+var $2,$1;
+$2=smalltalk.send(smalltalk.send(self,"_runs",[]),"__eq_eq",[smalltalk.send(self,"_total",[])]);
 if(! smalltalk.assert($2)){
-$1=_st(aBlock)._value_(_st(_st(self)._runs()).__plus((1)));
+$1=smalltalk.send(aBlock,"_value_",[smalltalk.send(smalltalk.send(self,"_runs",[]),"__plus",[(1)])]);
 };
 return $1;
-}, self, "nextRunDo:", [aBlock], smalltalk.TestResult)}
+}
 }),
 smalltalk.TestResult);
 
@@ -399,17 +399,17 @@ smalltalk.method({
 selector: "runCase:",
 fn: function (aTestCase){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st((function(){
-return _st((function(){
-_st(self)._increaseRuns();
-return _st(aTestCase)._runCase();
-}))._on_do_((smalltalk.TestFailure || TestFailure),(function(ex){
-return _st(self)._addFailure_(aTestCase);
-}));
-}))._on_do_((smalltalk.Error || Error),(function(ex){
-return _st(self)._addError_(aTestCase);
-}));
-return self}, self, "runCase:", [aTestCase], smalltalk.TestResult)}
+smalltalk.send((function(){
+return smalltalk.send((function(){
+smalltalk.send(self,"_increaseRuns",[]);
+return smalltalk.send(aTestCase,"_runCase",[]);
+}),"_on_do_",[(smalltalk.TestFailure || TestFailure),(function(ex){
+return smalltalk.send(self,"_addFailure_",[aTestCase]);
+})]);
+}),"_on_do_",[(smalltalk.Error || Error),(function(ex){
+return smalltalk.send(self,"_addError_",[aTestCase]);
+})]);
+return self}
 }),
 smalltalk.TestResult);
 
@@ -417,10 +417,10 @@ smalltalk.addMethod(
 "_runs",
 smalltalk.method({
 selector: "runs",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@runs"];
-}, self, "runs", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    return self['@runs'];
+}
 }),
 smalltalk.TestResult);
 
@@ -428,22 +428,22 @@ smalltalk.addMethod(
 "_status",
 smalltalk.method({
 selector: "status",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$3,$1;
-$2=_st(_st(self)._errors())._isEmpty();
-if(smalltalk.assert($2)){
-$3=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($3)){
-$1="success";
-} else {
-$1="failure";
-};
-} else {
-$1="error";
-};
-return $1;
-}, self, "status", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    var $2, $3, $1;
+    $2 = smalltalk.send(smalltalk.send(self, "_errors", []), "_isEmpty", []);
+    if (smalltalk.assert($2)) {
+        $3 = smalltalk.send(smalltalk.send(self, "_failures", []), "_isEmpty", []);
+        if (smalltalk.assert($3)) {
+            $1 = "success";
+        } else {
+            $1 = "failure";
+        }
+    } else {
+        $1 = "error";
+    }
+    return $1;
+}
 }),
 smalltalk.TestResult);
 
@@ -451,10 +451,10 @@ smalltalk.addMethod(
 "_timestamp",
 smalltalk.method({
 selector: "timestamp",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@timestamp"];
-}, self, "timestamp", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    return self['@timestamp'];
+}
 }),
 smalltalk.TestResult);
 
@@ -462,10 +462,10 @@ smalltalk.addMethod(
 "_total",
 smalltalk.method({
 selector: "total",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@total"];
-}, self, "total", [], smalltalk.TestResult)}
+fn: function () {
+    var self = this;
+    return self['@total'];
+}
 }),
 smalltalk.TestResult);
 
@@ -473,10 +473,11 @@ smalltalk.addMethod(
 "_total_",
 smalltalk.method({
 selector: "total:",
-fn: function (aNumber){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@total"]=aNumber;
-return self}, self, "total:", [aNumber], smalltalk.TestResult)}
+fn: function (aNumber) {
+    var self = this;
+    self['@total'] = aNumber;
+    return self;
+}
 }),
 smalltalk.TestResult);
 
@@ -489,8 +490,8 @@ smalltalk.method({
 selector: "announcer",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@announcer"];
-}, self, "announcer", [], smalltalk.TestSuiteRunner)}
+return self["@announcer"];
+}
 }),
 smalltalk.TestSuiteRunner);
 
@@ -500,10 +501,10 @@ smalltalk.method({
 selector: "initialize",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
-self["@announcer"]=_st((smalltalk.Announcer || Announcer))._new();
-self["@result"]=_st((smalltalk.TestResult || TestResult))._new();
-return self}, self, "initialize", [], smalltalk.TestSuiteRunner)}
+smalltalk.send(self,"_initialize",[],smalltalk.Object);
+self["@announcer"]=smalltalk.send((smalltalk.Announcer || Announcer),"_new",[]);
+self["@result"]=smalltalk.send((smalltalk.TestResult || TestResult),"_new",[]);
+return self}
 }),
 smalltalk.TestSuiteRunner);
 
@@ -513,8 +514,8 @@ smalltalk.method({
 selector: "result",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@result"];
-}, self, "result", [], smalltalk.TestSuiteRunner)}
+return self["@result"];
+}
 }),
 smalltalk.TestSuiteRunner);
 
@@ -524,23 +525,21 @@ smalltalk.method({
 selector: "run",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { var worker;
-_st(self["@result"])._total_(_st(self["@suite"])._size());
-_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
+var worker;
+smalltalk.send(self["@result"],"_total_",[smalltalk.send(self["@suite"],"_size",[])]);
+smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
 worker=(function(){
-return _st(self["@result"])._nextRunDo_((function(index){
-return _st((function(){
-return _st(self["@result"])._runCase_(_st(self["@suite"])._at_(index));
-}))._ensure_((function(){
-_st(worker)._valueWithTimeout_((0));
-return _st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-}));
-}));
+return smalltalk.send(self["@result"],"_nextRunDo_",[(function(index){
+return smalltalk.send((function(){
+return smalltalk.send(self["@result"],"_runCase_",[smalltalk.send(self["@suite"],"_at_",[index])]);
+}),"_ensure_",[(function(){
+smalltalk.send(worker,"_fork",[]);
+return smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
+})]);
+})]);
 });
-_st(_st(_st(self["@suite"])._size())._min_((25)))._timesRepeat_((function(){
-return _st(worker)._valueWithTimeout_((0));
-}));
-return self}, self, "run", [], smalltalk.TestSuiteRunner)}
+smalltalk.send(worker,"_fork",[]);
+return self}
 }),
 smalltalk.TestSuiteRunner);
 
@@ -550,8 +549,8 @@ smalltalk.method({
 selector: "suite:",
 fn: function (aCollection){
 var self=this;
-return smalltalk.withContext(function($ctx) { self["@suite"]=aCollection;
-return self}, self, "suite:", [aCollection], smalltalk.TestSuiteRunner)}
+self["@suite"]=aCollection;
+return self}
 }),
 smalltalk.TestSuiteRunner);
 
@@ -562,8 +561,8 @@ smalltalk.method({
 selector: "new",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._shouldNotImplement();
-return self}, self, "new", [], smalltalk.TestSuiteRunner.klass)}
+smalltalk.send(self,"_shouldNotImplement",[]);
+return self}
 }),
 smalltalk.TestSuiteRunner.klass);
 
@@ -573,10 +572,10 @@ smalltalk.method({
 selector: "on:",
 fn: function (aCollection){
 var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(smalltalk.Object.klass.fn.prototype._new.apply(_st(self), []))._suite_(aCollection);
+var $1;
+$1=smalltalk.send(smalltalk.send(self,"_new",[],smalltalk.Object.klass),"_suite_",[aCollection]);
 return $1;
-}, self, "on:", [aCollection], smalltalk.TestSuiteRunner.klass)}
+}
 }),
 smalltalk.TestSuiteRunner.klass);
 

+ 235 - 236
js/SUnit.js

@@ -7,8 +7,8 @@ selector: "result",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@result"];
-}, self, "result", [], smalltalk.ResultAnnouncement)},
+return self["@result"];
+},
 args: [],
 source: "result\x0a\x09^result",
 messageSends: [],
@@ -23,8 +23,8 @@ selector: "result:",
 category: 'accessing',
 fn: function (aTestResult){
 var self=this;
-return smalltalk.withContext(function($ctx) { self["@result"]=aTestResult;
-return self}, self, "result:", [aTestResult], smalltalk.ResultAnnouncement)},
+self["@result"]=aTestResult;
+return self},
 args: ["aTestResult"],
 source: "result: aTestResult\x0a\x09result := aTestResult",
 messageSends: [],
@@ -40,10 +40,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "assert:",
 category: 'testing',
-fn: function (aBoolean){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_description_(aBoolean,"Assertion failed");
-return self}, self, "assert:", [aBoolean], smalltalk.TestCase)},
+fn: function (aBoolean) {
+    var self = this;
+    smalltalk.send(self, "_assert_description_", [aBoolean, "Assertion failed"]);
+    return self;
+},
 args: ["aBoolean"],
 source: "assert: aBoolean\x0a\x09self assert: aBoolean description: 'Assertion failed'",
 messageSends: ["assert:description:"],
@@ -56,12 +57,13 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "assert:description:",
 category: 'testing',
-fn: function (aBoolean,aString){
-var self=this;
-return smalltalk.withContext(function($ctx) { if(! smalltalk.assert(aBoolean)){
-_st(self)._signalFailure_(aString);
-};
-return self}, self, "assert:description:", [aBoolean,aString], smalltalk.TestCase)},
+fn: function (aBoolean, aString) {
+    var self = this;
+    if (!smalltalk.assert(aBoolean)) {
+        smalltalk.send(self, "_signalFailure_", [aString]);
+    }
+    return self;
+},
 args: ["aBoolean", "aString"],
 source: "assert: aBoolean description: aString\x0a\x09aBoolean ifFalse: [self signalFailure: aString]",
 messageSends: ["ifFalse:", "signalFailure:"],
@@ -74,12 +76,12 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "assert:equals:",
 category: 'testing',
-fn: function (expected,actual){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(self)._assert_description_(_st(expected).__eq(actual),_st(_st(_st("Expected: ").__comma(_st(expected)._asString())).__comma(" but was: ")).__comma(_st(actual)._asString()));
-return $1;
-}, self, "assert:equals:", [expected,actual], smalltalk.TestCase)},
+fn: function (expected, actual) {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(self, "_assert_description_", [smalltalk.send(expected, "__eq", [actual]), smalltalk.send(smalltalk.send(smalltalk.send("Expected: ", "__comma", [smalltalk.send(expected, "_asString", [])]), "__comma", [" but was: "]), "__comma", [smalltalk.send(actual, "_asString", [])])]);
+    return $1;
+},
 args: ["expected", "actual"],
 source: "assert: expected equals: actual\x0a\x09^ self assert: (expected = actual) description: 'Expected: ', expected asString, ' but was: ', actual asString",
 messageSends: ["assert:description:", "=", ",", "asString"],
@@ -92,10 +94,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "deny:",
 category: 'testing',
-fn: function (aBoolean){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st(aBoolean)._not());
-return self}, self, "deny:", [aBoolean], smalltalk.TestCase)},
+fn: function (aBoolean) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(aBoolean, "_not", [])]);
+    return self;
+},
 args: ["aBoolean"],
 source: "deny: aBoolean\x0a\x09self assert: aBoolean not",
 messageSends: ["assert:", "not"],
@@ -110,8 +113,8 @@ selector: "performTest",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._perform_(_st(self)._selector());
-return self}, self, "performTest", [], smalltalk.TestCase)},
+smalltalk.send(self,"_perform_",[smalltalk.send(self,"_selector",[])]);
+return self},
 args: [],
 source: "performTest\x0a\x09self perform: self selector\x0a",
 messageSends: ["perform:", "selector"],
@@ -126,13 +129,13 @@ selector: "runCase",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st((function(){
-_st(self)._setUp();
-return _st(self)._performTest();
-}))._ensure_((function(){
-return _st(self)._tearDown();
-}));
-return self}, self, "runCase", [], smalltalk.TestCase)},
+smalltalk.send((function(){
+smalltalk.send(self,"_setUp",[]);
+return smalltalk.send(self,"_performTest",[]);
+}),"_ensure_",[(function(){
+return smalltalk.send(self,"_tearDown",[]);
+})]);
+return self},
 args: [],
 source: "runCase\x0a\x09[\x09self setUp.\x0a\x09\x09self performTest ] ensure: [\x0a\x09\x09self tearDown.\x0a\x09\x09\x22self cleanUpInstanceVariables\x22 ]\x0a",
 messageSends: ["ensure:", "tearDown", "setUp", "performTest"],
@@ -145,10 +148,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "selector",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@testSelector"];
-}, self, "selector", [], smalltalk.TestCase)},
+fn: function () {
+    var self = this;
+    return self['@testSelector'];
+},
 args: [],
 source: "selector\x0a\x09^testSelector",
 messageSends: [],
@@ -161,10 +164,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "setTestSelector:",
 category: 'accessing',
-fn: function (aSelector){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@testSelector"]=aSelector;
-return self}, self, "setTestSelector:", [aSelector], smalltalk.TestCase)},
+fn: function (aSelector) {
+    var self = this;
+    self['@testSelector'] = aSelector;
+    return self;
+},
 args: ["aSelector"],
 source: "setTestSelector: aSelector\x0a\x09testSelector := aSelector",
 messageSends: [],
@@ -177,9 +181,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "setUp",
 category: 'running',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self}, self, "setUp", [], smalltalk.TestCase)},
+fn: function () {
+    var self = this;
+    return self;
+},
 args: [],
 source: "setUp",
 messageSends: [],
@@ -192,10 +197,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "should:",
 category: 'testing',
-fn: function (aBlock){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st(aBlock)._value());
-return self}, self, "should:", [aBlock], smalltalk.TestCase)},
+fn: function (aBlock) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(aBlock, "_value", [])]);
+    return self;
+},
 args: ["aBlock"],
 source: "should: aBlock\x0a\x09self assert: aBlock value",
 messageSends: ["assert:", "value"],
@@ -208,15 +214,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "should:raise:",
 category: 'testing',
-fn: function (aBlock,anExceptionClass){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st((function(){
-_st(aBlock)._value();
-return false;
-}))._on_do_(anExceptionClass,(function(ex){
-return true;
-})));
-return self}, self, "should:raise:", [aBlock,anExceptionClass], smalltalk.TestCase)},
+fn: function (aBlock, anExceptionClass) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(function () {smalltalk.send(aBlock, "_value", []);return false;}, "_on_do_", [anExceptionClass, function (ex) {return true;}])]);
+    return self;
+},
 args: ["aBlock", "anExceptionClass"],
 source: "should: aBlock raise: anExceptionClass\x0a\x09self assert: ([aBlock value. false] \x0a\x09\x09on: anExceptionClass \x0a\x09\x09do: [:ex | true])",
 messageSends: ["assert:", "on:do:", "value"],
@@ -229,15 +231,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "shouldnt:raise:",
 category: 'testing',
-fn: function (aBlock,anExceptionClass){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._assert_(_st((function(){
-_st(aBlock)._value();
-return true;
-}))._on_do_(anExceptionClass,(function(ex){
-return false;
-})));
-return self}, self, "shouldnt:raise:", [aBlock,anExceptionClass], smalltalk.TestCase)},
+fn: function (aBlock, anExceptionClass) {
+    var self = this;
+    smalltalk.send(self, "_assert_", [smalltalk.send(function () {smalltalk.send(aBlock, "_value", []);return true;}, "_on_do_", [anExceptionClass, function (ex) {return false;}])]);
+    return self;
+},
 args: ["aBlock", "anExceptionClass"],
 source: "shouldnt: aBlock raise: anExceptionClass\x0a\x09self assert: ([aBlock value. true] \x0a\x09\x09on: anExceptionClass \x0a\x09\x09do: [:ex | false])",
 messageSends: ["assert:", "on:do:", "value"],
@@ -250,13 +248,14 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "signalFailure:",
 category: 'private',
-fn: function (aString){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1,$2;
-$1=_st((smalltalk.TestFailure || TestFailure))._new();
-_st($1)._messageText_(aString);
-$2=_st($1)._signal();
-return self}, self, "signalFailure:", [aString], smalltalk.TestCase)},
+fn: function (aString) {
+    var self = this;
+    var $1, $2;
+    $1 = smalltalk.send(smalltalk.TestFailure || TestFailure, "_new", []);
+    smalltalk.send($1, "_messageText_", [aString]);
+    $2 = smalltalk.send($1, "_signal", []);
+    return self;
+},
 args: ["aString"],
 source: "signalFailure: aString\x0a\x09TestFailure new\x0a\x09\x09messageText: aString;\x0a\x09\x09signal",
 messageSends: ["messageText:", "new", "signal"],
@@ -269,9 +268,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "tearDown",
 category: 'running',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self}, self, "tearDown", [], smalltalk.TestCase)},
+fn: function () {
+    var self = this;
+    return self;
+},
 args: [],
 source: "tearDown",
 messageSends: [],
@@ -285,17 +285,17 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "allTestSelectors",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-var selectors;
-selectors=_st(self)._testSelectors();
-$1=_st(self)._shouldInheritSelectors();
-if(smalltalk.assert($1)){
-_st(selectors)._addAll_(_st(_st(self)._superclass())._allTestSelectors());
-};
-return selectors;
-}, self, "allTestSelectors", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    var $1;
+    var selectors;
+    selectors = smalltalk.send(self, "_testSelectors", []);
+    $1 = smalltalk.send(self, "_shouldInheritSelectors", []);
+    if (smalltalk.assert($1)) {
+        smalltalk.send(selectors, "_addAll_", [smalltalk.send(smalltalk.send(self, "_superclass", []), "_allTestSelectors", [])]);
+    }
+    return selectors;
+},
 args: [],
 source: "allTestSelectors\x0a\x09| selectors |\x0a\x09selectors := self testSelectors.\x0a\x09self shouldInheritSelectors ifTrue: [\x0a\x09\x09selectors addAll: self superclass allTestSelectors].\x0a\x09^selectors",
 messageSends: ["testSelectors", "ifTrue:", "addAll:", "allTestSelectors", "superclass", "shouldInheritSelectors"],
@@ -308,14 +308,12 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "buildSuite",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(self)._allTestSelectors())._collect_((function(each){
-return _st(self)._selector_(each);
-}));
-return $1;
-}, self, "buildSuite", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(self, "_allTestSelectors", []), "_collect_", [function (each) {return smalltalk.send(self, "_selector_", [each]);}]);
+    return $1;
+},
 args: [],
 source: "buildSuite\x0a\x09^self allTestSelectors collect: [:each | self selector: each]",
 messageSends: ["collect:", "selector:", "allTestSelectors"],
@@ -328,12 +326,12 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "isAbstract",
 category: 'testing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(self)._name()).__eq("TestCase");
-return $1;
-}, self, "isAbstract", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(self, "_name", []), "__eq", ["TestCase"]);
+    return $1;
+},
 args: [],
 source: "isAbstract\x0a\x09^ self name = 'TestCase'",
 messageSends: ["=", "name"],
@@ -346,10 +344,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "lookupHierarchyRoot",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return (smalltalk.TestCase || TestCase);
-}, self, "lookupHierarchyRoot", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    return smalltalk.TestCase || TestCase;
+},
 args: [],
 source: "lookupHierarchyRoot\x0a\x09^TestCase",
 messageSends: [],
@@ -362,15 +360,15 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "selector:",
 category: 'accessing',
-fn: function (aSelector){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$3,$1;
-$2=_st(self)._new();
-_st($2)._setTestSelector_(aSelector);
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
-}, self, "selector:", [aSelector], smalltalk.TestCase.klass)},
+fn: function (aSelector) {
+    var self = this;
+    var $2, $3, $1;
+    $2 = smalltalk.send(self, "_new", []);
+    smalltalk.send($2, "_setTestSelector_", [aSelector]);
+    $3 = smalltalk.send($2, "_yourself", []);
+    $1 = $3;
+    return $1;
+},
 args: ["aSelector"],
 source: "selector: aSelector\x0a\x09^self new\x0a\x09\x09setTestSelector: aSelector;\x0a\x09\x09yourself",
 messageSends: ["setTestSelector:", "new", "yourself"],
@@ -383,12 +381,12 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "shouldInheritSelectors",
 category: 'testing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(self).__tild_eq(_st(self)._lookupHierarchyRoot());
-return $1;
-}, self, "shouldInheritSelectors", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(self, "_~_eq", [smalltalk.send(self, "_lookupHierarchyRoot", [])]);
+    return $1;
+},
 args: [],
 source: "shouldInheritSelectors\x0a\x09^self ~= self lookupHierarchyRoot",
 messageSends: ["~=", "lookupHierarchyRoot"],
@@ -401,14 +399,12 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "testSelectors",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(_st(_st(self)._methodDictionary())._keys())._select_((function(each){
-return _st(each)._match_("^test");
-}));
-return $1;
-}, self, "testSelectors", [], smalltalk.TestCase.klass)},
+fn: function () {
+    var self = this;
+    var $1;
+    $1 = smalltalk.send(smalltalk.send(smalltalk.send(self, "_methodDictionary", []), "_keys", []), "_select_", [function (each) {return smalltalk.send(each, "_match_", ["^test"]);}]);
+    return $1;
+},
 args: [],
 source: "testSelectors\x0a\x09^self methodDictionary keys select: [:each | each match: '^test']",
 messageSends: ["select:", "match:", "keys", "methodDictionary"],
@@ -426,10 +422,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "addError:",
 category: 'accessing',
-fn: function (anError){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(_st(self)._errors())._add_(anError);
-return self}, self, "addError:", [anError], smalltalk.TestResult)},
+fn: function (anError) {
+    var self = this;
+    smalltalk.send(smalltalk.send(self, "_errors", []), "_add_", [anError]);
+    return self;
+},
 args: ["anError"],
 source: "addError: anError\x0a\x09self errors add: anError",
 messageSends: ["add:", "errors"],
@@ -442,10 +439,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "addFailure:",
 category: 'accessing',
-fn: function (aFailure){
-var self=this;
-return smalltalk.withContext(function($ctx) { _st(_st(self)._failures())._add_(aFailure);
-return self}, self, "addFailure:", [aFailure], smalltalk.TestResult)},
+fn: function (aFailure) {
+    var self = this;
+    smalltalk.send(smalltalk.send(self, "_failures", []), "_add_", [aFailure]);
+    return self;
+},
 args: ["aFailure"],
 source: "addFailure: aFailure\x0a\x09self failures add: aFailure",
 messageSends: ["add:", "failures"],
@@ -458,10 +456,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "errors",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@errors"];
-}, self, "errors", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    return self['@errors'];
+},
 args: [],
 source: "errors\x0a\x09^errors",
 messageSends: [],
@@ -474,10 +472,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "failures",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@failures"];
-}, self, "failures", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    return self['@failures'];
+},
 args: [],
 source: "failures\x0a\x09^failures",
 messageSends: [],
@@ -490,10 +488,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "increaseRuns",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@runs"]=_st(self["@runs"]).__plus((1));
-return self}, self, "increaseRuns", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    self['@runs'] = smalltalk.send(self['@runs'], "__plus", [1]);
+    return self;
+},
 args: [],
 source: "increaseRuns\x0a\x09runs := runs + 1",
 messageSends: ["+"],
@@ -506,15 +505,16 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "initialize",
 category: 'initialization',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
-self["@timestamp"]=_st((smalltalk.Date || Date))._now();
-self["@runs"]=(0);
-self["@errors"]=_st((smalltalk.Array || Array))._new();
-self["@failures"]=_st((smalltalk.Array || Array))._new();
-self["@total"]=(0);
-return self}, self, "initialize", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    smalltalk.send(self, "_initialize", [], smalltalk.Object);
+    self['@timestamp'] = smalltalk.send(smalltalk.Date || Date, "_now", []);
+    self['@runs'] = 0;
+    self['@errors'] = smalltalk.send(smalltalk.Array || Array, "_new", []);
+    self['@failures'] = smalltalk.send(smalltalk.Array || Array, "_new", []);
+    self['@total'] = 0;
+    return self;
+},
 args: [],
 source: "initialize\x0a\x09super initialize.\x0a\x09timestamp := Date now.\x0a\x09runs := 0.\x0a\x09errors := Array new.\x0a\x09failures := Array new.\x0a\x09total := 0",
 messageSends: ["initialize", "now", "new"],
@@ -529,13 +529,13 @@ selector: "nextRunDo:",
 category: 'running',
 fn: function (aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$1;
-$2=_st(_st(self)._runs()).__eq_eq(_st(self)._total());
+var $2,$1;
+$2=smalltalk.send(smalltalk.send(self,"_runs",[]),"__eq_eq",[smalltalk.send(self,"_total",[])]);
 if(! smalltalk.assert($2)){
-$1=_st(aBlock)._value_(_st(_st(self)._runs()).__plus((1)));
+$1=smalltalk.send(aBlock,"_value_",[smalltalk.send(smalltalk.send(self,"_runs",[]),"__plus",[(1)])]);
 };
 return $1;
-}, self, "nextRunDo:", [aBlock], smalltalk.TestResult)},
+},
 args: ["aBlock"],
 source: "nextRunDo: aBlock\x0a\x22Runs aBlock with index of next run\x0aor does nothing if no more runs\x22\x0a^self runs == self total\x0a\x09ifFalse: [ aBlock value: self runs + 1 ]",
 messageSends: ["ifFalse:", "value:", "+", "runs", "==", "total"],
@@ -550,17 +550,17 @@ selector: "runCase:",
 category: 'running',
 fn: function (aTestCase){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st((function(){
-return _st((function(){
-_st(self)._increaseRuns();
-return _st(aTestCase)._runCase();
-}))._on_do_((smalltalk.TestFailure || TestFailure),(function(ex){
-return _st(self)._addFailure_(aTestCase);
-}));
-}))._on_do_((smalltalk.Error || Error),(function(ex){
-return _st(self)._addError_(aTestCase);
-}));
-return self}, self, "runCase:", [aTestCase], smalltalk.TestResult)},
+smalltalk.send((function(){
+return smalltalk.send((function(){
+smalltalk.send(self,"_increaseRuns",[]);
+return smalltalk.send(aTestCase,"_runCase",[]);
+}),"_on_do_",[(smalltalk.TestFailure || TestFailure),(function(ex){
+return smalltalk.send(self,"_addFailure_",[aTestCase]);
+})]);
+}),"_on_do_",[(smalltalk.Error || Error),(function(ex){
+return smalltalk.send(self,"_addError_",[aTestCase]);
+})]);
+return self},
 args: ["aTestCase"],
 source: "runCase: aTestCase\x0a\x09[[\x09self increaseRuns.\x0a    \x09aTestCase runCase]\x0a\x09on: TestFailure do: [:ex | self addFailure: aTestCase]]\x0a\x09on: Error do: [:ex | self addError: aTestCase]\x0a",
 messageSends: ["on:do:", "addError:", "addFailure:", "increaseRuns", "runCase"],
@@ -573,10 +573,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "runs",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@runs"];
-}, self, "runs", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    return self['@runs'];
+},
 args: [],
 source: "runs\x0a\x09^runs",
 messageSends: [],
@@ -589,22 +589,22 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "status",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { var $2,$3,$1;
-$2=_st(_st(self)._errors())._isEmpty();
-if(smalltalk.assert($2)){
-$3=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($3)){
-$1="success";
-} else {
-$1="failure";
-};
-} else {
-$1="error";
-};
-return $1;
-}, self, "status", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    var $2, $3, $1;
+    $2 = smalltalk.send(smalltalk.send(self, "_errors", []), "_isEmpty", []);
+    if (smalltalk.assert($2)) {
+        $3 = smalltalk.send(smalltalk.send(self, "_failures", []), "_isEmpty", []);
+        if (smalltalk.assert($3)) {
+            $1 = "success";
+        } else {
+            $1 = "failure";
+        }
+    } else {
+        $1 = "error";
+    }
+    return $1;
+},
 args: [],
 source: "status\x0a\x09^self errors isEmpty \x0a\x09\x09ifTrue: [\x0a\x09\x09\x09self failures isEmpty \x0a\x09\x09\x09\x09ifTrue: ['success']\x0a\x09\x09\x09\x09ifFalse: ['failure']]\x0a\x09\x09ifFalse: ['error']",
 messageSends: ["ifTrue:ifFalse:", "isEmpty", "failures", "errors"],
@@ -617,10 +617,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "timestamp",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@timestamp"];
-}, self, "timestamp", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    return self['@timestamp'];
+},
 args: [],
 source: "timestamp\x0a\x09^timestamp",
 messageSends: [],
@@ -633,10 +633,10 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "total",
 category: 'accessing',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx) { return self["@total"];
-}, self, "total", [], smalltalk.TestResult)},
+fn: function () {
+    var self = this;
+    return self['@total'];
+},
 args: [],
 source: "total\x0a\x09^total",
 messageSends: [],
@@ -649,10 +649,11 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "total:",
 category: 'accessing',
-fn: function (aNumber){
-var self=this;
-return smalltalk.withContext(function($ctx) { self["@total"]=aNumber;
-return self}, self, "total:", [aNumber], smalltalk.TestResult)},
+fn: function (aNumber) {
+    var self = this;
+    self['@total'] = aNumber;
+    return self;
+},
 args: ["aNumber"],
 source: "total: aNumber\x0a\x09total := aNumber",
 messageSends: [],
@@ -670,8 +671,8 @@ selector: "announcer",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@announcer"];
-}, self, "announcer", [], smalltalk.TestSuiteRunner)},
+return self["@announcer"];
+},
 args: [],
 source: "announcer\x0a\x09^announcer",
 messageSends: [],
@@ -686,10 +687,10 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
-self["@announcer"]=_st((smalltalk.Announcer || Announcer))._new();
-self["@result"]=_st((smalltalk.TestResult || TestResult))._new();
-return self}, self, "initialize", [], smalltalk.TestSuiteRunner)},
+smalltalk.send(self,"_initialize",[],smalltalk.Object);
+self["@announcer"]=smalltalk.send((smalltalk.Announcer || Announcer),"_new",[]);
+self["@result"]=smalltalk.send((smalltalk.TestResult || TestResult),"_new",[]);
+return self},
 args: [],
 source: "initialize\x0a\x09super initialize.\x0a\x09announcer := Announcer new.\x0a    result := TestResult new",
 messageSends: ["initialize", "new"],
@@ -704,8 +705,8 @@ selector: "result",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { return self["@result"];
-}, self, "result", [], smalltalk.TestSuiteRunner)},
+return self["@result"];
+},
 args: [],
 source: "result\x0a\x09^result",
 messageSends: [],
@@ -720,26 +721,24 @@ selector: "run",
 category: 'actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { var worker;
-_st(self["@result"])._total_(_st(self["@suite"])._size());
-_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
+var worker;
+smalltalk.send(self["@result"],"_total_",[smalltalk.send(self["@suite"],"_size",[])]);
+smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
 worker=(function(){
-return _st(self["@result"])._nextRunDo_((function(index){
-return _st((function(){
-return _st(self["@result"])._runCase_(_st(self["@suite"])._at_(index));
-}))._ensure_((function(){
-_st(worker)._valueWithTimeout_((0));
-return _st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-}));
-}));
+return smalltalk.send(self["@result"],"_nextRunDo_",[(function(index){
+return smalltalk.send((function(){
+return smalltalk.send(self["@result"],"_runCase_",[smalltalk.send(self["@suite"],"_at_",[index])]);
+}),"_ensure_",[(function(){
+smalltalk.send(worker,"_fork",[]);
+return smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
+})]);
+})]);
 });
-_st(_st(_st(self["@suite"])._size())._min_((25)))._timesRepeat_((function(){
-return _st(worker)._valueWithTimeout_((0));
-}));
-return self}, self, "run", [], smalltalk.TestSuiteRunner)},
+smalltalk.send(worker,"_fork",[]);
+return self},
 args: [],
-source: "run\x0a\x09| worker |\x0a\x09result total: suite size.\x0a    announcer announce: (ResultAnnouncement new result: result).\x0a    worker := [ result nextRunDo: [ :index |\x0a\x09\x09[ result runCase: (suite at: index) ]\x0a\x09\x09ensure: [ worker valueWithTimeout: 0.\x0a        \x09announcer announce: (ResultAnnouncement new result: result) ]]].\x0a\x09(suite size min: 25) timesRepeat: [ worker valueWithTimeout: 0 ]",
-messageSends: ["total:", "size", "announce:", "result:", "new", "nextRunDo:", "ensure:", "valueWithTimeout:", "runCase:", "at:", "timesRepeat:", "min:"],
+source: "run\x0a\x09| worker |\x0a\x09result total: suite size.\x0a    announcer announce: (ResultAnnouncement new result: result).\x0a    worker := [ result nextRunDo: [ :index |\x0a\x09\x09[ result runCase: (suite at: index) ]\x0a\x09\x09ensure: [ worker fork.\x0a        \x09announcer announce: (ResultAnnouncement new result: result) ]]].\x0a\x09worker fork",
+messageSends: ["total:", "size", "announce:", "result:", "new", "nextRunDo:", "ensure:", "fork", "runCase:", "at:"],
 referencedClasses: ["ResultAnnouncement"]
 }),
 smalltalk.TestSuiteRunner);
@@ -751,8 +750,8 @@ selector: "suite:",
 category: 'accessing',
 fn: function (aCollection){
 var self=this;
-return smalltalk.withContext(function($ctx) { self["@suite"]=aCollection;
-return self}, self, "suite:", [aCollection], smalltalk.TestSuiteRunner)},
+self["@suite"]=aCollection;
+return self},
 args: ["aCollection"],
 source: "suite: aCollection\x0a\x09suite := aCollection",
 messageSends: [],
@@ -768,8 +767,8 @@ selector: "new",
 category: 'instance creation',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx) { _st(self)._shouldNotImplement();
-return self}, self, "new", [], smalltalk.TestSuiteRunner.klass)},
+smalltalk.send(self,"_shouldNotImplement",[]);
+return self},
 args: [],
 source: "new\x0a\x09self shouldNotImplement",
 messageSends: ["shouldNotImplement"],
@@ -784,10 +783,10 @@ selector: "on:",
 category: 'instance creation',
 fn: function (aCollection){
 var self=this;
-return smalltalk.withContext(function($ctx) { var $1;
-$1=_st(smalltalk.Object.klass.fn.prototype._new.apply(_st(self), []))._suite_(aCollection);
+var $1;
+$1=smalltalk.send(smalltalk.send(self,"_new",[],smalltalk.Object.klass),"_suite_",[aCollection]);
 return $1;
-}, self, "on:", [aCollection], smalltalk.TestSuiteRunner.klass)},
+},
 args: ["aCollection"],
 source: "on: aCollection\x0a\x09^super new suite: aCollection",
 messageSends: ["suite:", "new"],

+ 4 - 5
js/boot.js

@@ -728,12 +728,11 @@ function Smalltalk() {
 	};
 
     /* Boolean assertion */
-
-    st.assert = function(boolean) {
-        if ((undefined !== boolean) && (boolean.klass === smalltalk.Boolean)) {
-            return boolean;
+    st.assert = function(shouldBeBoolean) {
+        if ((undefined !== shouldBeBoolean) && (shouldBeBoolean.klass === smalltalk.Boolean)) {
+            return shouldBeBoolean;
         } else {
-            st.NonBooleanReceiver._new()._object_(boolean)._signal();
+            smalltalk.NonBooleanReceiver._new()._object_(shouldBeBoolean)._signal();
         }
     };
 

文件差異過大導致無法顯示
+ 0 - 0
js/lib/peg-0.6.2.min.js


文件差異過大導致無法顯示
+ 7 - 0
js/lib/peg-0.7.0.min.js


文件差異過大導致無法顯示
+ 280 - 233
js/parser.js


+ 4 - 22
js/parser.pegjs

@@ -9,8 +9,7 @@ keyword        = first:identifier last:[:] {return first + last}
 className      = first:[A-Z] others:[a-zA-Z0-9]* {return first + others.join("")}
 string         = ['] val:(("''" {return "'"} / [^'])*) ['] {
                      return smalltalk.ValueNode._new()
-                            ._position_((line).__at(column))
-                            ._value_(val.join("").replace(/\"/ig, '"'));
+                            ._value_(val.join("").replace(/\"/ig, '"'))
                  }
 
 symbol         = "#"val:(
@@ -18,29 +17,25 @@ symbol         = "#"val:(
                        / node:string {return node._value()})*
                   {
                       return smalltalk.ValueNode._new()
-                             ._position_((line).__at(column))
                              ._value_(smalltalk.symbolFor(val.join("").replace(/\"/ig, '"')))
                   }
-number         = n:(float / integer) {
+number         = n:(hex / float / integer) {
                      return smalltalk.ValueNode._new()
-                            ._position_((line).__at(column))
                             ._value_(n)
                  }
+hex            = neg:[-]? "16r" num:[0-9a-zA-Z]+ {return parseInt((neg + num.join("")), 16)}
 float          = neg:[-]?int:[0-9]+ "." dec:[0-9]+ {return parseFloat((neg + int.join("") + "." + dec.join("")), 10)}
 integer        = neg:[-]?digits:[0-9]+ {return (parseInt(neg+digits.join(""), 10))}
 literalArray   = "#(" ws lits:(lit:literal ws {return lit._value()})* ws ")" {
                      return smalltalk.ValueNode._new()
-                            ._position_((line).__at(column))
                             ._value_(lits)
                  }
 dynamicArray   = "{" ws expressions:expressions? ws "."? "}" {
                      return smalltalk.DynamicArrayNode._new()
-                            ._position_((line).__at(column))
                             ._nodes_(expressions)
                  }
 dynamicDictionary = "#{" ws expressions: expressions? ws "}" {
                         return smalltalk.DynamicDictionaryNode._new()
-                               ._position_((line).__at(column))
                                ._nodes_(expressions)
                     }
 pseudoVariable = val:(
@@ -48,7 +43,6 @@ pseudoVariable = val:(
                  / 'false' {return false}
                  / 'nil' {return nil}) {
                        return smalltalk.ValueNode._new()
-                              ._position_((line).__at(column))
                               ._value_(val)
                    }
 literal        = pseudoVariable / number / literalArray / dynamicDictionary / dynamicArray / string / symbol / block
@@ -56,12 +50,10 @@ literal        = pseudoVariable / number / literalArray / dynamicDictionary / dy
 
 variable       = identifier:varIdentifier {
                      return smalltalk.VariableNode._new()
-                            ._position_((line).__at(column))
                             ._value_(identifier)
                  }
 classReference = className:className {
                      return smalltalk.ClassReferenceNode._new()
-                            ._position_((line).__at(column))
                             ._value_(className)
                  }
 
@@ -69,7 +61,7 @@ reference      = variable / classReference
 
 keywordPair    = key:keyword ws arg:binarySend ws {return {key:key, arg: arg}}
 
-binarySelector = bin:[\\+*/=><,@%~|&-]+ {return bin.join("")}
+binarySelector = bin:[\\+*/=><,@%~|&-]+ {return bin.join("").replace(/\\/g, '\\\\')}
 unarySelector  = identifier
 
 keywordPattern = pairs:(ws key:keyword ws arg:identifier {return {key:key, arg: arg}})+ {
@@ -99,14 +91,12 @@ expressions    = first:expression others:expressionList* {
 
 assignment     = variable:variable ws ':=' ws expression:expression {
                      return smalltalk.AssignmentNode._new()
-                            ._position_((line).__at(column))
                             ._left_(variable)
                             ._right_(expression)
                  }
 
 ret            = '^' ws expression:expression ws '.'? {
                      return smalltalk.ReturnNode._new()
-                            ._position_((line).__at(column))
                             ._nodes_([expression])
                  }
   
@@ -128,14 +118,12 @@ statements     = ret:ret [.]* {return [ret]}
 
 sequence       = temps:temps? ws statements:statements? ws {
                      return smalltalk.SequenceNode._new()
-                            ._position_((line).__at(column))
                             ._temps_(temps || [])
                             ._nodes_(statements || [])
                  }
 
 block          = '[' ws params:blockParamList? ws sequence:sequence? ws ']' {
                      return smalltalk.BlockNode._new()
-                            ._position_((line).__at(column))
                             ._parameters_(params || [])
                             ._nodes_([sequence._asBlockSequenceNode()])
                  }
@@ -146,7 +134,6 @@ operand        = literal / reference / subexpression
 
 unaryMessage   = ws selector:unarySelector ![:] {
                      return smalltalk.SendNode._new()
-                            ._position_((line).__at(column))
                             ._selector_(selector)
                  }
 
@@ -170,7 +157,6 @@ unarySend      = receiver:operand ws tail:unaryTail? {
 
 binaryMessage  = ws selector:binarySelector ws arg:(unarySend / operand) {
                      return smalltalk.SendNode._new()
-                            ._position_((line).__at(column))
                             ._selector_(selector)
                             ._arguments_([arg])
                  }
@@ -202,7 +188,6 @@ keywordMessage = ws pairs:(pair:keywordPair ws {return pair})+ {
                           args.push(pairs[i].arg);
                       }
                       return smalltalk.SendNode._new()
-                             ._position_((line).__at(column))
                              ._selector_(selector.join(""))
                              ._arguments_(args)
                  }
@@ -220,21 +205,18 @@ cascade        = ws send:(keywordSend / binarySend) messages:(ws ";" ws mess:mes
                          cascade.push(messages[i]);
                      }
                      return smalltalk.CascadeNode._new()
-                            ._position_((line).__at(column))
                             ._receiver_(send._receiver())
                             ._nodes_(cascade)
                  }
 
 jsStatement    = "<" val:((">>" {return ">"} / [^>])*) ">" {
                      return smalltalk.JSStatementNode._new()
-                            ._position_((line).__at(column))
                             ._source_(val.join(""))
                  }
 
 
 method         = ws pattern:(keywordPattern / binaryPattern / unaryPattern) ws sequence:sequence? ws {
                       return smalltalk.MethodNode._new()
-                             ._position_((line).__at(column))
                              ._selector_(pattern[0])
                              ._arguments_(pattern[1])
                              ._nodes_([sequence])

+ 1 - 1
repl/Makefile

@@ -1,5 +1,5 @@
 repl.js: REPL.st
-	../bin/amberc -m Repl -l Compiler,parser REPL.st amber
+	../bin/amberc -m Repl REPL.st amber
 
 run: repl.js
 	../bin/amber

+ 1 - 1
repl/REPL.js

@@ -61,7 +61,7 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-smalltalk.send(self, "_initialize", [], smalltalk.Object);
+smalltalk.send(self, "_initialize", [], smalltalk.Repl.superclass || nil);
 (self['@readline']=smalltalk.send((typeof require == 'undefined' ? nil : require), "_value_", ["readline"]));
 (self['@util']=smalltalk.send((typeof require == 'undefined' ? nil : require), "_value_", ["util"]));
 return self;},

文件差異過大導致無法顯示
+ 322 - 228
repl/amber.js


+ 6 - 6
server/FileServer.st

@@ -71,8 +71,8 @@ handleGETRequest: aRequest respondTo: aResponse
 	| uri filename |
 	uri := (url parse: aRequest url) pathname.
 	filename := path join: self basePath with: uri.
-	path exists: filename do: [:boolean | 
-		boolean 
+	path exists: filename do: [:aBoolean |
+		aBoolean
 			ifFalse: [self respondNotFoundTo: aResponse]
 			ifTrue: [self respondFileNamed: filename to: aResponse]]
 !
@@ -595,15 +595,15 @@ mimeTypeFor: aString
 !FileServer class methodsFor: 'initialization'!
 
 main
-	| fileServer arguments portOption port|
+	| fileServer arguments portOption portNumber|
 	fileServer := self new.
 	fileServer checkDirectoryLayout.
 
 	arguments := process argv.
 	portOption := arguments at: 3 ifAbsent: [nil].
-	port := arguments at: 4 ifAbsent: [nil].
-	('-p' = portOption and: [port notNil]) ifTrue: [
-		fileServer port: port.
+	portNumber := arguments at: 4 ifAbsent: [nil].
+	('-p' = portOption and: [portNumber notNil]) ifTrue: [
+		fileServer port: portNumber.
 	].
 	^fileServer start
 ! !

+ 8 - 8
server/server.js

@@ -932,7 +932,7 @@ selector: "deprecatedAPI",
 category: 'error handling',
 fn: function () {
     var self = this;
-    smalltalk.send(console, "_warn_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(thisContext, "_home", []), "_asString", []), "__comma", [" is deprecated! (in "]), "__comma", [smalltalk.send(smalltalk.send(smalltalk.send(thisContext, "_home", []), "_home", []), "_asString", [])]), "__comma", [")"])]);
+    smalltalk.send(console, "_warn_", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.getThisContext(), "_home", []), "_asString", []), "__comma", [" is deprecated! (in "]), "__comma", [smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.getThisContext(), "_home", []), "_home", []), "_asString", [])]), "__comma", [")"])]);
     return self;
 },
 args: [],
@@ -13988,10 +13988,10 @@ var uri=nil;
 var filename=nil;
 (uri=smalltalk.send(smalltalk.send(self['@url'], "_parse_", [smalltalk.send(aRequest, "_url", [])]), "_pathname", []));
 (filename=smalltalk.send(self['@path'], "_join_with_", [smalltalk.send(self, "_basePath", []), uri]));
-smalltalk.send(self['@path'], "_exists_do_", [filename, (function(boolean){return ((($receiver = boolean).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send(self, "_respondNotFoundTo_", [aResponse]);})() : (function(){return smalltalk.send(self, "_respondFileNamed_to_", [filename, aResponse]);})()) : smalltalk.send($receiver, "_ifFalse_ifTrue_", [(function(){return smalltalk.send(self, "_respondNotFoundTo_", [aResponse]);}), (function(){return smalltalk.send(self, "_respondFileNamed_to_", [filename, aResponse]);})]));})]);
+smalltalk.send(self['@path'], "_exists_do_", [filename, (function(aBoolean){return ((($receiver = aBoolean).klass === smalltalk.Boolean) ? (! $receiver ? (function(){return smalltalk.send(self, "_respondNotFoundTo_", [aResponse]);})() : (function(){return smalltalk.send(self, "_respondFileNamed_to_", [filename, aResponse]);})()) : smalltalk.send($receiver, "_ifFalse_ifTrue_", [(function(){return smalltalk.send(self, "_respondNotFoundTo_", [aResponse]);}), (function(){return smalltalk.send(self, "_respondFileNamed_to_", [filename, aResponse]);})]));})]);
 return self;},
 args: ["aRequest", "aResponse"],
-source: "handleGETRequest: aRequest respondTo: aResponse\x0a\x09| uri filename |\x0a\x09uri := (url parse: aRequest url) pathname.\x0a\x09filename := path join: self basePath with: uri.\x0a\x09path exists: filename do: [:boolean | \x0a\x09\x09boolean \x0a\x09\x09\x09ifFalse: [self respondNotFoundTo: aResponse]\x0a\x09\x09\x09ifTrue: [self respondFileNamed: filename to: aResponse]]",
+source: "handleGETRequest: aRequest respondTo: aResponse\x0a\x09| uri filename |\x0a\x09uri := (url parse: aRequest url) pathname.\x0a\x09filename := path join: self basePath with: uri.\x0a\x09path exists: filename do: [:aBoolean |\x0a\x09\x09aBoolean\x0a\x09\x09\x09ifFalse: [self respondNotFoundTo: aResponse]\x0a\x09\x09\x09ifTrue: [self respondFileNamed: filename to: aResponse]]",
 messageSends: ["pathname", "parse:", "url", "join:with:", "basePath", "exists:do:", "ifFalse:ifTrue:", "respondNotFoundTo:", "respondFileNamed:to:"],
 referencedClasses: []
 }),
@@ -14257,17 +14257,17 @@ var self=this;
 var fileServer=nil;
 var arguments=nil;
 var portOption=nil;
-var port=nil;
+var portNumber=nil;
 (fileServer=smalltalk.send(self, "_new", []));
 smalltalk.send(fileServer, "_checkDirectoryLayout", []);
 (arguments=smalltalk.send((typeof process == 'undefined' ? nil : process), "_argv", []));
 (portOption=smalltalk.send(arguments, "_at_ifAbsent_", [(3), (function(){return nil;})]));
-(self['@port']=smalltalk.send(arguments, "_at_ifAbsent_", [(4), (function(){return nil;})]));
-((($receiver = smalltalk.send(smalltalk.send("-p", "__eq", [portOption]), "_and_", [(function(){return smalltalk.send(self['@port'], "_notNil", []);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return smalltalk.send(fileServer, "_port_", [self['@port']]);})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return smalltalk.send(fileServer, "_port_", [self['@port']]);})]));
+(portNumber=smalltalk.send(arguments, "_at_ifAbsent_", [(4), (function(){return nil;})]));
+((($receiver = smalltalk.send(smalltalk.send("-p", "__eq", [portOption]), "_and_", [(function(){return smalltalk.send(portNumber, "_notNil", []);})])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return smalltalk.send(fileServer, "_port_", [portNumber]);})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return smalltalk.send(fileServer, "_port_", [portNumber]);})]));
 return smalltalk.send(fileServer, "_start", []);
 return self;},
 args: [],
-source: "main\x0a\x09| fileServer arguments portOption port|\x0a\x09fileServer := self new.\x0a\x09fileServer checkDirectoryLayout.\x0a\x0a\x09arguments := process argv.\x0a\x09portOption := arguments at: 3 ifAbsent: [nil].\x0a\x09port := arguments at: 4 ifAbsent: [nil].\x0a\x09('-p' = portOption and: [port notNil]) ifTrue: [\x0a\x09\x09fileServer port: port.\x0a\x09].\x0a\x09^fileServer start",
+source: "main\x0a\x09| fileServer arguments portOption portNumber|\x0a\x09fileServer := self new.\x0a\x09fileServer checkDirectoryLayout.\x0a\x0a\x09arguments := process argv.\x0a\x09portOption := arguments at: 3 ifAbsent: [nil].\x0a\x09portNumber := arguments at: 4 ifAbsent: [nil].\x0a\x09('-p' = portOption and: [portNumber notNil]) ifTrue: [\x0a\x09\x09fileServer port: portNumber.\x0a\x09].\x0a\x09^fileServer start",
 messageSends: ["new", "checkDirectoryLayout", "argv", "at:ifAbsent:", "ifTrue:", "and:", "=", "notNil", "port:", "start"],
 referencedClasses: []
 }),
@@ -14347,4 +14347,4 @@ smalltalk.classes()._do_(function(each) {
 if(this.smalltalkReady) {
     this.smalltalkReady();
 }
-smalltalk.FileServer._main()
+smalltalk.FileServer._main()

+ 1 - 3
st/IDE.st

@@ -2053,9 +2053,7 @@ testCases
 !TestRunner methodsFor: 'actions'!
 
 performFailure: aTestCase
-	aTestCase setUp.
-    [ aTestCase perform: aTestCase selector ]
-  		ensure: [ aTestCase tearDown ]
+	aTestCase runCase
 !
 
 run: aCollection

+ 37 - 0
st/Kernel-Collections.st

@@ -1477,6 +1477,43 @@ includes: anObject
 	^elements includes: anObject
 ! !
 
+Object subclass: #Queue
+	instanceVariableNames: 'read readIndex write'
+	package: 'Kernel-Collections'!
+
+!Queue methodsFor: 'accessing'!
+
+back: anObject
+	write add: anObject
+!
+
+front
+    ^self frontIfAbsent: [ self error: 'Cannot read from empty Queue.']
+!
+
+frontIfAbsent: aBlock
+	| result |
+	result := read at: readIndex ifAbsent: [
+		write isEmpty ifTrue: [
+			readIndex > 1 ifTrue: [ read := #(). readIndex := 1 ].
+			^aBlock value ].
+    	read := write.
+    	readIndex := 1.
+    	write := OrderedCollection new.
+    	read first ].
+    read at: readIndex put: nil.
+    readIndex := readIndex + 1.
+    ^result
+! !
+
+!Queue methodsFor: 'initialization'!
+
+initialize
+	read := #().
+    readIndex := 1.
+    write := OrderedCollection new
+! !
+
 Object subclass: #RegularExpression
 	instanceVariableNames: ''
 	package: 'Kernel-Collections'!

+ 53 - 7
st/Kernel-Methods.st

@@ -56,13 +56,7 @@ applyTo: anObject arguments: aCollection
 !
 
 ensure: aBlock
-	| success |
-	success := false.
-	^[self value. success := true. aBlock value]
-		on: Error
-		do: [:ex |
-			success ifFalse: [aBlock value].
-			ex signal]
+	<try{return self()}finally{aBlock._value()}>
 !
 
 new
@@ -121,6 +115,10 @@ valueWithPossibleArguments: aCollection
 
 !BlockClosure methodsFor: 'timeout/interval'!
 
+fork
+	ForkPool default fork: self
+!
+
 valueWithInterval: aNumber
 	<return setInterval(self, aNumber)>
 !
@@ -214,6 +212,54 @@ source: aString
 	self basicAt: 'source' put: aString
 ! !
 
+Object subclass: #ForkPool
+	instanceVariableNames: 'poolSize maxPoolSize queue worker'
+	package: 'Kernel-Methods'!
+
+!ForkPool methodsFor: 'action'!
+
+addWorker
+	worker valueWithTimeout: 0.
+    poolSize := poolSize + 1
+!
+
+fork: aBlock
+	poolSize < maxPoolSize ifTrue: [ self addWorker ].
+	queue back: aBlock
+! !
+
+!ForkPool methodsFor: 'initialization'!
+
+initialize
+	| sentinel |
+	poolSize := 0.
+    maxPoolSize := self class defaultMaxPoolSize.
+    queue := Queue new.
+    sentinel := Object new.
+    worker := [
+		| block |
+        poolSize := poolSize - 1.
+		block := queue frontIfAbsent: [ sentinel ].
+        block == sentinel ifFalse: [
+        	[ block value ] ensure: [ self addWorker ]]].
+! !
+
+ForkPool class instanceVariableNames: 'default'!
+
+!ForkPool class methodsFor: 'accessing'!
+
+default
+	^default ifNil: [ default := self new ]
+!
+
+defaultMaxPoolSize
+	^100
+!
+
+resetDefault
+	default := nil
+! !
+
 Object subclass: #Message
 	instanceVariableNames: 'selector arguments'
 	package: 'Kernel-Methods'!

+ 5 - 1
st/Kernel-Tests.st

@@ -10,7 +10,11 @@ testCompiledSource
 !
 
 testEnsure
-	self assert: ([Error new] ensure: [true])
+	self assert: 3 equals: ([3] ensure: [4])
+!
+
+testEnsureRaises
+	self should: [[Error new signal] ensure: [true]] raise: Error
 !
 
 testNumArgs

+ 9 - 3
st/Makefile

@@ -3,10 +3,10 @@
 # javascript files from them, for both debug and deployment.
 #
 # Where we find the current runnable code and where we put our js files on install
-JS	:= ../js/
+JS      := ../js/
 
 # The compiler script
-AMBERC	:= ../bin/amberc
+AMBERC  := ../bin/amberc
 
 # Generic flags to AMBERC
 FLAGS   := -d
@@ -23,9 +23,15 @@ all: $(OBJECTS)
 # First we copy the core javascript files from current working files
 # into this directory. These files are hand written or generated using
 # other tools (parser.js). $@ is the target name.
-boot.js init.js parser.js:
+boot.js init.js:
 	cp ../js/$@ .
 
+# generate the parser
+# $@ is the target
+# $< is the prerequisite
+parser.js: ../js/parser.pegjs
+	pegjs -e smalltalk.parser $< $@
+
 # Then we compile Kernel-*.st files depending on having boot.js, init.js and parser.js
 # $< means the first dependency - in other words Kernel-*.st
 Kernel-Objects.js: Kernel-Objects.st boot.js init.js parser.js

+ 2 - 2
st/SUnit.st

@@ -234,9 +234,9 @@ run
     announcer announce: (ResultAnnouncement new result: result).
     worker := [ result nextRunDo: [ :index |
 		[ result runCase: (suite at: index) ]
-		ensure: [ worker valueWithTimeout: 0.
+		ensure: [ worker fork.
         	announcer announce: (ResultAnnouncement new result: result) ]]].
-	(suite size min: 25) timesRepeat: [ worker valueWithTimeout: 0 ]
+	worker fork
 ! !
 
 !TestSuiteRunner methodsFor: 'initialization'!

部分文件因文件數量過多而無法顯示