Skip to content

Commit

Permalink
Merge pull request #4 from sigmasoldi3r/main
Browse files Browse the repository at this point in the history
Nightly - Static dispatch, comments, and refined OOP
  • Loading branch information
sigmasoldi3r authored May 15, 2023
2 parents d0be56e + 3640576 commit e5bb39c
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 34 deletions.
1 change: 1 addition & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.lua
21 changes: 21 additions & 0 deletions examples/hello_world_oop.saturn
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This example contains basic Hello World like OOP

// In Saturnus, inheritance is disabled by design.
// Use composition instead.
class Greeter
let name = "";

// A simple method:
fn greet(self)
return "Hello " .. self.name .. "!";
end

// Static methods just don't use the "self" param:
fn new(name)
return Greeter { name };
end
end

// Test it out!
let world = Greeter::new("World");
print(world.greet());
49 changes: 49 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,45 @@ 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.

## Getting started

In order to compile your first file, you can check out the `/examples` folder,
and then, invoke the compiler from a terminal like:

```sh
./saturnus -i examples/hello_world_oop.saturn
```

(Or if you're using windows cmd)
```cmd
.\saturnus.exe -i examples\hello_world_oop.saturn
```

To get more help about the parameters, type:
```sh
./saturnus --help
```

### Where to get the binaries?

Currently the CD is disabled, however you can grab the latest [artifacts from
the nightly branch][nightly], **BUT!**

[nightly]: https://github.com/sigmasoldi3r/Saturnus/actions/workflows/build-artifacts.yml

**BUT...** beware that the artifacts will be surely outdated.

The safest way is just to clone this repository, and run:

```sh
cargo build --release
```

Then you will have the executable at `target/release/saturnus`. (You need the
[Rust tooling][rustup] to make that happen).

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

## Why replace Lua?

I like many aspects of Lua, specially how fast and lightweight the VM is. But
Expand Down Expand Up @@ -54,3 +93,13 @@ end
let greeter = Greeter::new("Saturnus");
println(greeter.greet());
```

## Yet TODO:

- [ ] Implement a simple build system
- [ ] Match structure
- [ ] Add loops (for, while and "loop")
- [ ] Decorator code generation
- [ ] Operator overload
- [ ] Bitwise operators (This one is easy)
- [ ] Custom operator dispatch code generation
8 changes: 4 additions & 4 deletions src/code.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{
Assignment, BinaryExpression, CallExpression, Class, Declaration, DotExpression, Expression,
Function, If, Lambda, Number, Return, Script, Table, Tuple, UnaryExpression, Vector,
Assignment, BinaryExpression, CallExpression, Class, DotExpression, Expression, Function, If,
Lambda, Let, Number, Return, Script, Table, Tuple, UnaryExpression, Vector,
};

#[derive(Debug)]
Expand All @@ -12,7 +12,7 @@ pub trait Visitor<T> {
fn visit_class(&self, ctx: T, stmt: &Class) -> Result<T, VisitError>;
fn visit_fn(&self, ctx: T, stmt: &Function) -> Result<T, VisitError>;
fn visit_assignment(&self, ctx: T, stmt: &Assignment) -> Result<T, VisitError>;
fn visit_declaration(&self, ctx: T, stmt: &Declaration) -> Result<T, VisitError>;
fn visit_declaration(&self, ctx: T, stmt: &Let) -> Result<T, VisitError>;
fn visit_expression_statement(&self, ctx: T, stmt: &Expression) -> Result<T, VisitError>;
fn visit_lambda(&self, ctx: T, expr: &Lambda) -> Result<T, VisitError>;
fn visit_reference(&self, ctx: T, expr: &DotExpression) -> Result<T, VisitError>;
Expand Down Expand Up @@ -56,7 +56,7 @@ pub trait Visitor<T> {
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::Declaration(e) => self.visit_declaration(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),
})
Expand Down
155 changes: 142 additions & 13 deletions src/lua.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::{
code::{self},
parser::{Assignment, BinaryExpression, DotExpression, Identifier, Operator},
parser::{
Assignment, BinaryExpression, DotExpression, Identifier, Lambda, LambdaBody, Operator,
},
};

pub struct LuaEmitter;
Expand All @@ -21,11 +23,90 @@ impl code::Visitor<code::Builder> for LuaEmitter {
ctx: code::Builder,
stmt: &crate::parser::Class,
) -> Result<code::Builder, code::VisitError> {
Ok(ctx
let ctx = ctx
.line()
.put("local ")
.put(stmt.name.0.clone())
.put(" = {};"))
.put(format!("local {} = {{}};", stmt.name.0.clone()))
.line()
.put(format!("{}.__meta__ = {{}};", stmt.name.0.clone()))
.line()
.put(format!(
"{}.__meta__.__call = function(self, struct)",
stmt.name.0.clone()
))
.push()
.line()
.put("return setmetatable(struct, self.prototype.__meta__);")
.pop()
.unwrap()
.line()
.put("end;")
.line()
.put(format!("{}.prototype = {{}};", stmt.name.0.clone()))
.line()
.put(format!(
"{}.prototype.__meta__ = {{}};",
stmt.name.0.clone()
))
.line()
.put(format!(
"{}.prototype.__meta__.__index = {}.prototype;",
stmt.name.0.clone(),
stmt.name.0.clone()
))
.line()
.put(format!(
"setmetatable({}, {}.__meta__);",
stmt.name.0.clone(),
stmt.name.0.clone()
));
let ctx = stmt.fields.iter().fold(Ok(ctx), |ctx, field| {
let ctx = ctx?.line();
let ctx = match field {
crate::parser::ClassField::Method(f) => {
let level = if let Some(first) = f.arguments.first() {
if first.name.0 == "self" {
".prototype."
} else {
"."
}
} else {
"."
}
.to_string();
let ctx = ctx
.put(stmt.name.0.clone())
.put(level)
.put(f.name.0.clone())
.put(" = ");
let ctx = self.visit_lambda(
ctx,
&Lambda {
arguments: f.arguments.clone(),
body: LambdaBody::Complex(f.body.clone()),
},
)?;
ctx.put(";")
}
crate::parser::ClassField::Let(f) => {
let ctx = ctx
.put(stmt.name.0.clone())
.put(".prototype.")
.put(f.target.0.clone())
.put(" = ");
let ctx = if let Some(value) = f.value.as_ref() {
self.visit_expression(ctx, value)?
} else {
ctx.put("nil")
};
ctx.put(";")
}
crate::parser::ClassField::Operator(_) => {
todo!("Operator overload not implemented yet")
}
};
Ok(ctx)
})?;
Ok(ctx)
}

fn visit_fn(
Expand Down Expand Up @@ -81,7 +162,7 @@ impl code::Visitor<code::Builder> for LuaEmitter {
fn visit_declaration(
&self,
ctx: code::Builder,
stmt: &crate::parser::Declaration,
stmt: &crate::parser::Let,
) -> Result<code::Builder, code::VisitError> {
let ctx = ctx
.line()
Expand Down Expand Up @@ -132,7 +213,11 @@ impl code::Visitor<code::Builder> for LuaEmitter {
ctx: code::Builder,
expr: &crate::parser::DotExpression,
) -> Result<code::Builder, code::VisitError> {
let ctx = ctx.put(expr.0.first().unwrap().0.clone());
let ctx = if let Some(first) = expr.0.first() {
ctx.put(first.0.clone())
} else {
ctx
};
let ctx = expr
.0
.iter()
Expand All @@ -146,13 +231,37 @@ impl code::Visitor<code::Builder> for LuaEmitter {
ctx: code::Builder,
expr: &crate::parser::CallExpression,
) -> Result<code::Builder, code::VisitError> {
let ctx = self.visit_reference(ctx, &expr.target)?.put("(");
let ctx = if let Some(first) = expr.arguments.0.first() {
let dot = if expr.static_target.is_some() {
expr.target.clone()
} else if expr.target.0.len() > 1 {
DotExpression(
expr.target
.0
.iter()
.rev()
.skip(1)
.rev()
.map(|x| x.clone())
.collect(),
)
} else {
expr.target.clone()
};
let ctx = self.visit_reference(ctx, &dot)?;
let ctx = if let Some(static_target) = expr.static_target.as_ref() {
ctx.put(".").put(static_target.0.clone())
} else if expr.target.0.len() > 1 {
ctx.put(":").put(expr.target.0.last().unwrap().0.clone())
} else {
ctx
};
let ctx = ctx.put("(");
let ctx = if let Some(first) = expr.arguments.first() {
self.visit_expression(ctx, first)?
} else {
ctx
};
let ctx = expr.arguments.0.iter().skip(1).fold(Ok(ctx), |ctx, expr| {
let ctx = expr.arguments.iter().skip(1).fold(Ok(ctx), |ctx, expr| {
self.visit_expression(ctx.map(|b| b.put(", "))?, expr)
})?;
Ok(ctx.put(")"))
Expand All @@ -163,7 +272,26 @@ impl code::Visitor<code::Builder> for LuaEmitter {
ctx: code::Builder,
expr: &crate::parser::Tuple,
) -> Result<code::Builder, code::VisitError> {
todo!()
let ctx = ctx.put("{");
let ctx = if let Some(first) = expr.0.first().as_ref() {
let ctx = ctx.put(format!("_0 = "));
self.visit_expression(ctx, first)?
} else {
ctx
};
let ctx = expr
.0
.iter()
.skip(1)
.fold(Ok((ctx, 1_u16)), |ctx, value| {
let (ctx, i) = ctx?;
let ctx = ctx.put(format!(", _{} = ", i));
let ctx = self.visit_expression(ctx, value)?;
Ok((ctx, i + 1))
})?
.0;
let ctx = ctx.put("}");
Ok(ctx)
}

fn visit_number(
Expand Down Expand Up @@ -196,22 +324,23 @@ impl code::Visitor<code::Builder> for LuaEmitter {
expr: &crate::parser::BinaryExpression,
) -> Result<code::Builder, code::VisitError> {
let ctx = self.visit_expression(ctx, &expr.left)?.put(" ");
let ctx = match expr.operator {
let ctx = match expr.operator.clone() {
// Basic math
Operator::Plus => ctx.put("+"),
Operator::Minus => ctx.put("-"),
Operator::Product => ctx.put("*"),
Operator::Quotient => ctx.put("/"),
Operator::Remainder => ctx.put("%"),
Operator::Power => ctx.put("**"),
Operator::Concat => ctx.put(".."),
// Comparison
Operator::Greater => ctx.put(">"),
Operator::GreaterEqual => ctx.put(">="),
Operator::Less => ctx.put("<"),
Operator::LessEqual => ctx.put("<="),
Operator::Equal => ctx.put("=="),
Operator::NotEqual => ctx.put("~="),
_ => todo!("Binary operator not supported!"),
op => todo!("Binary operator {:?} not supported!", op),
};
let ctx = self.visit_expression(ctx.put(" "), &expr.right)?;
Ok(ctx)
Expand Down
18 changes: 17 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ struct Args {
help = "Prints the compilation result to the standard output"
)]
pipe: bool,
#[arg(
long,
help = "If used, the compilation output emits tab characters. Ignores indentation parameter"
)]
use_tabs: bool,
#[arg(
default_value = "2",
long,
help = "The amount of space characters to use in each tab"
)]
indentation: usize,
}

fn get_default_output(str: &String) -> String {
Expand All @@ -46,14 +57,19 @@ fn get_default_output(str: &String) -> String {

fn main() {
let args = Args::parse();
let indent = if args.use_tabs {
"\t".to_string()
} else {
" ".repeat(args.indentation)
};
let in_path = args.input;
let out_path = args.output.unwrap_or(get_default_output(&in_path));
let mut in_file = File::open(in_path).unwrap();
let mut input = String::new();
in_file.read_to_string(&mut input).unwrap();
let output = lua::LuaEmitter
.visit_script(
code::Builder::new(" "),
code::Builder::new(indent),
&parser::Script::parse(input).unwrap(),
)
.unwrap()
Expand Down
Loading

0 comments on commit e5bb39c

Please sign in to comment.