Getting Started

Explore, learn, and contribute.


Before you get started, checkout Objeck's class libraries. Over the years, the language has become a collection of libraries to get stuff done. See what's available and read on.

Installation

Objeck can be installed using the Windows installer or compressed archives. Supported platforms are Windows (x64), macOS (ARM64) and Linux (x64 and AMD64).

In order to compile programs outside of the bin directory the OBJECK_LIB_PATH environment variable must be set. When this variable is set all library files must be in the directory specified. If the Windows installer is used these variables will be automatically when you launch the Objeck command prompt.

Windows

To manually setting the environment paths in Windows:

set OBJECK_LIB_PATH=C:\Users\[account]\objeck-lang\lib
set PATH=%PATH%;C:\Users\[account]\objeck-lang\bin;C:\Users\[account]\objeck-lang\lib\sdl

macOS and Linux

To manually setting the environment paths in macOS and Linux:

export PATH=$PATH:/home/[account]/objeck-lang/bin
export OBJECK_LIB_PATH=/home/[account]/objeck-lang/lib

Simple Compile & Execute

obc -src hello.obs
obc -src 'C:\Program Files\objeck-lang\examples\encrypt.obs' -lib encrypt -dest encrypt
obr hello.obe

Compile/execute for code that has library dependencies:

obc -src ..\examples\xml_path.obs -lib xml

Hello World!

class Hello {
  function : Main(args : String[]) ~ Nil {
    "Hello World"->PrintLine();
    "Καλημέρα κόσμε"->PrintLine();
    "こんにちは 世界"->PrintLine();
  }
}

Save the code above in file called "hello.obs". To compile the code execute the following command:

obc hello

This command compiles the source file "hello.obs" into the target file "hello.obe" and links in any dependent libraries. To run the program execute the command:

obr hello

Video Tutorials

To assist with getting starting with Objeck video tutorials are available. The videos cover a range of topics and are updated periodically.

Compiling Code

The Objeck compiler produces two types of binaries. The first is a VM executable and the second is a library. Libraries can be linked to executables by passing the names of libraries to the compiler when compiling an executable. As a naming convention executables end with *.obe while shared libraries end with *.obl.

Here are a few examples. The first example compiles and runs a program that processes XML. For this program we link in the collections and XML parsing libraries.

obc -src examples/2_xml.obs -lib xml
obr xml_path.obe

The next example compiles and runs program that uses the encryption library.

obc -src examples/7_encrypt.obs -lib encrypt
obr encryption.obe
TABLE 1 – COMPILER OPTIONS
Option Description
-src path to source files delimited by commas
-lib list of linked libraries separated by commas
-ver displays the compiler version
-tar ttarget type 'lib' for linkable library or 'exe' for executable default is 'exe'
-opt optimization level, s0 thru s3 being most aggressive; default is s3
-dest binary output filename
-asm emits a human readable debug byte assembly file
-alt use C like syntax instead of UML like default
-debug compile with debug symbols (must be last argument)
-strict exclude default system libraries and provide them manually

obr --GC_THRESHOLD=128m hello.obe
TABLE 2 – PREPENDED VM OPTIONS
Option Description
--OBJECK_STDIO if set, STDIO is binary on Windows, default is false
--GC_THRESHOLD initial garbage collection threshold (i.e.; 128m), default is 1/16 of host memory

Basics

Let's first look at literals, variables, comments, control flow and basic data types.

Literals and variables

Literals are defined as they are in most programming languages. In Objeck literals are treated as objects and may have methods associated with them.

'\u00BD'->PrintLine();
13->Min(3)->PrintLine();
3.89->Sin()->PrintLine();
0xCB0->PrintLine(); # hex
-0b1101->PrintLine(); # bin
052->PrintLine(); # oct
"Hello World"->Size()->PrintLine();

Here are a few examples of variable declarations and assignments. Variable types can be explicitly defined or implicitly inferred through assignments or casts. If a variable's type is inferred it cannot be redefined later in the program however it can be cast.

a : Int;
b : Float := 13.5;
c := 7.25; # type inferred as Float
d := (b * 2)->As(Int); # type inferred as Int

Also support for multi-variable assignment and negation.

a, b : Float := 13.5;
b += -a * 13;
"{$b}"->PrintLine();
a := b := c := 3.15;
"{$a} {$b} {$c}"->PrintLine();
d := a < b ? "less" : "greater"; # conditional operator

