From 2970520a9592b5c6d45291f54073552a474b71b4 Mon Sep 17 00:00:00 2001 From: Cody Date: Thu, 30 Mar 2023 02:44:54 -0500 Subject: Restructure --- documentation/tour/annotations.sloth | 24 ++++++++++ documentation/tour/functions.sloth | 68 +++++++++++++++++++++++++++ documentation/tour/literals.sloth | 79 +++++++++++++++++++++++++++++++ documentation/tour/modules.sloth | 82 ++++++++++++++++++++++++++++++++ documentation/tour/tooling.sloth | 88 ++++++++++++++++++++++++++++++++++ documentation/tour/traits.sloth | 34 ++++++++++++++ documentation/tour/types.sloth | 91 ++++++++++++++++++++++++++++++++++++ documentation/tour/variables.sloth | 16 +++++++ 8 files changed, 482 insertions(+) create mode 100644 documentation/tour/annotations.sloth create mode 100644 documentation/tour/functions.sloth create mode 100644 documentation/tour/literals.sloth create mode 100644 documentation/tour/modules.sloth create mode 100644 documentation/tour/tooling.sloth create mode 100644 documentation/tour/traits.sloth create mode 100644 documentation/tour/types.sloth create mode 100644 documentation/tour/variables.sloth (limited to 'documentation') 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 "); +@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`. +## +## 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) -> 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 + "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 = [1, 2, 3, 2]; +val sets: Set = {1, 2, 3, 2}; + +val maps = { + "foo": 48, + "bar": 97, +}; + +val maps: Map = { + "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 +if 'p' in "Apple" {} # impl Contains for String +if "ppl" in "Apple" {} # impl Contains for String +if /[0-9]/ in "24" {} # impl Contains 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 +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, +}; + +# You can easily derive from more traits using the `derive` keyword. +type Person derives Serialize, Deserialize = { + name: String, + age: i32, + hobbies: Set, +}; 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, + grades: Map, +}; + +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 = + | 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; + +# 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; -- cgit v1.2.3