Flatscript
Flatscript is a language and compiler that generates Javascript from a Python like language.
Installation and Build
Flatscript is implemented by Flatscript itself so it is not possible to build it from scratch.
You need to install it from npm before compiling it.
npm install -g flatscript
You could check the installation by the following command
flsc -h
Key Features
Javascript is famous for its callback-hell coding style. In Flatscript it allows developers to write code in synchronous styles and the compiler will translate it to asynchronous Javascript code.
For example, Flatscript code like:
for i range 10
if i != 0
setTimeout(%, 1000)
console.log(i)
will print 0
up to 9
and "sleep" for 1 second between each print.
And for Flatscript code like:
fs: require('fs')
try
console.log(fs.readFile('a.txt', %%) + fs.readFile('b.txt', %%))
catch e
console.error(e)
console.log('end')
will work in this order
- read "a.txt"
- read "b.txt"
- concatenate their content
- output to console
If any error, like file not found, occurs, the work flow will be interrupted and the error will be caught and sent to stderr. A message "end" would get outputted in the end.
Those features are an alternative of ES7 await
. The lexical token %
and %%
indicates the argument should be a callback whose body will be generated by the compiler from the latter part of the syntax tree.
To read a list of files and store their content, Flatscript code is like
fs: require('fs')
files: ['a.txt', 'b.txt', 'c.txt']
content: []
try
for i range files.length
content.push(fs.readFile(files[i], %%).toString())
console.log('content', content)
catch e
console.error(e)
console.log('end')
Or more simply, to use Flatscript pipeline syntax like (using pipeline mapping operator |:
)
fs: require('fs')
files: ['a.txt', 'b.txt', 'c.txt']
try
console.log('content', files |: fs.readFile($, %%).toString())
catch e
console.error(e)
console.log('end')
To encapsule this into a regular asynchronous function (like an async
function in ES7)
fs: require('fs')
func readFiles(fileList, %%)
return fileList |: fs.readFile($, %%).toString()
try
console.log('content', readFiles(['a.txt', 'b.txt', 'c.txt'], %%))
catch e
console.error(e)
console.log('end')
Other Features
Indentation-indicated syntax
Code samples
# define a function that calculates fibonacci number
func fib(n)
if n <= 1
return 1
return fib(n - 1) + fib(n - 2)
Ouput
function $Rfib($Rn) {
if (($Rn <= 1)) {
return 1;
}
return ($Rfib(($Rn - 1)) + $Rfib(($Rn - 2)));
}
Flatscript will also do some name mangling.
It is easy to break a long line into shorter ones, by hitting return after proper tokens. Code samples
['this', 'is', 'a',
'long', 'list']
callFunction('with', 'several'
, 'arguments')
x: a +
b
Anonymous function in an easy way
Anonymous functions are written in this way without any keywords
(parameters):
function-body
Code samples
fs.read('some-file', (error, content):
console.log(content.toString())
)
Output
fs.read("some-file", (function ($Rerror, $Rcontent) {
console.log($Rcontent.toString());
}));
Convert synchronous code into asynchronous
In a call to a function which takes a callback with parameters (error, result)
, the callback argument could be represented as %%
, and latter expressions and statements will become the body of the callback. The former and latter relationship is determined by the syntax tree, for instance, in the binary operation a + b
, b
is the latter of a
. More detailed example:
func read(fileA, fileB, %%)
return fs.read(fileA, %%) + fs.read(fileB, %%)
JS code generated as (demangled)
function read(fileA, fileB, $racb) {
fs.read(fileA, (function (err, $ar_0) {
if (err) return $racb(err);
fs.read(fileB, (function (err, $ar_1) {
if (err) return $racb(err);
return $racb(null, $ar_0 + $ar_1);
}));
}));
}
Similarly, in a call to a function which takes a callback with no parameters, the callback could be represented as %
. For example
console.log(0)
setTimeout(%, 1000)
console.log(1)
setTimeout(%, 1000)
console.log(2)
JS code generated as
console.log(0);
setTimeout((function() {
console.log(1);
setTimeout((function() {
console.log(2);
}), 1000);
}), 1000);
Pipe a list into a result
It uses a pipeline to iterate over a list. Pipeline operators are |:
and |?
. The former represents a mapping operation while the latter represents a filtering. Within a pipeline, use $
to reference the element, and $i
for the index.
Code sample
x: [1, 1, 2, 3, 5, 8, 13]
console.log(x |: $ * $)
console.log(x |? $ % 3 = 1)
console.log(x |: $i % 2 = 0)
console.log(x |? $i % 2 = 0)
Results
[1, 1, 4, 9, 25, 64, 169]
[1, 1, 13]
[true, false, true, false, true, false, true]
[1, 2, 5, 13]
Pipeline could be used along with regular asynchronous calls.
Code sample
func readFiles(fileList, %%)
fileContent: fileList |: fs.read($, %%)
return fileContent.join('')
Output
function $RreadFiles($RfileList, $racb) {
var $RfileContent;
var $ar_0 = (function ($list) {
function $next($index, $result) {
var $key = null;
if ($index === $list.length) {
$RfileContent = $result;
return $racb(null, $RfileContent.join(""));
} else {
var $element = $list[$index];
fs.read($element, (function ($cb_err, $ar_1) {
if ($cb_err) return $racb($cb_err);
$result.push($ar_1);
return $next($index + 1, $result);
}));
}
}
$next(0, []);
})($RfileList);
}
Use the Compiler
Run
Flatscript will read source code from stdin or a file (with -i
option), and print Javascript via stdout, or to a file (with -o
option). The ordinary ways to compile
flsc < source.fls > output.js
flsc -i source.fls -o output.js
Or pipe the program to node
flsc < source.fls | node
flsc -i source.fls | node
FAQ
Why the compiler complains names like 'require'/'document'/'window' not defined?
Flatscript checks name definition at compile time, and it is not possible to use any name that is not defined or not marked as external.
You could declare external names via -e
option, like
flsc -e document -e window -i client/source.fls > client/output.js
flsc -e document:window -i client/source.fls > client/output.js
flsc -e require -i server/source.fls -o server/output.js
Or using extern
statement in the source file:
extern require
fs: require('fs')
console.log(fs.readFile('a.txt', %%))
How could I use jQuery in Flatscript?
Use jQuery
the identifier instead of $
because $
means the current list element in pipeline context, like
buttons: jQuery('.btn')
For More Information
Please read the wiki pages.