├── .gitignore ├── ADTs ├── README.md └── adts.b ├── Args-M ├── README.md └── args.b ├── Arrays ├── README.md └── arrays.b ├── Channels ├── README.md └── chans.b ├── Constants ├── README.md └── const.b ├── Exceptions ├── README.md └── exceptions.b ├── Function-Refs ├── README.md └── funcrefs.b ├── Functions ├── README.md └── func.b ├── Generics ├── README.md └── generics.b ├── HelloWorld ├── README.md └── hello.b ├── If-Else ├── README.md └── ifelse.b ├── LICENSE ├── Lists ├── README.md └── lists.b ├── Loops ├── README.md └── loops.b ├── Modules ├── README.md ├── mkfile ├── modules.b ├── persons.b ├── persons.m ├── towns.b ├── towns.m └── util.b ├── README.md ├── Slices ├── README.md └── slices.b ├── Spawn ├── README.md └── spawn.b ├── Switch ├── README.md └── switch.b └── Values ├── README.md └── values.b /.gitignore: -------------------------------------------------------------------------------- 1 | *.sbl 2 | *.dis 3 | -------------------------------------------------------------------------------- /ADTs/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Data Types (ADT's) 2 | 3 | Limbo supports Abstract Data Types (ADT's). These are analogous, but not equivocal to, structs in C and Go. 4 | 5 | For further reading on ADT's and how they compose in different contexts, see [Modules](../Modules) and [Generics](../Generics). 6 | 7 | ## Source 8 | 9 | ### adts.b:12,16 10 | 11 | The type `Person` is an ADT declared within a module definition possessing an integer, string, and function reference member. 12 | 13 | The function is named stringify and takes an argument named `p` which is a reference to the adt variable instance which called the function. This function could be thought as being analogous to a method. 14 | 15 | Method-like function references declared with `self` as a keyword for an argument are not required to be called with said argument being passed. 16 | 17 | Note: Modules are a form of ADT with some special rules associated with them that differentiates them from a normal ADT. 18 | 19 | ### adts.b:19,23 20 | 21 | The type `Town` is an ADT declared outside of a module definition possessing an array of references to Person ADT's, a string, and a function reference. 22 | 23 | ### adts.b:28,29 24 | 25 | The variable `p` is created as a reference to an instance of a `Person` type which is instantiated using constructor-like syntax. The `Person` element `name` is then printed. 26 | 27 | Note: Although the syntax used to instantiate the `Person` type instance appears to be a function, it is closer to a tuple being passed into an instance of a `Person`, but used in an in-line fashion. That is, the tuple must contain all non-function reference types contained within the ADT. 28 | 29 | ### adts.b:31,36 30 | 31 | The variable `t` is created as an instance of a `Town` ADT. The non-function reference elements are then initialized manually. Note that the `ref Person(...)` instantiation can be used as a valid variable instance. 32 | 33 | ### adts.b:41,52 34 | 35 | The method-like function references are declared with a bit of special syntax. That is, the function name takes the form of `ADT.func()`. 36 | 37 | ## Demo 38 | 39 | ; adts 40 | Spike 41 | Name: Mars 42 | Size: 2 43 | Members: 44 | → Spike 45 | → Ed 46 | ; 47 | 48 | ## Exercises 49 | 50 | - Can you instantiate an ADT via its constructor without all its non-function reference members? 51 | - How would you format the constructor-style instantiation of a `ref Town`? 52 | -------------------------------------------------------------------------------- /ADTs/adts.b: -------------------------------------------------------------------------------- 1 | implement ADTs; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | ADTs: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | 12 | Person: adt { 13 | age: int; 14 | name: string; 15 | stringify: fn(p: self ref Person): string; 16 | }; 17 | }; 18 | 19 | Town: adt { 20 | pop: array of ref Person; 21 | name: string; 22 | stringify: fn(t: self ref Town): string; 23 | }; 24 | 25 | init(nil: ref Draw->Context, nil: list of string) { 26 | sys = load Sys Sys->PATH; 27 | 28 | p := ref Person(27, "Spike"); 29 | print("%s\n", p.name); 30 | 31 | t: Town; 32 | t.pop = array[] of {p, ref Person(13, "Ed")}; 33 | t.name = "Mars"; 34 | 35 | town := ref t; 36 | print("%s\n", town.stringify()); 37 | 38 | exit; 39 | } 40 | 41 | Person.stringify(p: self ref Person): string { 42 | return p.name; 43 | } 44 | 45 | Town.stringify(t: self ref Town): string { 46 | s := "Name: " + t.name + "\nSize: " + string len t.pop + "\nMembers:"; 47 | 48 | for(i := 0; i < len t.pop; i++) 49 | s += "\n→ " + t.pop[i].stringify(); 50 | 51 | return s; 52 | } 53 | -------------------------------------------------------------------------------- /Args-M/README.md: -------------------------------------------------------------------------------- 1 | # Command-Line Arguments 2 | 3 | Inferno has a dedicated module for processing commandline flags and arguments, [arg(2)](http://man.cat-v.org/inferno/2/arg). 4 | 5 | ## Source 6 | 7 | ### args.b:17,22 8 | 9 | The module `Arg` is loaded and initialized. As per the manual, the `arg->init()` function must be called before any other functions can be called from `Arg`. 10 | 11 | The usage message is also set pre-emptively for use later, if necessary. 12 | 13 | ### args.b:24,37 14 | 15 | There are two flag arguments, `r` and `c` which set reversal of arguments and the list item indicator mark, respectively. 16 | 17 | The flag `r` is a binary option toggle, the flag's presence as an argument is sufficient to change the value of `rev`. 18 | 19 | The flag `c` takes an argument. The `arg->earg()` function is used to pop the flag's argument out of the list. Specifically with regards to `earg()` rather than `arg()` as per the manual, `earg()` will call `arg->usage()` if the relevant argument does not exist. 20 | 21 | Note: After the processing of all flags, `argv` is re-set from the value contained within `arg` to remove the elements (if any) utilized by flags and their arguments. 22 | 23 | ### args.b:41,51 24 | 25 | This section utilizes the `rev` variable to (naively) reverse the list `argv`. After the `rev` check, the list `argv` is printed in order from beginning to end with each element being printed on a new line with the `mark` variable being prefixed to the list element currently at the head of the `argv` list each iteration during printout. 26 | 27 | ## Demo 28 | 29 | ; limbo args.b 30 | ; args -h 31 | usage: args [-r] [-c mark] words... 32 | ; args a b c d 33 | Argc after flags: 4 34 | → a 35 | → b 36 | → c 37 | → d 38 | ; args -r a b c d 39 | Argc after flags: 4 40 | → d 41 | → c 42 | → b 43 | → a 44 | ; args -c 'quack: ' a b c d 45 | Argc after flags: 4 46 | quack: a 47 | quack: b 48 | quack: c 49 | quack: d 50 | ; args -r -c '-> ' d c b a 51 | Argc after flags: 4 52 | -> a 53 | -> b 54 | -> c 55 | -> d 56 | ; 57 | 58 | ## Exercises 59 | 60 | - Can you trick `earg()` into accepting a nil value? 61 | - How would you pull an `int` out of `earg()`? 62 | - How would you trip the `usage()` message call-able from `earg()`? 63 | -------------------------------------------------------------------------------- /Args-M/args.b: -------------------------------------------------------------------------------- 1 | implement Args; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | include "arg.m"; 7 | 8 | sys: Sys; 9 | print: import sys; 10 | 11 | Args: module { 12 | init: fn(nil: ref Draw->Context, argv: list of string); 13 | }; 14 | 15 | init(nil: ref Draw->Context, argv: list of string) { 16 | sys = load Sys Sys->PATH; 17 | arg := load Arg Arg->PATH; 18 | if(arg == nil) 19 | raise "bad module load"; 20 | 21 | arg->init(argv); 22 | arg->setusage(arg->progname() + " [-r] [-c mark] words..."); 23 | 24 | rev := 0; 25 | mark := "→ "; 26 | 27 | while((opt := arg->opt()) != 0) 28 | case opt { 29 | 'r' => 30 | rev = 1; 31 | 'c' => 32 | mark = arg->earg(); 33 | * => 34 | arg->usage(); 35 | } 36 | 37 | argv = arg->argv(); 38 | 39 | print("Argc after flags: %d\n", len argv); 40 | 41 | if(rev) { 42 | argl: list of string; 43 | 44 | for(; argv != nil; argv = tl argv) 45 | argl = hd argv :: argl; 46 | 47 | argv = argl; 48 | } 49 | 50 | for(; argv != nil; argv = tl argv) 51 | sys->print("%s %s\n", mark, hd argv); 52 | 53 | exit; 54 | } 55 | -------------------------------------------------------------------------------- /Arrays/README.md: -------------------------------------------------------------------------------- 1 | # Arrays 2 | 3 | Arrays in Limbo are dynamic and indexed by 0. They can be initialized providing a size, or declared and initialized in two separate statements if no size is specified. 4 | 5 | Disclaimer: I don't like this example very much, but I'm not sure how to shore it up. The lesson here is to use lists. 6 | 7 | ## Source 8 | 9 | ### arrays.b:20,30 10 | 11 | This shows the initialization of an array of integers using a variable, `width`, as the count of elements for the array. 12 | 13 | Note that on `arrays.b:13,15` the array of integers, `nums`, and the integer, `width`, are declared. 14 | 15 | ### arrays.b:32,44 16 | 17 | Limbo supports indexing strings. The length, as per `len`, of a string is equivalent to the number of utf-8 runes within a given string. All strings in Limbo are utf-8 strings. 18 | 19 | This section demonstrates the copying of a string into an array of bytes. 20 | 21 | ### arrays.b:46,57 22 | 23 | Since the size of arrays is dynamic, one can declare an array to the size of a dynamic value. The array of bytes, `dbl`, is initialized to double the size of the array `arr`. 24 | 25 | Each index of `arr` is copied to two neighboring indices in `dbl`. 26 | 27 | ### arrays.b:59,67 28 | 29 | Arrays can be initialized to a given set of values as well. In this case, the characters `[a-f]` are used as values to populate the array of strings, `chars`. The size of the array will be equal to the number of elements provided in the aforementioned set. 30 | 31 | ### arrays.b:69,84 32 | 33 | Arrays can be declared to be arrays of other arrays. In this case, the array `nest` is an array of four arrays of strings of size `2 << i`. 34 | 35 | ### arrays.b:86,97 36 | 37 | Arrays can be conditionally initialized to values. The syntax is similar to the `case` statement syntax. In this case, the first three indices are initialized to the byte value of 4. Indices three and greater are initialized to the byte value of 0. 38 | 39 | Note: Multiple conditional sections are not necessary to initialize to a given value. 40 | 41 | ### arrays.b:99,123 42 | 43 | This shows the declaration of an array of size 4 containing lists of string pairs. The lists begin as empty lists, comparable to nil. 44 | 45 | The lists at indices 0 and 2 are prepended with pairs of strings. The head of the respective lists are then printed by extracting the pairs of strings prepended previously. 46 | 47 | ## Demo 48 | 49 | ; limbo arrays.b 50 | ; arrays 51 | Len nums: 0 52 | Len nums: 6 53 | [ 0 2 4 6 8 10] 54 | 55 | Len arr: 12 56 | [ b a b y d u c k s ! !] 57 | 58 | Len dbl: 24 59 | [ b b a a b b y y d d u u c c k k s s ! ! ! !] 60 | 61 | Len chars: 6 62 | [ a b c d e f] 63 | 64 | Len nest: 0 65 | Len nest: 4 66 | Lens: [ 2 4 8 16] 67 | 68 | Len buf: 10 69 | [ 4 4 4 3 3 3 3 3 3 3] 70 | 71 | Len two: 4 72 | Lens: [ 1 0 1 0] 73 | [ (ducks quack) (nil, nil) (inferno os) (nil, nil)] 74 | ; 75 | 76 | ## Exercises 77 | 78 | - Play with the widths of different arrays, what happens? 79 | - What can you initialize to with the `* =>` operator, what can't you? 80 | - Remove a case section from the `* =>` operator section, are all indices set to that value? 81 | -------------------------------------------------------------------------------- /Arrays/arrays.b: -------------------------------------------------------------------------------- 1 | implement Arrays; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Arrays: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | nums: array of int; 14 | 15 | width: int = 6; 16 | 17 | init(nil: ref Draw->Context, nil: list of string) { 18 | sys = load Sys Sys->PATH; 19 | 20 | # Nums 21 | print("Len nums: %d\n", len nums); 22 | 23 | nums = array[width] of int; 24 | 25 | print("Len nums: %d\n", len nums); 26 | 27 | print("["); 28 | for(i := 0; i < width; i++) 29 | print(" %d", nums[i] = i << 1); 30 | print("]\n\n"); 31 | 32 | # Arr 33 | arr := array[12] of byte; 34 | arrstr := "baby ducks!!"; 35 | 36 | for(i = 0; i < len arrstr; i++) 37 | arr[i] = byte arrstr[i]; 38 | 39 | print("Len arr: %d\n", len arr); 40 | 41 | print("["); 42 | for(i = 0; i < len arr; i++) 43 | print(" %c", int arr[i]); 44 | print("]\n\n"); 45 | 46 | # Dbl 47 | dbl := array[len arr *2] of byte; 48 | 49 | for(i = 0; i < len arr; i++) 50 | dbl[i*2] = dbl[i*2+1] = arr[i]; 51 | 52 | print("Len dbl: %d\n", len dbl); 53 | 54 | print("["); 55 | for(i = 0; i < len dbl; i++) 56 | print(" %c", int dbl[i]); 57 | print("]\n\n"); 58 | 59 | # Chars 60 | chars := array[] of {"a", "b", "c", "d", "e", "f"}; 61 | 62 | print("Len chars: %d\n", len chars); 63 | 64 | print("["); 65 | for(i = 0; i < len chars; i++) 66 | print(" %s", chars[i]); 67 | print("]\n\n"); 68 | 69 | # Nest 70 | nest: array of array of string; 71 | 72 | print("Len nest: %d\n", len nest); 73 | 74 | nest = array[4] of array of string; 75 | 76 | print("Len nest: %d\n", len nest); 77 | 78 | for(i = 0; i < len nest; i++) 79 | nest[i] = array[2 << i] of string; 80 | 81 | print("Lens: ["); 82 | for(i = 0; i < len nest; i++) 83 | print(" %d", len nest[i]); 84 | print("]\n\n"); 85 | 86 | # Buf 87 | buf := array[10] of { 88 | 0 to 2 => byte 4, 89 | * => byte 3 90 | }; 91 | 92 | print("Len buf: %d\n", len buf); 93 | 94 | print("["); 95 | for(i = 0; i < len buf; i++) 96 | print(" %d", int buf[i]); 97 | print("]\n\n"); 98 | 99 | # Two 100 | two := array[4] of list of (string, string); 101 | 102 | two[0] = ("ducks", "quack") :: two[0]; 103 | 104 | two[2] = ("inferno", "os") :: two[2]; 105 | 106 | print("Len two: %d\n", len two); 107 | 108 | print("Lens: ["); 109 | for(i = 0; i < len two; i++) 110 | print(" %d", len two[i]); 111 | print("]\n"); 112 | 113 | print("["); 114 | for(i = 0; i < len two; i++) { 115 | if(two[i] == nil) { 116 | print(" (nil, nil)"); 117 | continue; 118 | } 119 | 120 | (s0, s1) := hd two[i]; 121 | print(" (%s %s)", s0, s1); 122 | } 123 | print("]\n"); 124 | 125 | exit; 126 | } 127 | -------------------------------------------------------------------------------- /Channels/README.md: -------------------------------------------------------------------------------- 1 | # Channels 2 | 3 | Limbo supports concurrent communication and synchronization between processes via channels and a few structures working in tandem with channels. 4 | 5 | The design for channels is inspired primarily by "Communicating Sequential Processes" (CSP) by Tony Hoare. 6 | 7 | Further reading on CSP: http://www.usingcsp.com/cspbook.pdf 8 | 9 | ## Source 10 | 11 | ### chans.b:33,36 12 | 13 | This statement shows three channels being made. 14 | 15 | `done` is an unbuffered channel which transports integer values. 16 | 17 | `msgChan` is an unbuffered channel, instantiated using buffered channel syntax, which transports strings. 18 | 19 | `numChan` is a buffered channel which transports a tuple representing an integer pair. 20 | 21 | ### chans.b:38 22 | 23 | The `spawn` statement is used to create a new process running a given function, in this case `summer()`. 24 | 25 | ### chans.b:40,45 26 | 27 | This section loads number pairs from the variable `i` and the number 2 from 0 to 14, covering 15 total iterations. After the iterative statement completes, a (later discarded) value is passed to the channel `done`. The first (and only) value in `msgChan` is then read out and printed. 28 | 29 | Note: Writing to a channel whose buffer (or lack thereof) is currently full is a blocking operation. Similarly, reading from a channel whose buffer (or lack thereof) is currently empty is a blocking operation. 30 | 31 | ### chans.b:13,28 32 | 33 | The function `summer()` operates an infinite loop which operates a single `alt` statement which is similar to a switch-case statement which operates on the presence of values within a channel. 34 | 35 | The three channels are used to coordinate operation between the two processes. 36 | 37 | `num` is checked first and a tuple of values is pulled out: a value and a power to raise said value to. The `n^p` operation is shown as `n**p` in Limbo, with the result being added to sum's current value. 38 | 39 | `done` is checked after `num` has no more values in its buffer. If a value is found, the sum is stringified and passed down `msg` and the looping condition is unset. 40 | 41 | The default value, indicated by `*=>`, is used when no channels have a value, so we sleep the current process for 5ms to de-schedule ourselves in case some form of scheduling is holding up our communications. 42 | 43 | ## Demo 44 | 45 | ; limbo chans.b 46 | ; chans 47 | Sum: 0 48 | Sum: 1 49 | Sum: 5 50 | Sum: 14 51 | Sum: 30 52 | Sum: 55 53 | Sum: 91 54 | Sum: 140 55 | Sum: 204 56 | Sum: 285 57 | Sum: 385 58 | Sum: 506 59 | Sum: 650 60 | Final sum: 650 61 | ; 62 | 63 | ## Exercises 64 | 65 | - Can you pass an ADT over a channel? 66 | - What happens if you pass a `ref` type over a channel? 67 | -------------------------------------------------------------------------------- /Channels/chans.b: -------------------------------------------------------------------------------- 1 | implement Chans; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Chans: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | summer(done: chan of int, msg: chan of string, num: chan of (int, int)) { 14 | sum := big 0; 15 | run := 1; 16 | 17 | while(run) 18 | alt { 19 | (n, p) := <-num => 20 | sum += big n**p; 21 | print("Sum: %bd\n", sum); 22 | <-done => 23 | msg <-= string sum; 24 | run = 0; 25 | * => 26 | sys->sleep(5); 27 | } 28 | } 29 | 30 | init(nil: ref Draw->Context, nil: list of string) { 31 | sys = load Sys Sys->PATH; 32 | 33 | n := 4; 34 | done := chan of int; 35 | msgChan := chan[0] of string; 36 | numChan := chan[n] of (int, int); 37 | 38 | spawn summer(done, msgChan, numChan); 39 | 40 | for(i := 0; i < 15; i++) 41 | numChan <-= (i, 2); 42 | 43 | done <-= 0; 44 | 45 | print("Final sum: %s\n", <-msgChan); 46 | 47 | buf := chan[20] of int; 48 | 49 | print("Len: %d\n", len buf); 50 | 51 | for(i = 0; i < 5; i++) 52 | buf <-= i; 53 | 54 | exit; 55 | } 56 | -------------------------------------------------------------------------------- /Constants/README.md: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | Limbo does not have enum types, but does have the `con` keyword to indicate a constant value. 4 | 5 | ## Source 6 | 7 | ### const.b:13,14 8 | 9 | n: con 7; 10 | Red, Green, Blue: con iota; 11 | 12 | The variable `n` is an integer constant with a value of 7. 13 | 14 | The variables `Red`, `Green`, and `Blue` are integer constants with values 0, 1, and 2, respectively. 15 | 16 | By default, the `iota` operator initializes variables on the left side with sequential values from 0 to n. Where n is the number of variables. The `iota` operator is only valid in expressions involving `con`. 17 | 18 | Note: these variables are bound within the scope of this module's file. 19 | 20 | ### const.b:19 21 | 22 | s: con "Limbo"; 23 | 24 | The variable `s` is a string constant which is bound within the scope of the `init()` function. 25 | 26 | ## Demo 27 | 28 | ; limbo const.b 29 | ; const 30 | 7 31 | Red: 0 Green: 1 Blue: 2 32 | Limbo 33 | ; 34 | 35 | ## Exercises 36 | 37 | - Try to declare constants of other types, does `con` allow you to do this? 38 | - Change the `iota` statement such that the values of Red, Green, Blue are not increments of 1 and do not start at 0. 39 | -------------------------------------------------------------------------------- /Constants/const.b: -------------------------------------------------------------------------------- 1 | implement Const; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Const: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | n: con 7; 14 | Red, Green, Blue: con iota; 15 | 16 | init(nil: ref Draw->Context, nil: list of string) { 17 | sys = load Sys Sys->PATH; 18 | 19 | s: con "Limbo"; 20 | 21 | print(string n + "\n"); 22 | print("Red: %d Green: %d Blue: %d\n", Red, Green, Blue); 23 | print(s + "\n"); 24 | 25 | exit; 26 | } 27 | -------------------------------------------------------------------------------- /Exceptions/README.md: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | 3 | Limbo supports the throwing, catching, and custom definition of exceptions. 4 | 5 | The example in this section is a derivative of the exceptions summary in the [Limbo Addendum](http://www.vitanuova.com/inferno/papers/addendum.pdf). 6 | 7 | ## Source 8 | 9 | ### exceptions.b:17,26 10 | 11 | This section calculates numbers within the fibonacci sequence to a given interval. In this case, the first 5 numbers are calculated within the sequence. 12 | 13 | Note the call to the `raise` keyword after the fibonacci calculations have completed, but before the program exits. 14 | 15 | ### exceptions.b:31,47 16 | 17 | The `fibonacci()` function attempts to perform a `fib()` call and, if an exception `e` is thrown, attempts to handle the exception in a manner depending on the exception type. 18 | 19 | Note that the attempted operation is wrapped in a `{ ... }` block. 20 | 21 | ### exceptions.b:49,63 22 | 23 | The `raises` clause allows for warnings to be generated for un-caught exception potential within a program. Note that these exception types can be user-defined. 24 | 25 | The `FIB` exception type is defined at: exceptions.b:12 26 | 27 | ## Demo 28 | 29 | ; limbo exceptions.b 30 | ; exceptions 31 | F(0) = 1 32 | F(1) = 1 33 | F(2) = 2 34 | F(3) = 3 35 | F(4) = 5 36 | sh: 798 "Exceptions":going down! 37 | ; 38 | 39 | ## Exercises 40 | 41 | - Try removing the raise for "going down!". 42 | - Remove the `i < 5` portion of the for loop, how many values are printed and why? 43 | - Try removing the exception-catching block from around calls to `fib()`, do you get warnings? 44 | - Try changing the raise type within `fib()`, does `fibonacci()` handle the exception `e` differently? 45 | -------------------------------------------------------------------------------- /Exceptions/exceptions.b: -------------------------------------------------------------------------------- 1 | implement Exceptions; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | 8 | Exceptions: module { 9 | init: fn(nil: ref Draw->Context, nil: list of string); 10 | }; 11 | 12 | FIB: exception(int, int); 13 | 14 | init(nil: ref Draw->Context, nil: list of string) { 15 | sys = load Sys Sys->PATH; 16 | 17 | for(i := 0; i < 5 ; i++) { 18 | f := fibonacci(i); 19 | 20 | if(f < 0) 21 | break; 22 | 23 | sys->print("F(%d) = %d\n", i, f); 24 | } 25 | 26 | raise "going down!"; 27 | 28 | exit; 29 | } 30 | 31 | fibonacci(n: int): int { 32 | { 33 | fib(1, n, 1, 1); 34 | } exception e { 35 | FIB => 36 | (x, nil) := e; 37 | return x; 38 | 39 | "*" => 40 | sys->print("unexpected string exception %s raised\n", e); 41 | 42 | * => 43 | sys->print("unexpected exception raised\n"); 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | fib(n, m, x, y: int) raises (FIB) { 50 | if(n >= m) 51 | raise FIB(x, y); 52 | { 53 | fib(n+1, m, x, y); 54 | } exception e { 55 | FIB => 56 | (x, y) = e; 57 | 58 | x = x+y; 59 | y = x-y; 60 | 61 | raise FIB(x, y); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Function-Refs/README.md: -------------------------------------------------------------------------------- 1 | # Function References 2 | 3 | Limbo supports references to functions, similar to closures, but more similar to function pointers. 4 | 5 | The example in this section is a derivative of the function reference summary in the [Limbo Addendum](http://www.vitanuova.com/inferno/papers/addendum.pdf). 6 | 7 | ## Source 8 | 9 | ### funcrefs.b:18,34 10 | 11 | The variable `fp` is a name whose type is a reference to a function with a given argument and return value scheme. A function which fulfills this scheme can be used to set the reference, in this case the `cmp()` function is referenced by `fp`. 12 | 13 | ### funcrefs.b:36,46 14 | 15 | The `sort()` function takes a function reference as an argument, using it as a comparator. In this case `cmp()` and `rcmp()` are passed as a function reference argument. 16 | 17 | ### funcrefs.b:48,53 18 | 19 | The `choose()` function returns a reference to a function, being called in this case to return a given comparator with the comparator being called in-line. 20 | 21 | ## Demo 22 | 23 | ; limbo funcrefs.b 24 | ; funcrefs 25 | Matched smiley! 26 | Did not match duck 27 | [ d b c a] 28 | [ a b c d] 29 | [ d c b a] 30 | quack ≥ quack 31 | ; 32 | 33 | ## Exercises 34 | 35 | - Change the value of `c` to 1, what happens? 36 | - Write a function which returns a function which returns a function. 37 | -------------------------------------------------------------------------------- /Function-Refs/funcrefs.b: -------------------------------------------------------------------------------- 1 | implement FuncRefs; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | FuncRefs: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | Cmp: type ref fn(s1: string, s2: string): int; 14 | 15 | init(nil: ref Draw->Context, nil: list of string) { 16 | sys = load Sys Sys->PATH; 17 | 18 | fp: ref fn(s1: string, s2: string): int; 19 | 20 | fp = cmp; 21 | 22 | a := fp("☺", ":)"); 23 | 24 | if(a) 25 | print("Matched smiley!\n"); 26 | else 27 | print("Did not match smiley\n"); 28 | 29 | b := fp("duck", "duck"); 30 | 31 | if(b) 32 | print("Matched duck!\n"); 33 | else 34 | print("Did not match duck\n"); 35 | 36 | unsorted := array[4] of {"d", "b", "c", "a"}; 37 | 38 | printarr(unsorted); 39 | 40 | carr := sort(unsorted, cmp); 41 | 42 | printarr(carr); 43 | 44 | rarr := sort(unsorted, rcmp); 45 | 46 | printarr(rarr); 47 | 48 | c := 0; 49 | 50 | if(choose(c)("quack", "quack") >= 0) 51 | print("quack ≥ quack\n"); 52 | else 53 | print("quack < quack\n"); 54 | 55 | exit; 56 | } 57 | 58 | cmp(s1: string, s2: string): int { 59 | if(s1 < s2) 60 | return -1; 61 | 62 | if(s1 > s2) 63 | return 1; 64 | 65 | return 0; 66 | } 67 | 68 | rcmp(s1: string, s2: string): int { 69 | return -cmp(s1, s2); 70 | } 71 | 72 | choose(i: int): Cmp { 73 | if(i) 74 | return rcmp; 75 | 76 | return cmp; 77 | } 78 | 79 | # Insertion sort 80 | sort(arr: array of string, comp: ref fn(s1, s2: string): int): array of string { 81 | i, j: int; 82 | key: string; 83 | n := len arr; 84 | 85 | for(i = 1; i < n; i++) { 86 | key = arr[i]; 87 | j = i-1; 88 | 89 | while(j >= 0 && comp(arr[j], key) > 0) { 90 | arr[j+1] = arr[j]; 91 | j = j-1; 92 | } 93 | 94 | arr[j+1] = key; 95 | } 96 | 97 | return arr; 98 | } 99 | 100 | printarr(arr: array of string) { 101 | print("["); 102 | for(i := 0; i < len arr; i++) 103 | print(" %s", arr[i]); 104 | print("]\n"); 105 | } 106 | -------------------------------------------------------------------------------- /Functions/README.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Functions in Limbo are similar to functions in Go moreso than functions in C. 4 | 5 | ## Source 6 | 7 | ### func.b:13,17 8 | 9 | This section shows a function prototype as well as the succeeding function definition. Prototypes in Limbo function similar to prototypes in C. 10 | 11 | The function `plus()` takes two integers as arguments and returns their sum. 12 | 13 | ### func.b:19,21 14 | 15 | The function `swp()` takes two integer arguments and returns two integers within a tuple. 16 | 17 | Note that the type statement `: int` is only provided once, with the list of variables to the left of the type statement being declared to be integers. 18 | 19 | ### func.b:36,38 20 | 21 | The function `plusplus()` takes three integer arguments and returns their sum, calling the function `plus()` in the process to sum the first two arguments. 22 | 23 | The return value of `plus()` is a single variable, so it can be added to `c` without any extra syntactical structures than using the return value as an unnamed variable. 24 | 25 | ## Demo 26 | 27 | ; limbo func.b 28 | ; func 29 | 3 + 5 = 8 30 | 7 + 3 + 4 = 14 31 | swap 6,7: 7,6 32 | ; 33 | 34 | ## Exercises 35 | 36 | - Try returning more than two values, can tuples store more than two ordered objects? 37 | -------------------------------------------------------------------------------- /Functions/func.b: -------------------------------------------------------------------------------- 1 | implement Func; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Func: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | plus: fn(a: int, b :int): int; 14 | 15 | plus(a: int, b: int): int { 16 | return a + b; 17 | } 18 | 19 | swp(a, b: int): (int, int) { 20 | return (b, a); 21 | } 22 | 23 | init(nil: ref Draw->Context, nil: list of string) { 24 | sys = load Sys Sys->PATH; 25 | 26 | print("3 + 5 = %d\n", plus(3, 5)); 27 | 28 | print("7 + 3 + 4 = %d\n", plusplus(7, 3, 4)); 29 | 30 | (t₀, t₁) := swp(6, 7); 31 | print("swap 6,7: %d,%d\n", t₀, t₁); 32 | 33 | exit; 34 | } 35 | 36 | plusplus(a, b, c: int): int { 37 | return plus(a, b) + c; 38 | } 39 | -------------------------------------------------------------------------------- /Generics/README.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | Limbo supports polymorphic types as well as a form of pseudo-unions and pseudo-interfaces as per C and Go respectively. 4 | 5 | Warning: This is an example about an undocumented feature of Limbo. All assertions or explanations provided are considered conjecture until proven otherwise. 6 | 7 | ## Source 8 | 9 | ### generics.b:17,21 10 | 11 | This is a polymorphic type. The type `T` is the generic type which will match whatever type is provided with a given instantiation/declaration of the `Rd` adt type. 12 | 13 | Note the method declaration format for the adt to operate on the variable instance the method is called upon. 14 | 15 | ### generics.b:23,32 16 | 17 | This is an adt with pick tags. An adt with pick tags is roughly equivalent to an interpretation of unions from C. 18 | 19 | If the variable type is instantiated with a `.Type` clause then the pick within the adt will be called looking for a match for `Type =>`. If the case is matched, then the contents of the case segment will be included with the related instantiation of the aforementioned variable. 20 | 21 | Note that there are two different calling formats for pick, in an adt, and in a function. 22 | 23 | ### generics.b:34,44 24 | 25 | This is a polymorphic adt which also possesses pick tags. 26 | 27 | Note that the member `w` is of type `T`, implying it is matched to the type with which the adt is intantiated. Additionally, pick tag types are separate from generic `T` types, as is shown later. 28 | 29 | ### generics.b:46,50 30 | 31 | This is an adt with a type definition declared which acts as a shortcut to a reference to an instance of said adt. 32 | 33 | ### generics.b:56,63 34 | 35 | This section instantiates an `Rd` instance with `T` declared to be of type `ref Iobuf` for the variable `rd`. The `ws()` function reads from the provided `ref Iobuf` until a non-whitespace character is processed. 36 | 37 | `.ws()` is defined at: generics.b:103,109 38 | 39 | Note that instances of polymorphic types are always prefixed with `ref`. 40 | 41 | ### generics.b:65,69 42 | 43 | This section instantiates two variable instances of the `Num` type using `Int` and `String` as the pick type, respectively. The `stringify()` method is called on both, fulfilling the `string` output type for printing despite the differing pick tag declarations. 44 | 45 | In this case, `stringify()` uses the pick switch to determine which variable to access and what string to output. 46 | 47 | Note that instances of types with pick tags are always prefixed with `ref`. 48 | 49 | ### generics.b:71,83 50 | 51 | This section uses both pick tags and polymorphic types at once. 52 | 53 | The list `words` is a list of references to `Word` adt's with `Int` as its `T` type and `String` as its pick tag. 54 | 55 | The variable `sword` instantiates a reference to a `Word` adt with `Int` as its `T` type and `String` as its pick tag. 56 | 57 | The `ismember()` function applies an interface-like check as a `for { T => }` on its arguments looking for a fulfilling `eq()` function to determine whether two types are equivalent. 58 | 59 | `ismember()` is defined at: generics.b:132,144 60 | 61 | `.eq()` is defined at: generics.b:111,114 62 | 63 | ### generics.b:85,87 64 | 65 | This section takes in a list and reverses said list. 66 | 67 | Note that the `rev()` function doesn't care what type the list is a list of, it uses the type provided within its arguments. 68 | 69 | `rev()` is defined at: generics.b:154,161 70 | 71 | ### generics.b:89,98 72 | 73 | The `pair()` function allows two separate types `T`, indicated as `T1` and `T2`. Both types can be used and are matched based on the order of types in its arguments. 74 | 75 | `pair()` is defined at: generics.b:146,152 76 | 77 | ## Demo 78 | 79 | ; generics 80 | Type something: asdf 81 | Broke on 'a' 82 | 5 83 | Five 84 | Found 9! 85 | Head was: :( 86 | Head is: ☺ 87 | Head of plist: ( hello, { :(, 7 } ) 88 | ; 89 | 90 | ## Exercises 91 | 92 | - Try changing the pick tag for one of the instances of `Num`, how does `stringify()` behave? 93 | - Can you find a way to make the `eq()` function do a deep comparison of `Int`'s? 94 | - Try removing the `.String` from the `words` definition, what happens? 95 | - Try removing `ref` anywhere, see what happens. 96 | 97 | ## References 98 | 99 | - https://github.com/caerwynj/inferno-lab/blob/master/78/README.md 100 | - https://github.com/caerwynj/inferno-lab/blob/master/27/sexprs.b 101 | - /appl/lib/lists.b 102 | -------------------------------------------------------------------------------- /Generics/generics.b: -------------------------------------------------------------------------------- 1 | implement Generics; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | include "bufio.m"; 6 | 7 | sys: Sys; 8 | print: import sys; 9 | 10 | bufio: Bufio; 11 | Iobuf: import bufio; 12 | 13 | Generics: module { 14 | init: fn(nil: ref Draw->Context, nil: list of string); 15 | }; 16 | 17 | # Polymorphic type 18 | Rd: adt[T] { 19 | t: ref Iobuf; 20 | ws: fn(rd: self ref Rd[T]): int; 21 | }; 22 | 23 | Num: adt { 24 | pick { 25 | String => 26 | s: string; 27 | Int => 28 | d: int; 29 | } 30 | 31 | stringify: fn(v: self ref Num): string; 32 | }; 33 | 34 | # Polymorphic and pick-tagged type 35 | Word: adt[T] { 36 | w: T; 37 | 38 | pick { 39 | String => 40 | s: string; 41 | } 42 | 43 | eq: fn(a, b: ref Word): int; 44 | }; 45 | 46 | Integer: adt { 47 | d: int; 48 | }; 49 | 50 | Int: type ref Integer; 51 | 52 | init(nil: ref Draw->Context, nil: list of string) { 53 | sys = load Sys Sys->PATH; 54 | bufio = load Bufio Bufio->PATH; 55 | 56 | in := bufio->open("/fd/0", bufio->OREAD); 57 | 58 | rd := ref Rd[ref Iobuf](in); 59 | 60 | print("Type something: "); 61 | 62 | c := rd.ws(); 63 | print("Broke on '%c'\n", c); 64 | 65 | d := ref Num.Int(5); 66 | s := ref Num.String("Five"); 67 | 68 | print("%s\n", d.stringify()); 69 | print("%s\n", s.stringify()); 70 | 71 | words: list of ref Word[Int].String; 72 | 73 | smiley := "☺"; 74 | frowny := ":("; 75 | 76 | # Format is: Adt[Type].PickTag(fields...) 77 | sword := ref Word[Int].String(Int(9), smiley); 78 | 79 | words = sword :: words; 80 | words = ref Word[Int].String(Int(7), frowny) :: words; 81 | 82 | if(ismember(sword, words)) 83 | print("Found %d!\n", sword.w.d); 84 | 85 | backwords := rev(words); 86 | 87 | print("Head was: %s\nHead is: %s\n", (hd words).s, (hd backwords).s); 88 | 89 | strings: list of string; 90 | strings = "hi" :: strings; 91 | strings = "hello" :: strings; 92 | 93 | # plist is a `list of (string, ref Word.String)` 94 | plist := pair(strings, words); 95 | 96 | (str, word) := hd plist; 97 | 98 | print("Head of plist: ( %s, { %s, %d } )\n", str, word.s, word.w.d); 99 | 100 | exit; 101 | } 102 | 103 | # Skip white space 104 | Rd[T].ws(rd: self ref Rd[T]): int { 105 | while(isspace(c := rd.t.getb())) 106 | {} 107 | 108 | return c; 109 | } 110 | 111 | Word[T].eq(a, b: ref Word): int 112 | { 113 | return a.w == b.w; # This is a shallow comparison, don't rely on this 114 | } 115 | 116 | Num.stringify(v: self ref Num): string { 117 | pick xv := v { 118 | String => 119 | return xv.s; 120 | Int => 121 | return string xv.d; 122 | * => 123 | raise "type error"; 124 | } 125 | } 126 | 127 | # Matches whitespace characters 128 | isspace(c: int): int { 129 | return c == ' ' || c == '\r' || c == '\t' || c == '\n'; 130 | } 131 | 132 | # Checks if x is a member of l 133 | ismember[T](x: T, l: list of T): int 134 | for { 135 | T => 136 | eq: fn(a, b: T): int; 137 | } 138 | { 139 | for(; l != nil; l = tl l) 140 | if(T.eq(x, hd l)) 141 | return 1; 142 | 143 | return 0; 144 | } 145 | 146 | # Pairs two lists of separate types(?) 147 | pair[T1, T2](l1: list of T1, l2: list of T2): list of (T1, T2) { 148 | if(l1 == nil && l2 == nil) 149 | return nil; 150 | 151 | return (hd l1, hd l2) :: pair(tl l1, tl l2); 152 | } 153 | 154 | # Reverse a list 155 | rev[T](l: list of T): list of T { 156 | r: list of T; 157 | for(; l != nil; l = tl l) 158 | r = hd l :: r; 159 | 160 | return r; 161 | } 162 | -------------------------------------------------------------------------------- /HelloWorld/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | This program prints the classic hello world message. 4 | 5 | ## Source 6 | 7 | ### hello.b:1 8 | 9 | implement Hello; 10 | 11 | The `implement` keyword tells us the name of the module being defined. This file is associated with the implementation of the `Hello` module. 12 | 13 | ### hello.b:3,4 14 | 15 | include "sys.m"; 16 | include "draw.m"; 17 | 18 | The `include` keyword provides the modules which we can load and unload from. As per limbo(1), the current working directory is searched, then `/modules/`, for valid module files to match the `include` statements. 19 | 20 | ### hello.b:6,8 21 | 22 | Hello: module { 23 | init: fn(nil: ref Draw->Context, nil: list of string); 24 | }; 25 | 26 | This is a module definition. Module definitions can appear in `.m` module files or they can be provided in a `.b` source file. 27 | 28 | The `init` line provides a definition for the name `init`, in this case, it's a function definition as indicated by `fn` which takes two arguments, but does not attribute variables to them due to the `nil` keyword. You could think of this definition as analogous to a function prototype in C. 29 | 30 | The `ref Draw->Context` definition states that the variable is a reference to a variable of type `Context` within the module `Draw`. 31 | 32 | The `list of string` definition states that the variable is a list of strings. 33 | 34 | ### hello.b:10 35 | 36 | init(nil: ref Draw->Context, nil: list of string) { 37 | ... 38 | } 39 | 40 | This is a function definition. The function is named `init` and its arguments and return type(s) must match that of the prototype in the module definition. 41 | 42 | ### hello.b:11 43 | 44 | sys := load Sys Sys->PATH; 45 | 46 | This is a variable definition and initalization for the name `sys`. 47 | 48 | The `:=` operator tells us that the variable's type is defined implicitly by the return value of the righthand side. 49 | 50 | The `load` keyword loads from a given module in the format `load Module path`. In this case, the path is provided by the module definition. If `Sys` were a module in our directory, we could load it via `load Sys "./sys.dis"`. The `Sys` module definition is provided inside Inferno at `/module/sys.m`. 51 | 52 | ### hello.b:13 53 | 54 | sys->print("Hello World! ☺\n"); 55 | 56 | This line calls a function named `print`, from the `Sys` module, which is loaded into the variable `sys`. The argument provided is a unicode string. All strings in Limbo are considered to be valid unicode (utf-8). 57 | 58 | ### hello.b:14 59 | 60 | exit; 61 | 62 | The `exit` keyword terminates a thread and frees any resources belonging exclusively to it. 63 | 64 | ## Demo 65 | 66 | ; limbo hello.b 67 | ; hello 68 | Hello World! ☺ 69 | ; 70 | 71 | ## Exercises 72 | 73 | - Try removing the arguments to `init()`, what happens? 74 | -------------------------------------------------------------------------------- /HelloWorld/hello.b: -------------------------------------------------------------------------------- 1 | implement Hello; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | Hello: module { 7 | init: fn(nil: ref Draw->Context, nil: list of string); 8 | }; 9 | 10 | init(nil: ref Draw->Context, nil: list of string) { 11 | sys := load Sys Sys->PATH; 12 | 13 | sys->print("Hello World! ☺\n"); 14 | exit; 15 | } 16 | -------------------------------------------------------------------------------- /If-Else/README.md: -------------------------------------------------------------------------------- 1 | # If-Else 2 | 3 | Branching in Limbo with if-else trees is similar to branching with if-else trees in C. 4 | 5 | ## Source 6 | 7 | ### ifelse.b:22,26 8 | 9 | if(bufio == nil) { 10 | sys->fprint(stderr, "err: could not load %s ­ %r\n", Bufio->PATH); 11 | raise "fail:load"; 12 | } 13 | 14 | This is a test whether the variable `bufio` is `nil`. 15 | 16 | The `nil` keyword is a special operator in that it represents a lack of value for a type and can be equivalent to different values depending on the context. 17 | 18 | Note: The value nil is the "undefined" or "explicitly empty" value for non-numeric types. That is, numeric types do not have a `nil` value equivalent. 0 is considered the presence of a value, etc. 19 | 20 | ### ifelse.b:28,33 21 | 22 | str := ""; 23 | if(str == nil) 24 | print("String %s is nil\n", str); 25 | 26 | if(str == "") 27 | print("String %s is empty\n", str); 28 | 29 | These tests against the string `str` show that for the purposes of a string, `nil` == `""`. That is, a lack of value in a string, is an empty string. 30 | 31 | ### ifelse.b:35,42 32 | 33 | l: list of string; 34 | if(l == nil) 35 | print("List is nil\n"); 36 | 37 | l = "ducks" :: l; 38 | if(l != nil) { 39 | print("List is not nil, 0th is: \"%s\"\n", hd l); 40 | } 41 | 42 | The variables `l` is a list containing a series of strings. If there are no strings within the string, it is considered a `nil` list. 43 | 44 | The `::` operator is unique to lists, it prepends to the list the value on its right side, the value on its left side. That is, the value is inserted in precedence to the head. In this case, there is no head value present, so the string `"ducks"` becomes to head of the list. 45 | 46 | The `hd` keyword stands for head and returns the head of the list to its right. 47 | 48 | ### ifelse.b:44,57 49 | 50 | Source is not shown for brevity. 51 | 52 | These tests are a series of numeric comparisons which provide output depending on properties of said numeric value. In this case, the numeric value is the variable `num`. Note the intermixed omission and presence of curly braces. 53 | 54 | ## Demo 55 | 56 | ; limbo ifelse.b 57 | ; ifelse 58 | String is nil 59 | String is empty 60 | List is nil 61 | List is not nil, 0th is: "ducks" 62 | 7 is odd 63 | 9 has one digit 64 | ; 65 | 66 | ## Exercises 67 | 68 | - Change the value of `num`, what output do you receive? 69 | - Change `Bufio->PATH` to a string such as `"ducks.dis"`, do you receive an exception or an error? 70 | -------------------------------------------------------------------------------- /If-Else/ifelse.b: -------------------------------------------------------------------------------- 1 | implement IfElse; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | include "bufio.m"; 6 | 7 | sys: Sys; 8 | print, FD: import sys; 9 | 10 | bufio: Bufio; 11 | 12 | stderr: ref FD; 13 | 14 | IfElse: module { 15 | init: fn(nil: ref Draw->Context, nil: list of string); 16 | }; 17 | 18 | init(nil: ref Draw->Context, nil: list of string) { 19 | sys = load Sys Sys->PATH; 20 | stderr = sys->fildes(2); 21 | 22 | bufio = load Bufio Bufio->PATH; 23 | if(bufio == nil) { 24 | sys->fprint(stderr, "err: could not load %s ­ %r\n", Bufio->PATH); 25 | raise "fail:load"; 26 | } 27 | 28 | str := ""; 29 | if(str == nil) 30 | print("String %s is nil\n", str); 31 | 32 | if(str == "") 33 | print("String %s is empty\n", str); 34 | 35 | l: list of string; 36 | if(l == nil) 37 | print("List is nil\n"); 38 | 39 | l = "ducks" :: l; 40 | if(l != nil) { 41 | print("List is not nil, 0th is: \"%s\"\n", hd l); 42 | } 43 | 44 | num := 7; 45 | if(num % 2 == 0) 46 | print("%d is even\n", num); 47 | else 48 | print("%d is odd\n", num); 49 | 50 | num = 9; 51 | if(num < 0) { 52 | print("%d is negative\n", num); 53 | } else if(num < 10) 54 | print("%d has one digit\n", num); 55 | else { 56 | print("%d is positive and has multiple digits\n", num); 57 | } 58 | 59 | exit; 60 | } 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sean Hinchee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lists/README.md: -------------------------------------------------------------------------------- 1 | # Lists 2 | 3 | Limbo supports lists of arbitrary types as a basic construct. 4 | 5 | ## Source 6 | 7 | ### lists.b:16,20 8 | 9 | Three lists are declared. 10 | 11 | The `len` statement can be used on a list to return the number of elements in the respective list. Note that the list `persons` is a list of tuples whose pairing is that of a string and an integer. 12 | 13 | ### lists.b:22,28 14 | 15 | The lists `names` and `ages` are both populated. 16 | 17 | The `::` operator functions on lists by prepending the given element before the head of the current list. Note the interaction between successive calls to the `::` operator prior to assignment and how it affects order. 18 | 19 | ### lists.b:30,46 20 | 21 | The list `persons` is built by making copies of the `names` and `ages` lists as `n` and `a`, respectively. 22 | 23 | The `hd` statement returns the head of the list to its right. 24 | 25 | The `tl` statement returns the tail, that is the list bar the head element, of the list to its right. 26 | 27 | An empty list is equivocal to `nil`. That is, a nil list is an empty list. 28 | 29 | Note: The effects upon temporary copies of `names` and `ages` do not affect the original lists they are copies of, as the temporary copies are copies, rather than references to the original lists. 30 | 31 | ## Demo 32 | 33 | ; limbo lists.b 34 | ; lists 35 | Lens: 0, 0, 0 36 | Lens: 3, 3, 0 37 | Persons: 38 | Spike: 27 39 | Jet: 36 40 | Ed: 13 41 | Tmp lens: 0, 0 42 | Lens: 3, 3, 0 43 | ; 44 | 45 | ## Exercises 46 | 47 | - Try reversing a list. 48 | - Try interleaving two lists. 49 | -------------------------------------------------------------------------------- /Lists/lists.b: -------------------------------------------------------------------------------- 1 | implement Lists; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Lists: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | init(nil: ref Draw->Context, nil: list of string) { 14 | sys = load Sys Sys->PATH; 15 | 16 | names: list of string; 17 | ages: list of int; 18 | persons: list of (string, int); 19 | 20 | print("Lens: %d, %d, %d\n", len names, len ages, len persons); 21 | 22 | names = "Spike" :: names; 23 | ages = 27 :: ages; 24 | 25 | names = "Ed" :: "Jet" :: names; 26 | ages = 13 :: 36 :: ages; 27 | 28 | print("Lens: %d, %d, %d\n", len names, len ages, len persons); 29 | 30 | n := names; 31 | a := ages; 32 | 33 | while(n != nil && a != nil) { 34 | persons = (hd n, hd a) :: persons; 35 | n = tl n; 36 | a = tl a; 37 | } 38 | 39 | print("Persons:\n"); 40 | for(; persons != nil; persons = tl persons) { 41 | (name, age) := hd persons; 42 | print("%s: %d\n", name, age); 43 | } 44 | 45 | print("Tmp lens: %d, %d\n", len n, len a); 46 | print("Lens: %d, %d, %d\n", len names, len ages, len persons); 47 | 48 | exit; 49 | } 50 | -------------------------------------------------------------------------------- /Loops/README.md: -------------------------------------------------------------------------------- 1 | # Loops 2 | 3 | Looping in Limbo is similar to looping in C. Supported loop formats are for, while, and do-while. 4 | 5 | ## Source 6 | 7 | ### loops.b:18,23 8 | 9 | for(i := 0; i < 10; i++){ 10 | if(i % 2 == 0) 11 | continue; 12 | 13 | print("%d\n", i * i); 14 | } 15 | 16 | A for loop consists of an initialization, qualification, operation, and post-iteration sequence. 17 | 18 | Before the loop begins, but before the initial check, `i := 0` is run. 19 | 20 | After each loop iteration, `i++` is performed, then `i < 10` is checked. 21 | 22 | The `continue` keyword, from the point it is called, skips to the next loop iteration. In this case, `continue` is called if `i` is an even number. If `i` is an odd number, `i²` is printed. 23 | 24 | Note that the `continue` keyword does not skip the post-iteration operation. 25 | 26 | ### loops.b:28,29 27 | 28 | while(n) 29 | print("%d\n", n--); 30 | 31 | A while loop will check the qualifier for whether to enter another iteration, then, perform its operation. This loop will print the value of `n` in the range [7,1]. 32 | 33 | Note that in this case the braces are omitted. 34 | 35 | ### loops.b:33,36 36 | 37 | do{ 38 | print("%d\n", ++n); 39 | break; 40 | }while(1); 41 | 42 | A do-while loop will perform its operation, then check the qualifier for whether to enter another iteration. In this case, the qualifier is a constant which is quantified as a true value, so, this loop would iterate forever. 43 | 44 | The `break` keyword interrupts iterative processing. In this case, it terminates the loop immediately after the print on its first iteration. 45 | 46 | Note that we reuse the value of `n`, In this case, the incremented value of `n` is printed, as when the preceding loop ends, the value of `n` is 0. 47 | 48 | ## Demo 49 | 50 | ; limbo loops.b 51 | ; loops 52 | == for 53 | 1 54 | 9 55 | 25 56 | 49 57 | 81 58 | == while 59 | 7 60 | 6 61 | 5 62 | 4 63 | 3 64 | 2 65 | 1 66 | == do 67 | 1 68 | ; 69 | 70 | ## Exercises 71 | 72 | - Try reversing the ++/-- operators in different places, what happens? 73 | - Try omitting various portions of the for loop syntax, what happens? 74 | - Is `for(;;);` an infinite loop? 75 | - Make the condition for a loop something similar to --n, how many iterations does it run for? 76 | -------------------------------------------------------------------------------- /Loops/loops.b: -------------------------------------------------------------------------------- 1 | implement Loops; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Loops: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | init(nil: ref Draw->Context, nil: list of string) { 14 | sys = load Sys Sys->PATH; 15 | 16 | print("== for\n"); 17 | 18 | for(i := 0; i < 10; i++){ 19 | if(i % 2 == 0) 20 | continue; 21 | 22 | print("%d\n", i * i); 23 | } 24 | 25 | print("== while\n"); 26 | 27 | n := 7; 28 | while(n) 29 | print("%d\n", n--); 30 | 31 | print("== do\n"); 32 | 33 | do{ 34 | print("%d\n", ++n); 35 | break; 36 | }while(1); 37 | 38 | exit; 39 | } 40 | -------------------------------------------------------------------------------- /Modules/README.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | Limbo supports compartmentalization of functionality through the dynamic loading and unloading of names and definitions through modules. 4 | 5 | A particular module may include a `.m` file module definition associated with a given implementation of said module contained within `.b` files. 6 | 7 | Disclaimer: At the time of writing I am not exceptionally well-versed with modules in Limbo. All assertions should be taken with a grain of salt. 8 | 9 | ## Source 10 | 11 | ### persons.m:1,11 12 | 13 | The `Persons` module exposes three functions and an ADT. 14 | 15 | Function definitions: 16 | 17 | - init() -- persons.b:7,9 18 | - mkperson() -- persons.b:11,13 19 | - getpop() -- persons.b:15,18 20 | 21 | Note: In a way, a module definition acts as an interface which dictates which functions, variables, and types are accessible from outside the module's implementation proper. 22 | 23 | ### towns.m:1,14 24 | 25 | The `Towns` module exposes two functions, a variable, and an ADT. 26 | 27 | Note that the module file includes `persons.m` to allow it to utilize the `Persons` definition, allowing a handle to be attached to the module `Persons` within the module `Towns` as per the `Towns->init()` definition. 28 | 29 | Function definitions: 30 | 31 | - init() -- towns.b:5,7 32 | - mktown() -- towns.b:9,11 33 | 34 | ### modules.b:25,29 35 | 36 | This section loads the auxiliary modules used within this example and calls their respective initialization functions. 37 | 38 | Note that no module should be considered usable or safe to reference before calling their respective initialization function, if any. 39 | 40 | ### modules.b:31,39 41 | 42 | This section demonstrates calling the `getpop()` function within the `Persons` module using the currently loaded handle to the `Persons` module, `persons`. An instance of `ref Person` is created and its fields filled manually. 43 | 44 | Note that across these two calls, the `population` within `modules->persons` increments, however, the `population` within `towns->persons` does not increment. This is because the two instances of the `Persons` module are separate and loaded separately. These two modules share no resources other than a common origin within their `.dis` bytecode files. 45 | 46 | ### modules.b:41,45 47 | 48 | This section features a call to `mktown()` from within the `Towns` module. The contents of the respective `ref Town` are filled manually. 49 | 50 | Note: The type `Person` is imported at modules.b:13. 51 | 52 | ## Demo 53 | 54 | ; mk 55 | ; modules 56 | 0 57 | 0 58 | 1 59 | 0 60 | Name: Mars 61 | Size: 2 62 | Members: 63 | → Spike 64 | → Ed 65 | ; 66 | 67 | ## Exercises 68 | 69 | - Can you access `persons->population` from `modules`? 70 | - Could you make a global variable by placing said variable in its respective module definition? 71 | - What happens if you remove the `import` statements for `Person` and `Town` in various `.b` files? 72 | - What happens if you include `persons.m` in `modules.b`? 73 | - What happens if you include `persons.m` in `modules.b` and remove the include for `persons.m` in `towns.m`? 74 | - Can you load the `Util` module contained within `util.b`? 75 | -------------------------------------------------------------------------------- /Modules/mkfile: -------------------------------------------------------------------------------- 1 | Context, nil: list of string); 20 | }; 21 | 22 | init(nil: ref Draw->Context, nil: list of string) { 23 | sys = load Sys Sys->PATH; 24 | 25 | persons = load Persons "./persons.dis"; 26 | towns = load Towns "./towns.dis"; 27 | 28 | persons->init(); 29 | towns->init(); 30 | 31 | print("%d\n", persons->getpop()); 32 | print("%d\n", towns->persons->getpop()); 33 | 34 | p := persons->mkperson(); 35 | p.name = "Spike"; 36 | p.age = 27; 37 | 38 | print("%d\n", persons->getpop()); 39 | print("%d\n", towns->persons->getpop()); 40 | 41 | t := towns->mktown(); 42 | t.pop = array[] of {p, ref Person(13, "Ed")}; 43 | t.name = "Mars"; 44 | 45 | print("%s\n", t.stringify()); 46 | 47 | exit; 48 | } 49 | -------------------------------------------------------------------------------- /Modules/persons.b: -------------------------------------------------------------------------------- 1 | implement Persons; 2 | 3 | include "persons.m"; 4 | 5 | population: int; 6 | 7 | init() { 8 | population = 0; 9 | } 10 | 11 | getpop(): int { 12 | return population; 13 | } 14 | 15 | mkperson(): ref Person { 16 | population++; 17 | return ref Person; 18 | } 19 | 20 | Person.stringify(p: self ref Person): string { 21 | return p.name; 22 | } 23 | -------------------------------------------------------------------------------- /Modules/persons.m: -------------------------------------------------------------------------------- 1 | Persons: module { 2 | init: fn(); 3 | mkperson: fn(): ref Person; 4 | getpop: fn(): int; 5 | 6 | Person: adt { 7 | age: int; 8 | name: string; 9 | stringify: fn(p: self ref Person): string; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /Modules/towns.b: -------------------------------------------------------------------------------- 1 | implement Towns; 2 | 3 | include "towns.m"; 4 | 5 | init() { 6 | persons = load Persons "./persons.dis"; 7 | } 8 | 9 | mktown(): ref Town { 10 | return ref Town; 11 | } 12 | 13 | Town.stringify(t: self ref Town): string { 14 | Person: import persons; 15 | 16 | s := "Name: " + t.name + "\nSize: " + string len t.pop + "\nMembers:"; 17 | 18 | for(i := 0; i < len t.pop; i++) 19 | s += "\n→ " + t.pop[i].stringify(); 20 | 21 | return s; 22 | } 23 | -------------------------------------------------------------------------------- /Modules/towns.m: -------------------------------------------------------------------------------- 1 | include "persons.m"; 2 | 3 | Towns: module { 4 | init: fn(); 5 | mktown: fn(): ref Town; 6 | 7 | persons: Persons; 8 | 9 | Town: adt { 10 | pop: array of ref Persons->Person; 11 | name: string; 12 | stringify: fn(t: self ref Town): string; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /Modules/util.b: -------------------------------------------------------------------------------- 1 | implement Util; 2 | 3 | Util: module { 4 | glenda: fn(): string; 5 | }; 6 | 7 | glenda(): string { 8 | return " (\\(\\\n �\". ..\n ( . .)\n | � �\n � ;\n c?\".UJ\""; 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Limbo by Example 2 | 3 | These are programming examples in the spirit of [gobyexample](https://github.com/mmcgrana/gobyexample), but targeted to the Limbo programming language. 4 | 5 | These examples are tested on the [purgatorio](http://code.9front.org/hg/purgatorio/) fork of the Inferno operating system. 6 | 7 | Examples will, if they reference lines in source within an explanation, utilize a plumbable string (see: plumb(1)) which indicates the line - or range of lines - which the explanation references. 8 | 9 | Examples are composed in acme(1) which allows you to right click these strings to jump to the relevant source lines. 10 | 11 | If you'd like to try Inferno without installing, I recommend trying Pete's on-demand VNC: http://tryinferno.reverso.be/ 12 | 13 | The `;` rune indicates a command to be run from the Inferno sh(1) shell. 14 | 15 | ## Building 16 | 17 | If a given example provides a mkfile: 18 | 19 | ; mk 20 | 21 | Otherwise, there will only be one file, a Limbo source file which can be built with: 22 | 23 | ; limbo file.b 24 | 25 | You could then run said file with: 26 | 27 | ; file 28 | 29 | ## Index 30 | 31 | Core language functionality: 32 | 33 | - [Hello World](./HelloWorld) 34 | - [Values](./Values) 35 | - [Constants](./Constants) 36 | - [Loops](./Loops) 37 | - [If Else](./If-Else) 38 | - [Switch Case](./Switch) 39 | - [Arrays](./Arrays) 40 | - [Slices](./Slices) 41 | - [Lists](./Lists) 42 | - [Functions](./Functions) 43 | - [Function References](./Function-Refs) 44 | - [Spawn](./Spawn) 45 | - [Channels](./Channels) 46 | - [Abstract Data Types (ADT's)](./ADTs) 47 | - [Modules](./Modules) 48 | - [Generics, Picks, and Interfaces (kind of)](./Generics) 49 | - [Exceptions](./Exceptions) 50 | 51 | Standard library modules: 52 | 53 | - [Command-Line Arguments](./Args-M) 54 | 55 | ## References 56 | 57 | - [The Limbo Programming Language](http://doc.cat-v.org/inferno/4th_edition/limbo_language/limbo) 58 | - [Addendum to The Limbo Programming Language](http://www.vitanuova.com/inferno/papers/addendum.pdf) 59 | - [Inferno Programming with Limbo](http://www.gemusehaken.org/ipwl/) 60 | - [A Descent into Limbo](http://doc.cat-v.org/inferno/4th_edition/limbo_language/descent) 61 | - [Limbo Programming](http://www.vitanuova.com/inferno/papers/limbomore.html) 62 | - [Inferno Lab](https://github.com/caerwynj/inferno-lab/) 63 | -------------------------------------------------------------------------------- /Slices/README.md: -------------------------------------------------------------------------------- 1 | # Slices 2 | 3 | Limbo supports slicing on arbitrary array types, including strings, as strings are arrays of bytes. 4 | 5 | ## Source 6 | 7 | ### slices.b:19,22 8 | 9 | This section shows slicing from the beginning of an array to the index up to before the variable `n`. 10 | 11 | ### slices.b:24,25 12 | 13 | This section shows slicing from the index 4 to the end of the array. 14 | 15 | ### slices.b:27,28 16 | 17 | This section shows several slicing operations. First `c` is created as the slice of `str` from index 2 through the index prior to the final index of `str`. The printout is then a string version of the slice of `c` from index 4 through the 7th index. 18 | 19 | ## Demo 20 | 21 | ; limbo slices.b 22 | ; slices 23 | little_baby_ducks 24 | lit 25 | le_baby_ducks 26 | _bab 27 | ; 28 | 29 | ## Exercises 30 | 31 | - What happens if you try to slice an empty string, `""`? 32 | - Can you slice in the format `[:n:]`? 33 | -------------------------------------------------------------------------------- /Slices/slices.b: -------------------------------------------------------------------------------- 1 | implement Slices; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Slices: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | init(nil: ref Draw->Context, nil: list of string) { 14 | sys = load Sys Sys->PATH; 15 | 16 | str := "little_baby_ducks"; 17 | print("%s\n", str); 18 | 19 | n := 3; 20 | 21 | a := str[:n]; 22 | print("%s\n", a); 23 | 24 | b := str[4:]; 25 | print("%s\n", b); 26 | 27 | c := array of byte str[2:len str -1]; 28 | print("%s\n", string c[4:8]); 29 | 30 | exit; 31 | } 32 | -------------------------------------------------------------------------------- /Spawn/README.md: -------------------------------------------------------------------------------- 1 | # Spawn 2 | 3 | Limbo supports the creation of processes operating on a given function via the `spawn` statement. Functions passed to spawn cannot have a return value. 4 | 5 | Inferno processes share memory with their parent processes which spawn them, bar the process's respective stack. Inferno processes are pre-emptively scheduled by the Inferno kernel. These processes are analogous, but not equivocal to, threads in unix-like systems. 6 | 7 | Synchronization between processes is recommended to be done through [channels](../Channels). 8 | 9 | For further reading on potential inspirations for Inferno processes, see [rfork(2)](http://man.cat-v.org/9front/2/fork). 10 | 11 | ## Source 12 | 13 | ### spawn.b:31,40 14 | 15 | This section spawns four processes all of which will attempt to print to their standard output. Two functions spawn running the `quacker()` function and two function spawn running the `summer()` function with varying arguments. 16 | 17 | Note: The call to `sleep()` in this context within the `init()` function. 18 | 19 | ## Demo 20 | 21 | ; limbo spawn.b 22 | ; spawn 23 | quack! 24 | Sum (2): 0 25 | quack! 26 | Sum (3): 0 27 | Sum (2): 1 28 | Sum (3): 1 29 | Sum (2): 3 30 | Sum (3): 3 31 | Final sum (2): 3 32 | Sum (3): 6 33 | Final sum (3): 6 34 | ; 35 | 36 | ## Exercises 37 | 38 | - How many different orders do you see the print statements occur in? 39 | - What happens if you remove the sleep? 40 | -------------------------------------------------------------------------------- /Spawn/spawn.b: -------------------------------------------------------------------------------- 1 | implement Spawn; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print: import sys; 8 | 9 | Spawn: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | summer(n: int) { 14 | sum := 0; 15 | 16 | for(i := 0; i <= n; i++) { 17 | sum += i; 18 | print("Sum (%d): %d\n", n, sum); 19 | } 20 | 21 | print("Final sum (%d): %d\n", n, sum); 22 | } 23 | 24 | quacker() { 25 | print("quack!\n"); 26 | } 27 | 28 | init(nil: ref Draw->Context, nil: list of string) { 29 | sys = load Sys Sys->PATH; 30 | 31 | spawn quacker(); 32 | 33 | spawn summer(2); 34 | 35 | spawn quacker(); 36 | 37 | n := 3; 38 | spawn summer(n); 39 | 40 | sys->sleep(10); 41 | 42 | exit; 43 | } 44 | -------------------------------------------------------------------------------- /Switch/README.md: -------------------------------------------------------------------------------- 1 | # Switch 2 | 3 | Limbo does not have a verbatim `switch` statement. Rather, it has a statement named `case` which is analogous, but not identical to C's switch-case construct. 4 | 5 | ## Source 6 | 7 | ### switch.b:16,31 8 | 9 | This segment exemplifies a few features of limbo's case statement. There is an iterative loop wrapped around a case statement which has a boolean `or`'d section and a default section, indicated by the wildcard `*` operator. 10 | 11 | Limbo case statements break by default and accept range matching operations involving the `or` and `to` keywords. 12 | 13 | A break or continue followed by a label causes a break out of, or the next iteration of, the enclosing construct that is labeled with the same label. 14 | 15 | ### switch.b:33,42 16 | 17 | This case statement demonstrates the use of the `to` range operator in a given section while providing a specific section to match the `C` character as well. 18 | 19 | ### switch.b:44,51 20 | 21 | Limbo is able to switch on string values, this can include a `nil` check, demonstrated by the `""` section. Note that there is no default section provided. The default section is not mandatory. 22 | 23 | ### switch.b:53,60 24 | 25 | This case verifies whether a value is `0` or `1` to determine if a value is binary. 26 | 27 | ### switch.b:62,69 28 | 29 | The valid types for case statements include: `int`, `string`, and `big`. 30 | 31 | Note that the `big` coercion statement is mandatory. 32 | 33 | ## Demo 34 | 35 | ; limbo switch.b 36 | ; switch 37 | Even 38 | Odd 39 | i's value: 9 40 | Valid hex 41 | Quack! 42 | This is binary 43 | Neither 4 nor 7 44 | ; 45 | 46 | ## Exercises 47 | 48 | - Try commenting out the `break` and/or `continue` keywords in the first switch, how does the behavior change? 49 | - Change the variable `c` to equal `'C'`, what's printed? 50 | -------------------------------------------------------------------------------- /Switch/switch.b: -------------------------------------------------------------------------------- 1 | implement Switch; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print, FD: import sys; 8 | 9 | Switch: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | init(nil: ref Draw->Context, nil: list of string) { 14 | sys = load Sys Sys->PATH; 15 | 16 | i := 10; 17 | 18 | # The label labelling the enclosing construct 19 | loop: 20 | 21 | for(; i >= 0; i--) 22 | case i { 23 | 0 or 2 or 4 or 6 or 8 or 10 => 24 | print("Even\n"); 25 | continue loop; 26 | * => 27 | print("Odd\n"); 28 | break loop; 29 | } 30 | 31 | print("i's value: %d\n", i); 32 | 33 | c := 'c'; 34 | 35 | case c { 36 | 'a' to 'f' => 37 | print("Valid hex\n"); 38 | 'C' => 39 | print("The letter 'C'\n"); 40 | * => 41 | print("The default\n"); 42 | } 43 | 44 | str := "ducks"; 45 | 46 | case str { 47 | "ducks" => 48 | print("Quack!\n"); 49 | "" => 50 | print("Nil string\n"); 51 | } 52 | 53 | n := 1; 54 | 55 | case n { 56 | 0 or 1 => 57 | print("This is binary\n"); 58 | * => 59 | print("This is non-binary\n"); 60 | } 61 | 62 | f := big 6; 63 | 64 | case f { 65 | big 4 => print("The number 4\n"); 66 | big 5 => print("The number 7\n"); 67 | * => 68 | print("Neither 4 nor 7\n"); 69 | } 70 | 71 | exit; 72 | } 73 | -------------------------------------------------------------------------------- /Values/README.md: -------------------------------------------------------------------------------- 1 | # Values 2 | 3 | Limbo has a variety of primitive types available and possesses the ability to coerce between them. 4 | 5 | ## Source 6 | 7 | ### values.b:6,7 8 | 9 | sys: Sys; 10 | print, sprint: import sys; 11 | 12 | The name `sys` is declared as type `Sys`. If you are defining a type for a name, the format is: `varname: typename`. 13 | 14 | The `import` statement allows you to define a local name which is tied to a name in another space. That is, the `print` name becomes equivalent to `sys->print`. 15 | 16 | ### values.b:16,19 17 | 18 | n := 7; 19 | b := big 8; 20 | f := real 3.2; 21 | str := "String!"; 22 | 23 | Three variables are defined, `n` is an integer, `b` is a long integer, `f` is a floating point number, `str` is a string. 24 | 25 | Rather than the word float, the word `real` is used to identify a floating point number. 26 | 27 | Note: At present, Inferno only supports 32-bit architectures, so all types are 32-bit types. 28 | 29 | ### values.b:21,22 30 | 31 | print("%d\n", 0 || 1); 32 | print("%d\n", 0 && 1); 33 | 34 | There are no boolean types in Limbo. As per C, 0 indicates false and values ≥ 1 indicate true. 35 | 36 | ### values.b:24,26 37 | 38 | print("%d\n", n / int f); 39 | print("%f\n", real n / f); 40 | print("%bd\n", b / big 8); 41 | 42 | Types and type conflicts are checked at compile-time. Prefacing a variable or value with a type name coerces the variable or value to an equivalent value of the provided type. 43 | 44 | There are no implicit conversions of types, all type coercion must be explicitly stated. 45 | 46 | ### values.b:28,29 47 | 48 | print("%s\n", str[:len str-1]); 49 | print("%s\n", str[2:]); 50 | 51 | Array types can be sliced. The `:` operator acts on a range between either a value or to one end of the array if no value is provided on a side of the `:`. 52 | 53 | The `len` statement provides the number of elements in a string, array, or list. 54 | 55 | ### values.b:31,32 56 | 57 | print("%s", "inferno " + "os " + sprint("%c", '\n')); 58 | print("limbo" + " " + "lang\n"); 59 | 60 | Strings can be concatenated via `+` and `+=` (not shown). 61 | 62 | ## Demo 63 | 64 | ; limbo values.b 65 | ; values 66 | 1 67 | 0 68 | 2 69 | 2.187500 70 | 1 71 | String 72 | ring! 73 | inferno os 74 | limbo lang 75 | ; 76 | 77 | ## Exercises 78 | 79 | - On values.b:31 remove the `"%s",`, what happens? 80 | - Try removing type coercions and change format specifiers, see what happens! 81 | -------------------------------------------------------------------------------- /Values/values.b: -------------------------------------------------------------------------------- 1 | implement Values; 2 | 3 | include "sys.m"; 4 | include "draw.m"; 5 | 6 | sys: Sys; 7 | print, sprint: import sys; 8 | 9 | Values: module { 10 | init: fn(nil: ref Draw->Context, nil: list of string); 11 | }; 12 | 13 | init(nil: ref Draw->Context, nil: list of string) { 14 | sys = load Sys Sys->PATH; 15 | 16 | n := 7; 17 | b := big 8; 18 | f := real 3.2; 19 | str := "String!"; 20 | 21 | print("%d\n", 0 || 1); 22 | print("%d\n", 0 && 1); 23 | 24 | print("%d\n", n / int f); 25 | print("%f\n", real n / f); 26 | print("%bd\n", b / big 8); 27 | 28 | print("%s\n", str[:len str-1]); 29 | print("%s\n", str[2:]); 30 | 31 | print("%s", "inferno " + "os " + sprint("%c", '\n')); 32 | print("limbo" + " " + "lang\n"); 33 | 34 | exit; 35 | } 36 | --------------------------------------------------------------------------------