Crates.io | tsar |
lib.rs | tsar |
version | 0.3.0 |
source | src |
created_at | 2019-10-05 21:39:47.134991 |
updated_at | 2019-12-22 02:02:55.997076 |
description | Tsar programming language |
homepage | https://github.com/adam-mcdaniel/tsar |
repository | https://github.com/adam-mcdaniel/tsar |
max_upload_size | |
id | 170230 |
size | 8,002,719 |
Tsar is a dynamically typed, ahead of time compiled programming language. Tsar targets the Xasm intermediate representation, which can compile to either Golang or Rust. As a result, if a Tsar program fails to build with Golang (the primary target language), Rust is used as a fallback.
tsar x.x.x
adam-mcdaniel <adam.mcdaniel17@gmail.com>
Compiler for the Tsar programming langauge
USAGE:
tsar [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
build Build a Tsar package
help Prints this message or the help of the given subcommand(s)
new Create new Tsar package
run Run a Tsar package
TODO: Make a document for each respective OS
First, install Rust.
# For *nix only
# Installs Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then install git.
# For debian based OS only
sudo apt install git
If you want to target golang (highly recommended), install it here
# For debian based OS only
sudo apt install golang-go
# For MacOS, use the golang link above.
Note that Tsar builds significantly slower on Windows. Expect a couple of seconds of extra time when compiling / running your tsar packages. Using Tsar on linux is recommended.
Before you start, you must install visual studio, and git. You will not be able to compile without these!
Be sure to follow the instructions carefully, and use the defaults for both installers.
After following the instructions for your respective operating system, run the following in your shell.
cargo install -f tsar
After the installation completes, you will be able to create a tsar package!
TODO: This will become its own book / document in the future
To start, create a tsar package and test it.
# Where I will create my package
cd ~/Documents/tsar
# This will create a package named `test` under a folder with the same name
tsar new test
# Enter the folder
cd test
# Edit your program
nano src/bin.ts
# Compile your package
tsar build
# Your executable is in the `target` folder!
# Alternatively, you can `run` your package
tsar run
Now we can start programming!
Although Tsar is meant to behave somewhat like Python, its syntax is inspired by Rust.
If you do somehow make a syntactical mistake, Tsar will try to help correct you.
Tsar uses C and C++ style comments.
// Im a comment!!
/*
Im also a comment!
*/
println("Im not a comment!");
Use
statements allow you to import objects from imported modules.
Use
statements MUST ONLY be at the top of your file! You may not use use
statements anywhere else in your code because of code cleanliness. This allows for more readable code.
Use statements must import by name only, you may not use the *
symbol like in Python.
You can use
one object from a module like so.
use core::string::fmt;
You can also import multiple objects.
use core::math::{sin, cos, tan};
There are two major kinds of identifiers: unscoped and scoped names.
Unscoped names are just regular identifiers like abc123
, tsar
, i
, hey_jude71
.
Scoped names use the ::
operator. This operator lets you access names from within modules.
If I want to call the hour
function from the time
module in the std
module, I can do it like so.
println(std::time::hour());
Assignment in Tsar is done with the =
operator. Variables are declared when they are first assigned to.
a = 5;
a = "String";
println(a);
Loops and If Statements are the structures for control flow in Tsar.
If statements are either single or double pronged like so.
fn odd(n) => n % 2;
fn collatz(n) {
if odd(n) { 3 * n + 1 }
else { n / 2 }
}
if true {
println(collatz(5));
}
Parentheses are not required for loop or if statements.
While loops are executed until the test expression is false.
n = 10
while n isnt 0 {
println(n);
n = n - 1;
}
println("loop finished");
For loops iterate over each index and value in a list.
use core::string::fmt;
for index, element in range(0, 10) {
println("Index of element: " + fmt(index));
println("Element itself: " + fmt(element));
}
For unused index or element values when iterating, use the _
variable for good convention sake.
In Tsar, the stack is shared across different scopes. This means that values returned by functions are simply pushed onto the stack to be retrieved later.
To return a value from a function, just leave the expression to return at the end of your function.
The last line of the function does not require a semicolon.
fn function_name(a, b) {
result = a + b;
result
}
Functions that only do one computation can be written like so.
fn sum(array) => reduce(array, add, 0);
Functions written like this do require a semicolon, though.
Anonymous functions can be expressed with the following syntax.
|a, b| {
result = a + b;
result
};
This function takes two arguments a
and b
, and returns a + b
.
Functions can be called with the ()
operators like in most other languages.
f = |a, b| { a + b };
fn increment(n) => n + 1;
println(f(4, 5));
println(increment(0));
Objects are very useful for encapsulating mutating code. To maintain code cleanliness, Tsar does away with class inheritance.
Objects are defined like so.
The new
method is used as the constructor for an object. THe new
method takes at least one argument (the reference to the current instance), and returns that instance.
YOU MUST RETURN YOUR INSTANCE AT THE END OF THE new
METHOD!!!
impl Point {
fn new(self, x, y) {
self.x = x;
self.y = y;
self
}
fn goto(self, x, y) {
self.x = x;
self.y = y;
}
}
To instantiate your object, use the new
function.
p1 = new(Point, 5, 6);
println(p1);
Attributes of an object can be accessed similarly to lists with the []
operators as well as the .
operator.
impl Example {
fn new(self) {
self.a = 5;
self
}
}
e = new(Example);
println(e["a"]);
println(e.a);
Before you start programming, you should know your available builtin functions imported from the core
and std
modules.
This function prints an object without printing a newline.
print("test\n");
This function prints an object and a newline.
println("test");
The following segment creates a list with 3 elements, [1, 2, 3]
. The []
syntax to create a list is just shorthand for creating a call to the list
functions.
n = 3
l = list(n, 1, 2, 3);
println(l);
This function returns the length of a list or string.
println(len("testing"));
println(len([1, 2, 3]));
This function returns a list with an object appended to the end.
println(push([1], 2) is [1, 2]);
This function returns
impl List {
fn new(self) { self.items = []; self }
fn push(self, item) {
self.items = push(self.items, item);
}
fn pop(self) {
self.items = pop(self.items);
}
}
l = new(List);
l.push(5);
println(l.pop());
This function returns a list of values from one number to another (exclusive).
println(range(0, 3))
This function returns a reversed list.
println(reverse(range(0, 3)));
This function maps a list into another using a function.
fn increment(n) => n + 1;
println(
map([0, 0, 0, 0], increment)
);
This function is used to filter a list for accepted values. It takes a list and a function. For each element in the list, if f(element)
is true, the item will be placed in the resulting list.
The following will print a list with only odd numbers.
fn odd(n) => (n % 2) is 1;
println(filter([1, 2, 3, 4, 5], odd));
This function is used to reduce a list into an atomic value. Reduce takes a list, a function to reduce with (that takes two arguments itself), and an initial value.
fn sum(values) => reduce(values, add, 0);
fn factorial(n) => reduce(range(1, n+1), mul, 1);
println(sum([1, 2, 3]));
println(factorial(5));
This function creates an instance of an object.
impl Point {
fn new(self, x, y) {
self.x = x;
self.y = y;
self
}
}
p1 = new(Point, 1, 1);
println(p1);