aboutsummaryrefslogtreecommitdiff
path: root/tour
diff options
context:
space:
mode:
Diffstat (limited to 'tour')
-rw-r--r--tour/annotations.sloth24
-rw-r--r--tour/functions.sloth68
-rw-r--r--tour/literals.sloth47
-rw-r--r--tour/modules.sloth51
-rw-r--r--tour/tooling.sloth88
-rw-r--r--tour/traits.sloth34
-rw-r--r--tour/types.sloth79
-rw-r--r--tour/variables.sloth16
8 files changed, 407 insertions, 0 deletions
diff --git a/tour/annotations.sloth b/tour/annotations.sloth
new file mode 100644
index 0000000..91e88eb
--- /dev/null
+++ b/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/tour/functions.sloth b/tour/functions.sloth
new file mode 100644
index 0000000..e6c8b89
--- /dev/null
+++ b/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) {
+ let result = lhs + rhs;
+ return result;
+}
+
+# ...or manually specified
+pub fn mul(lhs: i32, rhs: i32) -> i32 {
+ let 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 {
+ let result = lhs - rhs;
+ return result;
+}
+
+## Fizzbuzz implementation from 1 through 100
+fn fizzbuzz() {
+ for x in 1..=100 {
+ let 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/tour/literals.sloth b/tour/literals.sloth
new file mode 100644
index 0000000..67e9071
--- /dev/null
+++ b/tour/literals.sloth
@@ -0,0 +1,47 @@
+# Literals
+let number = 85; #TODO: Decide on default integer type
+let number = 85.0; # f64 is the default float type
+
+let number: u16 = 27; # If you want more control over memory usage you can specify a integer type
+let number: u16 = 27u16;
+let number: u16 = 0x1B;
+let number: u16 = 0x1Bu16;
+
+let number: BigInt = BigInt::from(73); #TODO: naming
+let number: BigFloat = BigFloat::from(73); #TODO: naming
+
+let chars: char = ' ';
+
+let strings: String = "Normal string";
+let strings: String = "Formated strings ${number}";
+let strings: String = """String literals""";
+
+let regex: Regex = /[0-9A-F]/;
+
+let list: List<i32> = [1, 2, 3, 2];
+let sets: Set<i32> = {1, 2, 3, 2};
+
+let maps = {
+ "foo": 48,
+ "bar": 97,
+};
+
+let maps: Map<String, i32> = {
+ "foo": 48,
+ "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]
+
+# Spreading
+let lhs = [1, 2, 3];
+let rhs = [4, 5, 6];
+let combined_list = [..lhs, ..rhs, 2, 4, 6];
+let combined_sets = {..lhs, ..rhs, 2, 4, 6};
diff --git a/tour/modules.sloth b/tour/modules.sloth
new file mode 100644
index 0000000..1523753
--- /dev/null
+++ b/tour/modules.sloth
@@ -0,0 +1,51 @@
+# 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 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.
+#
+# 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;
+
+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/tour/tooling.sloth b/tour/tooling.sloth
new file mode 100644
index 0000000..74a74fe
--- /dev/null
+++ b/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/tour/traits.sloth b/tour/traits.sloth
new file mode 100644
index 0000000..80319de
--- /dev/null
+++ b/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/tour/types.sloth b/tour/types.sloth
new file mode 100644
index 0000000..a47fd3a
--- /dev/null
+++ b/tour/types.sloth
@@ -0,0 +1,79 @@
+# Structure Type
+type Person = {
+ name: String,
+ age: i32,
+ hobbies: Set<String>,
+ grades: Map<String, i32>,
+};
+
+let 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)
+ | Literal = i32;
+
+# Type Alias
+type OptionPos = Option<Position>;
+
+# 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
+ }
+
+ let h = (hue * 6).floor() as i32;
+
+ let f = hue * 6 - h;
+ let p = value * (1 - saturation);
+ let q = value * (1 - f * saturation);
+ let 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/tour/variables.sloth b/tour/variables.sloth
new file mode 100644
index 0000000..d105716
--- /dev/null
+++ b/tour/variables.sloth
@@ -0,0 +1,16 @@
+# Variables can be declared using the `let` keyword, however they will be deeply
+# immutable. If you would like to make the variable mutable you can use `let mut`
+#
+# Variables can not be exported and can not be referrenced accross modules.
+let x = 0;
+let mut y = 0;
+
+x = 5; # Invalid
+y = 5; # Valid
+
+# 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;