Skip to content

Commit

Permalink
Merge pull request #1 from sigmasoldi3r/main
Browse files Browse the repository at this point in the history
Nightly release test
  • Loading branch information
sigmasoldi3r authored May 13, 2023
2 parents 44d064b + 0cde5c5 commit 2420687
Show file tree
Hide file tree
Showing 12 changed files with 727 additions and 165 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/build-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Build artifacts

on:
push:
branches: [ "nightly", "release" ]

env:
CARGO_TERM_COLOR: always

jobs:
build_matrix:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
profile: [dev, release]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Build artifacts
run: cargo build --profile ${{ matrix.profile }} --verbose
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3.1.2
with:
path: target/debug/saturnus
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test

on:
pull_request:
branches: [ "main", "release", "nightly" ]
push:
branches: [ "main", "nightly" ]

env:
CARGO_TERM_COLOR: always

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: cargo test --verbose
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "mantra"
name = "saturnus"
version = "0.1.0"
edition = "2021"

Expand Down
56 changes: 56 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Saturnus

**Saturnus** is a programming language that aims to have a simplified mix of
[Rust programming language](https://www.rust-lang.org/) and [Lua](https://www.lua.org/).

The main target for Saturnus compiler is **Lua**, but multi-target compilation
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.

## Why replace Lua?

I like many aspects of Lua, specially how fast and lightweight the VM is. But
original Lua syntax is nowadays a little bit old, and it needs some rework to
make the scripts less verbose and more easy to write.

Among other things, here are some key aspects that Saturnus changes:

- Function syntax is simpler, `fn` instead of `local function`.
- Lambdas are simpler yet familiar, Eg: `fn() 1 + 2 end`.
- More idiomatic class definitions: `class MyClass end` instead of [the classic one](https://www.lua.org/manual/2.4/node36.html).
- Decorators!
- A built-in prelude library for runtime type checks.
- Nice string interpolation.
- Terser loops.
- Built-in operator overloading.
- Custom operators.
- Some [RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) (Which enables reflection).

## Some examples

Some little examples:

```rs
use println from "prelude";
use rti.Typed from "prelude";

class Greeter
let who;

// This will make the function panic if "who" is not a string!
@Typed([rti.String])
fn new(who)
Greeter { who }
end

fn greet(self)
return "Hello {self.who}!";
end
end

// The classic OOP hello world:
let greeter = Greeter::new("Saturnus");
println(greeter.greet());
```
36 changes: 0 additions & 36 deletions src/ast_visitor.rs

This file was deleted.

152 changes: 152 additions & 0 deletions src/code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::f32::consts::E;

use crate::parser::{
Assignment, BinaryExpression, CallExpression, Class, Declaration, DotExpression, Expression,
Function, Lambda, Number, Return, Script, Tuple, UnaryExpression,
};

#[derive(Debug)]
pub struct VisitError;

pub trait Visitor<T> {
// Those need to be implemented explicitly by the user:
fn visit_return(&self, context: T, stmt: &Return) -> Result<T, VisitError>;
fn visit_class(&self, context: T, stmt: &Class) -> Result<T, VisitError>;
fn visit_fn(&self, context: T, stmt: &Function) -> Result<T, VisitError>;
fn visit_assignment(&self, context: T, stmt: &Assignment) -> Result<T, VisitError>;
fn visit_declaration(&self, context: T, stmt: &Declaration) -> Result<T, VisitError>;
fn visit_expression_statement(&self, context: T, stmt: &Expression) -> Result<T, VisitError>;
fn visit_lambda(&self, context: T, expr: &Lambda) -> Result<T, VisitError>;
fn visit_reference(&self, context: T, expr: &DotExpression) -> Result<T, VisitError>;
fn visit_call(&self, context: T, expr: &CallExpression) -> Result<T, VisitError>;
fn visit_tuple(&self, context: T, expr: &Tuple) -> Result<T, VisitError>;
fn visit_number(&self, context: T, expr: &Number) -> Result<T, VisitError>;
fn visit_string(&self, context: T, expr: &String) -> Result<T, VisitError>;
fn visit_unit(&self, context: T) -> Result<T, VisitError>;
fn visit_binary(&self, context: T, expr: &BinaryExpression) -> Result<T, VisitError>;
fn visit_unary(&self, context: T, expr: &UnaryExpression) -> Result<T, VisitError>;

// Generically implementable matching patterns:
fn visit_expression(&self, context: T, expression: &Expression) -> Result<T, VisitError> {
match expression {
Expression::Lambda(e) => self.visit_lambda(context, e),
Expression::Reference(e) => self.visit_reference(context, e),
Expression::Call(e) => self.visit_call(context, e),
Expression::Tuple(e) => self.visit_tuple(context, e),
Expression::Number(e) => self.visit_number(context, e),
Expression::String(e) => self.visit_string(context, e),
Expression::Unit => self.visit_unit(context),
Expression::Binary(e) => self.visit_binary(context, e),
Expression::Unary(e) => self.visit_unary(context, e),
}
}
fn visit_script(&self, context: T, script: &Script) -> Result<T, VisitError> {
script
.statements
.iter()
.fold(Ok(context), |context, stmt| match stmt {
crate::parser::Statement::If => todo!(),
crate::parser::Statement::For => todo!(),
crate::parser::Statement::Loop => todo!(),
crate::parser::Statement::While => todo!(),
crate::parser::Statement::Return(e) => self.visit_return(context?, e),
crate::parser::Statement::Class(e) => self.visit_class(context?, e),
crate::parser::Statement::Function(e) => self.visit_fn(context?, e),
crate::parser::Statement::Assignment(e) => self.visit_assignment(context?, e),
crate::parser::Statement::Declaration(e) => self.visit_declaration(context?, e),
crate::parser::Statement::Match => todo!(),
crate::parser::Statement::Expression(e) => {
self.visit_expression_statement(context?, e)
}
})
}
}

pub struct UnevenIndentationError;
impl std::fmt::Debug for UnevenIndentationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Uneven Indentation: Attempting to pop furhter than 0!")
.finish()
}
}

