Flatscript is a language and compiler that generates Javascript from a Python like language.
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
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')
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 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());
}));
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);
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);
}
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
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', %%))
Use jQuery
the identifier instead of $
because $
means the current list element in pipeline context, like
buttons: jQuery('.btn')
Please read the wiki pages.