Definition-of-Jaft.md @master — view 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