/// # String code builder
///
/// This simple immutable builder accounts for raw strings (So it is agnostic
/// from the targetted output), but retains indentation aspects.
///
/// This means that you have an indentation stack, with it's state retained
/// between calls, without having to store it in your code emitter.
///
/// Each call, consumes the builder and returns an extended version of it.
/// If you want to preserve the state, clone the structure by calling `.clone()`
/// explicitly.
///
/// Example:
/// ```rs
/// let out = Builder::new(" ")
/// .put("hello")
/// .push().line()
/// .put("my")
/// .pop().unwrap().line()
/// .put("world!")
/// .collect()
/// ```
/// Yields:
/// ```
/// hello
/// my
/// world
/// ```
#[derive(Clone)]
pub struct Builder {
level: u16,
indent: String,
buffer: String,
}
impl Builder {
pub fn new<T>(indent: T) -> Self
where
T: Into<String>,
{
Builder {
level: 0,
indent: indent.into(),
buffer: Default::default(),
}
}
pub fn collect(self) -> String {
self.buffer
}
pub fn push(self) -> Self {
Builder {
level: self.level + 1,
..self
}
}
pub fn pop(self) -> Result<Self, UnevenIndentationError> {
if self.level == 0 {
Err(UnevenIndentationError)
} else {
Ok(Builder {
level: self.level - 1,
..self
})
}
}
pub fn put<T>(self, fragment: T) -> Self
where
T: Into<String>,
{
Builder {
buffer: format!("{}{}", self.buffer, fragment.into()),
..self
}
}
pub fn line(self) -> Self {
Builder {
buffer: format!("{}\n{}", self.buffer, self.indent.repeat(self.level.into())),
..self
}
}
}
10 changes: 10 additions & 0 deletions src/example.foo → src/example.saturn
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ class Foo

end

// How decorators decorate the world:
fn FooFighters()
return fn(target, meta)
println("Decorating class ", meta.name)
return target
end
end

let monoid = fn() true end

@FooFighters
fn foo_fighters()
class bar end
Expand Down
Loading

0 comments on commit 2420687

Please sign in to comment.