Types can also be described and referenced using aliases.

function : Multiplier(a : FloatRef, b : FloatRef) ~ \Func->Proc {
  return \Func->Proc : (c) =>  a * b * c;
  }
}

alias Func {
  Proc : (FloatRef) ~ FloatRef
}
TABLE 3 – DATA TYPES
Type Description
Char Unicode character value (4-bytes POSIX, 2-bytes Windows)
Char[] Unicode character array (4-bytes POSIX, 2-bytes Windows)
Bool Boolean value (1-byte)
Bool[] Boolean array (1-byte)
Byte 1-byte integer value
Byte[] 1-byte integer array
Int 8-byte integer value (4-bytes on file system)
Int[] 8-byte integer array (4-bytes on file system)
Float 8-byte decimal value
Float[] 8-byte decimal array
Object Reference to an abstract data type (8-bytes)
Object[] Array of abstract data types (8-bytes)
Function Functional reference (8-byte, paring)

Comments

Comments may span a single line or multiple lines. In addition, comments for bundles, classes, interfaces and functions/methods may be used to produce code documentation. Please refer to the project website for additional information about generating documentation from your code.

flag := false; # single line comment
#~
multiline comment. flag above
may be set to true or false
~#

Control Flow

As with most languages Objeck supports conditional expressions and control flow logic. One nuance is that conditional statements end with semi-colons.

If/else

An "if/else" statement is a basic control statement.

number := Console->ReadLine()->ToInt();
if(number <> 3) {
 "Not equal to 3"->PrintLine();
} else if(number < 13) {
 "Less than 13"->PrintLine();
} else {
 "Some other number"->PrintLine();
};

Select

Select statements can be used to efficiently map integer and enum values to blocks of code.

select(c) {
 label Color->Red: { "Red"->PrintLine(); }
 label Color->Green: { "Green"->PrintLine(); }
 label Color->Purple: { "Purple"->PrintLine(); }
 other: { "Another color"->PrintLine(); }
};

select(n) {
 label 9:
 label 19: { n->PrintLine(); } 
 label 0x1b: { (3 * 9 = n)->PrintLine(); }
};

The language supports for the following looping statements

Do/While

A "do/while" loop is a basic post-test loop.

i := 0;
do {
 i->PrintLine();
 i += 1;
} while(i <> 10);

For

A "for" loop is a controlled loop with an explicated control expression.

location := "East Bay";
for(i := 0; i < location->Size(); i += 1;) {
 location->Get(i)->PrintLine();
};

Each

An "each" loop is a controlled loop that iterates though all elements in an array or collection. Items can also be iterated in reverse using the "reverse" keyword.

area_code := Int->New[3];
area_codes := Int->New[3];
area_codes[0] := 5;
area_codes[1] := 1;
area_codes[2] := 0;

sum := 0;
each(i : area_codes) {
 sum += area_codes[i];
};
sum->PrintLine();

Reverse

The "reverse" keyword iterates items in reverse.

numbers := [1, 2, 3, 4, 5];
reverse(i : numbers) {
	numbers[i]->PrintLine();
};

An "each" or "reverse" loop can also bind to a variable using the assignment operator instead of a colon.

strings := Generic.Vector->New()<String>;
strings->AddBack("abd");
strings->AddBack("efg");
strings->AddBack("hij");
strings->AddBack("lmn");

reverse(string := strings) {
	string += ", 123";
	each(letter := string) {
		letter->PrintLine();
	};
};

Leaving

The "leaving" keyword is used to execute a block of code at the end of method/function


function : WriteSocket(bytes : Byte[]) ~ Nil {
	client := TCPSocket->New("localhost", 4660);
	leaving {
		client->Close();
	};

	if(client->IsOpen()) {
		size := bytes->Size();
		size_str := size->ToString();
		client->WriteString(size_str);
		client->WriteBuffer(0, size, bytes)->PrintLine();
	};
}

Operators

There’s support for logical, mathematical and bitwise operators. Operator precedence from weakest to strongest is: logical, [+, -] and [*, /, %, <<, >>, and, or, xor]. Operators of the same precedence are evaluated from left-to-right.

TABLE 4 - LOGICAL

Operator Description
&And
|Or
=Equal
<>Not equal and unary not
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal

TABLE 5 - MATHEMATICAL

