steel-lang v0.0.19
Steel
Strongly Typed Experimental Expressive Language
Language that transpile to TypeScript and JavaScript.
Steel is a bootstraped language. That means the code itself is developed in Steel.
Check ./src folder for steel sources. Check ./lib for the compiled sources.
Highly inspired from LiveScript.
Goals
This language tries to implement strong static typing over a subset of Livescript.
To do so, it transpile first to TypeScript, and let it transpile to Javascript. This allow to use the power of a well typed and designed language while smoothing its syntax.
It alse tries not to fall into the same 'over-simplification' Livescript does, and avoid to implement features complexifying the read and understanding of the code.
At term, this language aim to be more functional, and might borrow some concepts from language like Haskell or Ocaml (custom operator?, immutability?, infinite lists?, ...).
Exemples
Basics
import
fs
path: { resolve }
'./localFile'
'./localFile2': customName
foo := number -> number -> number
foo = (a, b) -> a + b
bar = (c: number, d: number): number ~>
if c? and d? and d isnt 0
c + d
else
0
nonReturning = !-> 1
map = <T>(f: (x: T): T, arr: T[]): T[] --> arr.map f
filter = <T>(f: (x: T): T, arr: T[]): T[] --> arr.filter f
[1, 2, 3]
|> map (+ 2)
|> filter (> 2)
class Animal
a: 1
b: -> 2
constructor: (val: number) -> @a = val
class Dog: Animal
dog: Dog = new Dog 1Transpiled in TypeScript with sc -cts source.s turns into:
(function () {
import fs = require('fs');
import _path = require('path');
let {resolve} = _path;
import localFile = require('./localFile');
import customName = require('./localFile2');
let foo:(a:number,b:number) => number = function (a, b) {
return a + b;
};
let bar = (c:number, d:number): number => {
if ((c != null) && (d != null) && d !== 0) {
return c + d;
} else {
return 0;
}
};
let nonReturning = function (it?:any) {
1;
};
let map = curry$(function (f:any, arr:any) {
return arr.map(f);
});
let filter = curry$(function (f:any, arr:any) {
return arr.filter(f);
});
(filter(function (it:any) {
return it>2;
})(map(function (it:any) {
return it+2;
})([1, 2, 3])));
class Animal {
a = 1;
b(it?:any) {
return 2;
}
constructor(val:number) {
this.a = val;
}
};
class Dog extends Animal {};
let dog:Dog = new Dog(1);
function curry$(f: any, bound?: any){ let context: any, _curry: any = function(args?: any){ return f.length > 1 ? function(){ var params = args ? args.concat() :[]; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }
})();Install
npm install -g steel-langCompiler usage
Compiler name is sc, and stands for Steel Compiler.
Usage
$> sc -h
Usage: sc [options] <files ...>
Options:
-V, --version output the version number
-c, --compile Compile files
-p, --print Print files
-o, --output <folder> File/folder of output
-s, --strict Disallow implicit use of <Any> type
-t, --typescript Output Typescript instead of Javascript (no typechecking)
-b, --bare Dont wrap into top level anonymous function
-q, --quiet Dont output types errors/warnings (useful with -p)
-h, --help output usage informationCompile and execute on the fly
sc file.sYou can only compile and execute single file on the fly
Files extensions are .s.
This extension is registered inside NodeJS when loading steel-lang to auto-compile steel files on the fly when required.
This means you can write
require('steel-lang');
const obj = require('./someFile.s');Compile a file/folder
sc -c file1.s file2.s
sc -c folder/*.s
sc -c folder/**/*.sThe format of the path is the same as for gulp. If you want to compile recursively or exclude some folders for exemple.
Compile to a diferent output
sc -o dir -c file1.s file2.s
sc -o dir -c folder/*.s Will output compiled files into dir folder.
If the folder doesnt exists, it is created on the fly.
The arborescence is recreated if a folder is recursively compiled.
Features
Indent based language
if a is b a else bVariable declaration
foo = 1 bar = -> 2Inline type declaration
foo: number = 1 bar = (a: number, b: number): number -> a + bExternalized type declaration
foo := number foo = 1 bar := number -> number -> number bar = (a, b) -> a + bFunction declaration
Standard function
foo = (a, b) -> a + bArrow function
foo = (a, b) ~> a + bLast statement is returned (can be disabled by '!')
foo = (a, b) -> a + b # bar does not return its result bar = (a, b) !-> a + bArgument types
foo = (a:number, b: number):number -> a + b
Function call
with parentheses
foo(1, 'a', var1)with spaces
foo 1, 'a', var1with '!' for no arguments
foo() # same as foo!
Generics
map = <T>(f: any, arr: T[]): T[] -> arr.map f map (+ 2), [1, 2, 3]Curry operator
# Exists for arrow function : '~~>' add = (a, b) --> a + b add5 = add 5 add5 5 # === 10Auto add argument 'it' when only one
a = -> it + 2 a 2 # === 4Shorthand function declaration
add2 = (+ 2) add2 2 # === 4 obj = a: 1 getA = (.a) getA obj # === 1Chained calls
[1, 2, 3] |> map (+ 2) |> filter (> 2)Objects
obj1 = {} obj1 = { a: 1 } obj1 = a: 1, b: variable, c: -> 3, d: e: 4 obj2 = a: 1 b: variable c: -> 3 d: e: 4Object usage
obj.a.b(a.b).1.c!.f 1, 2 .dArray
arr = [1, 2, 3]Class
class Animal walk: -> 1 class Dog: Animal a: 1 walk: -> super.walk! + 1Interface
interface Test a: number b?: stringIf / Else
if a is b a else bFor / While
while a < b a = a + 1 for i = 0; i < 10; i++ console.log iTry / Catch
try a = JSON.parse foo catch e console.error e'not' stands for '!'
- '?' for existance checks (!= null)
- 'is / isnt' become '=== / !=='
- 'and / or' become '&& / ||'
Arithmetic operations
Import
import fs lodash: _ path: { resolve } './foo/bar'Become
import fs = require('fs'); import _ = require('lodash'); import _path = require('path'); let {resolve} = _path; import bar = require('./foo/bar');You can use
requireinstead ofimportto make a regular require
TODO:
- fix bug with $curry that swallows arguments type
- generics specification on func call (map(f, arr))
- decimals
- return loops
- typeof, delete, instanceof, ...
- Exports
- Expression as assignable (if, while, ...)
- implements
- Class visibility (public, private,...)
- class static
- Class types for methods and properties
- cast
- Generics
- readonly for interface
- propname for interface
- index for interface
- func type for interface
- abstract
- Transpile code in template strings
- Better error system (more details, more accuracy)
- Std lib
- Multiline string
- Better error management for on-the-fly compilation (get rid of typescript-simple)
- Import native (through options)
- Add tests for
- Operation
- Existance
- Function shorthand
- Import native
- Full compile
- Comments
- Generics / array type
- Support tsconfig.json
- (Plugin system ?)
- |< operator for chained call with flipped arguments