Programs compiled by the #Smalltalk compiler must be specified in the ANSI
Smalltalk SIF format. For a simple example, suppose we wish to evaluate:
"Transcript print: 1000 factorial". First, we must create a SIF file for this
source:
Smalltalk interchangeVersion: '1.0'!
Global initializer!
"Here to add a prereq annotation"!
Annotation key: 'Prerequisite' value: 'Smalltalk.sif'!
Global initializer!
Transcript print: 1000 factorial!
The Prerequisite annotation tells the compiler to include the Smalltalk.sif
file. That file contains all of the Smalltalk classes for the base class
library. You will need to include that file for any program that you write. Once
you have entered the source code, you need to save it to a file. In this case,
we can save it as "factorial.sif". Now we are ready to compile the file. To
compile the file, we can simply evaluate "sst factorial.sif" from the command
line. Once the compiler finishes running, it produces a factorial.exe program
that can be run from the command line (you will need copy the LargeInteger.dll
to the same directory as your factorial.exe program).
Sample Programs
There are some sample programs shipped with #Smalltalk that you can compile.
The Calculator.sif program is a simple command line calculator like bc. It
support +, -, *, /, ^ (exponentiation), and ! (factorial) (e.g., "10000! /
9999!"). There is also the #Smalltalk compiler. It is written in #Smalltalk and
is in the sst.sif file. The Interpreter.sif is a command line interpreter. You
can enter expressions separated by !. The results of each expression is printed
to the command line.
If you download the examples, then you can also compile the Benchmarks.sif
and the ANSI tests derived from the Camp Smalltalk work. The Benchmarks.sif file
contains Squeak's #tinyBenchmarks and Bruce Samuelson's Slopstone & Smopstone
benchmarks. The ANSI tests are in the ANSITests.sif file. If you run these, it
should print that all 3046 tests pass.
Glorp and Swazoo are also included in the examples. The Glorp tests are in
the GlorpTests.sif file. Assuming that you have changed the data source in the
GlorpDBTests.sif file, all tests should pass. The Swazoo-Tests.sif file has the
tests for Swazoo -- all tests should pass. You can read the readme.txt file for
information on setting up a Swazoo server.
Compile Options
You can specify some compile time options when compiling your program.
You can get a list of these options by just running the compiler without any
input files.
Program type: You can specify the program type by using one of
--DLL, --CONSOLE, or --WINDOWS. Both console and windows options produce
.exe files that can be run link normal programs. The main difference is that
console applications are run by the DOS command line. Control is returned
back to the command line after the program finishes. However, if you use the
--WINDOWS option then control is returned when the program starts.
Essentially, the --WINDOWS option is for GUI applications.
Libraries: You can use any .NET class in #Smalltalk. However, for
classes that aren't in the standard mscorlib.dll library, you will need to
tell the compiler where to locate the class. You can do this by using the
--LIBRARY= option. For example if you want to link to the Windows forms library,
you can use --LIBRARY=System.Windows.Forms. Once you have linked to that
library, you could compile code that uses the message box (e.g., "MessageBox
show: 'this is a test'").
Output: Normally, when you compile a program it makes the program
have the same name as the last input file. For example, if you run "sst
test.sif", it saves a "test.exe" program. However, sometimes you want to
save to a different filename. For example, if you are modifying the
#Smalltalk compiler, you will want to save the compiler to a different name.
You will want to test out the new compiler before replacing the old one.
You can specify the name of the output by using the --OUTPUT= switch.
For example, if you run "sst test.sif --OUTPUT=foo", then it will create a
foo.exe program instead of a test.exe program.
Primitives
Most Smalltalk's specify primitives by special "<>" tags at the beginning of
the method. #Smalltalk does not do this. Instead it uses a special message sent
to "Compiler". This allows you to insert a "primitive" in the middle of a
method. The format of this message is "Compiler primitive: [:codeGenerator ... |
code to run] ({evaluate: code} | {argument: code} | {usesArgument: code})*".
When this is compiled, the code specified by the primitive: block is evaluated.
Arguments specified by the evaluate: argument are pushed on the stack before the
code in the primitive block is run. The arguments given by the argument: and
usesArgument: are passed to the primitive: block as a parse tree.
Special Messages
The #Smalltalk compiler doesn't have special messages for ifTrue:, whileFalse:,
etc. Instead it uses
macros. Macros are defined by
rewrite rules
from the Refactoring Browser. By defining an annotation with a key value of
"Macro". The value of the annotation is a string that when parsed is a literal
array. The first item in the literal array is the name of the macro, the second
item is the search pattern of the rewrite rule and the last item is the replace
pattern.
Macros are inherited. If you define a macro in a class, all subclasses also
use the macro. However, you can remove the macro from a subclass by adding
another Macro annotation with the same macro name and nil for the search and
replace patterns.
The Smalltalk.sif file contains macros defined on Object to optimize several
common messages (e.g., ifTrue:, whileTrue:, etc.). These macros transform the
messages into the "Compiler primitive:" syntax.
Prerequisites
The SIF format doesn't define any standard way to specify prerequisites. In
#Smalltalk you can define prerequisites using an annotation with a key of
"Prerequisite" and a value that is the filename to be included. Whenever a
prerequisite annotation is parsed, the file referenced by the prerequisite is
immediately parsed. Once the prerequisite file is parsed, the remaining code in
the original file is parsed. #Smalltalk searches for files in the current
directory first, and if the file is not found, then it searches for it in the
Source subdirectory where the sst.exe executable is located.
Typed Instance Variables
You can specify types for instance variables by using an Annotation. If an
annotation for a class has a key of InstanceVariableType, then the value is the
instance variable type. The value for an InstanceVariableType annotation must be
a string that when evaluated is a literal array. The first item in the literal
array is the variable name and the last item is the full .NET type name (e.g.,
System.Object).
.NET Classes and Methods
You can reference .NET classes directly in your code by using the full name.
For example, to reference the .NET object class, you need to use System.Object.
If the class name isn't overridden by a Smalltalk class, you can also refer to
the .NET class by its short name. For example, System.Windows.Forms.Button can
be referenced as Button.
To reference the .NET variables, properties, or methods, you send messages. The
first keyword of the Smalltalk message must be the name of the item you are
referencing. If you are wanting to call the constructor, then the first keyword
should be #new if there aren't any arguments or #new: if the constructor takes
arguments. The other keyword parts for messages or constructors can be anything.
For example, to evaluate the System.Math::Round(double, int32) static method,
you can evaluate "Math round: 1.234 with: 2".
.NET/#Smalltalk Object Conversions
When you reference external methods, properties, or variables, objects must
be converted from a #Smalltalk object to a .NET object and vice versa. For
example, an integer in Smalltalk could be a SmallInteger or a LargeInteger
object, but in .NET they might be an int32, uint32, int64, etc. The .NET
compiler automatically performs such conversions.
Most conversions are based on the typed instance variables. For example, if
you have a class that defines a typed instance variable for System.Double (e.g.,
FloatD), then when you pass that object to .NET it will load the System.Double
part from the FloatD. Also, when you convert from a System.Double back into a
#Smalltalk object, it will create a FloatD #Smalltalk object to represent the
System.Double object. There are some conversions that aren't based on the typed
instance variables. In particular, integers and booleans are handled specially
by the compiler. If you need to change how these are converted, then you must
modify the compiler.
When converting from a .NET object back to a #Smalltalk object, it may be
possible that the object being returned doesn't have a #Smalltalk class that
defines a typed instance variable for that class. In these cases, the object
will be wrapped in a #Smalltalk ObjectWrapper. This class uses #doesNotUnderstand:
error handling to forward messages to the original .NET object.
Occasionally, they might be a .NET interface that contains an overloaded
function with several types of integers (e.g., int32, uint32, int64, etc.).
Since the #Smalltalk compiler just picks one, it might not pick the correct
overloaded function. In these cases you can first convert your #Smalltalk
integer to another #Smalltalk class. For example, if you want to call the
function that takes an uint32, you can convert your integer to a UInt32 first
(e.g., "Some.Net.Class SomeMethod: (UInt32 convertFrom: 1)").
Delegates
Delegates in #Smalltalk are handled by blocks. Whenever you send a message
that takes a delegate, you can pass a block instead. The one restriction with
delegates is that they must be known at compile time. To compensate for this
limitation, the compiler has a special selector (#asDelegate:) that converts a
block to a .NET delegate. The argument to the #asDelegate: message must be a
.NET delegate type. For example, you can specify a callback for the clicked
event of a button with:
button add_Click: ([:o :e | self doSomething] asDelegate: System.EventHandler)
Events
There isn't direct support for .NET events like there is for variables,
properties, and methods. If you need to add or remove events, you will need to
do so directly through the methods. For example, you can evaluate "System.Windows.Forms.Application
add_ApplicationExit: [:object :args | ...]" to add an event handler for the
ApplicationExit event.
Sharp Smalltalk Links:
Download Now!
Class Library
Compiler
Debugger
Implementation
To Do |