Operator Description
+Add
+=Add to variable
++Increment variable
-Subtract
-=Subtract from variable
--Decrement variable
*Multiply
*=Multiply to variable
/Divide
/=Divide from variable
%Modulus

TABLE 6 – BITWISE

Operator Description
<<Shift left
>>Shift right
andBitwise and
orBitwise or
xorBitwise xor

class Luhn {
  function : IsValid(cc : String) ~ Bool {
    isOdd := true; oddSum := 0; evenSum := 0;
    for(i := cc->Size() - 1; i >= 0; i -= 1;) {
      digit : Int := cc->Get(i) - '0';
      if(isOdd) {
        oddSum += digit;
      } else {
        evenSum += digit / 5 + (2 * digit) % 10;
      };
      isOdd := isOdd <> true;
    };
    return (oddSum + evenSum) % 10 = 0;
  }

  function : Main(args : String[]) ~ Nil {
    IsValid("49927398716")->PrintLine();
    IsValid("49927398717")->PrintLine();
    IsValid("1234567812345678")->PrintLine();
    IsValid("1234567812345670")->PrintLine();
  }
}

Code fragment from the Base64 encoding class

# Primary encoding loop
r := ""; i : Int; a := 0;
for(i := 0; i < end; i += 3;) {
  a := (data[i] << 16) or (data[i+1] << 8) or (data[i+2]);
  r->Append( lut[0x3F and (a >> 18)] );
  r->Append( lut[0x3F and (a >> 12)] );
  r->Append( lut[0x3F and (a >> 6)] );
  r->Append( lut[0x3F and a] );
};

The language has support for dynamically allocated arrays, Unicode strings and various containers.

Arrays

Arrays can hold an indexed list of like types. Arrays are dynamically allocated from the heap and their memory managed by the garbage collector. The runtime system supports bounds checking and will cease execution (generating a stack trace) if array bounds are violated.

Allocating and indexing arrays

# allocate Int array
boxes := Int->New[2,3];
boxes[0,0] := 2;
boxes[0,1] := 4;
boxes[0,2] := 8;
boxes[1,0] := 1;
boxes[1,1] := 2;
boxes[1,2] := 3;
dims := boxes->Size(); # get the dimensions
dims[0]->PrintLine(); # dimension 1
dims[1]->PrintLine(); # dimension 2

# create some strings an iterate over them
directions := String->New[4];
directions[0] := "North";
directions[1] := "South";
directions[2] := "East";
directions[3] := "West";
each(i : directions) {
  directions[i]->PrintLine();
};

Strings

Character strings are a collection of Unicode characters backed by the String class. The string class supports a number of operations such as insert, find, substring, type parsing (i.e. String to Float), etc. Variable values can also be inlined into string literals. Strings can be converted into character arrays and UTF-8 byte arrays. The $ character can be used to interpret strings verbatim, ignoring escape characters.

name := "DJ";
name += ' ';
name += "Premier";
name += $"bell \b \b bell";
name->PrintLine();
name->SubString(2)->PrintLine();
name->Size()->PrintLine();

Code fragment from a sundial program

"Hour\t\tsun hour angle\t\tdial hour line angle from 6am to 6pm"->PrintLine();
for(h := -6; h <= 6; h+=1;) {
  hra := 15.0 * h;
  hra -= lng - ref;
  hla := (slat* (hra*2*Float->Pi()/360.0)->Tan())
    ->ArcTan() * 360.0 / (2*Float->Pi());
  "HR={$h}\t\tHRA={$hra}\t\tHLA={$hla}"->PrintLine();
};

Enums and Constants

Enums are enumerated named constant values. Enums values are sequential and start from an offset. Constants, like enums, are named constant values however each value can be uniquely assigned.

enum Color {
 Red,
 Blue,
 Green
}

class Character {
  @move : Character->Direction;

  enum Direction {
   Left := -100,
   Right,
   Up,
   Down
  }
}
	
consts Products {
 PipBoy := 101,
 ValutSuit := 111,
 Laser := 675
}

Properties

Property name/value pairs can be set programmatically or external via a configuration file. The configuration file must be named config.prop and reside in the same that the directory that the program is executed from.

ren=stimpy
cola=pepsi

Here is an example using an external proprieties file and creating new a new property.

Runtime->GetProperty("ren")->PrintLine();
Runtime->GetProperty("cola")->PrintLine();

