Little Smallscript is a dialect of Smalltalk that compiles into JavaScript. Hidden behind the huge Smalltalk Environment, Smalltalk always had a beautiful syntax that is fit for object oriented scripting. Little Smallscript is an attempt to let Smalltalk see the light of day again. (Although I've never written Smalltalk before ;p)

The golden rule of LittleSmallscript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly from LittleSmallscript (and vice-versa). The compiled output is readable and pretty-printed, passes through JSHint without warnings, will work in every JavaScript runtime, and tends to run as fast or faster than the equivalent handwritten JavaScript. This paragraph is borrowed from coffeescript.org

Latest Version: 1.0.0

Overview

LittleSmallscript on the left, compiled JavaScript output on the right.

"Variable declaration"
| number opposite square list math cubes |

"Assignment:"
number   := 42.
opposite := true.
"or"
number   <- 42.
opposite <- true.

"Conditions:"
number := opposite ifTrue: [-42].

"Functions:"
square := [:x | x * x].

"Arrays:"
list := #(1 2 3 4 5).

"Objects:"
math := #{
  #root: Math at: #sqrt
  #square: square
  #cube: [:x | (square value: x) * x]
}.

"Prelude methods:"
cubes := list collect: [:num | math cube: num]
            
var number, opposite, square, list, math, cubes;
number = 42;
opposite = true;
number = 42;
opposite = true;
number = opposite ? (function () {
  return -42;
})() : void 0;
square = function (x) {
  return (x * x);
};
list = [1, 2, 3, 4, 5];
math = {
  "root": Math.sqrt,
  "square": square,
  "cube": function (x) {
    return (square(x) * x);
  }
};
cubes = list.collect(function (num) {
  return math.cube(num);
});
            

Installation

Type the following sequence of commands in your terminal.

git clone https://github.com/ympbyc/LittleSmallscript.git

cd LittleSmallscript/

npm install -g
        

Usage

When you 'npm install -g', a command line tool named 'littlesmallscript' gets installed.

#compile a file to JavaScript and save as .js file
littlesmallscript -c from.st
littlesmallscript --compile from.st

#print out the compiled JavaScript
littlesmallscript -p from.st
littlesmallscript --print from.st

#run an interactive LittleSmallscript
littlesmallscript -i
littlesmallscript --interactive
        

Language Reference

blah blah blah

Syntax

LittleSmallscript has a minimum set of syntax that everyone can learn in a few minutes.

A statement starts with an optional variable declaration.

| message1 message2 numbers |
            
var message1, message2, numbers;
            

A statement body is made of one or more expressions separated by periods

'hello'.
1234
            
"hello";
1234;
            

Assingnments can only appear at the start of an expression

message1 := message2 := 'Hello, world!'.
numbers := #(1 2 3 4 5)
            
message1 = message2 = "Hello, world!";
numbers = [1, 2, 3, 4, 5];
            

A message passing expression starts with a receiver followed by selector(s) and arguments

"Unary messages take no arguments"
numbers pop.

"Binary messages take a JS operator and an argument"
2 + 3.

"Keyword messages take one or more selectors and arguments"
numbers at: 4 put: 100.

"Messages can be chained. This example is interpreted as ((Date new) getTime)"
Date new getTime.

"Unary messages have the highest precedence, followed by binary messages, followed by keyword messages."
a kw: b + c sel.
b + a kw: c sel.
c sel + b kw: a
            
numbers.pop();

(2 + 3);

numbers[4] = 100;

(new Date().getTime());

a.kw((b + c.sel()));
(b + a).kw(c.sel());
(c.sel() + b).kw(a);
            

Cascades are generalized form of method chaining. The left most expression becomes the receiver for all of the following messages. The product of this expression is the receiver.

LittleSmallscript implements Little Smalltalk style cascades. Their behaviour differ from cascades in other common implementations of Smalltalk.

Object new ; at: 'a' put: 1 ; at: 'b' put: 2 ; at: 'c' put: 3
            
