Language Tour

A Very Simple Program 

Let’s start with a simple one-line program.

print('I can fly!');

As you can probably guess, it prints this to the browser:

I can fly!

How It Works

Let’s take a look at each part.

print('I can fly!');   // "print" is the "function", or command.
^^^^^

print('I can fly!');   // A text "string" surrounded by single
      ^^^^^^^^^^^^     // quotes (which are not printed).

print('I can fly!');   // Our string is passed into the function by
     ^            ^    // surrounding it with parentheses.

print('I can fly!');   // Every statement ends in a semicolon, like
                   ^   // a period at the end of a sentence.

JargonIn the correct terminology, we’d say we are “calling the 'print' function, with the argument 'I can fly!'”

Printing 

The print function is like a guidepost to help you make sure your code is on track. It’s mainly for you as the developer to experiment and debug.

It’ll also take a variety of data types and display them in a readable format.

To send HTML to the browser, you will use methods in the Web module, as described in How to Create a Basic Web App.

Readability Checker 

Messy code can be frustrating and stressful to work with. It also creates places where nasty bugs can hide.

To help you write clean code, OWL includes a Readability Checker. It provides instant feedback about how to format your code so that it’s easy to read and work with.

Spacing is especially important!

// NO :(
treeHeight=(10+5)~'meters';

// YES :)  Cleaner spacing
treeHeight = (10 + 5) ~ 'meters';

See Readability Checker for more information.

Comments 

A comment is a line starting with //. It’s not executed as part of the program. It can appear on a line by itself, or at the end of a code line.

Comments are useful for a few things:

Explaining code. Explain parts of the program to help any future reader (including you!) modify it later.

// Apply the Zuckerdorf friendship formula to determine how
// much a friend this person is.
let friendLevel = (numFriends * 1.2345) + 42;

You don’t need a comment if the code is self-explanatory.

// Set number of apples  --> NO, this is redundant :(
let numApples = 1;

Disabling code. Temporarily remove lines of code while debugging.

// showWidgets(widgets);

TODO's. Record “To Do” reminders for later.

// TODO: add in the rest of the seven dwarves
let dwarves = ['Sleepy', 'Sneezy', 'Doc'];

Multiline Comments

Surrounding text with /// creates a multi-line comment block.

///
    TODO:
    - update header styles
    - fix profile bug
    - add About page
///

TipComments are important, but it’s also possible to have too many of them, which can get in the way of reading the actual code.

Instead of writing long comments, try to make the code itself more self-explanatory, by using clearer variable names, etc.

Variables 

A variable is like a box that contains information. You can give it a label and change what is inside.

Variables are declared (created) with the let keyword. A variable must be declared before it is used.

Data values are assigned to variables with =.

let location = 'Forest';
let numTrees = 1337;
let isEnchanted = true;

// OK :) 'location' was declared above, so it can be changed.
location = 'Dark Cavern';

// ERROR :(  'isSpooky' was never declared with 'let'.
isSpooky = false;

Case Matching

Variable and function names are case-sensitive. They must match exactly.

let numApples = 10;
numapples = 11;  // ERROR :(  'A' should be uppercase.

Print('Hello!');  // ERROR :(  'print' should be lowercase.

Basic Data Types 

There are 3 basic types of data:

// Numbers
42     // positive
-62    // negative
1.618  // fraction (aka 'floating point' or 'float')

// String (single-quotes)
'This is a string!'

// Flags
true
false

Initializing Variables 

New variables must always be given a value.

If you’re not sure what to use, pick the “empty” version of the type you need:

let myString = '';
let myNumber = 0;
let myFlag = false;

Variable Names 

Names for variables and functions are always written in camelCase format:

Examples:

user
userId
firstName
isDeleted
numRecords
httpVersion
userHasIphone
convertJsonToXml

Naming Tips

Coming up with good names is one of the most important (and difficult) skills in programming.

It’s always worth spending the extra time to think of names that clearly represent what they are labelling.

Guidelines:

Examples:

Bad           Better
------        ------------
usr           user
fname         firstName
nbrUsers      numUsers
cmntTxt       commentText
banned        isBanned
activeTime    numDaysActive
distance      numMiles
userList      users
active        activeUsers
a             (be more specific)
temp          (be more specific)

Strings 

Strings are always surrounded by single quotes.

'Hello, deer!';  // OK :)

"Hello, deer!";  // ERROR :(  Double-quotes are not valid.
Hello, deer!;    // ERROR :(  'Hello' is an unknown command.

Combining Strings

Strings can be combined with the stringy operator ~ (tilde).