Runtime->SetProperty("hello", "world");
Runtime->GetProperty("hello")->PrintLine();

Classes and Interfaces

A class is an abstract collection of data with related operations. An interface is a set of operations (a contract) that implementing classes must honor. In Objeck, interfaces are public and classes may be public or private. Member variables are private and must accessed using "getters" and "setters". Classes may also define enum types.

Below is a code example of that demonstrates many of these concepts. There are code examples for other features such as reflection, serialization and more.

interface Registration {
  method : virtual : public : GetColor() ~ String;
  method : virtual : public : GetMake() ~ String;
  method : virtual : public : GetModel() ~ String;
}
enum EngineType {
  Gas := 200,
  Hybrid,
  Electric,
  Warp
}

class : public : Vehicle {
  @wheels : Int;
  @color : String;
  @engine_type : EngineType;
  
  New(wheels : Int, color : String, engine_type : EngineType) {
    @wheels := wheels;
    @color := color;
    @engine_type := engine_type;
 }
 
 method : public : GetColor() ~ String {
   return @color;
 }
 
 method : public : GetEngine() ~ EngineType {
   return @engine_type;
 }
}
 

class : public StarShip from Vehicle implements Registration {
  New() {
    Parent(13, "Metal Fuschia", EngineType->Warp);
  }
 
  method : public : GetMake() ~ String {
    return "Excelsior";
  }
 
  method : public : GetModel() ~ String {
    return "NX-2000";
  }

  method : public : EchoDescription() ~ Nil {
    "Partying with the Borg, they brought drinks!"->PrintLine();
  }
}

class : public Pinto from Vehicle implements Registration {
  New() {
    Parent();
  }
 
  method : public : GetMake() ~ String {
    return "Ford";
  }
  
  method : public : GetModel() ~ String {
    return "Pinto";
  }
}

class : public VehicleTest {
  function : Main(args : String[]) ~ Nil {
    pinto := Pinto->New();
    star_ship := StarShip->New();
    type_of := pinto->TypeOf(Vehicle);
    type_of->PrintLine();
    type_of := star_ship->TypeOf(Vehicle);
    type_of->PrintLine();
    type_of := pinto->TypeOf(StarShip);
    type_of->PrintLine();
    registration := star_ship->As(Registration);
    registration->GetMake()->PrintLine();
    registration->GetColor()->PrintLine();
    enterprise := registration->As(StarShip);
    enterprise->EchoDescription();
  }
}

Anonymous classes can be created that define "inline" required interface methods or functions. External variables may be referenced within an anonymous class if they’re passed as references to the class constructor.

interface Greetings {
  method : virtual : public : SayHi() ~ Nil;
}

class Hello {
  function : Main(args : String[]) ~ Nil {
    hey := Base->New() implements Greetings {
      New() {}
      method : public : SayHi() ~ Nil {
        "Hey..."->PrintLine();
      }
    };
 
    howdy := Base->New() implements Greetings {
      New() {}
      method : public : SayHi() ~ Nil {
        "Howdy!"->PrintLine();
      }
    };
  }
}

Collections

In addition, to arrays the language supports various generic collections such as Vectors, Lists, Maps, Stack, Queues, etc. To learn more about the collections classes please check out the API documentation. In order to use these classes you must "use" the collections bundle or use a fully qualified name consider the following lines of code:

use Collection;

genres := Vector->New()<String>;	
genres := Collection.Vector->New()<String>;

Vectors

Vectors are arrays that can be dynamically resized. They support fast indexing, iterating and appending of values. In order to improve performance memory for vectors is preallocated.

genres := Vector->New()<String>;
genres->AddBack("Hip hop");
genres->AddBack("Classical");
genres->AddBack("Jazz");
genres->AddBack("Rock");
genres->AddBack("Folk");
each(i : genres) {
  genres->Get(i)->PrintLine();
};

Lists

Lists are a collection of linear linked nodes. They support the fast insertion and removal of values. Memory for nodes are allocated on-demand however nodes may not be directly indexed as with Vectors.

artists := List->New()<String>;
artists->AddBack("Hendrix");
artists->AddFront("Beck");
# move cursor back for middle insertion
artists->Back();
artists->Insert("Common");
# move cursor to start of list
artists->Rewind();
# iterate over values
while(artists->More()) {
  artists->Get()->PrintLine();
  artists->Next();
};

