Skip to content

Commit

Permalink
Merge pull request #5 from sigmasoldi3r/main
Browse files Browse the repository at this point in the history
Nightly - Fixed assignments, loops, Janus tooling and more
  • Loading branch information
sigmasoldi3r authored May 16, 2023
2 parents e5bb39c + a75a6df commit 8a9671f
Show file tree
Hide file tree
Showing 17 changed files with 1,701 additions and 328 deletions.
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rust-analyzer.linkedProjects": [
".\\tools\\janus\\Cargo.toml",
".\\Cargo.toml"
]
}
9 changes: 9 additions & 0 deletions examples/hello_world_oop.saturn
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ class Greeter
fn new(name)
return Greeter { name };
end

// Operator overload also is here!
operator +(self, other)
return Greeter::new(self.name .. " & " .. other.name);
end
end

// Test it out!
let world = Greeter::new("World");
print(world.greet());

// Test operators:
let sum = Greeter::new("Foo") + Greeter::new("Bar");
print(sum.greet());
182 changes: 179 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ will arrive in the future, so stay tuned if you like the language.
The original purpose of this language was to provide an easy-to-learn syntax,
and fast compilation times, to replace Lua scripts currently.

Wanna see how it looks? [Jump to Language Basics](#language-basics)!

## Getting started

In order to compile your first file, you can check out the `/examples` folder,
Expand Down Expand Up @@ -48,6 +50,176 @@ Then you will have the executable at `target/release/saturnus`. (You need the

[rustup]: https://www.rust-lang.org/learn/get-started

## Language Basics

> *note*: Some structures will be pretty similar to Lua, so this assumes you
> have some knowledge about programming in Lua.
Declarations and function calls:

```rs
// Variables are easy stuff in Saturnus:
let a = "be something";
// period.
```

Now, function calls can be something more complex.

Imagine that you have static functions (aka do not belong to an object):

```rs
some_function(1, 2, 3);
let b = fetch("some url");
// Note that like in lua, you can pass [], {} and "" without parentheses:
let bar = joining [1, 2, 3]
let c = foo { bar }
// etc
```

Those will be dispatched statically, and everyone will be safe & sound. But in
real world, you will have functions inside raw objects or class instances, here
is where things get tricky:

```rs
// Imagine a table with a static function like math's max()
// You have to use the static dispatch mode:
let max = math::max(1, 2);
// This is crucial, otherwise things will berak.

// BUT!
// If the object is an instance of a class, or an object that needs to access
// self's context, like, for example a "person" instance, you will have to use
// the dot (aka dynamic dispatch mode):
let name = person.get_name();
// As long as you remember that, you'll be safe & sound.
// Another side note, that obviously does not apply to fields of an object:
let things = that_do.not.care.about_dispatch;
// Because fields do not have the notion of "dispatching" (They're not functions
// at all!).
```

The loops:

In _Saturnus_ you can loop with four different structures: `while`, `while let`,
`for` and `loop` (See comments):

```rs
// The basic loop!
// Will repeat as long as the expression between "while" and "do" words is
// true-like (Can evaluate to "true").
while something() do
print("Something is true!");
end

// This one is a sugar syntax introduced by Saturnus!
// Imagine you want to loop as long as you have a result that is not null, you
// could use iterators, reserve your own local variables and such, but we
// have a more idiomatic syntax sugar for you:
while let some = thing() do
// The "some" variable is only visible within the loop, and you know that
// will be a true-ish value (Like 1, true or something not null).
print("Some is " .. some);
end

// Now, the classical foreach:
for entry in entries() do
print(entry._0 .. " = " .. entry._1);
end
// Note: This is a raw iterator loop, and cannot be used in place of an
// iterator! This means that is no replacement for pairs function (and also
// it does NOT work well with it...)
// This assumes that what you have between "in" and "do" returns an iterator
// of a single entry value.
// To transform collections to iterators, you will need some prelude functions.

// And the final, the simplest and the dumbest:
loop
print("I'm looping forever...");
if should_exit() then
print("Or I am?");
return true;
end
end
// Note: Has no exit condition, you will have to either "break" or "return"!
```

That covers what _Saturnus_ can offer for now, in terms of looping.

Now, this follows conditions! We have `if`, `if else` and `else` at the moment:

```rs
// If statements are pretty close to Lua, as you can witness here:
if something() then
print("Something was true!");
end

if a then
print("A");
else
print("Not A...");
end

// The only difference is that "else if" is separated with a space instead
// of being the word elseif.
if a then
print("A");
else if b then
print("B");
else
print("woops");
end
```

Functions!

Functions are declared like Lua ones, using `fn` keyword, but with a catch:
They are **always** local, never global (That is forbidden by design).

```rs
// Fair enough:
fn some_func(a, b)
return a + b;
end

// Oh, you can also have anonymous functions by the way!
let anon = fn(a, b)
return a + b;
end

// And if an anonymous function ONLY has one expression inside (Without ";"),
// that expression is an implicit return statement:
collections::reduce([1, 2, 3], fn(a, b) a + b end);
// Pretty cool
```

Time for some object oriented programming! Yes, _Saturnus_ has classes, of
course, but with a catch: We forbid inheritance by design, which does not
eliminate polymorphism.

```rs
class Person
// Fields (which are optional btw), are declared as variables:
let name = "unnamed";

// Methods, like normal functions, but remember that if the first (and only
// the first) argument is "self", it will be a dynamic method, and if that is
// absent, it will be compiled as a static method:
fn get_name(self)
return self.name;
end

// Example of an static method, where the usage is shown later:
fn greet(person)
print("Greetings " .. person.name .. "!");
end
end

// Here you'll clearly see the difference:
let person = Person { name: "Mr. Foo" };
let name = person.get_name(); // Dynamic dispatch
Person::greet(person); // Static method dispatch!
```

## Why replace Lua?

I like many aspects of Lua, specially how fast and lightweight the VM is. But
Expand All @@ -67,16 +239,19 @@ Among other things, here are some key aspects that Saturnus changes:
- Custom operators.
- Some [RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) (Which enables reflection).

## Some examples
## How does it look?

Some little examples:
> *note*: The "prelude" library is not yet implemented, and the module import
> is yet to be drafted.
```rs
use println from "prelude";
use rti.Typed from "prelude";
// Old lua way? This is also compatible, but not very cross-target friendly.
let my_mod = require "My Mod";

class Greeter
let who;
let who = "Unnamed";

// This will make the function panic if "who" is not a string!
@Typed([rti.String])
Expand All @@ -103,3 +278,4 @@ println(greeter.greet());
- [ ] Operator overload
- [ ] Bitwise operators (This one is easy)
- [ ] Custom operator dispatch code generation
- [ ] Destructuring assignment
33 changes: 18 additions & 15 deletions src/code.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::parser::{
Assignment, BinaryExpression, CallExpression, Class, DotExpression, Expression, Function, If,
Lambda, Let, Number, Return, Script, Table, Tuple, UnaryExpression, Vector,
};
use ast::*;

use crate::parser::{ast, Script};

#[derive(Debug)]
pub struct VisitError;
Expand All @@ -26,6 +25,10 @@ pub trait Visitor<T> {
fn visit_if(&self, ctx: T, expr: &If) -> Result<T, VisitError>;
fn visit_table(&self, ctx: T, expr: &Table) -> Result<T, VisitError>;
fn visit_vector(&self, ctx: T, expr: &Vector) -> Result<T, VisitError>;
fn visit_for(&self, ctx: T, expr: &For) -> Result<T, VisitError>;
fn visit_while(&self, ctx: T, expr: &While) -> Result<T, VisitError>;
fn visit_loop(&self, ctx: T, expr: &Loop) -> Result<T, VisitError>;
fn visit_match(&self, ctx: T, expr: &Match) -> Result<T, VisitError>;

// Generically implementable matching patterns:
fn visit_expression(&self, ctx: T, expression: &Expression) -> Result<T, VisitError> {
Expand All @@ -48,17 +51,17 @@ pub trait Visitor<T> {
.statements
.iter()
.fold(Ok(ctx), |ctx, stmt| match stmt {
crate::parser::Statement::If(e) => self.visit_if(ctx?, e),
crate::parser::Statement::For => todo!(),
crate::parser::Statement::Loop => todo!(),
crate::parser::Statement::While => todo!(),
crate::parser::Statement::Return(e) => self.visit_return(ctx?, e),
crate::parser::Statement::Class(e) => self.visit_class(ctx?, e),
crate::parser::Statement::Function(e) => self.visit_fn(ctx?, e),
crate::parser::Statement::Assignment(e) => self.visit_assignment(ctx?, e),
crate::parser::Statement::Let(e) => self.visit_declaration(ctx?, e),
crate::parser::Statement::Match => todo!(),
crate::parser::Statement::Expression(e) => self.visit_expression_statement(ctx?, e),
ast::Statement::If(e) => self.visit_if(ctx?, e),
ast::Statement::For(e) => self.visit_for(ctx?, e),
ast::Statement::Loop(e) => self.visit_loop(ctx?, e),
ast::Statement::While(e) => self.visit_while(ctx?, e),
ast::Statement::Return(e) => self.visit_return(ctx?, e),
ast::Statement::Class(e) => self.visit_class(ctx?, e),
ast::Statement::Function(e) => self.visit_fn(ctx?, e),
ast::Statement::Assignment(e) => self.visit_assignment(ctx?, e),
ast::Statement::Let(e) => self.visit_declaration(ctx?, e),
ast::Statement::Match(e) => self.visit_match(ctx?, e),
ast::Statement::Expression(e) => self.visit_expression_statement(ctx?, e),
})
}
}
Expand Down
Loading

0 comments on commit 8a9671f

Please sign in to comment.