let numLives = 9;
let firstName = 'Bob';
let lastName = 'Cat';
let fullName = firstName ~ ' ' ~ lastName;  //= 'Bob Cat'

print(fullName ~ ' has ' ~ numLives ~ ' lives.');
//= 'Bob Cat has 9 lives.'

Multi-line Strings

Multi-line strings are surrounded with triple-quotes '''.

The quotes must appear on separate lines from the inner text. Any surrounding whitespace and extra indentation will be automatically trimmed.

let poem = '''
    Roses are red,
    violets are blue,
    OWL is beautiful,
    and so are waffles.
''';

Special Characters

For convenience, backticks ` are converted into single-quotes.

'Bob`s a bobcat.';  //= 'Bob's a bobcat.'

Special characters can be used in strings, using backslash \.

'This is line 1.\nThis is line 2.'   // This is line 1.
                                     // This is line 2.

'Ann\tTeeter\t28'  //= 'Ann    Teeter   28'
'Bob\tCat\t34'     //= 'Bob    Cat      34'

Escapes

Use a backslash \ to make a character appear as-is.

'Bob\'s a bobcat.'  //=  'Bob's a bobcat.'

'Backslashes (\\) and forward slashes (/)'
//= Backslashes (\) and forward slashes (/)'

LockStrings 

Injection attacks are the #1 security vulnerability on the web.

OWL introduces LockStrings to mitigate this risk. Just prepend 'L' to any literal string to mark it as safe. (e.g. L'my string')

LockStrings can not be modified in ways that would expose them to insecure data (e.g. user input from a web form.)

Sensitive operations (printing HTML, database access, system calls, etc.) all require LockStrings as input.

// DANGEROUS >:(
// A value like this from attacker would add
// a statement that deletes your 'users' table.

let userId = '1';                     // normal value
let userId = '1; drop table users;';  // dangerous value

let sql = 'select * from users where userId = ' ~ userId;


// SAFE :)
// Create a LockString and attach the value as a placeholder.
let sql = L'select * from users where userId = {}';
sql.fill(userId);

// The Db module knows how to safely escape placeholder values.
let row = Db.selectRow(sql);

You can join two LockStrings together, even with placeholders.

let lock1 = L'Value 1: {}\n';
let lock2 = L'Value 2: {}\n';

let combined = lock1 ~ lock2;

print(combined.fill(['a', 'b']));
//= Value 1: a
//= Value 2: b

See the LockString class for a list of methods.

Math Operators 

Numbers can be modified with the familiar math operators + - * /.

Expressions can be grouped together with parenthesis ( ).

let numPosts = 5;
let numUpvotes = 7;
let numKarma = (numPosts * 2) + numUpvotes;

print('You have ' ~ numKarma ~ ' karma points!');
//= 'You have 17 karma points!'

Combined Operators

Operators can be combined with assignment =, as a convenient shortcut.

let numKarma = 5;

// Add 2 karma (the long way)
numKarma = numKarma + 2;  //= 7

// The short way
numKarma += 2;

message = 'Karma: ';
message ~= numKarma;
//= 'Karma: 7'

// More examples
a += 2;  // a = a + 2;
a -= 2;  // a = a - 2;
a *= 2;  // a = a * 2;
a /= 2;  // a = a / 2;

Comparison Operators 

Comparison operators compare two values, and return true or false.

let a = 1;

a == 1;  //= true  (equal)
a != 5;  //= true  (not equal)
a == 2;  //= false
a != 1;  //= false

a > -1;  //= true  (greater than)
a >= 1;  //= true  (equal or greater than)
a < 5;   //= true  (less than)
a <= 0;  //= false (equal or less than)

let name = 'Ann';
name == 'Ann';   //= true
name == 'Ann ';  //= false (extra space)
name != 'Bob';   //= true

TipTry not to confuse = (assign) with == (compare). OWL will try to prevent this common mistake, but be careful.

Conditional Statements 

if Statement

The if statement executes a block of code if the condition is true. A block of code is surrounded by curly braces { }.

if (condition) {
    // code to execute if condition is true
}

// Example
if (numCats > 100) {
    print('It`s a cat-astrophe!');
}

if / else Statement

The else statement executes a block of code if the condition is false.

if (condition) {
    // code to execute if condition is true
} else {
    // code to execute if condition is false
}
// Example
if (numGold >= priceOfHat) {
    print('You can buy the hat! :)');
} else {
    let needNumGold = priceOfHat - numGold;
    print('You need ' ~ needNumGold ~ ' more gold. :(');
}

else if Statement

The else and if statements can be chained together.

if (condition1) {
    // code to execute if condition1 is true
} else if (condition2) {
    // code to execute if condition1 is false and condition2 is true
} else {
    // code to execute if all conditions above are false
}
// Example
if (score == 0) {
    print('Did you even try? :(');
} else if (score > highScore) {
    print('You set a new high score! :)');
} else {
    print('Try again. :|');
}

Logical Operators 

Logical operators let you join comparison operators in two ways: && (and) and || (or).

// Both conditions must be true
if (ageYears < 13 && movieType == 'horror') {
    print('You should probably watch something else.');
}

// Any condition must be true
if (heightCm < 120 || isAfraidOfHeights) {
    print('Do not enter this ride!');
}

Methods 

Methods are functions that are attached to data. They are called with the dot . operator.

All built-in data types have methods.

let phrase = 'Hello, deer!  ';

phrase.trim();             //= 'Hello, deer!' (no extra spaces)
phrase.contains('deer');   //= true
phrase.length();           //= 14

Modules

A module is a bundle of related functions. Module names are always UpperCamelCase.

OWL comes with essential modules like Math, Date, and Web.

Math.floor(3.15);        //= 3 (rounded down)
Math.pi();               //= 3.1415926535898
Date.now();              //= current timestamp in seconds
File.read('words.txt');  //= contents of file

ReferenceAll built-in modules and methods are listed in the Manual.

Lists 

A List is a variable that can hold multiple values, separated by commas.

// a list of strings
let letters = ['a', 'b', 'c'];

// A list of numbers
let ages = [0, 2, 4, 6, 8, 10];

// Multi-line format
let countries = [
    'Argentina',
    'Brazil',
    'Chile'
];

// Each item can be an expression
let nums = [1 + 9, 2 + 8];

JargonIn other languages, Lists are sometimes called Arrays.

List Elements

You can access elements of a list by the index number of its position, which always starts at 0.

let letters = ['a', 'b', 'c', 'd'];

let first = letters[0];  //= 'a'
let second = letters[1]; //= 'b'

// Negative indexes start counting from the end
let last = letters[-1];        //= 'c'
let nextToLast = letters[-2];  //= 'd'

// Modify an index directly
letters[0] = 'yay!';
print(letters);
//= ['yay!', 'b', 'c', 'd']

List Methods

Here are some common List methods. See List for all methods.

let animals = ['squirrel', 'snail'];

animals.contains('snail'); //= true
animals.length();          //= 2
animals.join(' & ');       //= 'squirrel & snail'

// Add item to the end
animals.add('sparrow');
print(users);  //= ['squirrel', 'snail', 'sparrow']

// Add item to the beginning (at index 0)
animals.add('deer', 0);
print(animals);
//= ['deer', 'squirrel', 'snail', 'sparrow']

// Remove item from the end and return it
animals.remove();  //= 'sparrow'
print(animals);
//= ['deer', 'squirrel', 'snail']

// Remove item from beginning (at index 0)
animals.remove(0);  //= 'deer'
print(animals);
//= ['squirrel', 'snail']

Maps 

A Map is a collection of key/value pairs. It’s like a list, but instead of having a specific order (0,1,2,3), each item corresponds to (or “maps” to) a key that is a String.

Maps are surrounded by curly braces { }. Each key/value pair is joined by a colon :, and pairs are separated by commas.

// A one-line Map
let animal = { name: 'Ann', type: 'Anteater' };

// An empty Map
let animal = {};

// Multi-line Map
let animal = {
    name: 'Ann',
    type: 'Anteater',
    job: 'Analyst',
};

// Assign a new key
animal['home'] = 'Antarctica';

// Get fields with [...] notation or dot notation
animal['name'];   //= 'Ann'
animal.name;      //= 'Ann'

// Dot notation is more strict with missing keys
animal['age']; //= '' (a safe falsey value)
animal.age;    //= ERROR :( 'age' is not defined

TipThink of a List as a collection of many things, while a Map holds the attributes of a single thing. (It’s very common to have a List of Maps, like the result of a database query.)

JargonIn other languages, Maps are called many different things: associative arrays, dictionaries, hash maps, hashes, or records. They all basically mean the same thing: a set of key/value pairs.

For Loops 

A for loop repeats the same block of code multiple times. They are most commonly used to iterate over a List.

// Loop over a list
let animals = ['snail', 'squirrel', 'sparrow'];
for (animal in animals) {
    print(animal.toUpperCaseFirst() ~ ' is in the forest.');
}
//= 'Snail is in the forest'
//= 'Squirrel is in the forest'
//= 'Sparrow is in the forest'

// Loop over a range of numbers.
for (n in range(0, 2)) {
    print(n ~ ' = ' ~ animals[n]);
}
//= '0 = snail'
//= '1 = squirrel'
//= '2 = sparrow'

Loop Control

Use break to immediately exit a loop. The program will jump to the end of the block and resume.

Use continue to skip the current cycle of a loop. It will immediately jump to the top of the block and resume the next cycle.

// Find the first quote that refers to 'humility'
let lines = File.read('quotes.txt');
let foundLine = '';
for (line in lines) {
    if (line == '') {
        continue;  // skip to the next cycle
    }
    if (line.contains('humility')) {
        foundLine = line;
        break;  // end the loop
    }
}
print(foundLine);

Forever Loops

A bare for loop will keep repeating until you exit via break.

This is useful when you don’t know how many times it will repeat.

// Print all quotes that refer to 'curiosity'
let file = File.open('quotes.txt');
for {
    let line = file.readLine();
    if (line.contains('curiosity')) {
        print(line);
    }
    // end of file
    if (line == false) {
        break;
    }
}

JargonOther languages achieve this with the while and do/while commands. OWL's approach is simpler, and avoids some common pitfalls with those commands (such as off-by-one bugs).

Functions 

A function is a block of code that can be re-used anywhere else in the program.

When you call a function, you can pass in zero or more arguments.

// A function with no arguments
function sayHi() {
    print('Hi!');
}

// With a 'message' argument
function saySomething(greeting) {
    print(message ~ '!');
}

// Call the functions
sayHi();                //= prints 'Hi!'
saySomething('Hello');  //= prints 'Hello!'
saySomething();         // ERROR :(  Needs one argument.

Arguments can have default values if they aren’t passed in.

// The 'greeting' argument has a default value
function saySomething(to, greeting = 'Hello') {
    print(message ~ ', ' ~ to ~ '!');
}

saySomething('Bob');         //= prints 'Hello, Bob!'
saySomething('Bob', 'Hey');  //= prints 'Hey, Bob!'

Template Functions 

Template functions let you include multi-line strings directly in your script.

These are mainly used for blocks of HTML, CSS, and JavaScript.

They support extra syntax, like double-braces {{ ... }} for embedding Owl expressions.

See Templates for more info.

template html(userName) {

    <h1>My Web Page</h1>

    <p>Hello {{ userName }}!</p>

}

Scope 

Variables are only accessible inside the block they are defined in. This includes functions, if/else blocks, and for loops.

Variables in the top-most scope are not available inside functions.

let myVar = 123; // in the top-level scope

function doSomething() {
    print(myVar); // ERROR :(
}

Variables in one function are not available in other functions.

function doSomething() {
    let myVar = 123; // in a different function
}

function doSomethingElse() {
    print(myVar);  // ERROR :(
}

Variables in inner blocks are not available in the outer scope.

if (true)
    let myVar = 123;  // inner block
}

print(myVar); // ERROR :(

Custom Modules 

Modules let you organize functions into separate files, which can be included from any page.

Module names are always UpperCamelCase, and go in the modules folder.

// file: modules/MyModule.owl

function sayHello(userName) {
    print('Hello, ' ~ userName ~ '!');
}

To call a function in a module, use dot . notation.

// file: pages/myPage.owl

MyModule.sayHello('Deer');  //= 'Hello, Deer!'

Modules in the root modules folder will be automatically included when they are used.

Modules in sub-folders need to be pulled in via import before they are used.

// pull in file 'modules/profiles/SnailProfile.owl'
import('profiles/SnailProfile');

SnailProfile.getSpeed(); //= '3 feet per hour'

Try/Catch 

When something goes wrong, it’s usually better to let the program die. A dead script can’t do any more damage, and is less likely to become a security vulnerability.

However, sometimes you want to provide a fallback for specific errors.

An Exception is an error that can be trapped with try/catch. The catch block will only run if there is an Exception in the try block.

let content = '';
try {
    // Will throw an Exception if it can't read the file.
    content = File.read('important.txt');
} catch (e) {
    // Error reading the file.  Try to restore it and continue.
    content = getDefaultContent();
    File.write('important.txt', content);
}

finally

A finally block can be used to define code that will run regardless of whether or not an Exception occurred.

This is optional, and is usually intended for cleanup logic that is invoked before the program ends.

try {
    // initial code
} catch (e) {
    // run if an Exception happens
    print(e.message());
} finally {
    // always run this after the above blocks complete
}

TipThis is usually unnecessary, thanks to PHP's share-nothing request cycle. Each OWL request is automatically cleaned up after it ends (e.g. closed file handles and database connections).

die

You can trigger an Exception manually with die.

if (!fileExists) {
    die('Important file not found.');
}

To halt the program without triggering an Exception, use System.exit().