Maps and Hashes

Map and Hash classes manage key/value pairs. Maps manage values in tree and allocate memory on demand. Maps are slower than hashes however manage memory better. Hashes use keys as indices into arrays and support fast insertion and deletion at the cost of memory. Maps are ordered while Hashes are not.

area_codes := Map->New()<IntRef, String>;
area_codes->Insert(510, "Oakland");
area_codes->Insert(415, "San Francisco");
area_codes->Insert(650, "Palo Alto");
area_codes->Insert(925, "San Jose");

area_codes->Find(510)->PrintLine();
codes := area_codes->GetValues()<String>;
each(code := codes) {
  code->PrintLine();
};

Sets

Sets are key are a collection of ordered keys.

area_codes := Set->New()<IntRef>;
area_codes->Insert(510);
area_codes->Insert(415);
area_codes->Insert(650);
area_codes->Insert(925);

area_codes->Has(510)->PrintLine();
codes := area_codes->GetKeys()<IntRef>;
each(code := codes) {
  code->PrintLine();
};

Queues

Queues support adding items to front and back of a stack-like structure.

queue := Collection.Queue->New();
queue->AddFront(33.3);
queue->AddBack(99.9);
queue->AddFront(66.6);
queue->Front()->PrintLine();

Closures and Lambda Expressions

First class functions allow programmers to use functions like variables. They can be passed into methods/functions, have methods/functions return functions and be assigned to variables. In addition, Objeck supports closures and lambda expressions.

class FofG {
  @f : static : (Int) ~ Int;
  @g : static : (Int) ~ Int;

  function : Main(args : String[]) ~ Nil {
    compose := Composer(F(Int) ~ Int, G(Int) ~ Int);
    compose(13)->PrintLine();
  }

  function : F(a : Int) ~ Int {
    return a + 14;
  }
 
  function : G(a : Int) ~ Int {
    return a + 15;
  }
 
  function : native : Compose(x : Int) ~ Int {
    return @f(@g(x));
  }

  function : Composer(f : (Int) ~ Int, g : (Int) ~ Int) ~ (Int) ~ Int {
    @f := f;
    @g := g;
    
    return Compose(Int) ~ Int;
  }
}

Lambda expressions allow programmers to create anonymous functions and assign references to these functions. Such functions can be used for inline calculations and programming logic. In addition to expressions, the language supports lambdas expressions that contain 'if' and 'select' statements and return a value.

vector := Vector->New()<Func2Ref <FloatRef, FloatRef> >;
# store functions in collections
vector->AddBack(Func2Ref->New(\Func->Double : (v) 
  =>  v * v)<FloatRef, FloatRef>);
# new function from preexisting function at run-time
vector->AddBack(Func2Ref->New(\Func->Double : (v) 
  => Float->SquareRoot(v->Get()))<FloatRef, FloatRef>);
# process collection
each(i : vector) {
  # return value of other functions and pass argument to other function
  Show(vector->Get(i)<Func2Ref>->Get()<FloatRef, FloatRef>);
};

Objeck supports closures by allowing the value of local variables to captured and used in later invocations of code.

funcs := Vector->New()<FuncRef<IntRef> >;
# create 10 functions capturing i     
for(i := 0; i < 10; i += 1;) {
  funcs->AddBack(FuncRef->New(\() ~ IntRef : () => i * i)<IntRef>);
};
# invoke functions
each(i : funcs) {
  func := funcs->Get(i)->Get()<IntRef>;
  func()->Get()->PrintLine();
};

Lambda parameters types can also be inferred using the modified syntax below.

map := Map->New()>IntRef, String>;
map->Insert(1, "One");
map->Insert(3, "Three");
map->Insert(5, "Five");
map->Insert(7, "Seven");
map->Insert(9, "Nine");
map->Each(\^(k, v) => "{$k}: {$v}"->PrintLine());

Threads

Threads can be created by subclassing the Thread class, implementing the Run(..) method, and then calling Execute(..) to start the thread. Multiple threads can be joined together using the Join(..) method. The ThreadMutex class is used to protect critical sections using the "critical" keyword for code blocks.

The code snippet below shows how to lock a critical section. Here is a complete code example demonstrating the concepts above.