(function () {
  var _receiver = new Object();
  _receiver.a = 1;
  _receiver.b = 2;
  _receiver.c = 3;
  return _receiver;
})();
            

Function

Functions are defined by an open square bracket, optional list of parameters followed by a vertical bar, the function body, and the closing square bracket. The empty function looks like this: []

| square cube |

square := [:x | x * x].
cube := [:x | (square value: x) * x].

[:x| | foo |
  foo := x.
  foo
] value: 2
            
var square, cube;

square = function (x) {
  return (x * x);
};

cube = function (x) {
  return (square(x) * x);
};

(function (x) {
  var foo;
  foo = x;
  return foo;
})(2);
            

Literals

The LittleSmallscriptscript literal for Arrays look similar to that of Smalltalk. However they accept and evaluate any expression thrown into them. (in Smalltalk, only limited variety of elements are accepted)

In JavaScript, objects are often used as hashes. Unlike Smalltalk, where you manualy have to "new" and "at:put:" to the Dictionary, Little Smallscript has an literal for creating objects.

| string shortString song singers bitlist kids |

"I am a
multi line comment"

string := 'I am a string. It is OK to have
newlines
inside of me'.

"A string composed of an Alpabetic character followed by Alpha-numeric characters can be created with #"
shortString := #foo.

"numbers"
1 .
-4 .

"array"
song := #('do' 're' 'mi' 'fa' 'so').

"hash"
singers := #{#Jagger: 'Rock' #Elvis: 'Roll'}.

bitlist := #(
  1 0 1
  0 0 1
  1 1 0
).

kids := #{
  #brother: #{
    #name: 'Max'
    #age:  11
  }
  #sister: #{
    #name: 'Ida'
    #age:  9
  }
}
            
var string, shortString, song, singers, bitlist, kids;

string = "I am a string. It is OK to have\nnewlines\ninside of me";

shortString = "foo";

1;
-4;

song = ["do", "re", "mi", "fa", "so"];

singers = {
  "Jagger": "Rock",
  "Elvis": "Roll"
};

bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];

kids = {
  "brother": {
    "name": "Max",
    "age": 11
  },
  "sister": {
    "name": "Ida",
    "age": 9
  }
};
            

Local Variable Declarations

Variable declaration can only appear at the start of a statement. Space delimited alpha-numeric charactors placed between two vertical bars are declared as local variables.

| foo bar |
foo := bar := 1.
[| foo |
  foo := 2.
  foo
] value. "=> 2"
foo      "=> 1"
            
var foo, bar;
foo = bar = 1;
(function () {
  var foo;
  foo = 2;
  return foo
})(); //=> 2
foo;  //=> 1
            

Little Smallscript is a lexicaly scoped language. Temporary variables are enclosed in its function scope.


IfTrue, IfFalse, and Conditional Assignment

There are no "if then else" syntax in Smalltalk. Instead "ifTrue" and "ifFalse" are defined as a method of Boolean's instances.

1 ifTrue: ['1 is true'].
0 ifFalse: ['0 is false'].
foo := x ifTrue: [1] ifFalse: [2]
            
1 ? (function () { return '1 is true'; })() : void 0;
0 ? void 0 : (function () { return '0 is false'; })();
foo = x ? (function () { return 1; })() : (function () { return 2; })();
            

Iteration Over Objects and Arrays

Various iteration methods are provided via the prelude module (/src/prelude.js). However the recommended practice is not to use this library and instead use the JS native methods like forEach and map.

| yearsOld |

"Eat Lunch"
#(#toast #cheese #wine) forEach: [:food | eat value: food].

"Objects"
require value: 'src/prelude'.
yearsOld := #{#max:10 #ida:9 #tim:11}.
ages := yearsOld collect: [:age :child |
  child + ' is ' + age
]
            
var yearsOld;

["toast", "cheese", "wine"].forEach(function (food) {
  eat(food);
});

