aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/modules/bot.sloth7
-rw-r--r--examples/modules/mod.sloth15
-rw-r--r--examples/webserver.sloth108
-rw-r--r--src/ast.rs0
-rw-r--r--src/ast/display.rs45
-rw-r--r--src/ast/evaluate.rs5
-rw-r--r--src/ast/mod.rs55
-rw-r--r--src/lexer.rs10
-rw-r--r--src/main.rs18
-rw-r--r--tour/literals.sloth48
-rw-r--r--tour/modules.sloth49
-rw-r--r--tour/types.sloth16
12 files changed, 356 insertions, 20 deletions
diff --git a/examples/modules/bot.sloth b/examples/modules/bot.sloth
new file mode 100644
index 0000000..1355ddb
--- /dev/null
+++ b/examples/modules/bot.sloth
@@ -0,0 +1,7 @@
+use slowcord::DiscordBot;
+
+pub type Bot;
+
+impl DiscordBot for Bot {
+ # TODO:
+}
diff --git a/examples/modules/mod.sloth b/examples/modules/mod.sloth
new file mode 100644
index 0000000..75be7e1
--- /dev/null
+++ b/examples/modules/mod.sloth
@@ -0,0 +1,15 @@
+# Root module - Run this to start program
+
+pub use extern "logging";
+pub use extern "dotenv";
+pub use extern "slowcord";
+pub use extern "sqlite";
+
+use logging::LogLevel;
+use bot::Bot;
+
+logging::set_loglevel(LogLevel::WARN);
+
+val token = dotenv::get("TOKEN");
+val bot = Bot::new();
+bot.start(token);
diff --git a/examples/webserver.sloth b/examples/webserver.sloth
new file mode 100644
index 0000000..abbead3
--- /dev/null
+++ b/examples/webserver.sloth
@@ -0,0 +1,108 @@
+# Include the external dependency itself as a module named "slow_api"
+use extern "slowapi" as slow_api;
+
+# Use some things from the "slow_api" module
+use std::serde::Serializable;
+use std::serde::format::Json;
+
+use slow_api::{SlowAPI, Method};
+
+# Construct a slow API server
+val server = SlowApi();
+
+type Person derives Serializable = {
+ name: String,
+ age: Option<String>
+};
+
+fn hello_route(
+ name: Argument<String>,
+ age: Argument<Option<String>>,
+) -> Json<Person> {
+ Person { name, age }
+}
+
+# Start the server
+server
+ .route(Method::GET, "/hello", hello_route)
+ .start("0.0.0.0:8000");
+#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###
+type Poggies;
+
+trait Constructor<..T> {
+ fn new(..T) -> Self;
+}
+
+impl Constructor<> for Poggies {
+ fn new() -> Self {
+ #
+ }
+}
+
+impl<T: Constructor<>> Default for T {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+###
+type Person = {
+ name: String,
+ age: i32,
+};
+
+type Person derives Serialize, Deserialize = {
+ name: String,
+ age: i32,
+};
+
+@route::get("/teacup") # vvvvvv - Requires T to implement Serialize
+fn teacup_route() -> Response<Person> {
+ Response(418, Person {
+ name: "Cody Q",
+ age: 17,
+ })
+}
diff --git a/src/ast.rs b/src/ast.rs
deleted file mode 100644
index e69de29..0000000
--- a/src/ast.rs
+++ /dev/null
diff --git a/src/ast/display.rs b/src/ast/display.rs
new file mode 100644
index 0000000..25b4d23
--- /dev/null
+++ b/src/ast/display.rs
@@ -0,0 +1,45 @@
+use std::fmt::Display;
+
+use super::{Expression, Statement, Value};
+
+impl<'a> Display for Statement<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{{")?;
+ let value = match self {
+ Statement::Val {
+ identifier,
+ initializer,
+ } => format!("val {} {}", identifier.lexeme, initializer),
+ Statement::Var {
+ identifier,
+ initializer,
+ } => format!("var {} {}", identifier.lexeme, initializer),
+ Statement::Expression { expr } => expr.to_string(),
+ };
+ write!(f, "{value}")?;
+ write!(f, "}}")?;
+
+ Ok(())
+ }
+}
+
+impl<'a> Display for Expression<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "(")?;
+ let value = match self {
+ Expression::Literal(value) => value.0.to_string(),
+ Expression::Unary { expr, .. } => format!("+ {}", expr),
+ Expression::Binary { lhs, rhs, .. } => format!("+ {lhs} {rhs}"),
+ };
+ write!(f, "{value}")?;
+ write!(f, ")")?;
+
+ Ok(())
+ }
+}
+
+impl Display for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
diff --git a/src/ast/evaluate.rs b/src/ast/evaluate.rs
new file mode 100644
index 0000000..4d47994
--- /dev/null
+++ b/src/ast/evaluate.rs
@@ -0,0 +1,5 @@
+impl<'a> Expression<'a> {
+ fn evaluate() -> Value {
+ Value(5)
+ }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
new file mode 100644
index 0000000..73e73b3
--- /dev/null
+++ b/src/ast/mod.rs
@@ -0,0 +1,55 @@
+#![allow(dead_code)]
+
+pub mod display;
+
+use crate::lexer::Token;
+
+#[derive(Clone)]
+pub enum Statement<'a> {
+ Val {
+ identifier: &'a Token<'a>,
+ initializer: &'a Expression<'a>,
+ },
+ Var {
+ identifier: &'a Token<'a>,
+ initializer: &'a Expression<'a>,
+ },
+ Expression {
+ expr: &'a Expression<'a>,
+ },
+}
+
+#[derive(Clone)]
+pub enum Expression<'a> {
+ // Basic
+ Literal(Value),
+ Unary {
+ operation: Operation,
+ expr: &'a Expression<'a>,
+ },
+ Binary {
+ operation: Operation,
+ lhs: &'a Expression<'a>,
+ rhs: &'a Expression<'a>,
+ },
+ // Grouping
+}
+
+#[derive(Clone)]
+pub enum Operation {
+ Add,
+ Subtract,
+}
+
+#[derive(Clone)]
+pub struct Value(pub i32);
+
+#[test]
+fn test() {
+ let right = Expression::Literal(Value(7));
+ let _ = Expression::Binary {
+ operation: Operation::Add,
+ lhs: &Expression::Literal(Value(5)),
+ rhs: &right,
+ };
+}
diff --git a/src/lexer.rs b/src/lexer.rs
index 74a006c..27669cb 100644
--- a/src/lexer.rs
+++ b/src/lexer.rs
@@ -3,6 +3,7 @@
#[derive(Debug, Eq, PartialEq)]
pub enum TokenType {
// Utility
+ DocComment(String),
Comment(String),
// Short
@@ -156,6 +157,15 @@ impl<'a> Iterator for Lexer<'a> {
let tt = match character {
// Whitespace & Comments
+ '#' if self.advance_if_eq(Some('#')) => {
+ let mut value = String::new();
+ while self.peek() != Some('\n') {
+ value.push(self.advance().unwrap());
+ }
+
+ TokenType::DocComment(value)
+ }
+
'#' => {
let mut value = String::new();
while self.peek() != Some('\n') {
diff --git a/src/main.rs b/src/main.rs
index 8f3c001..f064d39 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,13 +10,16 @@
pub mod ast;
pub mod lexer;
+use ast::{Expression, Operation, Value};
use lexer::Lexer;
+use crate::ast::Statement;
+
const SOURCE: &str = r#"
val variable = 5;
-if variable >= 7 {
+if variable <= 7 {
print "Hello World";
}
@@ -27,4 +30,17 @@ fn main() {
for token in lexer {
print!("{} ", token.lexeme);
}
+
+ println!("-------");
+
+ let a = Expression::Literal(Value(7));
+ let b = Expression::Binary {
+ operation: Operation::Add,
+ lhs: &Expression::Literal(Value(5)),
+ rhs: &a,
+ };
+
+ let stmt = Statement::Expression { expr: &b };
+
+ println!("{stmt}");
}
diff --git a/tour/literals.sloth b/tour/literals.sloth
index 4aa4533..e2be0d0 100644
--- a/tour/literals.sloth
+++ b/tour/literals.sloth
@@ -31,14 +31,46 @@ val maps: Map<String, i32> = {
"bar": 97,
};
-# `value?` Can be used to bubble up an Option or Result
-# `value!` Can be used to panic on None or Error
-
-maps["foo"] # Option<i32>
-maps["foo"]! # 48
-maps["foo"]? # 48 - Caller of function is responsible for None case
-maps.keys() # ["foo", "bar"]
-maps.values() # [48, 97]
+# Types can be 'any' and be assigned to anything
+var anything: any = "I'm a string right now";
+anything = 53;
+anything = "I was a number- but now I'm a string again";
+
+# You can use the `is` keyword to check if a type is something
+if anything is String {
+ # Now I can call functions that take a String
+ anything.split('-');
+}
+
+# TODO: HMMMMMMM-
+if anything is Some(it) {}
+
+# You can use the `in` keyword to check if something is in a collection
+if "hello" in ["hello", "hola"] {
+ #
+}
+
+# ... or a range
+if 5 in 2..17 {
+ #
+}
+
+# ... or anything that implements Contains<T>
+if 'p' in "Apple" {} # impl Contains<char> for String
+if "ppl" in "Apple" {} # impl Contains<String> for String
+if /[0-9]/ in "24" {} # impl Contains<Regex> for String
+
+# `value!` Can be used to bubble up an Option or Result
+# `value!!` Can be used to panic on None or Error
+# `value?` Can be used to optionally chain
+# `value ?: 0` Can be used to provide a default
+
+maps["foo"] # Option<i32>
+maps["foo"]!! # 48 - Panics in None case
+maps["foo"]! # 48 - Caller of function is responsible for None case
+maps["foo"]?.signum() ?: 0 # 1 - Provide a default for None case
+maps.keys() # ["foo", "bar"]
+maps.values() # [48, 97]
# Spreading
val lhs = [1, 2, 3];
diff --git a/tour/modules.sloth b/tour/modules.sloth
index 1523753..b91261e 100644
--- a/tour/modules.sloth
+++ b/tour/modules.sloth
@@ -24,18 +24,49 @@ use extern "git://github.com/CatDevz/AdventOfCode.git" as spookylib;
use extern "ftp://codyq.dev/libs/spookylib.sloth" as spookylib;
use extern "https://codyq.dev/libs/spookylib.sloth" as spookylib;
-# In order to use modules or members of modules without quantifying the entire
-# path you must include them using a `use` statement. Star imports do not exist
-# because they fucking suck.
+# In sloth files will automatically become modules. Files named `mod.sloth` will
+# become the module for the directory. If you have a project with multiple files
+# the root module will be `mod.sloth`. Anything exposed in this can just strait up
+# be used.
#
+# If no `mod.sloth` exists, for example when running single file scripts, it will
+# be trated as if there is one but its empty.
+
+# /mod.sloth
+pub fn fib(n: i32) -> i32 {
+ match n {
+ 0 | 1 => n,
+ _ => fib(n - 1) + fib(n - 2),
+ }
+}
+
+# /foo.sloth
+use fib; # TODO:
+
+fib(5); # Works because everything inside of mod.sloth is fair game
+
+# This means if you want an extern library to be available in every module you just
+# need to add a `pub(pkg) use extern "lib"`
+
+# In order to use modules or members of modules without quantifying the entire
+# path you must include them using a `use` statement.
+use foo::bar;
+
+# Sloth will use your root module (the one used as your entrypoint) for imports.
+# In order to import from modules relative to your own you must prefix the use
+# with a `::`
+use ::from::relative;
+
+# If you would live to traverse up the module tree you can use the `super`
+# psudo-module.
+use
+
+# TODO:
# Sloth will automatically turn files relative to your own and directories
# relative to your own with a `mod.sloth` into modules. In order to traverse
-# up the module tree you can use the `super` and `pkg` psudo-modules.
-#
-# The super psudo-module will go up a single module in the module tree whereas
-# the pkg psudo-module will go to the root module in the module tree.
-use pkg::a;
-use pkg::b;
+# up the module tree you can use the `super` psudo-module.
+use super::a;
+use super::b;
use std::rand::random;
use std::uuid;
diff --git a/tour/types.sloth b/tour/types.sloth
index c7ef2f5..0fdae33 100644
--- a/tour/types.sloth
+++ b/tour/types.sloth
@@ -32,12 +32,24 @@ type Option<T> =
type Class = Archer | Mage | Tank;
type Operation =
- | Add = (Operation, Operation)
- | Literal = i32;
+ | Add = (Operation, Operation)
+ | Lit = i32;
+
+# Untagged Union Type
+#
+# Unlike tagged unions if all variants of a untagged union implement a specific
+# trait you will be able to call those trait methods without getting the variant
+# first.
+type Untagged = i32 + String;
# Type Alias
type OptionPos = Option<Position>;
+# You can define untagged union types and tuple types inline without a name, this
+# may be useful for one-off types only used by a single function.
+val example: String + i32 = 52;
+val example: (i32, i32) = (5, 5);
+
# Functions can be associated with types using 'impl' blocks.
type Color = (f64, f64, f64);