# lock cache for while we search or insert content
critical(@content_mutex) {
	# found in cache
	found := @content_cache->Find(path_name);
	if(found <> Nil) {
		content := found->Get();
	}
	# not found, add to cache
	else {
		content := System.IO.Filesystem.FileReader->ReadBinaryFile(path_name);
		if(content <> Nil) {
			@content_cache->Insert(path_name, ByteArrayRef->New(content));
		};
	};
};

JIT Compilation

Method and function may be JIT compiler to speed up their execution. The Methods/functions are compiled the first time they are called and subsequent calls execute the per-compiled code. To direct the runtime to JIT compile code for a given function/method use the "native" keyword.

Good candidates for JIT compilation are computationally expensive blocks of code with lots of loops. Code that contains numerous floating-point calculations may also benefit from being sped up.

As an example, observe the execution time of this prime number program with and without the use of the native keyword.

class FindPrime {
   function : Main(args : System.String[]) ~ Nil {
     Run(100000);
   }
 
   function : native : Run(topCandidate : Int) ~ Nil {
     candidate := 2;
     while(candidate <= topCandidate) {
       trialDivisor := 2;
       prime := 1;

       found := true;
       while(trialDivisor * trialDivisor <= candidate & found) {
         if(candidate % trialDivisor = 0) {
           prime := 0;
           found := false;
         }
         else {
           trialDivisor++;
         };
       };

       if(found) {
         candidate->PrintLine();
       };
       candidate++;
     };
   }
}

Creating Libraries

Creating class libraries is pretty straightforward. Put one or more classes into one or more files and compile the code with the “-tar lib” option. Code for libraries cannot contain a “main” function.

class Pair {
  @key : Compare;
  @value : Base;
 
  New(key : Compare, value : Base) {
    @key := key;
    @value := value;
  }
 
  method : public : GetKey() ~ Compare {
    return @key;
  }
 
  method : public : Get() ~ Base {
    return @value;
  }
}

To compile the code type the following:

obc -src pair.obs –tar lib -dest pair.obl

To use the library in program type the following:

obc -src points.obs –lib pair

Activities

Here's a list of common programming tasks implemented in Objeck.

File Read & Write

Reads and prints a file

content := System.IO.Filesystem.FileReader->ReadFile("in.txt");
content->PrintLine();
System.IO.Filesystem.FileReader->WriteFile("out.txt", content);

HTTPS Client & JSON Parsing

Read JSON from a timeserver and parser the date/time

get_buf := Web.HTTP.HttpClient->QuickGet(Web.HTTP.Url->New("http://worldtimeapi.org/api/ip"));
if(get_buf <> Nil) {
	json_elem := Data.JSON.JsonParser->TextToElement(get_buf->ToString());
	if(json_elem <> Nil) {
		date_time_elem := json_elem->Get("datetime");
		if(date_time_elem <> Nil) {
			date_time := ParseTime(date_time_elem->GetString());
			if(date_time <> Nil) {
				date_time->ToString()->PrintLine();
			};
		};
	};
};

Parse XML

Parse and XML document and access elements

