aboutsummaryrefslogtreecommitdiff
path: root/documentation/tour
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-03-30 02:44:54 -0500
committerCody <cody@codyq.dev>2023-03-30 02:44:54 -0500
commit2970520a9592b5c6d45291f54073552a474b71b4 (patch)
treef42ecc1be0989367cf7c70d0b7909bac0b86904e /documentation/tour
parentbb95375f8b24141bf7dfe5a8b1bba5c995f61253 (diff)
downloadsloth-2970520a9592b5c6d45291f54073552a474b71b4.tar.gz
Restructure
Diffstat (limited to 'documentation/tour')
-rw-r--r--documentation/tour/annotations.sloth24
-rw-r--r--documentation/tour/functions.sloth68
-rw-r--r--documentation/tour/literals.sloth79
-rw-r--r--documentation/tour/modules.sloth82
-rw-r--r--documentation/tour/tooling.sloth88
-rw-r--r--documentation/tour/traits.sloth34
-rw-r--r--documentation/tour/types.sloth91
-rw-r--r--documentation/tour/variables.sloth16
8 files changed, 482 insertions, 0 deletions
diff --git a/documentation/tour/annotations.sloth b/documentation/tour/annotations.sloth
new file mode 100644
index 0000000..91e88eb
--- /dev/null
+++ b/documentation/tour/annotations.sloth
@@ -0,0 +1,24 @@
+# Annotations can be used to provide metadata used by the interpreter or via
+# code using reflection (?).
+#
+# Annotations are scoped with the closest scopes take precedence, so if you
+# had a package scoped annotation with strict mode enabled, but then a locally
+# scoped annotation on a function with strict mode disabled strict mode would
+# be disabled for that function.
+#
+# Scopes available:
+# - package :: the current module and all other modules in the package
+# - module :: only the current module
+# - local :: only the current scope (default)
+# - expr :: only the following expression
+@package:name("Example Sloth program");
+@package:author("Cody <cody@codyq.dev>");
+@package:summary("This program is a little tour de Sloth");
+@package:license("MIT");
+
+# Similarly to TypeScript Sloth is a hybrid between a dyncmially typed and
+# statically typed language, however if you would like to enforce static typing
+# you can enable strict mode.
+#
+# Using strict mode is required for publishing to canopy.
+@package:strict(true);
diff --git a/documentation/tour/functions.sloth b/documentation/tour/functions.sloth
new file mode 100644
index 0000000..529d108
--- /dev/null
+++ b/documentation/tour/functions.sloth
@@ -0,0 +1,68 @@
+# Types can be inferred.
+# If inferrence fails it will be set to "any" unless strict mode is on
+pub fn add(lhs, rhs) {
+ val result = lhs + rhs;
+ return result;
+}
+
+# ...or manually specified
+pub fn mul(lhs: i32, rhs: i32) -> i32 {
+ val result = lhs * rhs;
+ return result;
+}
+
+## Docstrings can be used with 2 pound signs
+##
+## lhs: Left hand side of subtraction
+## rhs: Right hand side of subtraction
+pub fn sub(lhs: i32, rhs: i32) -> i32 {
+ val result = lhs - rhs;
+ return result;
+}
+
+## Fizzbuzz implementation from 1 through 100
+fn fizzbuzz() {
+ for x in 1..=100 {
+ val message = match (x % 5, x % 3) {
+ (0, 0) => "FizzBuzz",
+ (0, _) => "Fizz",
+ (_, 0) => "Buzz",
+ _ => x,
+ };
+
+ print(message);
+ }
+}
+
+## Fizzbuzz implementation using a generator and a range passed into the function
+##
+## Generator functions are convenient ways to create iterators. Whatever the
+## return type is will automatically be wrapped in an Iterator. In the following
+## example the function return type would become `Iterator<String>`.
+##
+## Unlike a normal function you use a yield statement which pauses the function
+## call until the next element is requested. Return can still be used in a
+## generator function, however it will be used to enact a full stop
+generator fn fizzbuzz(range: Range<i32>) -> String {
+ for i in range {
+ yield match (i % 5, i % 3) {
+ (0, 0) => "FizzBuzz",
+ (0, _) => "Fizz",
+ (_, 0) => "Buzz",
+ _ => i,
+ };
+ }
+}
+
+fn print_fizzbuzz() {
+ for message in fizzbuzz(1..=100) {
+ print(message)
+ }
+}
+
+pub fn splitting() {
+ # You are able to call .split and pass in anything that implements Into<Pattern>
+ "Ylc xsBDSv4e BL5m 1BgDSjv dbQj".split(' ');
+ "Ylc xsBDSv4e BL5m 1BgDSjv dbQj".split("DS");
+ "Ylc xsBDSv4e BL5m 1BgDSjv dbQj".split(/[0-9A-F]{2}/);
+}
diff --git a/documentation/tour/literals.sloth b/documentation/tour/literals.sloth
new file mode 100644
index 0000000..53e29cd
--- /dev/null
+++ b/documentation/tour/literals.sloth
@@ -0,0 +1,79 @@
+# Literals
+val number = 85; #TODO: Decide on default integer type
+val number = 85.0; # f64 is the default decimal type
+
+val number: u16 = 27; # If you want more control over memory usage you can specify a integer type
+val number: u16 = 27u16;
+val number: u16 = 0x1B;
+val number: u16 = 0x1Bu16;
+
+val number: BigInt = BigInt::from(73); #TODO: naming
+val number: BigFloat = BigFloat::from(73); #TODO: naming
+
+val chars: char = ' ';
+
+val strings: String = "Normal string";
+val strings: String = "Formated strings ${number}";
+val strings: String = """String literals""";
+
+val regex: Regex = /[0-9A-F]/;
+
+val list: List<i32> = [1, 2, 3, 2];
+val sets: Set<i32> = {1, 2, 3, 2};
+
+val maps = {
+ "foo": 48,
+ "bar": 97,
+};
+
+val maps: Map<String, i32> = {
+ "foo": 48,
+ "bar": 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];
+val rhs = [4, 5, 6];
+val combined_list = [..lhs, ..rhs, 2, 4, 6];
+val combined_sets = {..lhs, ..rhs, 2, 4, 6};
diff --git a/documentation/tour/modules.sloth b/documentation/tour/modules.sloth
new file mode 100644
index 0000000..b91261e
--- /dev/null
+++ b/documentation/tour/modules.sloth
@@ -0,0 +1,82 @@
+# Sloth projects are managed completely within their source code. While this
+# without a doubt has its downsides it is generally nice as to distribute small
+# sloth scripts you only need to distribute the sloth file. No more trying to
+# run a script only to find out you need to install multiple 3rd party packages.
+#
+# As a result of this there needs to be a way in sloth itself to specify
+# dependencies, and this is done with the `use extern` statement.
+#
+# So long as a version is specified and the repository is canopy this is safe
+# without a lock file because all packages published on canopy are required to
+# specify versions for all dependencies, can not override already published
+# versions and can only depend on other packages inside of canopy.
+use extern "http";
+use extern "http" as web;
+use extern "http:1.0.27";
+use extern "canopy://http:1.0.27"; # Explicitly specify `canopy` protocol.
+
+# While it is recommended that you only depend on packages from canopy, you can
+# use packages from 3rd party sources using git (over https), ftp or https. When
+# doing so however you are required to provide a module name with `as`.
+#
+# Versions can only be specified when using `sloth` or `git`
+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 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` psudo-module.
+use super::a;
+use super::b;
+
+use std::rand::random;
+use std::uuid;
+
+use spookylib::spook;
+use http::{get, post};
+use web::Client as WebClient;
+
+# If you would like to export a module you can use the `pub` keyword, unlike
+# other times when the pub keyword is used however sloth will by default only
+# publish it to `pkg`
+pub use parse;
+pub use blog;
diff --git a/documentation/tour/tooling.sloth b/documentation/tour/tooling.sloth
new file mode 100644
index 0000000..74a74fe
--- /dev/null
+++ b/documentation/tour/tooling.sloth
@@ -0,0 +1,88 @@
+#!/usr/bin/env sloth
+
+## Making sure all your imports are correct and lint rules are being followed is
+## important. You can do this in sloth using the `--check` flag.
+##
+## Examples:
+## sloth --check file.sloth
+
+## Testing is important when trying to write resiliant, bug free software. Sloth
+## comes with a full featured testing framework built in. In order to test your
+## projects you can use the `--test` flag.
+##
+## Examples:
+## sloth --test file.sloth
+
+## Benchmarking is important to make sure our software is fast. Sloth comes with
+## a full featured micro-benchmarking framework built in. In order to benchmark
+## your project you can use the `--bench` flag.
+##
+## With our benchmarking framework you will get 3 responses:
+## - cold :: execution time before any code was JIT compiled
+## - warm :: execution time after some code was JIT compiled
+## - hot :: execution time after all code that can be JIT compiled is JIT
+## compiled
+##
+## Examples:
+## sloth --bench file.sloth
+
+## Maintaining the same code style accross an entire project is important while
+## collaborating with others. In order to help with maintining these code styles
+## sloth has a built in formatter that can be ran with the `--format` flag.
+##
+## In addition you can use `--format-mode check` in order to only check if the
+## styles are valid, this is useful for CI pipelines.
+##
+## Examples:
+## slock --format file.sloth
+## slock --format --format-mode check file.sloth
+
+## Dealing with dependencies can be a bit of a pain, in order to make it a bit
+## easier you can automatically update all dependencies in a project using the
+## `--update` flag. This will scan through your project looking for looking for
+## any `use extern` statements with an outdated version specified and update them.
+##
+## Examples:
+## slock --update file.sloth
+
+## In order to push to canopy (the package repository) your dependencies must
+## be locked to a specific version. In order to do this easily you can use the
+## `--lock` flag. This will scan through your project looking for any `use extern`
+## statements without a version specified and automatically specify the latest
+## version.
+##
+## Examples:
+## sloth --lock file.sloth
+
+## Publishing sloth packages to canopy can be done in 3 days, from the site by
+## uploading a zip, from the site from a git repo or using the CLI. It can be
+## done through the CLI using the `--publish` flag.
+##
+## Examples:
+## sloth --publish file.sloth
+
+## If you wish to ahead of time compile your sloth code you can do so with the
+## `--aot` flag. By default this will ahead of time compile to your platform
+## however you can optionally specify one using the `--aot-target` flag.
+##
+## Limitations:
+## - AOT compilation requires strict mode
+##
+## Examples:
+## sloth --aot file.sloth
+## sloth --aot --aot-target wasm file.sloth
+
+# Easily write tests with the test and assert keywords
+test "add function" {
+ assert add(5, 5) == 10;
+ assert add(10, 5) == 15;
+ assert add(10, 5) != 10;
+}
+
+# Easily write benchmarks with the bench & prepare keyword
+bench "add function" {
+ # Use the `prepare` keyword to exclude code from the benchmark
+ prepare client = WebClient::new();
+
+ client.get("https://example.com");
+}
diff --git a/documentation/tour/traits.sloth b/documentation/tour/traits.sloth
new file mode 100644
index 0000000..80319de
--- /dev/null
+++ b/documentation/tour/traits.sloth
@@ -0,0 +1,34 @@
+# Much like Rust's traits or Haskell's type classes sloth uses a trait system for
+# polymorphism.
+trait BasicTrait {
+ fn add() -> i32;
+}
+
+trait AddAssign: Add {
+ fn add_assign(value: i32, rhs: i32) -> i32;
+}
+
+trait Add {
+ fn add(lhs: i32, rhs: i32) -> i32;
+
+ default impl AddAssign {
+ fn add_assign(value: i32, rhs: i32) -> i32 {
+ return add(value, rhs);
+ }
+ }
+}
+
+# In order to make implementing traits easier you can automatically derive traits.
+# Types will implicitly derive from Debug, Copy, Eq and Ord if possible.
+type Person = {
+ name: String,
+ age: i32,
+ hobbies: Set<String>,
+};
+
+# You can easily derive from more traits using the `derive` keyword.
+type Person derives Serialize, Deserialize = {
+ name: String,
+ age: i32,
+ hobbies: Set<String>,
+};
diff --git a/documentation/tour/types.sloth b/documentation/tour/types.sloth
new file mode 100644
index 0000000..8af0856
--- /dev/null
+++ b/documentation/tour/types.sloth
@@ -0,0 +1,91 @@
+# Structure Type
+type Person = {
+ name: String,
+ age: i32,
+ hobbies: Set<String>,
+ grades: Map<String, i32>,
+};
+
+val cody = Person(
+ name: "Cody Q",
+ age: 17,
+ hobbies: {
+ "Computer Science",
+ "Cybersecurity",
+ },
+ grades: {
+ "Computer Science": 100,
+ "Mathmatics": 96,
+ "Physics": 93,
+ "English": 78,
+ },
+);
+
+# Tuple Type
+type Position = (i32, i32);
+
+# Tagged Union Type
+type Option<T> =
+ | None
+ | Some = T;
+
+type Class = Archer | Mage | Tank;
+
+type Operation =
+ | 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);
+
+impl Color {
+ pub const BLACK = Color(0.0, 0.0, 0.0);
+ pub const WHITE = Color(1.0, 1.0, 1.0);
+ pub const RED = Color(1.0, 0.0, 0.0);
+ pub const GREEN = Color(0.0, 1.0, 0.0);
+ pub const BLUE = Color(0.0, 0.0, 1.0);
+
+ ## Get a color from red green and blue
+ pub fn rgb(red: u8, green: u8, blue: u8) -> Color {
+ Color(red / 255.0, green / 255.0, blue / 255.0)
+ }
+
+ ## Get a color from a hue, saturation and value
+ pub fn hsv(hue: f64, saturation: f64, value: f64) -> Color {
+ fn c(value: f64) -> u8 {
+ (value * 255) as u8
+ }
+
+ val h = (hue * 6).floor() as i32;
+
+ val f = hue * 6 - h;
+ val p = value * (1 - saturation);
+ val q = value * (1 - f * saturation);
+ val t = value * (1 - (1 - f) * saturation);
+
+ return match h {
+ 0 => Color(c(value), c(t), c(p)),
+ 1 => Color(c(q), c(value), c(p)),
+ 2 => Color(c(p), c(value), c(t)),
+ 3 => Color(c(p), c(q), c(value)),
+ 4 => Color(c(t), c(p), c(value)),
+ 5 => Color(c(value), c(p), c(q)),
+ _ => Color::BLACK,
+ }
+ }
+}
diff --git a/documentation/tour/variables.sloth b/documentation/tour/variables.sloth
new file mode 100644
index 0000000..c3a9137
--- /dev/null
+++ b/documentation/tour/variables.sloth
@@ -0,0 +1,16 @@
+# Variables can be declared using the `var` keyword, however they will be mutable.
+# If you would like to make the variable immutable you can use `val`
+#
+# Variables can not be exported and can not be referrenced accross modules.
+var x = 0;
+val y = 0;
+
+x = 5; # Valid
+y = 5; # Invalid
+
+# Constants can be declared using the `const` keyword, unlike variables they can
+# be exported and accessed across modules.
+const FPS = 60;
+
+pub const WIDTH = 5;
+pub const HEIGHT = 17;