Linux Build (Travis-CI) | Code Coverage |
---|---|
[Work In Progress]
Cross-platform intermediate shell scripting language.
A transpiler to generate native OS shell commands from a shared code base with a powerfull class library and rich language features.
Learning all of the shell scripting languages with all those details could be so frustrating and it can be forgotten so easily due to tons of details and special cases, the idea of ShellScript is to create an intermediate language that can be transpiled (source to source compilation) into any shell language with use of paltform-dependent utilities.
sudo pacman -S shellscript
You can install dotnet package using (the above command will install dotnet automatically):
sudo pacman -S dotnet-runtime
sudo pacman -S dotnet-host
First you need to install dotnet core host and runtime along side with SDK:
Arch Linux:
sudo pacman -S dotnet-host
sudo pacman -S dotnet-runtime
sudo pacman -S dotnet-sdk
For other operating systems please refer to Microsoft .NET downloads and install .NET Core 2.1
Clone the repository and build the solution:
git clone git@github.com:amkherad/ShellScript.git
cd ShellScript
dotnet build ShellScript.sln
- You can find the binaries in ShellScript/ShellScript/bin/[Release-Debug]/[Framework]
ShellScript is a C#-like language with less features from C# and some new features to allow coding for shell scripting environments.
As of today ShellScript supports transpiling to Unix-Bash and Windows-Batch shell languages.
Here are some rules of the ShellScript:
- Keywords in ShellScript are case-sensitive.
- White-spaces are totally ignored unless they're inside single or double quote.
- Blocks are free to use (i.e. you can have {} wherever you want just like C#)
- Variables are accessible to their scope only.
- Using arrays will generate hacks to implement the functions, so avoid using arrays as possible.
- API functions and objects will be written to the output only when they're used inside the code.
- ShellScript API methods will use platform's dedicated way to get the results wherever possible instead of writing it's own function.
int x = -23; echo Math.Abs(x); //will generate 'echo ${x#-}' in bash.
- There's no label/goto syntax.
ShellScript is a strong-typed/static-typed language, all data types are listed below:
Data Type | Alias(es) | Description |
---|---|---|
Integer | int, long | Any integer number. (e.g. 956) |
Float | double, float | Any float number. (e.g. 56.48) |
Number | number | Any number, either integer or float. |
String | string | Representing a string of characters. |
Boolean | bool | Representing a boolean value. (i.e. true or false) |
Void | void | Void data type. (The only usage is to define a void method) |
Object | object | Representing an instance of a class. (different from object in C#) [NOT IMPLEMENTED YET] |
Array | DATATYPE[] | Represents an array of items of the given data type. (e.g. int[]) |
Delegate | delegate | Holds a reference to a callable object (function). |
Example of data types in code:
int i = 956;
long l = 956;
float f = 56.48;
float f = +8.56e-23;
double d = +8.56e-23;
number n = 956;
number m = +8.56e-23;
string s = "Hello World";
boolean b = true || false;
-
Types have no boundaries or limitation in the language itself but they're limited to target platform specifications.
-
Only integer types can be casted to float types implicitly, for other types an explicit cast is required.
Variable definitions are similar to C#, except there is no var keyword for auto typing.
There are four places for variable definitions:
-
Defining a variable inside a block of code:
int myVariable = 43;
-
Defining a variable inside for/foreach loop:
for (int myVariable = 0; myVariable < 10; myVariable++) { } foreach (int myVariable in GetIntegers()) { }
- foreach variable is immutable inside foreach block (and inaccessible outside of the block, if it's defined in the foreach statement).
-
Defining a parameter in a function definition:
void myFunction (int myParam1) { }
- Just like C#, variable definition is not an embedded statement, example:
for (int i = 0; i < 100; i++) int j = i; //Compiler error. for (int i = 0; i < 100; i++) { int j = i; //OK }
There are four places for assignments:
-
Assigning a variable inside a block of code:
myVariable = 43;
-
Assigning a variable inside for/foreach loop:
for (myVariable = 0; myVariable < 10; myVariable++) { } foreach (myVariable in GetIntegers()) { }
-
Assigning a parameter to a default value:
void myFunction (int myParam1 = 10) { }
-
Assigning a variable inside an evaluation expression:
myVariable = x = 2; while ((that = that.Parent) != null) { }
ShellScript use same syntax for function definition as C#.
int myFunction (int parameter1) {
return 0;
}
Same as the C#, all code paths must return a value, so this is a compiler error:
double myDouble (int parameter1) {
if (parameter1 < 10) {
echo "ERROR";
//No return from if body. :CompilerError
} else {
return 0;
}
}
if
block is implemented exactly as C#.
if (condition) {
echo ("condition is true");
} else if (condition2 == true) {
echo ("condition2 is true");
} else {
echo ("condition and condition2 are false");
}
switch case
block is implemented exactly as C#.
switch (value1) {
case "Item1": {
echo ("Item1");
break;
}
default: {
echo ("Default");
}
}
- Conditional blocks with constant conditions will be compiled if their condition is true.
if (true) { echo ("true"); } else if (false) { echo ("false"); } else { echo ("else"); } //The entire if statement will be removed and only one echo ("true") will be generated.
if
may compile to aswitch case
syntax if required or vice-versa.if
andswitch case
considered as branch statements, they're required to return a value in every branch inside a non-void method, and may be converted to arithmetic/logical expression to remove branch.
There are four loops in ShellScript.
for
syntax used to iterate using a condition and an optional counter:
for (int i = 0; i < 10; i++) {
echo ("Line: " + i);
}
foreach
syntax used to iterate over an array:
foreach (int age in GetAges()) {
echo ("Age is: " + age);
}
while
syntax used to iterate using a condition:
while (_continue) {
echo ($"Continue is {_continue}");
}
do while
syntax used to iterate at least once using a condition:
do {
echo ("Going to check the condition.");
} while (_continue);
- Empty loops will be ignored.
- Preferred loop for infinite iterations is
for
.for (;;) { //Infinite loop }
There's no limitations on expressions, but it's highly suggested to use parenthesis to clarify expressions.
Here are all the operators with their order (first row has the most priority):
Category | Operator | Associativity |
---|---|---|
Primary/Postfix | () [] ++ -- . | Left to right |
Unary | + - ! ~ ++ - - (type) | Right to left |
Multiplicative | * / % | Left to right |
Additive | + - | Left to right |
Shift | << >> |
Left to right |
Relational | < <= > >= | Left to right |
Equality | == != | Left to right |
Bitwise AND | & | Left to right |
Bitwise XOR | ^ | Left to right |
Bitwise OR | | | Left to right |
Logical AND | && | Left to right |
Logical OR | || | Left to right |
Conditional | ?: |
Right to left |
Assignment | = += -= *= /= %=>>= <<= &= ^= |= |
Right to left |
Comma | , | Left to right |
Some operators are not implemented yet.
return (1024 ^ 1023) + 1024 * 2; //2047 + 2048 = 4095
Non-void functions are considered as evaluation expression:
return 2 * factorial(20);
- Expressions are parsed from left to right.
- Expressions may be truncated into multiple helper variables.
- Constant expressions will be calculated at compile-time. (i.e. 1024 * 2 will generate 2048 constant value)
- Both void methods or non-void methods will be inlined if there's only one statement inside.
- By default ShellScript only check errors/exceptions for the methods/commands having throw syntax inside or marked by a throws syntax.
- Writing raw platform-specific code is considered unsafe and should be avoided unless there's no other way.
- You can enable value tracking to treat variables as constant if their value is not changed.
int x = 10; return x * 10 / 3; //if value-tracking enabled it will generate "return 33" constant value instead of arithmetic evaluation. //that's because value of x is not changed before reading it.
- Evaluation of second operand in logical operators is UB (Undefined Behavior) and it depends on the target shell.
- It's better to avoid micro-optimizations or outsmarting the compiler, because ShellScript will optimize the well-known statements to platform's dedicated way to implement the functionality, even ignore statements or reorder for better results, and doing so will prevent ShellScript from recognizing the function.
//keep files of a directory in an array to optimize performance. string[] files = Directory.GetFiles("Path-To-Directory"); foreach (string fileName in files) { echo (fileName); } //somewhere else in the code, we need to iterate the files again. foreach (string fileName in files) { echo (fileName); } //the code above will prevent to query the file system twice, but as said before using arrays will generate hacks, //so the generated code might not be as good as expected. //now iterating directory's files by querying file system separately. foreach (string fileName in Directory.GetFiles("Path-To-Directory")) { echo (fileName); } //somewhere else in the code, we need to iterate the files again. foreach (string fileName in Directory.GetFiles("Path-To-Directory")) { echo (fileName); } //this code will generate "for filename in Path-To-Directory/*; do" in bash and //it's easier to understand (if required to read the output) and it's not using any hacks. //but it may be slower (you can benchmark your code to choose which is better for your requirements)
You can concatenate strings using addition operator (+) or using string interpolation ($"").
return "Hello" + " " + "World " + dateValue;
return $"Hello World {dateValue}";
- You can use both single quote or double quote. (both are strings, ShellScript has no character data type)
- String escaping is exactly like C#.
- Concatenating constant strings will generate a concatenated constant string.
- You can use multiplication operator on one integer and one string to repeat the string.
echo (80 * "-"); //will repeat (-) 80 times.
Transpiler will do it's best for reliability and high performance, but sometimes the performance and correctness relies on external utilities, like using floating-point arithmetic in bash, it will use awk
or other utilities available to do the math but they can reject to calculate the expression. (e.g. compiling awk
with no math enabled)
Echo is one of the special syntaxes in ShellScript. it's classified as a void function call. (i.e. cannot be used inside evaluation expressions)
The echo syntax doesn't require parenthesis unlike general function calls.
echo "Hello World";
Most of the times echo is a syntax-to-syntax transpilation, but it can generate different syntaxes on different target platforms, look at this code:
int myFunction (int param1) {
echo ("Hello World");
return 10;
}
This code will transpile to the following script in unix-bash:
function myFunction () {
echo "Hello World" > /dev/tty
echo 10;
return 0; //this might be omitted.
}
The first echo inside the method writes directly to /dev/tty
in unix. because shell environments use standard-output to return a value. (i.e. by redirecting the output of a command/function to a variable or another command)
Like every C-like language ShellScript has pre-processors.
Pre-processors are checked before the compilation begins, so you can make statements and tokens conditional, or changing some compilation behaviors.
#if
used to make statements and tokens conditional:
#if (Bash)
echo ("Welcome bash users");
#elseif (Batch)
echo ("Welcome batch users");
#else
echo ("Welcome everyone");
#endif
#option
used to set or override compiler options.
#option ("Author", "Ali Mousavi Kherad") //this will set the author property.
#option ("Awk", "Disable") //this will disable usage of awk
- If pre-processor MUST have parenthesis unlike C#.
ShellScript provide some API methods to minify the need to write platform-specific code for each platform.
These methods try to use the target shell's dedicated way to get the results but in cases they will generate meta functions inside the output script file. (meta codes are at the beginning of the file)
- It's possible to override API functions by knowing the generated function name and writing your own function with that name before the first usage of the API.
Enter the API documentation here
Command-line format is ShellScript command [parameters] [-switch-name[=switch-value]]
To show the help.
To print the installed target platforms.
To print the version of the compiler.
To compile a file/project.
ShellScript compile Source Output Platform [-switch-name[=switch-value]]
Example:
ShellScript compile /home/github/projects/test.shellscript /home/github/projects/build/test.bash Unix-Bash --verbose
Please read Contributing.md for details on our code of conduct, and the process for submitting pull requests to us.
- Ali Mousavi Kherad - Owner
See also the list of contributors who participated in this project.
This project is licensed under the MIT License - see the https://opensource.org/licenses/MIT for details