class Pair {
in := "";
in += "";
in += "
"; in += ""; in += "Invisibility Cream"; in += "14.50"; in += "Makes you invisible"; in += ""; in += ""; in += "Levitation Salve"; in += "23.99"; in += "Levitate yourself for up to 3 hours per application"; in += ""; in += "
"; in += "
"; in += ""; in += "Blork and Freen Instameal"; in += "4.95"; in += "A tasty meal in a tablet; just add water"; in += ""; in += ""; in += "Grob winglets"; in += "3.56"; in += "Tender winglets of Grob. Just add water"; in += ""; in += "
"; in += "
"; parser := XmlParser->New(in); if(parser->Parse()) { # get first item results := parser->FindElements("/inventory/section[1]/item[1]"); if(results <> Nil) { Standard->Print("items: ")->PrintLine(results->Size()); }; # get all prices results := parser->FindElements("/inventory/section/item/price"); if(results <> Nil) { each(i : results) { element := results->Get(i); element->GetContent()->PrintLine(); }; }; # get names results := parser->FindElements("/inventory/section/item/name"); if(results <> Nil) { Standard->Print("names: ")->PrintLine(results->Size()); }; };

Alarm

Sets alarms for 3 and 5 seconds

System.Time.Alarm->New() {
	New() {
		Parent(1500);
	}
	
	method : public : Ring(b : Base) ~ Nil {
		v := b->As(IntRef)->Get();
		"Foo {$v}!"->PrintLine();
	}
}->Start(IntRef->New(7));

System.Time.Alarm->New() {
	New() {
		Parent(3000);
	}
	
	method : public : Ring(b : Base) ~ Nil {
		str := b->As(String);
		"Bar {$str}."->PrintLine();
	}
}->Start("It's Math...");

System.Time.Alarm->New() {
	New() {
		Parent(5000, true);
	}
	
	method : public : Ring(b : Base) ~ Nil {
		"!Soap!"->PrintLine();
	}
}->Start(Nil);

"Thread started..."->PrintLine();
System.Concurrency.Thread->Sleep(30000);
"Done."->PrintLine();

Regular Expression

Sample RegEx queries

class Pair {
string := "I am a string";
# exact match
regex := RegEx->New(".*string");
if(regex->MatchExact(".*string")) {
	"ends with 'string'"->PrintLine();
};
# replace all
regex := RegEx->New(" a ");
regex->ReplaceAll(string, " another ")->PrintLine();

regex := RegEx->New("((4\\.[0-3])|(2\\.[0-3]))");
found := regex->Find("Mozilla/4.0");
each(i : found) {
	found->Get(i)->ToString()->PrintLine();
};

regex := RegEx->New("1[3-5]?9");
match := regex->Match("149");
if(match <> Nil) {
	match->PrintLine();
};

regex := RegEx->New("([a-z]|[0-9]|-)+");
match := regex->Match("my_title_here");
if(match <> Nil) {
	match->PrintLine();
};

Web Client

Fetched a webpage and parses the URLs

class Pair {
input := args[0];

output := HttpsClient->New()->QuickGet(Web.HTTP.Url->New(input))->ToString();
output_len := output->Size();
"URL: {$input}, read: {$output_len} character(s)"->PrintLine();

"running regex..."->PrintLine();		
expr := "(href|HREF|src|SRC)=(\"|')(http://|https://|/)?((\\w|\\d|-|_)+(\\.|/)?)+(\\?(\\w|\\d|-|_)+=(\\w|\\d|-|_)+)?(&(\\w|\\d|-|_)+=(\\w|\\d|-|_)+)?(\"|')";

found := RegEx->New(expr)->Find(output);
"---"->PrintLine();
each(i : found) {
	found->Get(i)->ToString()->PrintLine();
};

Web Server

HTTPS web sever for user authentication

class Pair {
use Web.HTTP.Server;

class Test {
	function : Main(args : String[]) ~ Nil {
		WebServer->ServeSecure("config/passwd_config.json");
	}
}

class RequestHandler from HttpsRequestHandler {
	New() {
		Parent();
	}
	
	method : ProcessGet(request : Request, response : Response) ~ Bool {
		# authenticated
		if(request->HasParam("unauthenticated")) {
			response->RemoveCookie("authenticated");
		}
		# unauthenticated
		else if(request->HasCookie("authenticated")) {
			return response->SetForwardAlias("auth.html");
		}
		# check name and password
		else if(request->HasParam("name") & request->HasParam("password")) {
			name := request->GetParam("name");
			password := request->GetParam("password");
			if(name->Equals("mega") & password->Equals("1234")) {
				response->AddCookie(Web.HTTP.Cookie->New("authenticated", "true"));
				return response->SetForwardAlias("auth.html");
			};
		};
		
		# show login page
		return response->SetForwardAlias("index.html");
	}

