diff options
Diffstat (limited to 'tour')
| -rw-r--r-- | tour/annotations.sloth | 24 | ||||
| -rw-r--r-- | tour/functions.sloth | 68 | ||||
| -rw-r--r-- | tour/literals.sloth | 47 | ||||
| -rw-r--r-- | tour/modules.sloth | 51 | ||||
| -rw-r--r-- | tour/tooling.sloth | 88 | ||||
| -rw-r--r-- | tour/traits.sloth | 34 | ||||
| -rw-r--r-- | tour/types.sloth | 79 | ||||
| -rw-r--r-- | tour/variables.sloth | 16 |
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; |
