git @ Cat's Eye Technologies Jaft / master doc / Definition-of-Jaft.md
master

Tree @master (Download .tar.gz)

Definition-of-Jaft.md @masterview markup · raw · history · blame

Definition of Jaft

This document aims to serve as the definion of the Jaft programming language. Jaft is a tiny, purely functional, non-recursive, (almost-)subset of JavaScript.

-> Functionality "Evaluate Jaft expression" is implemented by
-> shell command "npm run --silent jafti %(test-body-file)"

-> Functionality "Evaluate Jaft expression with trunc" is implemented by
-> shell command "npm run --silent jafti %(test-body-file) -- --expose trunc:Math.trunc"

-> Functionality "Evaluate Jaft function" is implemented by
-> shell command "npm run --silent jafti %(test-body-file) -- --expose trunc:Math.trunc --fundef"

These tests are written in Falderal format. Each indented code block (generally preceded by a description) represents a test. All the lines of the code block up until the ===> line give the input to be tested; the text after ===> gives the expected output. ???> gives an expected error message, which is permitted to be a partial (substring) match.

Rudiments

-> Tests for functionality "Evaluate Jaft expression"

Data types

Jaft supports a subset of data types used in JavaScript.

Numbers:

24
===> 24

27.44517
===> 27.44517

Note there is no unary minus in the syntax. The usual idiom is to subtract from zero.

0 - 27.44517
===> -27.44517

String literals can be given. Note that single quotes and backquotes are not supported.

"Hello, world!"
===> "Hello, world!"

Boolean literals can be given.

true
===> true

false
===> false

Expressions

Jaft supports a subset of the operators that appear in JavaScript, with the same precedence and semantics.

4 * 6 + 1
===> 25

Parentheses can be used to adjust precedence as usual.

4 * (6 + 1)
===> 28

Parentheses can occur around an entire expression.

// (4 * (6 + 1))
// ===> 28

Comparison operators evaluate to a boolean.

5 > 1
===> true

5 > 5
===> false

5 > 7
===> false

5 >= 1
===> true

5 >= 5
===> true

5 >= 7
===> false

5 <= 1
===> false

5 <= 5
===> true

5 <= 7
===> true

5 === 1
===> false

5 === 5
===> true

5 === 7
===> false

5 !== 1
===> true

5 !== 5
===> false

5 !== 7
===> true

Only strict equality is supported. Coercing equality isn't.

(5 == 1)
???> Expected

Nor is coercing inequality.

(5 != 1)
???> Unexpected

Conditional Evaluation

Jaft supports conditional evaluation using the same "ternary" operator as JavaScript.

true ? 1 : 2
===> 1

false ? 1 : 2
===> 2

Name Binding

We can bind names to values. This uses a very JavaScript-like syntax, which is mostly compatible with JavaScript in spirit, but there are differences.

const x = 1;
x
===> 1

const x = 1;
const y = 2;
x + y
===> 3

All values in Jaft are immutable. Yet, there are two ways to bind values: const and let.

let x = 1;
x
===> 1

let x = 1;
let y = 2;
x + y
===> 3

The difference between const and let in Jaft is not in the mutability of the value; the difference is in the shadowability of the name.

Names defined with let may be shadowed.

let x = 1;
let x = 2;
x
===> 2

let x = 1;
const x = 2;
x
===> 2

Names defined with const may not be shadowed.

const x = 1;
const x = 2;
x
???> Cannot shadow const binding

const x = 1;
let x = 2;
x
???> Cannot shadow const binding

Multiple name bindings can be given in a single let or const.

let x = 1, y = 2, z = 3;
x + y * z
===> 7

const x = 1, y = 2, z = 3;
x + y * z
===> 7

But a name may not be re-used in a single let or const.

let x = 1, y = 2, x = 3;
x + y * x
???> Cannot re-bind

const x = 1, y = 2, x = 3;
x + y * x
???> Cannot re-bind

Another difference between these name binding structures, and those in JavaScript, is that these are actually expressions.

true ? (const x = 2; x * x) : (const x = 3; x * x)
===> 4

(Some may disagree with the choice of precedence here)

true ? const x = 2; x * x : const x = 3; x * x
===> 4

They can be nested in parentheses.

0 + (const x = 2; x * x) + (const x = 3; x * x)
===> 13

Functions

Named functions can only be called if they have been injected into the environment.

trunc(4.1)
???> Error: Unknown function: trunc

Even if they exist on globalThis.

isNan(4.1)
???> Error: Unknown function: isNan

Once exposed to the Jaft evaluation environment, they can be called.

-> Tests for functionality "Evaluate Jaft expression with trunc"

trunc(4.1)
===> 4

We can define functions. We use the ES6 arrow syntax to do so.

-> Tests for functionality "Evaluate Jaft function"

(x) => x * 2
<=== 123
===> 246

(x) => x * 2
<=== 29.5
===> 59

Tuples

A function can return a tuple. Tuples look like JavaScript list literals, and are intended primarily for when a function wants to return multiple values.

(x) => [x, x * 2, x * 3]
<=== 123
===> [123,246,369]

Tuples can be destructured in a let binding.

(x) => let [a, b] = [x, x * 2]; a + b
<=== 10
===> 30

Tuples can be destructured in a const binding.

(x) => const [a, b] = [x, x * 2]; a + b
<=== 5
===> 15

Tuples may appear in most places in expressions.

(x) => x > 100 ? [x, x * 2] : [0, 0]
<=== 123
===> [123,246]

A tuple may appear as the value in a let binding.

(x) => let y = [x, x * 2]; y
<=== 123
===> [123,246]

A tuple may not appear on the left of a property access. (FIXME: this test case should error. This should be a static analysis error.)

(x) => [x, x * 2].sneeze
<=== 123
===> undefined

There are no binary operators that can work with values of tuple type. On destructuring can do that. (FIXME: this test case should error. This should be a static analysis error.)

(x) => [x, x * 2] * 1
<=== 123
===> null