	method : ProcessPost(request : Request, response : Response) ~ Bool {
		return false;
	}
}

Programming Environment

Tools to make Objeck programming easier to use.

REPL Shell

The interactive read–eval–print loop (REPL) shell allows users to quickly test their code without writing a complete program or compiling source files. REPL commands can be listed by typing 'help' or 'h.' Future releases of Objeck will include enhancements to the shell, such as the ability to load and save source files and enhanced editing capabilities.

Debugger

The command line debugger allows to inspect the runtime behavior of a program. To use the debugger a program must first be compiled with debug symbols by passing the "-debug" option to the compiler. For a working example let's use the program in "Figure 1". We'll start by saving the program to a text file call "luhn.obe"

To compile the code type the following:
obc -src luhn.obs -debug
For this example let’s assume the source file is in the same location as the executable. To start the debugger type the following:
obd -bin luhn.obe -src_dir .
Let’s first set a breakpoint on line 17 and run the program.
> b 17
added breakpoint: file='luhn.obs:17'
> r
break: file='luhn.obs:17', method='Luhn->Main(..)'
Next let’s list the code around the breakpoint.
> l
   12: };
   13: return (oddSum + evenSum) % 10 = 0;
   14: }
   15:
   16: function : Main(args : String[]) ~ Nil {
=> 17:    IsValid("49927398716")->PrintLine();
   18:    IsValid("49927398717")->PrintLine();
   19:    IsValid("1234567812345678")->PrintLine();
   20:    IsValid("1234567812345670")->PrintLine();
   21: }
   22: }
Now let’s step into the “IsValid” function:
> s
> break: file='luhn.obs:2', method='Luhn->IsValid(..)'
> l
   1: class Luhn {
=> 2: function : IsValid(cc : String) ~ Bool {
   3:   isOdd := true; oddSum := 0; evenSum := 0;
   4:   for(i := cc->Size() - 1; i >= 0; i -= 1;) {
   5:     digit : Int := cc->Get(i) - '0';
   6:     if(isOdd) {
   7:       oddSum += digit;
   8:     } else {
   9:       evenSum += digit / 5 + (2 * digit) % 10
   10:    };
> n
Let’s print out the value for “cc”.
> p cc
print: type=System.String, value="49927398716"
Now let’s break on line 9 and print the value for “evenSum”.
> b 9
added breakpoint: file='luhn.obs:9'
> c
> break: file='luhn.obs:9', method='Luhn->IsValid(..)'
> n
> break: file='luhn.obs:11', method='Luhn->IsValid(..)'
> p evenSum
print: type=Int, value=2
Lastly, let’s print out the call stack before exiting.
> stack
stack:
 frame: pos=2, class=Luhn, method=IsValid(o.System.String), file=luhn.obs:5
 frame: pos=1, class=Luhn, method=Main(o.System.String*), file=luhn.obs:17
> q
breakpoints cleared.
goodbye.
TABLE 7 – DEBUGGER COMMANDS
Command Description Example
[b]reak sets a breakpoint b luhn.obs:17
breaks shows all breakpoints
[d]elete deletes a breakpoint d luhn.obs:17
clear clears all breakpoints
[n]ext moves to the next line within the same method/function with debug information
[s]tep moves to the next line with debug information
[j]ump jumps out of an existing method/function and moves to the next line with debug information
args specifies program arguments "'First' 'Second'"
[r]un runs loaded program
[m]emory shows used memory and garbage collection threshold memory: allocated=0.32k, threshold=3,072k
[p]rint prints the value of an expression, along with metadata print: type=System.String, value="49927398716"
[l]ist lists a range of lines in a source file or the lines near the current breakpoint
[i]nfo displays the variables for a class program executable: file='hiya.obe' current file='fred.obs:9', method='Luhn->IsValid(..)'
[s]tack displays the call stack p cc
bin loads a new executable binary
src_dir specifies a new source directory
[q]uit exits a given debugging session

Generating a Portable Runtime

A portable runtime environment can be created using the "obb" command

Encrypt and decrypt example:

obb -src_file encrypt_7.obe -to_dir /tmp -to_name encrypt

Optionally, application specific resources can be copied using the "-src_dir" option.

Here's the step-by-step process for Windows:
1. Create an empty working directory for your app
2. Copy the code below to a file called 'hello.obs' and save it to the working directory

class Native {
  function : Main(args : String[]) ~ Nil {
    "Hello World!"->PrintLine();
  }
}
3. Compile 'hello.obs' and verify the existence of the compiled program 'hello.obe'.
obc hello.obs
4. Create the native and executable
obb -src_file hello.obe -to_dir . hello
5. Test the app binary 'hello'
cd hello && hello && cd ..

Examples

Please refer to the list of applications below that have been developed for more in-depth coding examples

  1. Gaming and graphics: 2D Side Scroller, Vibrating Rectangles, Lazy Foo SDL2 Examples
  2. AI: Neural Network, Tic-Tac-Toe
  3. Text Processing: XML[library], JSON[library], CSV[library]
  4. Web: RSS Reader, HTTPS Server
  5. Compilers: Tiny Language, Brain F**k, Lisp
  6. Benchmarks: Computer Language Benchmarks Game