Express is an experimental, self-managed programming language designed for performance within an interpreted environment. It features a Pascal-inspired syntax, iwth features taken from a number of newer languages.
The aim is a programming language that will be a direct conduit from a developer's mind to the machine, not a maze of mandatory abstractions, or symbolic verbose boilerplate.
Express is actively in development and its features and performance characteristics are subject to change.
Microbenchmark Performance: The interpreter's Just-In-Time (JIT) compiler allows Express to achieve improved performance on algorithmic code.
In numerical microbenchmarks, Express significantly outperforms:
- Lape: By a factor of 3-4x.
- JVM (Interpreted Mode): By a factor of 2x.
- Python: By an order of magnitude.
Note: Using global references, and reference args incurs a small penalty due to design choices. The same goes for type mixing, which even hits harder, and is not recommended where avoidable.
In limited tests the performance peaks at about (JS) v8 Node.js/chrome speed, but usually slower.
- Operators: Full suite of arithmetic (+, -, *, /, %, **), bitwise (&, |, xor, shl, shr, sar), and compound assignment
- Statically-Typed: Clear and safe code with types like
Int32,String,Boolean, and user-defined classes. - Object-Oriented:
class-based OOP with single inheritance, virtual methods,const(readonly) fields, andinheritedcalls. - Modern Control Flow:
if/elif/else,for,while,repeat..until,break,continue. - Class-Based Exceptions: Safe error handling with
try...except on E: TExceptionType do. - Extension Methods: Add new methods to any type, including built-in arrays and records.
- Automatic Memory Management: Reference counting for strings, arrays, and class instances. No manual
freeis needed in most cases. - Module System: Organize code with
import 'path' as Alias. Access through namespaceAlias::Func() - Pointers: Supports native pointers, with
addr(x)you can get the address of a variable. - Destructuring assignment Records can be assigned directly to local variables
(x,y) := myPoint - Anonymous functions Separate function type that captures references to local variables with
lambda. - Easy FPC Integration: A simple, high-level API (
TExpress) makes it trivial to embed Express into your Free Pascal applications for powerful, two-way scripting.
These short examples showcase some of its current key features.
Express features a simple and powerful object model. No override keyword is needed.
type TAnimal = class
var name: String
func Create(aName: String)
self.name := aName
end
func Speak()
print self.name + ' makes a sound.'
end
end
type TCat = class(TAnimal)
func Speak() // This automatically overrides the parent's method
print self.name + ' says "Meow!"'
end
end
// Polymorphism: a TAnimal variable can hold a TCat object.
var myPet: TAnimal := new TCat('Misty')
// The correct, overridden method is called at runtime.
myPet.Speak() // Output: Misty says "Meow!"A classic recursive Fibonacci implementation.
func Fib(n: Int64): Int64
if (n <= 1) then
return n
end
return Fib(n - 1) + Fib(n - 2)
end
var n := Fib(10)
print 'Fib(10) is ' + n.ToStr() // Output: Fib(10) is 55Catch specific errors using class-based exception handling.
type EMyError = class(Exception) end
try
print 'About to raise an error...'
raise EMyError('Something went wrong!')
except on E: EMyError do
print 'Caught it: ' + E.Message
except on E: Exception do
// anything goes with capture
except
// anything goes
end
print 'Program continued safely.'Add new functionality to any existing type, like TIntArray.
type TIntArray = array of Int64;
// Add a 'Sum' method to all TIntArray variables.
func TIntArray.Sum(): Int64
for(ref item in self)
Result += item
end
var numbers: TIntArray
numbers.SetLen(3)
numbers[0] := 10
numbers[1] := 20
numbers[2] := 70
var sum := numbers.Sum();
print 'Sum is ' + sum.ToStr() // Output: Sum is 100Familiar control flow with a clean, semicolon-free syntax.
var total := 0
for (var i := 1; i <= 10; i += 1) do
if (i % 2 = 0) then
total += i // Add even numbers
end
end
// Ternary expressions are great for simple assignments.
var message := if (total > 20) 'Big number' else 'Small number'
print message + ': ' + total.ToStr() // Output: Big number: 30
var i := 0
while (i < 10) do
i += 1
if (i % 2 <> 0) then
continue // Skips the print for odd numbers
end
if (i > 8) then
break // Exits the loop early
end
print 'Processing even number: ' + i.ToStr()
end
// Output: 2, 4, 6, 8
var countdown := 3
repeat // This loop body always executes at least once.
print countdown.ToStr() + '...'
countdown -= 1
until (countdown = 0)
// Output: 3..., 2..., 1...Express is not just a high-level language; it provides the power of a low level languages like Pascal and C for performance and direct memory control. It features:
- Typed Pointers: Create pointers to any type, like
^Int32or^MyRecord. - Address-Of Operator: Use the
addr()intrinsic to get the memory address of any variable. - FPC-Style Indexing: Use the familiar
ptr[i]syntax to treat any pointer as an array. - Pascal-Style Dereferencing: Use the
ptr^syntax for direct dereferencing. - Pointer Arithmetic: Add offsets to pointers to manually traverse memory layouts.
Express modernizes Pascals record type, adopting features from languages like Go and Swift to make them more lightweight and powerful. Records are value types (copied on assignment) and are perfect for grouping data without the overhead of classes.
- Simpler record declaration: Define record types in a short simple manner on the fly
- Initializer Lists: Construct and assign to records with a clean, literal syntax.
- Destructuring Assignment: Unpack record fields into local variables in a single, readable line.
// A function can return an anonymous record type, similar to Go or TypeScript.
// And use list init for the return value.
func GetPoint(): (x,y:int)
Result := [100,200]
end
// Use destructuring assignment to unpack records into new variables.
var (px, py) := GetPoint()
print 'The point is (' + px.ToStr() + ', ' + py.ToStr() + ')'
// Output: The point is (100, 200)
// You can also assign to **any compatible** variables.
var fx, fy: float
(fx, fy) := GetPoint()Express is evolving. Here are some of the key features planned for the near future: Enums and Sets: For more expressive and safe code.
- Operator Overloading: Allowing user-defined types to work with standard operators.
- Properties: Class and record fields with custom getter/setter logic.
- Default function parameters with assign by name
- Strings are currently limited to Ansistring.
Express is designed to be a powerful, high-performance scripting companion for Free Pascal.
A simple API (TExpress) handles all the complexity of compilation and execution, making it trivial to embed into your FPC applications.
This allows your FPC application to dynamically run Express code, exchange variables, and call native functions.
Here is a minimal example showing how to run a script, share a variable, and get a result back.
program HostApp;
{$APPTYPE CONSOLE}
uses
SysUtils, Variants,
xpr.Express;
var
Script: TExpress;
ScriptCode: string;
ScriptResult: Variant;
MyFPCVar: Int32 = 10; // A native FPC variable we want to share with the script.
begin
Script := TExpress.Create;
try
// 2. Bind the native FPC variable to the script as 'SharedVar'.
Script.Bind.AddVar('SharedVar', @MyFPCVar, Script.Context.GetType(xtInt32));
// 3. Define a script that uses the shared variable.
ScriptCode :=
'print "SharedVar from FPC was " + SharedVar.ToStr();'+ LineEnding +
'SharedVar := SharedVar + 5;' + LineEnding +
'var Result := 10000';
// 4. Compile and run the script.
ScriptResult := Script.RunCode(ScriptCode);
// 5. Print the results.
WriteLn('Script said: ', ScriptResult.AsString);
WriteLn('FPC variable is now: ', MyFPCVar);
finally
Script.Free;
end;
end.Further more you can read any global variable output by name as such:
WriteLn(Script.GetVar('x'));
WriteLn(Script.GetVar('y'));
WriteLn(Script.GetVar('Result'));