TASI is an interpreted programming language, which is meant to be simple, intuitive, and consistent. It is open source under the MIT License and its code can be found here under the following link "https://github.com/Ekischleki/TASI".
TASI has 6 token/command types, to which I will refer later in the documentation:
- Function calls
- Statements
- Calculations
- Strings
- Code containers
- End commands
This token type is more or less the base of this language. Code containers kinda describe themselves, as just a container for code. This code can then be interpreted in different interpretation modes, but the most common one is the "InterpretNormalMode". A code container is built up out of curly brackets, "{" marks the beginning of a code container, while "}" marks the end. Here is an example of a code container token:
{
code;
}
Code containers can contain every type of token. E.g. a code container can contain another code container.
{
code;
{
code inside a code container inside a code container;
}
}
A string is text, that is marked with ". By that logic, that means, that if you want to use " inside a string, it would end the string, but you can use it anyways with \". But what if you wanted to print out '\"'? You can print a '\' by just putting another '\' at the beginning. So you would print out '\"' like this: '\\\"'. Here are some example strings:
"So this is what they call a \"string\"..."
"Just some text, nothing else..."
"Inside here one can be free from all other types of token"
"I can put here anything I want, I just gotta be carefull with the \"'s, but you can put them in your strings anyways, by using '\\\"'"
"\"But doesn't it fail if you use a \"{\" or a \"}\"?\" they asked..."
"But I said: Didn't you listen?! These \"strings\" are free from any other type of token!"
Comments aren't extra tokens and get treated extra. Comments are marked with a #, and it doesn't matter where it is, it will comment out everything till the end of the line. So if you put a # inside a string, it will comment out the whole end of the string. If you want to prevent that, you can put a \ in front of the #. E.g.:
#A whole line can be a comment
"But also just a part of the line" #And this is it!
"So you can just place a \# anywhere to comment it out" #Just like every programming language ever lol.
"Comments are even more free from all types of token then I am" #Yes, this is because nothing can stop me!
"Except a newline..."
There are two types of statements in TASI, the return statement and the normal statement. They don't differentiate in terms of syntax, they're both just written in plain and space separated. They will also immediately end if they encounter sth. like a method, string, etc... Normal statements are used directly and don't return anything while return statements return a value, TASI knows whether a value is needed, and can therefore decide on which to use. The normal statement can therefore only be used directly, therefore they need an end command (semicolon aka ";") at the end Here is a list of all statements in the normal interpret mode:
- return:
When using a return statement inside a code container, the execution of the code in the code container will end and return a value. There are two types of usage for the return statement:
return; Returns nothing and just exits the code container (returns a void value).
return <value>; Returns any value and exits the code container.
Example:
{
return;
}
{
return "...a string value";
}
- set:
With this statement, you can set a variable to a value. There is one type of the using for the set statement:
set <statement: variable name> <value>;
Example:
set variable "string value";
set toastAmount "None, cause I ate it all";
- while:
The while statement can repeat the code inside a code container (executed in normal mode), while a specific condition is true. If you use the return statement inside a while loop, it will return, what the return statement returned. Here is the usage for this statement:
while <bool: condition> {<code to execute>};
Note that the while loop is a statement like any other, therefore it needs an end command at the end. Example:
while true
{
set stillRunning "Seems like it...";
};
- if:
The if statement is meant to execute a code container (in normal mode), if some condition is true, if this condition is false, it can otherwise execute another code container, but this is optional. If you use the return statement inside an if statement, it will return, what the return statement returned. The if statement has two different types of usage:
-
if <bool: value> <code container>; # Will execute code container only, if value is true
-if <bool: value> <code container1> else <code container2>; # Will execute code container 1 if value is true, otherwise it will execute code container 2
These were all meaningful normal statements (till now) there are still some more statements for helping with methods and namespaces: helpm, listm, and rootm. But instead of explaining them, I encourage you to try them out yourself and let TASI tell you more about them. Just don't forget the end command!
Most statements will, when they need a value, only accept single-line return statements. You can use the following to get around that limitation: ($ <multi line return statement>)
- link
The link statement is used to link the value of a variable to the value of another one. You can also link multiple variables to the same value. Here's the usage:
-
link <statement: variable1> <statement: variable2>;
Example:
[Inf.DefVar:"string","stringValue1"]
[Inf.DefVar:"string","StringValue2"] #Create both variables.
link stringValue1 stringValue2; #Link the value of both variables (the value of the first one will be discarded.)
set stringValue2 "Hello World!"; #Set both variables to "Hello World!"
[Console.WriteLine:stringValue1] #This should now write Hello World! to the console.
- unlink
The unlink statement can unlink two previously linked variables. Both will keep their previously shared value, but it will be uncoupled. Here's the usage:
-
unlink <statement: variable to unlink>;
- makevar
The makevar statement can create a variable. It's used like this:
makeVar <statement: var type> <statement: var name>;
- loop
The loop statement is a special statement, which can only be used inside loops. It will jump to the beginning of the loop. The usage is really simple:
loop;
- true: does nothing and returns a bool-type with the value of true.
- false: does nothing and returns a bool-type with the value of false (ctrl + c, ctrl + v. Yes yes, very boring).
- void: does nothing and returns a void type (idk why you would wanna use that, but there must be a case. That's why I put it in.)
- nl: does nothing and returns a string type with the value of \n (new line). TASI doesn't have something like \n, so you must use this return statement instead.
- if: Yes, if again?! Well, let me explain. This is an if statement, with only the if-else variant. You can use the return statement inside of the code containers, the tell the if return statement what to return. Example:
set variable ($ if false {return "conspiracy?! This should never be the value of \"variable\"";} else {return "Much better! After this, variable should be equal to this text";}); # I will explain these weird braces soon, they're just here, because most of the statements only support return-statements that are one statement long. I could code it to make it work without these braces, but why if there's already a way to make it work?
Here is the example, but it's split up, so you can read it better:
set variable ($
if false
{
return "conspiracy?! This should never be the value of \"variable\"";
}
else
{
return "Much better! After this, variable should be equal to this text";
}
);
- do: Just like the return statement, just that you don't need a condition and only have one code container that can be executed. You can use the return statement too, to make it return a value. Example:
set variable ($ do { set variable "value"; return "Different and final value";});
- linkable: The linkable return statement is a weird one. Basically, it just makes a variable usable with function calls, that accept links as input. Example:
[Inf.DefVar:"num","numVal"]
[Example.SetValueTo5:linkable numVal] #Pass numVal as a link to the function, so that any change that happens to the value, that the function is using, is also happening to the numVal variable.
[Console.WriteLine:numVal] #This should display 5
There is an exception to return statements though: if you enter a number as a statement, it will try to parse into its literal value before checking whether it's a variable. So the return statement 15
will return a value of 15.
VARIABLES ARE RETURN STATEMENTS TOO!!! It's very important to keep that in mind!
Let's talk about one of the most important parts of this language. Function calls are somewhat like a black box - you put something in (sometimes), and something comes out (sometimes). So methods can have an input (they must not have an input) and they can also return something (they can also return a void). We'll talk about defining your own functions later (in the header part), but now we'll just talk about function calls. A function call is built up like this:
[<function name>:<function arguments; comma seperated>]
or
[<function name>]
You can use function calls directly in normal interpreting mode, but you can also use them as values because they return values. The function name is built up like this:
<namespace>.<function name>
What a namespace is, will we discuss later (in the header part) Here are some examples of directly calling a function:
[Console.ReadLine]
[Console.WriteLine:"This is a function call with one argument, therefore we don't need a comma to seperate arguments, because there's only one. The method before that had zero arguments, so it didn't even need a colon to sepperate args from name"]
[Test.HelloWorld:true, "This function call now has two arguments and we can keep going like this"]
Here are some examples of using functions as values:
set stringVar [Function.ThatReturnsThisString:"this string?!"]; # This will set the variable stringVar to the output of "Function.ThatReturnsThisString"
[Console.WriteLine:[Console.ReadLine]] #This will print the output of "Console.ReadLine" out to the screen. BTW. when you use a function call directly, you don't need a semicolon after it.
Calculations are used to combine multiple values or things into one value. Calculations are always in braces. There are operators, which I'll list in a sec. The normal calculation rules like the ones in math don't apply. It's just left to right and if there's something in braces, calculate it in a batch. At the end of the calculation, there can only be one value left. Values and operators must be space-separated. If you want to use return statements inside a calculation, you need a return statement calculation brace. But there's an exception to this, if your return statement is only one statement long (like variables and e.g. the return statements true and false), you can use it without the return statement calculation brace. It's basically like a normal calculation brace, just that you have to put a '$' at the first position inside the brace.
set numVar ($do {return 15;}); #This is not a practical use, you should rather just use set numVar 15; but it's just an example to better explain it.
set numVar (15 + ($variable)); #Variables are return statements too, but you could also put the variable without the "return statement calculation brace" there because it's just 1 statement long.
set boolVarUserEnteredJEEP ($ if ([Console.ReadLine] == "JEEP") {return true;} else {return false;}); #This is an actual good example OMG
Functions, strings, and other calculations can be used as normal inside calculations:
set stringVar ("Hello W" + (1 - 1) + [Function.ThatReturnsRLD]); #This would be Hello W0rld
If you use a bool value inside a calculation, it will be converted to a num value (true becomes 1, and false becomes 0).
Before we come to the operators, I'll tell you a little fun fact, did you know, that because of the handling of calculation, something like this would be possible:
set youLLGoToHellIfYouUseThis (15 5 + - + 25); #This is 5 lmao
but as I said, you'll probably go to hell if you use this kind of syntax.
Operators:
- "+" that's the addition operator. It accepts 2 numeric- and string-type values. If you add string types, this operator will join them together. If you add a string type with a numeric type, it will treat both as strings. E.g.: ("1" + 1) is 11
- "-" that's the subtraction operator. It accepts 1 or 2 numeric-type values. If you use it with 1 value, it will invert the sign of the value. If you use 2 values, it's just a normal subtraction.
- "*" that's the multiplication operator. It accepts 2 numeric-type values and is just a normal multiplication.
- "/" that's the division operator. It accepts 2 numeric-type values and is just a normal division.
- "and" that's the and operator. It accepts 2 bool (or automatically to bool converted) values and will return true if both are true.
- "or" that's the or operator. It accepts 2 bool (or automatically to bool converted) values and will return true if at least one of both values is true.
- "!" or "not" that's the not operator. It accepts 1 bool (or automatically to bool converted) value and will invert the bool.
- "%" that's the mod operator. It accepts 2 numeric-type values and is just a normal mod operation.
- "=" that's the non strict equal operator. It needs at least 3 values. It will convert all values to in the first value specified type and then compare them. If one value can't be converted, it will return false. So ("bool" "Hi" = "Hi") is false, because "Hi" can't be converted to a bool. If you have more than 3 values, it will compare all of them. E.g. ("string" 23 "23" = (20 + 3) "23") will return true, because if you convert all values to a string, they all have the same value. If you're not sure how to use multiple values per operator, don't use them.
- "==" that's the type strict equal operator. It needs at least 2 values. If all values don't have the same type, it will return false. E.g. (15 == "15") is false. It has the same acceptance rules as the non strict equal operator and can also compare multiple values.
- "<" that's the less-than operator. It accepts 2 numeric-type values and is just a less-than operation.
- ">" that's the greater-than operator. It accepts 2 numeric-type values and is just a greater-than operation.
Maybe there will be a way to define your own operators one day. But this day is not today.
Header Every file is its own namespace. You need to define the properties of the namespace at the Header layer so at the outer layer. This layer will be interpreted in header mode, where you can't use method calls and can only use header statements:
-
name: The name statement is built up like this: name <statement: namespace name>; and can be used, to define the namespace name of the current file.
-
type: The type statement is built up like this: type <statement: type>; and can be used, to set the type of the current namespace. Types are:
- generic: A generic namespace with a start part. - library: A library namespace, can't get started directly, and can only be used if you import it from another namespace. - internal: An internal namespace. You can't create those, but they still exist. It's used for internal functions, like "Console.WriteLine"
-
start: The start statement is built up like this: start {}; and it contains the start code of the namespace, that'll get executed, if it gets started.
-
function: The function statement is built up like this:
function <statement: return-type> <statement: function name> {<input types in var def interpreter mode>} {<method code>};
the function statement is used, to define your own functions. You're probably still confused because of the var def interpreter mode. But don't worry, I'll talk about it in a second. -
import: The import statement is built up like this:
import <string: full path>;
orimport base <string: path from current file on>;
and it can import functions of other namespaces to the current. An example of a header:
name HeaderExampleLibrary;
type library;
function num ReturnRandomValue {num seed;}
{
set seed [Convert.ToNum:(($seed) + "3"), true];
return (($seed) * ($seed) * ($seed) * ($seed) % 10000);
};
name HeaderExampleNamespace;
type Generic;
import base "HeaderExampleLibrary.TASI"; #You would need the full path of the file with just the import <string path>; statement
start
{
[Inf.DefVar:"num","seed"]
set seed (12);
while true
{
set seed [HeaderExampleLibrary.ReturnRandomValue:seed];
[Console.WriteLine:seed]
};
};
- makeglobalvar:
This statement will create a global variable, that can be used everywhere in your project (So you don't need to import a namespace, to access its global variable (I still gotta implement that)).
Here is how you build the statement:
makeGlobalVar <statement: var type> <statement: var name>;
ormakeGlobalVar <statement: var type> <statement: var name> <static value: initialize value>;
This is just another interpreter mode just like header mode, just with some different rules. Its only purpose is to define a list of variables and it only has one statement type. The statement is built up like this:
<statement var-type> <var name>;
or like this:
link <statement var-type> <var name>; #This will require, that the input is a link.
Here are some examples:
num numberVariable;
string stringVariable;
num aDifferentNumberVariable;
bool aBool;
Btw.: all available types in TASI are:
- num - an internal double number with general purpose number use.
- int - a signed integer for general use, such as file streams.
- string - a text with general purpose text use.
- bool - a true or false value which is internally just another double that's either 1 or 0
- void - void variables don't store values and just symbolize nothing.
Other stuff you may want to know:
-
Errors should get reported - Sometimes there's nothing wrong with your TASI code but with my code. The interpreter tells you (most of the time) if there was an internal error. If you encountered something, always tell us via an issue. Sometimes an internal error also leads to a code syntax error, where the interpreter tells you, that there is something wrong with your code. If however, there seems to be nothing wrong with the code feel free to write an issue. But please check twice that it was actually an internal error.
-
TASI is not case sensitive - Most of the time TASI is just not case sensitive. I tried to stay consistent with that most of the time, but it can happen and become case-sensitive again. If you find a case like that, report it to me.
-
I can't write documentation - If you want to write better documentation, PLEASE do that. Otherwise, if you have any questions about the language, please ask (ask me and not on stack overflow; they'll vote you down - 6 feet under...)
-
You should maybe read this again to fully understand everything...
Best of luck with TASI! You can get the newest release here: https://github.com/Ekischleki/TASI/releases Keep in mind, that what's been written here might not 100% reflect what's actually true. TASI is a language, that's still in development. We'll try to update the documentation with the latest changes.