Browse Source

Merge pull request #280 from mkroehnert/amberc

new amberc.js compiler + small fixes for server and repl projects
Nicolas Petton 12 năm trước cách đây
mục cha
commit
c4f4a88495
10 tập tin đã thay đổi với 1464 bổ sung596 xóa
  1. 10 350
      bin/amberc
  2. 761 0
      bin/amberc.js
  3. 353 0
      bin/amberc.sh
  4. 1 1
      js/Kernel-Objects.deploy.js
  5. 1 1
      js/Kernel-Objects.js
  6. 1 1
      repl/Makefile
  7. 1 1
      repl/REPL.js
  8. 322 228
      repl/amber.js
  9. 6 6
      server/FileServer.st
  10. 8 8
      server/server.js

+ 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"
-
-# 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
+

+ 1 - 1
js/Kernel-Objects.deploy.js

@@ -203,7 +203,7 @@ smalltalk.method({
 selector: "deprecatedAPI",
 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;
 }
 }),

+ 1 - 1
js/Kernel-Objects.js

@@ -280,7 +280,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: [],

+ 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;},

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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()

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác