The Language

The FunL interpreter can be used under different circumstances. Usually, it will be used to run a program stored in a source file, which can in turn import modules which are also source files. Or, it may be used from within an application to execute a code snippet. It is also used by the REPL (Repeat Evaluate Print Loop) utility to execute individual lines of code.

Source

If the interpreter is to run a program or import a module, the input is refered to as ‘source’ in the grammar:

source ::= Newline | statements

statements ::= statement+

A source is just a list of statements that are execute one after the other. If a source file is empty, the lexer will still emit a Newline token.

Statements

A statement in FunL is either an expression or a declaration:

statement ::=
    |   expression Newline
        declaration

Expressions return a value and declarations don’t. The value returned by an expression statement may or may not be discarded depending on whether it occurs at the end of a block or not.

Objects, Values, Data Types

A FunL program manipulates objects. An object is something that lives in the computer’s memory and carries some kind of value. Two different objects can represent the same value. So, a value is something that a human can understand. Values are categorized into data types or just types.

There are two kinds of objects: mutable and immutable. Generally, anything that can be created directly using the syntax of the language is immutable. Built-in functions and type constructors are used to create mutable objects. And there are also functions that create immutables too.

Immutable types in FunL are all created using a type of expression called a literal.

Strings

A string literal is a list of characters enclosed in either single or double quotes, such as: 'this is a "string"' or "that's cool". Strings used for representing text. There is no separate character literal in FunL as in other languages, however FunL can deal with Java character objects.

Numbers

FunL knows about several different types of numbers:

  • integers can be any number of digits. Internally FunL uses two different ways of dealing with integers depending on how big they are. The conversion is handled automatically.
  • decimals are IEEE 754 double precision (binary64) number objects. FunL 1.0 will support arbitrary precision BCD numbers as well.
  • rationals or fractions are what you get if you divide two integers where the first is not a multiple of the second. So, strictly speaking there’s no literal syntax for rationals. FunL just gives the feeling that there is. If you type 2/3, you’ll get a rational object with the value of two thirds. There’s no limit to the size of the numerator or denominator of a rational number object.
  • complex numbers are supported directly. Current, complex numbers are a pair of double precision numbers, however FunL 1.0 will support arbitrary precision as well as complex (Gaussian) integers and complex fractions. As for rationals, there’s no literal syntax for complex numbers. The variables i and j, which are equal and represent the imaginary unit, are predefined. So, you can type 2 + 3i, for example to the complex number equal to 2 plus 3 times the imaginary unit. Note that an asterisk (*) is not needed between the 3 and the i. FunL understands that it’s multiplication.

Booleans

A boolean literal is just true or false. Boolean values are the results of comparisons and logical connectives.

Closures

A closure is an immutable object (though it may refer internally to variables that are mutable) that encapsulates a calculation and all variable references needed by that calculation. A closure can be called or invoked using the so-called application syntax to get a value from it (which may be compound).

Lists

A list literal is a list of expressions separated by commas and enclosed in square brackets. For example, [1, 2, 3] creates a list with the numbers 1, 2 and 3 in it. The empty list is [].

Expressions

A expression is a way of creating an object by way of applying operations and functions to other objects. There are a number of categories of expressions.

Applications

Applications refers to the well-known syntax for calling or invoking a function. The term is not being used here to refer to software applications. Generally, the application syntax is:

applyExpression ::=
        applyExpression ('(' (expression (',' expression)*)? ')')
    | applyExpression ('.' | '.>') ident
    | primaryExpression

For example to print the number 123 write:

println( 123 )

Arithmetic

FunL has the following arithmetic operators: +, -, *, /, \, %, mod.

Comparisons

<, <=, >, >=, ==, !=

Predicates

|, /|, in, not in, is

Logical

and, or, xor, not

Bitwise

and, or, xor, not, >>, <<, rotateright, rotateleft

Conditional

A conditional expression takes on a value conditionally based on a boolean expression. The syntax for conditionals is:

'if' booleanExpression ('then' expressionOrBlock | blockExpression) elif* elsePart

where:

expressionOrBlock ::= expression | blockExpression

elif ::= optionalNewline 'elif' booleanExpression ('then' expressionOrBlock | blockExpression)

elsePart ::= (optionalNewline 'else' expressionOrBlock)?

Case

A case expression has the syntax:

'case' expression ('of' functionExpression | caseFunctionExpression)

where:

lambdaExpression ::=
    ('(' pattern (',' pattern)* ')' | pattern) ('|' booleanExpression)? '->' expression

caseFunctionExpression ::=
    Indent lambdaExpression (Newline lambdaExpression)* Dedent

functionExpression ::=
    lambdaExpression | caseFunctionExpression

Conditional Loops

FunL has four kinds of loop constructs that could be called “conditional”;

  • while
  • do...while
  • do...until
  • repeat

Iterator Loop

An iterator loop, also known as a for loop, causes an express or block of expressions to be executed iteratively, one iteration for each element of an iterator object. “for” loops have the syntax:

'for' generators ('do' expressionOrBlock | blockExpression) elsePart

where:

generator ::=
    pattern '<-' expression ('if' expression)?

generators ::= generator (',' generator)*

expressionOrBlock ::= expression | blockExpression

elsePart ::= (optionalNewline 'else' expressionOrBlock)?

For example:

for (key, value) <- {'one': 1, 'two': 2, 'three': 3}
    println( key + ' -> ' + value )

produces:

one -> 1
two -> 2
three -> 3

and:

for i <- 1..5 by 2
    println( i )

produces:

1
3
5

Simple Loop

The simple loop is for situation where you either want to terminate the loop using a break or return, or the application doesn’t terminate. For example:

n = 1

for println( n++ )

prints numbers starting from 1 without end.

Sections

Lambda Expressions

Declarations

Import

Definition