require('src/prelude');
yearsOld = {"max":10, "ida":9, "tim":11};
ages = yearsOld.map(function (age, child) {
  return ((child + " is ") + age);
});
            

While loop is created with whileTrue - an instance method of Function defined in prelude.js.

| counter |
counter := 0.
[counter < 10] whileTrue: [window alert: counter. counter += 1]
            
var counter;

counter = 0;

(function () {
  return (counter < 10);
}).whileTrue(function () {
  window.alert(counter);
  return counter += 1;
});
            

Classes and Inheritance

LittleSmallscript has a class definition syntax that is an imitation of Little Smalltalk v5's.
A class definition is composed of two parts, a class header and method definition. Methods can be added anywhere in the code.

+Object subclass:#Animal variables:#(#name).

!Animal setName: aName
  name := aName !.

!Animal move: metre
  window alert: name + ' moved ' + metre + 'm.' !.

+Animal subclass:#Snake variables:#().

!Snake move
  window alert: 'Slithering...'.
  Snake super:#move arguments:#(5) !.

Snake new
; setName: 'Sammy the Python'
; move
            
var Animal;
Animal = function () {
  this.name = null;
  if (this.init) {
    this.init.apply(this, arguments);
  }
};
Animal.__super = Object.prototype;
Animal.prototype = new Object();
Animal.prototype.setName = function (aName) {
  var _this = this;
  return _this.name = aName;
};
Animal.prototype.move = function (metre) {
  var _this = this;
  return window.alert((((_this.name + " moved ") + metre) + "m."));
};
var Snake;
Snake = function () {
  if (this.init) {
    this.init.apply(this, arguments);
  }
};
Snake.__super = Animal.prototype;
Snake.prototype = new Animal();
Snake.prototype.move = function () {
  var _this = this;
  window.alert("Slithering...");
  return Snake.__super.move.apply(_this, [5]);
};
(function () {
  var _receiver = new Snake();
  _receiver.setName("Sammy the Python");
  _receiver.move();
  return _receiver;
})();
            

Embedded Javascript

Hopefully, you'll never need to use it, but if you ever need to intersperse snippets of JavaScript within your LittleSmallscript, you can use < and > to pass it straight through.

hi := <function () {
  return [document.title, "Hello Javascript"].join(": ");
}>.
            
hi = function () {
  return [document.title, "Hello Javascript"].join(": ");
};
            

Try/Catch/Finally

tryCatch: is an instance method of block closures.

[
  allHellBreaksLoose.
  catsAndDogsLivingTogether
] tryCatch: [:error |
  window alert: error
]
            
(function () {
  var _ret;
  try {
    _ret = (function () {
      allHellBreaksLoose;
      return catsAndDogsLivingTogether;
    })();
  } catch (err) {
    _ret = function (error) {
      return window.alert(error);
    }(err);
  }
  return _ret;
})();
            


Change Log

v1.0.4 - Jan 29, 2013

Inputs can now be piped-in to littlesmallscript. e.g:

echo '(1 to: 5) map: [ :n | n * n]' | littlesmallscript
.

v1.0.3 - Nov 2, 2012

String literal supports backslash escaping. String literals compile into single quoted string instead of double quotes

v1.0.2 - Oct 17, 2012

Super Methods can be called using the following syntax

ClassName super:#superMethodName.
"or"
ClassName super:#superMethodName arguments:#(1 2 3)
          

v1.0.0 - Sep 30, 2012

The first major version!
Every parser is now written in LittleSmallscript itself.
v1 is not backward compatible with v0.
Syntax for accessing instance variables has changed.
Class definition syntax has been added.

v0.0.4 - Sep 20, 2012

Binary selectors are now bare js operators. (e.g: before: 1 <+> 1 now: 1 + 1)
Installed the Precedence order. Unary messages have the highest precedence, followed by binary messages, followed by keyword messages.

v0.0.3 - Sep 19, 2012

Hello, world!