diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-17 15:41:56 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | 406da94f13f47a9c7aef6b9c11cfe057772630cf (patch) | |
| tree | dd98e12eddd421e2883c90239cb10225bb75b07e /docs | |
| parent | builtin/trae: print literals literally (diff) | |
| download | alive-406da94f13f47a9c7aef6b9c11cfe057772630cf.tar.gz alive-406da94f13f47a9c7aef6b9c11cfe057772630cf.zip | |
split guide into multiple pages
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/gen/layout.moon | 43 | ||||
| -rwxr-xr-x | docs/gen/md | 10 | ||||
| -rw-r--r-- | docs/guide.md | 548 | ||||
| -rw-r--r-- | docs/guide/basic-types.md | 59 | ||||
| -rw-r--r-- | docs/guide/copilot-gui.png (renamed from docs/copilot-gui.png) | bin | 4445 -> 4445 bytes | |||
| -rw-r--r-- | docs/guide/defining-symbols.md | 37 | ||||
| -rw-r--r-- | docs/guide/evaltime-and-runtime.md | 45 | ||||
| -rw-r--r-- | docs/guide/functions.md | 54 | ||||
| -rw-r--r-- | docs/guide/getting-started-guide.md | 25 | ||||
| -rw-r--r-- | docs/guide/hello-world.md | 33 | ||||
| -rw-r--r-- | docs/guide/importing-operators.md | 26 | ||||
| -rw-r--r-- | docs/guide/installation.md | 53 | ||||
| -rw-r--r-- | docs/guide/making-sound.md | 58 | ||||
| -rw-r--r-- | docs/guide/scopes.md | 82 | ||||
| -rw-r--r-- | docs/guide/syntax.md | 43 | ||||
| -rw-r--r-- | docs/guide/working-with-the-copilot.md | 59 | ||||
| -rw-r--r-- | docs/index.md | 2 | ||||
| -rw-r--r-- | docs/style.css | 28 |
18 files changed, 645 insertions, 560 deletions
diff --git a/docs/gen/layout.moon b/docs/gen/layout.moon index 4d43737..1b1cdaa 100644 --- a/docs/gen/layout.moon +++ b/docs/gen/layout.moon @@ -86,6 +86,46 @@ autoref = (str) -> str = str\gsub '%[([^%]]-)%]%[:(.-):%]', r str +subnav = do + split_name = (file) -> + href, label = file\match '^docs/(.*/([%w%-]+)%.html)' + label = label\gsub '-', ' ' + label, href + + subnav_link = (dir, file) -> + import span, a, u from dom + + if not file + return span '' + + label, href = split_name file + label = switch dir + when 'l' + "◂ back: #{u label}" + when 'r' + "next: #{u label} ▸" + + a label, href: abs href + + (all) -> + assert OUT, "OUT needs to be set" + import div, nav, h1 from dom + + local c + for i, src in ipairs all + if OUT == src + c = i + break + + div { + class: 'subheader' + h1 (split_name OUT) + nav { + subnav_link 'l', all[c-1] + subnav_link 'r', all[c+1] + } + } + aopts = (href, pat) -> { href: abs href @@ -110,7 +150,7 @@ layout = (opts) -> } div class: 'grow' a 'home', aopts 'index.html', 'index.html$' - a 'getting started', aopts 'guide.html', 'guide.html$' + a 'guide', aopts 'guide/getting-started-guide.html', 'guide' a 'reference', aopts 'reference/index.html', 'reference' a 'internals', aopts 'internals/index.html', 'ldoc' } @@ -148,4 +188,5 @@ layout = (opts) -> :autoref :render :layout + :subnav } diff --git a/docs/gen/md b/docs/gen/md index 32d4fdd..f22268c 100755 --- a/docs/gen/md +++ b/docs/gen/md @@ -1,5 +1,5 @@ #!/usr/bin/env moon -import layout, autoref from require 'docs.gen.layout' +import layout, subnav, autoref from require 'docs.gen.layout' import compile from require 'discount' export OUT, ESCAPE @@ -18,6 +18,8 @@ ESCAPE = (str) -> (str\gsub '([*`])', '\\%1') { OUT, file } = arg - -contents = slurp file -spit OUT, layout compile (autoref contents), 'githubtags', 'fencedcode' +content = compile (autoref slurp file), 'githubtags', 'fencedcode' +if #arg > 2 + sub = subnav [a for a in *arg[3,]] + content.body = table.concat { sub, content.body }, '\n\n' +spit OUT, layout content diff --git a/docs/guide.md b/docs/guide.md deleted file mode 100644 index e58b977..0000000 --- a/docs/guide.md +++ /dev/null @@ -1,548 +0,0 @@ -% getting started -% - -% - -# getting started with alv -`alv` ("alive") is a language for creating and changing realtime programs while -they are running continuously. It can be used to create music, visuals or -installations, but by itself creates neither sound nor video. Rather, `alv` is -used together with other tools and synthesizers (for example -[SuperCollider][supercollider] or [Pilot][pilot]). In such an ensemble of -tools, `alive` takes the role of a 'conductor', telling the other tools what to -play when by sending commands to them using a variety of protocols, such as OSC -and MIDI. - -Before we get to making sound though, we should learn a bit about the `alv` -programming language, and how to install and use it. - -## installation -`alv` is written in the Lua programming language, and is compatible with both -Lua 5.3 and luajit. - -### unix/linux and mac os -Your distribution should provide you with packages for Lua and Luarocks. On Mac -OS X, both are provided through [homebrew][homebrew]. After installing both of -these, you should be able to start the Lua interpreter from the shell: - - $ lua - Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio - > - -You can exit using `CTRL+C`. If the version you see is not 5.3, double check -your distribution packages or see if it was installed as `lua5.3` or `lua53`. -Similarily, you should be able to run `luarocks`, `luarocks53` or `luarocks5.3`: - - $ luarocks list - - Rocks installed for Lua 5.3 - --------------------------- - -Again, double check your installation or try adding `--lua-version 5.3` if the -displayed version is not 5.3. - -With everything ready to go, you can now install `alv`: - - $ luarocks install alive - -To use the copilot GUI, you will also need the `fltk4lua` package, which requires -installing or building FLTK (also available through homebrew). - - $ luarocks install fltk4lua - -With the `alive` package, two binaries should have been installed on your system: -`alv` and `alv-fltk`. If you do not find these in your `$PATH`, you may need to -apply the exports from `luarocks path` upon login, e.g. in your `.bashrc`. - -### windows -For Windows, a binary package is available from the latest -[github release][:*release*:]. It includes not only the `alv` source code, but -also a compiled version of Lua 5.3 as well as Luarocks and all of `alv`'s -dependencies. - -To use the binary package, simply extract the archive and move the folder -wherever you want. You can now start the `hello.alv` example script by dragging -it onto the `alv.bat` or `alv-fltk.bat` file in the folder. - -If you are going to use the command-line `alv.bat`, it is recommended to add -the directory containing it to `%PATH%`, so that you can use the `alv` command -anywhere on your system. - -## starting the copilot GUI -On Linux and Mac OS X, you can launch the GUI by executing the `alv-fltk` -command. On Windows, you can double-click `alv-fltk.bat`. This window should -open: - - - -To run a script, open it using `File > Open Script` or `^O` (control-O). You can -pause and resume execution using the `Run` button or `^P`. To stop execution -simply close the window. - -## starting the copilot in the terminal -To run a file in the terminal, invoke the command `alv <path/to/file.alv>`. If -your system cannot find the `alv` command, check your installation and `PATH`. -On Windows, it is also possible to drag your `alv` file onto `alv.bat`. - -You can stop the copilot by pressing `^C` (control-C). - -## writing and running a script -To use the copilot, you first need a file to run. Create an empty file in your -preferred text editor and save it as `test.alv`. Before adding any code, start -the copilot. - -Either start the GUI and open the file we just created, or run the following -command from the terminal: - - $ alv test.alv - test.alv changed at 1585138092 - -You should see a note indicating that `alv` processed the file. The note will -show up in the upper pane (in the GUI), or colored green (in the Terminal) to -mark the message as an *eval-time message*. This means that the message was a -direct response to the file changing or being loaded the first time. - -From now on, whenever you change `test.alv` and save the file, the copilot will -reload it and execute your new code. When you are done, you can stop the copilot -at any time by closing the GUI window or using `^C` (control-C). - -`alv`'s syntax is very similar to Lisp. Expressions take the form of -parenthesized lists like `(head a b c...)`, where the first element of the list -(`head`) is the name of an operator or function, which defines what the -expression as a whole will do, while the other elements are parameters whose -meaning depends on the `head`. Let's start with a simple operator, [print][]: -[print][] is used simply to print messages to the copilot console. Enter the -following in your file and save it: - - (print "hello world!") - -As soon as you save the file, you should notice two things happening: - -1. The copilot will print two new lines: - - hello.alv changed at 1583424169 - hello world! - - In the first line, it notifies us that the file has changed. In the second - line, you can see the output from the [print][] expression. -2. The copilot will make a small modification to your file. Depending on the - editor you are using, this may either result in you seeing the modification - immediately, or a notice appearing that offers the option to reload the - file. If it is the latter, confirm the notification to accept the changes. - If there is an option to do so, you may want to configure your editor to - always reload the file automatically. - -The code should now look like this: - - ([1]print "hello world") - -The `[1]` that the copilot added to your expression is that expression's `tag`. -In `alv`, every expression has a tag that helps the copilot to identify the -individual expressions as you make changes to your code. The copilot will make -sure that all expressions are tagged by adding missing tags when you save the -file, but you have to watch out not to duplicate a tag when copying and pasting -code. When you duplicate code that already has tags, you can either manually -change the tags to be unique, or simply delete the whole tag (including the -square brackets) and let the copilot generate a new one for you the next time -you save the file. - -## syntax -As we just saw, *expressions* in alv take the form of parenthesized lists. -Elements of an expression have to be separated by whitespace, but any type of -and amount of whitespace is valid: feel free to use spaces, tabs, and newlines -to format code to your liking. The following are all equal and valid examples: - - (print "hello world") - - (+ 1 - 2 - 3) - - (print - "hello world") - - ( print "hello world" ) - -It is however recommended to follow the [clojure style guide][clojure-style] as -much as it does apply to alv. All further examples in this guide will respect -this guideline. - -### basic types -Strings can be written in two ways: using double quotes (`"`), as we did above, -or using single quotes (`'`). In both types of strings, you can escape a quote -that otherwise would signify the end of the string by adding a single backslash -before it. Consequently, backslashes also have to be escaped in the same way. -The following are all valid strings: - - "hello world" - 'hello world' - "it's a beautiful day" - 'it\'s a beautiful day' - "this is a backslash: \\" - "this is a double quote: \"" - "" - '' - -Aside from strings, there are two more types of values that you can use when -writing alv programs: numbers and booleans. Numbers use the digits 0-9 and -can be integers, contain a decimal point, or start or end with a decimal point. -Numbers can start with a negetive sign. The following are all valid numbers: - - 0 - 12 - -7 - 0.1 - 10. - .1 - 123. - -There are only two boolean values, `true` and `false`: - - true - false - -The operator [print][], that we have been using above, only works on strings, -but there is a similar operator called [trace][] that can be used to inspect -any kind of value. It prints the value itself alongside more information, such -as the values type. Give it a try: - - (trace "hello") - (trace 2) - (trace true) - -This will print the following: - - toast.alv changed at 1585138575 - trace <ValueStream str: hello>: <ValueStream str: hello> - trace <ValueStream num: 2>: <ValueStream num: 2> - trace <ValueStream sym: true>: <ValueStream bool: true> - -### comments -To annotate your code, you can use comments. In `alv`, comments begin with -`#(` and end on a matching `)`. This way you can comment out a complete -expression simply by adding a `#` character in front. - - #(this is a comment) - - #(this is a long, - multi-line comment, - (and it also has nested parentheses). - It ends after this sentence.) - -You can put comments anywhere in your program where whitespace is allowed and -it will simply be ignored by `alv`. - -### importing modules -Apart from [trace][], there are only very little builtin operators in `alv` - -you can see all of them in the *builtins* section of the [reference][:/:]. -All of the 'real' functionality of `alv` is grouped into *modules*, that have -to be loaded individually. *Modules* help organize all of the operators so that -it is less overwhelming to look for a concrete feature. It is also possible to -create your own plugins as new modules, which will be covered in another guide -soon. - -Let's try using the [`+` operator][:math/+:] from the [math/][] module. To use -operators from a module, we need to tell `alv` to load it first: We can load -*all* the operators from the [math/][] module into the current scope using the -[import*][] builtin: - - (import* math) - (trace (+ 1 2)) - -prints - - trace (+ 1 2): <Value num: 3> - -Because it can get a bit confusing when all imported operators are mixed in the -global scope, it is also possible to load the module into its own scope and use -it with a prefix. This is what the [import][] builtin is for: - - (import math) - (trace (math/+ 1 2)) - -### defining symbols -Another element of code in `alv` that we haven't discussed in detail yet are -*symbols*. *Symbols* (like `trace`, `import*` or `math/+`) are names that serve -as placeholders for previously *defined* values. When code is evaluated, symbols -are looked up in the current *scope* and replaced with the corresponding value -found there. - -When an `alv` file starts running, a number of symbols are defined in the -default scope: These are the *builtins* mentioned above, and of which we have -already been using [trace][], [import][], and [import*][]. - -To *define a symbol* yourself, the [def][] builtin is used. It takes the symbol -as its first, and the value to associate as its second parameter. After a symbol -is defined, the name becomes an alias that behaves like the value itself. For -example, we can use [def][] to associate the result of our calculation with the -symbol `result`, and then refer to it by that symbol in the [trace][] operator: - - (import* math) - - (def result (+ 1 2)) - (trace result) - -Symbols need to start with a letter or one of the characters `-_+*/.!?=%`. -After the first character, numbers are also allowed. There are two types of -symbols that are treated specially: symbols containing a slash (`math/+`), and -symbols starting and ending with asterisks (`*clock*`): - -- Symbols containing slashes (except at beginning and end of the symbol) are - split into multiple symbols, and looked up recursively in the scope. For - example, `math/+` is found by first looking for a value for the symbol `math`, - and then looking for the symbol `+` in that value. If the value for the - symbol `math` is not a scope, an error is thrown. -- Symbols starting and ending with asterisks are called `dynamic symbols` and - are looked up in a different way inside user-defined functions. This will be - covered in detail later. -- The two special formats can be mixed: when evaluating `*hello*/world`, - `alv` will look for the symbol `world` within the scope found by dynamically - resolving `*hello*`. - -Both [import][] and [import*][] are actually shorthands and what they -accomplish can be done using the lower-level builtins [def][], [use][] and -[require][]. Here is how you could replace [import][]: - - #(with import:) - (import math) - (trace (math/+ 1 2)) - - #(with def and require:) - (def math (require "math")) - (trace (math/+ 1 2)) - -[require][] returns a *scope*, which is defined as the symbol `math`. -Then `math/+` is resolved by looking for `+` in this nested scope. Note that -the symbol that the scope is defined as and the name of the module that is -loaded do not have to be the same, you could call the alias whatever you want: - - #(this not possible with import!) - (def fancy-math (require "math")) - (trace (fancy-math/+ 1 2)) - -Most of the time the name of the module makes a handy prefix already, so -[import][] can be used to save a bit of typing and make the code look a bit -cleaner. [import*][], on the other hand, defines every symbol from the imported -module individually. It could be implemented with [use][] like this: - - (use (require "math")) - (trace (+ 1 2)) - -[use][] copies all symbol definitions from the scope it is passed to the -current scope. - -Note that [import][], [import*][], [def][], and [use][] all can take multiple -arguments: - - #(using the shorthands:) - (import* math logic) - (import midi osc) - - #(using require, use and def:) - (use (require "math") (require "logic")) - (def midi (require "midi") - osc (require "osc")) - -It is common to have an [import][] and [import*][] expression at the top of an -`alv` program to load all of the modules that will be used later, but the -modules don't necessarily have to be loaded at the very beginning, as long as -all symbols are defined before they are being used. - -### nested scopes -Once a symbol is defined, it cannot be changed or removed: - - (def a 3) - (def a 4) #(error!) - -It is, however, possible to 'shadow' a symbol by re-defining it in a nested -scope: So far, all symbols we have defined - using `def`, [import][] and -[import*][] - have been defined in the *global scope*, the scope that is active -in the whole `alv` program. The [do][] builtin can be used to create a new -scope and evaluate some expressions in it: - - (import string) - - (def a 1 - b 2) - - (trace (.. "first: " a " " b)) - (do - (def a 3) - (trace (.. "second: " a " " b)) - (trace (.. "third: " a " " b)) - -This example prints the following: - - trace (.. "first: " a " " b): <Value str: first: 1 2> - trace (.. "second: " a " " b): <Value str: second: 3 2> - trace (.. "third: " a " " b): <Value str: third: 1 2> - -As you can see, within a nested scope it is possible to overwrite a definition -from the parent scope. Symbols that are not explicitly redefined in a nested -scope keep their values, and changes in the nested scope do not impact the -parent scope. - -### defining functions -Another builtin that creates a nested scope is [fn][], which is used to -create a *user-defined function*, which can be used to simplify repetitive -code, amongst other things: - - (import* math) - - (def add-and-trace - (fn - (a b) - (trace (+ a b)))) - - (add-and-trace 1 2) - (add-and-trace 3 4) - -Here a *function* `add-and-trace` is defined. When defining a function, first -the names of the parameters have to be given. The function defined here takes -two parameters, `a` and `b`. The last part of the function definition is called -the *function body*. - -A function created using [fn][] can be called just like an operator. When a -function is called, the parameters to the function are defined with the names -given in the definition, and then the function body is executed. The previous -example is equivalent to the following: - - (import* math) - - (def add-and-trace - (fn - (a b) - (trace (+ a b))) - - (do - (let a 1 - b 2) - (trace (+ a b))) - - (do - (let a 3 - b 4) - (trace (+ a b))) - -and the output of both is: - - trace (+ a b): <Value num: 3> - trace (+ a b): <Value num: 7> - -In `alv`, functions are first-class values and can be passed around just like -numbers, strings, etc. However it is very common to define a function with a -name, so there is the `defn` shorthand, which combines the `def` and `fn` -builtins into a single expression. Compare this equivalent definition of the -`add-and-trace` function: - - (defn add-and-trace (a b) - (trace (+ a b))) - -### evaltime and runtime -So far, `alv` may seem a lot like any other programming language - you write -some code, save the file, and it runs, printing some output. "What about the -'continuously running' aspect from the introduction?", you may ask yourself. - -So far, we have only seen *evaltime* execution in alv - but there is also -*runtime* behavior. At *evaltime*, that is whenever there is change to the -source code, `alv` behaves similar to a Lisp. This is the part we have seen -so far. But once one such *eval cycle* has executed, *runtime* starts, and -`alv` behaves like a dataflow system like [PureData][pd], [Max/MSP][max] or -[vvvv][vvvv]. - -What looked so far like static constants are actually *streams* of values. -Whenever an input to an operator changes, the operator (may) update and respond -with a change to its output as well. To see this in action, we need to start -with a changing value. Number literals like `1` and `2`, which we used so far, -are *evaltime constant*, which means simply that they will never update. Since -all inputs to our [math/+][] operator are *evaltime constant*, the result is -constant as well. To get some *runtime* activity, we have to introduce a -side-effect input from somewhere outside the system. - -The [time/][] module contains a number of operators whose outputs update -over time. Lets take a look at [time/tick][]: - - (import* time) - (trace (tick 1)) - -This will print a series of numbers, incrementing by 1 every second. The -parameter to [time/tick][] controls how quickly it counts - try changing it to `0.5` -or `2`. As you can see, we can change [time/tick][] *while it is running*, but it -doesn't lose track of where it was! - -All of the other things we learned above apply to streams of values as well - -we can use [def][] to store them in the scope, transform them using the ops -from the [math/][] module and so on: - - (import* time math) - (def tik (tick 0.25)) - (trace (/ tik 4)) - -Note that if you leave the [time/tick][]'s *tag* in place when you move it into -the [def][] expression, it will keep on running steadily even then. - -## making sound -As mentioned earlier, `alv` doesn't produce sound by itself. Instead, it is -paired with other tools, and takes the role of a 'Conductor', sending commands -and sequencing other tools. - -For the sake of this guide, we will be controlling [Pilot][pilot], a simple -UDP-controlled synthesizer. You can go ahead and download and open it now. -You should see a small window with a bunch of cryptic symbols and a little -command line at the bottom. To verify that everything is working so far, -try typing in `84c` and hitting enter. This should play a short sound (the note -4C, played by the 8th default synthesizer voice in Pilot). - -To talk to Pilot from `alv`, we will use the [pilot/][] module. Note that for -this module to work, you have to have the `osc` and `luasocket` dependencies -installed. To play the same sound we played by entering `84c` above every 0.5 -seconds, we can use [time/every][] to send a `bang` to [pilot/play][]: - - (import* time) - (import pilot) - (pilot/play (every 0.5) 8 4 'c') - -You can play with the voice, octave and note values a bit. To add a simple -melody, we can use [util/switch][], which will cycle through a list of -parameters when used together with [time/tick][]: - - (import* time util) - (import pilot) - (pilot/play (every 0.5) 8 4 - (switch (tick 0.5) 'c' 'd' 'a' 'f')) - -Now we can have the voice change every other loop as well: - - (import* time util) - (import pilot) - (pilot/play (every 0.5) - (switch (tick 4) 8 9) - 4 (switch (tick 0.5) 'c' 'd' 'a' 'f')) - -To round off the sound a bit, we can turn on Pilot's reverb using -[pilot/effect][]. Add the following somewhere in your file: - - (pilot/effect "REV" 2 8) - -Now it's time to add some rhythm. The kick drum is voice 12 by default, -and we can also add something like a snare on channel 3: - - (pilot/play (every 0.75) - 12 2 'd' 3) - (pilot/play (every 2) - 13 4 'a' 4) - -Note that since we are using multiple individual [time/every][] instances, -the timing of our voices relative to each other is not aligned - each voice -started playing when the file was first saved with it added, and kept the -rhythmn since. By deleting all their tags and re-saving the file, we can force -`alv` to re-instantiate them all at the same time, thereby synchronising -them. - -[supercollider]: https://supercollider.github.io/ -[pilot]: https://github.com/hundredrabbits/Pilot -[homebrew]: https://brew.sh -[luarocks]: https://github.com/luarocks/luarocks/#installing -[reference]: reference/ -[clojure-style]: https://github.com/bbatsov/clojure-style-guide -[pd]: http://puredata.info/ -[max]: https://cycling74.com/products/max -[vvvv]: https://vvvv.org/ diff --git a/docs/guide/basic-types.md b/docs/guide/basic-types.md new file mode 100644 index 0000000..c22929c --- /dev/null +++ b/docs/guide/basic-types.md @@ -0,0 +1,59 @@ +Strings can be written in two ways: using double quotes (`"`), as we did above, +or using single quotes (`'`). In both types of strings, you can escape a quote +that otherwise would signify the end of the string by adding a single backslash +before it. Consequently, backslashes also have to be escaped in the same way. +The following are all valid strings: + + "hello world" + 'hello world' + "it's a beautiful day" + 'it\'s a beautiful day' + "this is a backslash: \\" + "this is a double quote: \"" + "" + '' + +Aside from strings, there are two more types of values that you can use when +writing alv programs: numbers and booleans. Numbers use the digits 0-9 and +can be integers, contain a decimal point, or start or end with a decimal point. +Numbers can start with a negetive sign. The following are all valid numbers: + + 0 + 12 + -7 + 0.1 + 10. + .1 + 123. + +There are only two boolean values, `true` and `false`: + + true + false + +The operator [print][], that we have been using above, only works on strings, +but there is a similar operator called [trace][] that can be used to inspect +any kind of value. It prints the value itself alongside more information, such +as the values type. Give it a try: + + (trace "hello") + (trace 2) + (trace true) + +This will print the following: + + changes to files: values.alv + trace "hello": <str= "hello"> + trace 2: <num= 2> + trace true: <bool= true> + +On the left side of the colon, [trace][] prints the expression that it is +evaluating. On the right side, three pieces of information are shown: + +- the *type*: `str`, `num`, `bool` +- the *value* itself: `"hello"`, `2`, `true` +- the *kind* of the result: `=` + +`=` means that these values are *constant* - they will not change by themselves +until the code is changed. For simple values like these that seems obvious, but +in `alv` we can also create values tha change over time, as we will see soon. diff --git a/docs/copilot-gui.png b/docs/guide/copilot-gui.png Binary files differindex e0b3e25..e0b3e25 100644 --- a/docs/copilot-gui.png +++ b/docs/guide/copilot-gui.png diff --git a/docs/guide/defining-symbols.md b/docs/guide/defining-symbols.md new file mode 100644 index 0000000..5fb6d3f --- /dev/null +++ b/docs/guide/defining-symbols.md @@ -0,0 +1,37 @@ +Another element of code in `alv` that we haven't discussed in detail yet are +*symbols*. *Symbols* (like `trace`, `import*` or `math/+`) are names that serve +as placeholders for previously *defined* values. When code is evaluated, symbols +are looked up in the current *scope* and replaced with the corresponding value +found there. + +When an `alv` file starts running, a number of symbols are defined in the +default scope: These are the *builtins* mentioned above, and of which we have +already been using [trace][], [import][], and [import*][]. + +To *define a symbol* yourself, the [def][] builtin is used. It takes the symbol +as its first, and the value to associate as its second parameter. After a symbol +is defined, the name becomes an alias that behaves like the value itself. For +example, we can use [def][] to associate the result of our calculation with the +symbol `result`, and then refer to it by that symbol in the [trace][] operator: + + (import* math) + + (def result (+ 1 2)) + (trace result) + +Symbols need to start with a letter or one of the characters `-_+*/.=~!?%`. +After the first character, numbers are also allowed. There are two types of +symbols that are treated specially: symbols containing a slash (`math/+`), and +symbols starting and ending with asterisks (`*clock*`): + +- Symbols containing slashes (except at beginning and end of the symbol) are + split into multiple symbols, and looked up recursively in the scope. For + example, `math/+` is found by first looking for a value for the symbol `math`, + and then looking for the symbol `+` in that value. If the value for the + symbol `math` is not a scope, an error is thrown. +- Symbols starting and ending with asterisks are called `dynamic symbols` and + are looked up in a different way inside user-defined functions. This will be + covered in detail later. +- The two special formats can be mixed: when evaluating `*hello*/world`, + `alv` will look for the symbol `world` within the scope found by dynamically + resolving `*hello*`. diff --git a/docs/guide/evaltime-and-runtime.md b/docs/guide/evaltime-and-runtime.md new file mode 100644 index 0000000..e718cf3 --- /dev/null +++ b/docs/guide/evaltime-and-runtime.md @@ -0,0 +1,45 @@ +So far, `alv` may seem a lot like any other programming language - you write +some code, save the file, and it runs, printing some output. "What about the +'continuously running' aspect from the introduction?", you may ask yourself. + +So far, we have only seen *evaltime* execution in alv - but there is also +*runtime* behavior. At *evaltime*, that is whenever there is change to the +source code, `alv` behaves similar to a Lisp. This is the part we have seen +so far. But once one such *eval cycle* has executed, *runtime* starts, and +`alv` behaves like a dataflow system like [PureData][pd], [Max/MSP][max] or +[vvvv][vvvv]. + +What looked so far like static constants are actually *streams* of values. +Whenever an input to an operator changes, the operator (may) update and respond +with a change to its output as well. To see this in action, we need to start +with a changing value. Number literals like `1` and `2`, which we used so far, +are *evaltime constant*, which means simply that they will never update. Since +all inputs to our [math/+][] operator are *evaltime constant*, the result is +constant as well. To get some *runtime* activity, we have to introduce a +side-effect input from somewhere outside the system. + +The [time/][] module contains a number of operators whose outputs update +over time. Lets take a look at [time/tick][]: + + (import* time) + (trace (tick 1)) + +This will print a series of numbers, incrementing by 1 every second. The +parameter to [time/tick][] controls how quickly it counts - try changing it to +`0.5` or `2`. As you can see, we can change [time/tick][] *while it is +running*, but it doesn't lose track of where it was! + +All of the other things we learned above apply to streams of values as well - +we can use [def][] to store them in the scope, transform them using the ops +from the [math/][] module and so on: + + (import* time math) + (def tik (tick 0.25)) + (trace (/ tik 4)) + +Note that if you leave the [time/tick][]'s *tag* in place when you move it into +the [def][] expression, it will keep on running steadily even then. + +[pd]: http://puredata.info/ +[max]: https://cycling74.com/products/max +[vvvv]: https://vvvv.org/ diff --git a/docs/guide/functions.md b/docs/guide/functions.md new file mode 100644 index 0000000..917b8d4 --- /dev/null +++ b/docs/guide/functions.md @@ -0,0 +1,54 @@ +Another builtin that creates a nested scope is [fn][], which is used to +create a *user-defined function*, which can be used to simplify repetitive +code, amongst other things: + + (import* math) + + (def add-and-trace + (fn + (a b) + (trace (+ a b)))) + + (add-and-trace 1 2) + (add-and-trace 3 4) + +Here a *function* `add-and-trace` is defined. When defining a function, first +the names of the parameters have to be given. The function defined here takes +two parameters, `a` and `b`. The last part of the function definition is called +the *function body*. + +A function created using [fn][] can be called just like an operator. When a +function is called, the parameters to the function are defined with the names +given in the definition, and then the function body is executed. The previous +example is equivalent to the following: + + (import* math) + + (def add-and-trace + (fn + (a b) + (trace (+ a b))) + + (do + (let a 1 + b 2) + (trace (+ a b))) + + (do + (let a 3 + b 4) + (trace (+ a b))) + +and the output of both is: + + trace (+ a b): <num= 3> + trace (+ a b): <num= 7> + +In `alv`, functions are first-class values and can be passed around just like +numbers, strings, etc. However it is very common to define a function with a +name, so there is the `defn` shorthand, which combines the `def` and `fn` +builtins into a single expression. Compare this equivalent definition of the +`add-and-trace` function: + + (defn add-and-trace (a b) + (trace (+ a b))) diff --git a/docs/guide/getting-started-guide.md b/docs/guide/getting-started-guide.md new file mode 100644 index 0000000..9ca2f63 --- /dev/null +++ b/docs/guide/getting-started-guide.md @@ -0,0 +1,25 @@ +`alv` ("alive") is a language for creating and changing realtime programs while +they are running continuously. It can be used to create music, visuals or +installations, but by itself creates neither sound nor video. Rather, `alv` is +used together with other tools and synthesizers (for example +[SuperCollider][supercollider] or [Pilot][pilot]). In such an ensemble of +tools, `alive` takes the role of a 'conductor', telling the other tools what to +play when by sending commands to them using a variety of protocols, such as OSC +and MIDI. + +## contents + +1. [installation](installation.html) +2. [hello world](hello-world.html) +3. [working with the copilot](working-with-the-copilot.html) +4. [syntax](syntax.html) +5. [basic types](basic-types.html) +6. [importing operators](importing-operators.html) +7. [defining symbols](symbols.html) +8. [scopes](scopes.html) +9. [functions](functions.html) +10. [evaltime and runtime](evaltime-and-runtime.html) +11. [making sound](making-sound.html) + +[supercollider]: https://supercollider.github.io/ +[pilot]: https://github.com/hundredrabbits/Pilot diff --git a/docs/guide/hello-world.md b/docs/guide/hello-world.md new file mode 100644 index 0000000..7f1517d --- /dev/null +++ b/docs/guide/hello-world.md @@ -0,0 +1,33 @@ +Before getting into all the details of the `alv` language, let's quickly run an +example program to make sure that everything is working correctly: + + ([1]import* time) + ([2]print ([3]every 0.5 "hello world!")) + +Open a text file save this piece of code as `hello.alv`. You can also find this +example program in the repository and the windows binary package. + +As mentioned earlier, there are two different ways to run `alv` programs: using +the copilot GUI, or in the terminal. + +## starting the copilot GUI +On Linux and Mac OS X, you can launch the GUI by executing the `alv-fltk` +command. On Windows, you can double-click `alv-fltk.bat`. This window should +open: + + + +Now open `hello.alv` using `File > Open Script` or the shortcut `^O` +(control-O). The copilot should start printing `hello world` over and over +again in the lower field. + +You can pause and resume execution using the `Run` button or the `^P` shortcut. +To stop the program simply close the window. + +## starting the copilot in the terminal +To run a file in the terminal, invoke the command `alv <path/to/hello.alv>`. If +your system cannot find the `alv` command, check your installation and `PATH`. +On Windows, it is also possible to drag your `alv` file onto `alv.bat`. + +You should now see the text `hello world` being printed over and over again, +twice a second. You can stop the copilot by pressing `^C` (control-C). diff --git a/docs/guide/importing-operators.md b/docs/guide/importing-operators.md new file mode 100644 index 0000000..4321e7a --- /dev/null +++ b/docs/guide/importing-operators.md @@ -0,0 +1,26 @@ +Apart from [trace][], there are only very little builtin operators in `alv` - +you can see all of them in the *builtins* section of the [reference][:/:]. +All of the 'real' functionality of `alv` is grouped into *modules*, that have +to be loaded individually. *Modules* help organize all of the operators so that +it is less overwhelming to look for a concrete feature. It is also possible to +create your own plugins as new modules, which will be covered in another guide +soon. + +Let's try using the [`+` operator][:math/+:] from the [math/][] module. To use +operators from a module, we need to tell `alv` to load it first: We can load +*all* the operators from the [math/][] module into the current scope using the +[import*][] builtin: + + (import* math) + (trace (+ 1 2)) + +prints + + trace (+ 1 2): <num= 3> + +Because it can get a bit confusing when all imported operators are mixed in the +global scope, it is also possible to load the module into its own scope and use +it with a prefix. This is what the [import][] builtin is for: + + (import math) + (trace (math/+ 1 2)) diff --git a/docs/guide/installation.md b/docs/guide/installation.md new file mode 100644 index 0000000..5a192fa --- /dev/null +++ b/docs/guide/installation.md @@ -0,0 +1,53 @@ +`alv` is written in the Lua programming language, and is compatible with both +Lua 5.3 and luajit. + +## unix/linux and mac os +Your distribution should provide you with packages for Lua and Luarocks. On Mac +OS X, both are provided through [homebrew][homebrew]. After installing both of +these, you should be able to start the Lua interpreter from the shell: + + $ lua + Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio + > + +You can exit using `CTRL+C`. If the version you see is not 5.3, double check +your distribution packages or see if it was installed as `lua5.3` or `lua53`. +Similarily, you should be able to run `luarocks`, `luarocks53` or `luarocks5.3`: + + $ luarocks list + + Rocks installed for Lua 5.3 + --------------------------- + +Again, double check your installation or try adding `--lua-version 5.3` if the +displayed version is not 5.3. + +With everything ready to go, you can now install `alv`: + + $ luarocks install alive + +To use the copilot GUI, you will also need the `fltk4lua` package, which requires +installing or building FLTK (also available through homebrew). + + $ luarocks install fltk4lua + +With the `alive` package, two binaries should have been installed on your system: +`alv` and `alv-fltk`. If you do not find these in your `$PATH`, you may need to +apply the exports from `luarocks path` upon login, e.g. in your `.bashrc`. + +## windows +For Windows, a binary package is available from the latest +[github release][:*release*:]. It includes not only the `alv` source code, but +also a compiled version of Lua 5.3 as well as Luarocks and all of `alv`'s +dependencies. + +To use the binary package, simply extract the archive and move the folder +wherever you want. You can now start the `hello.alv` example script by dragging +it onto the `alv.bat` or `alv-fltk.bat` file in the folder. + +If you are going to use the command-line `alv.bat`, it is recommended to add +the directory containing it to `%PATH%`, so that you can use the `alv` command +anywhere on your system. + +[homebrew]: https://brew.sh +[luarocks]: https://github.com/luarocks/luarocks/#installing diff --git a/docs/guide/making-sound.md b/docs/guide/making-sound.md new file mode 100644 index 0000000..1987dac --- /dev/null +++ b/docs/guide/making-sound.md @@ -0,0 +1,58 @@ +As mentioned earlier, `alv` doesn't produce sound by itself. Instead, it is +paired with other tools, and takes the role of a 'Conductor', sending commands +and sequencing other tools. + +For the sake of this guide, we will be controlling [Pilot][pilot], a simple +UDP-controlled synthesizer. You can go ahead and download and open it now. +You should see a small window with a bunch of cryptic symbols and a little +command line at the bottom. To verify that everything is working so far, +try typing in `84c` and hitting enter. This should play a short sound (the note +4C, played by the 8th default synthesizer voice in Pilot). + +To talk to Pilot from `alv`, we will use the [pilot/][] module. Note that for +this module to work, you have to have the `osc` and `luasocket` dependencies +installed. To play the same sound we played by entering `84c` above every 0.5 +seconds, we can use [time/every][] to send a `bang` to [pilot/play][]: + + (import* time) + (import pilot) + (pilot/play (every 0.5) 8 4 'c') + +You can play with the voice, octave and note values a bit. To add a simple +melody, we can use [util/switch][], which will cycle through a list of +parameters when used together with [time/tick][]: + + (import* time util) + (import pilot) + (pilot/play (every 0.5) 8 4 + (switch (tick 0.5) 'c' 'd' 'a' 'f')) + +Now we can have the voice change every other loop as well: + + (import* time util) + (import pilot) + (pilot/play (every 0.5) + (switch (tick 4) 8 9) + 4 (switch (tick 0.5) 'c' 'd' 'a' 'f')) + +To round off the sound a bit, we can turn on Pilot's reverb using +[pilot/effect][]. Add the following somewhere in your file: + + (pilot/effect "REV" 2 8) + +Now it's time to add some rhythm. The kick drum is voice 12 by default, +and we can also add something like a snare on channel 3: + + (pilot/play (every 0.75) + 12 2 'd' 3) + (pilot/play (every 2) + 13 4 'a' 4) + +Note that since we are using multiple individual [time/every][] instances, +the timing of our voices relative to each other is not aligned - each voice +started playing when the file was first saved with it added, and kept the +rhythmn since. By deleting all their tags and re-saving the file, we can force +`alv` to re-instantiate them all at the same time, thereby synchronising +them. + +[pilot]: https://github.com/hundredrabbits/Pilot diff --git a/docs/guide/scopes.md b/docs/guide/scopes.md new file mode 100644 index 0000000..87568ef --- /dev/null +++ b/docs/guide/scopes.md @@ -0,0 +1,82 @@ +Both [import][] and [import*][] are actually shorthands and what they +accomplish can be done using the lower-level builtins [def][], [use][] and +[require][]. Here is how you could replace [import][]: + + #(with import:) + (import math) + (trace (math/+ 1 2)) + + #(with def and require:) + (def math (require "math")) + (trace (math/+ 1 2)) + +[require][] returns a *scope*, which is defined as the symbol `math`. +Then `math/+` is resolved by looking for `+` in this nested scope. Note that +the symbol that the scope is defined as and the name of the module that is +loaded do not have to be the same, you could call the alias whatever you want: + + #(this not possible with import!) + (def fancy-math (require "math")) + (trace (fancy-math/+ 1 2)) + +Most of the time the name of the module makes a handy prefix already, so +[import][] can be used to save a bit of typing and make the code look a bit +cleaner. [import*][], on the other hand, defines every symbol from the imported +module individually. It could be implemented with [use][] like this: + + (use (require "math")) + (trace (+ 1 2)) + +[use][] copies all symbol definitions from the scope it is passed to the +current scope. + +Note that [import][], [import*][], [def][], and [use][] all can take multiple +arguments: + + #(using the shorthands:) + (import* math logic) + (import midi osc) + + #(using require, use and def:) + (use (require "math") (require "logic")) + (def midi (require "midi") + osc (require "osc")) + +It is common to have an [import][] and [import*][] expression at the top of an +`alv` program to load all of the modules that will be used later, but the +modules don't necessarily have to be loaded at the very beginning, as long as +all symbols are defined before they are being used. + +## nested scopes +Once a symbol is defined, it cannot be changed or removed: + + (def a 3) + (def a 4) #(error!) + +It is, however, possible to 'shadow' a symbol by re-defining it in a nested +scope: So far, all symbols we have defined - using `def`, [import][] and +[import*][] - have been defined in the *global scope*, the scope that is active +in the whole `alv` program. The [do][] builtin can be used to create a new +scope and evaluate some expressions in it: + + (import string) + + (def a 1 + b 2) + + (trace (.. "first: " a " " b)) + (do + (def a 3) + (trace (.. "second: " a " " b)) + (trace (.. "third: " a " " b)) + +This example prints the following: + + trace (.. "first: " a " " b): <Value str: first: 1 2> + trace (.. "second: " a " " b): <Value str: second: 3 2> + trace (.. "third: " a " " b): <Value str: third: 1 2> + +As you can see, within a nested scope it is possible to overwrite a definition +from the parent scope. Symbols that are not explicitly redefined in a nested +scope keep their values, and changes in the nested scope do not impact the +parent scope. diff --git a/docs/guide/syntax.md b/docs/guide/syntax.md new file mode 100644 index 0000000..93f1c61 --- /dev/null +++ b/docs/guide/syntax.md @@ -0,0 +1,43 @@ +`alv`'s syntax is very similar to Lisp. Expressions take the form of +parenthesized lists like `(head a b c...)`, where the first element of the list +(`head`) is the name of an operator or function, which defines what the +expression as a whole will do, while the other elements are parameters whose +meaning depends on the `head`. Let's start with a simple operator, [print][]: +[print][] is used simply to print messages to the copilot console. + +## expressions +Elements of an expression have to be separated by whitespace, but any type of +and amount of whitespace is valid: feel free to use spaces, tabs, and newlines +to format code to your liking. The following are all equal and valid examples: + + (print "hello world") + + (+ 1 + 2 + 3) + + (print + "hello world") + + ( print "hello world" ) + +It is however recommended to follow the [clojure style guide][clojure-style] as +much as it does apply to alv. All further examples in this guide will respect +this guideline, so you might just pick it up simply by following this guide. + +## comments +To annotate your code, you can use comments. In `alv`, comments begin with +`#(` and end on a matching `)`. This way you can comment out a complete +expression simply by adding a `#` character in front. + + #(this is a comment) + + #(this is a long, + multi-line comment, + (and it also has nested parentheses). + It ends after this sentence.) + +You can put comments anywhere in your program where whitespace is allowed and +it will simply be ignored by `alv`. + +[clojure-style]: https://github.com/bbatsov/clojure-style-guide diff --git a/docs/guide/working-with-the-copilot.md b/docs/guide/working-with-the-copilot.md new file mode 100644 index 0000000..f40d82d --- /dev/null +++ b/docs/guide/working-with-the-copilot.md @@ -0,0 +1,59 @@ +While it is possible to simply run finished programs as we just did with the +`hello.alv` example, `alv` is a *livecoding language*, which means that it is +designed so that programs can be written interactively while they are already +running. To see how this works, let's re-write the `hello.alv` example +step-by-step. + +First you will need an empty file to start from. Open a new file in your +preferred text editor and save it as `test.alv`. Before adding any code, start +the copilot (see the last page on the two ways of doing that). + + $ alv test.alv + changes to files: test.alv + +You should see a note indicating that `alv` processed the file. The note will +show up in the upper pane labelled `eval` (in the GUI), or colored green (in +the terminal). This marks the message as an *eval-time message*, meaning that +the message was printed as a direct response to the file changing or being +loaded the first time. Other messages that might print to the `eval` section +are things like errors in your program, and one-time debugging messages. + +Now that the copilot is running, whenever you change `test.alv` and save it, the +copilot will reload it and execute your new code. When you are done, you can +stop the copilot at any time by closing the GUI window or pressing `^C` +(control-C) in the terminal. + +Let's start with a simple operator, [print][]: [print][] is used simply to print +messages to the copilot console. Enter the following in your file and save it: + + (print "hello world!") + +As soon as you save the file, you should notice two things happening: + +1. The copilot will print two new lines: + + changes to files: hello.alv + hello world! + + In the first line, it notifies us that the file has changed. In the second + line, you can see the output from the [print][] expression. +2. The copilot will make a small modification to your file. Depending on the + editor you are using, this may either result in you seeing the modification + immediately, or a notice appearing that offers the option to reload the + file. If it is the latter, confirm the notification to accept the changes. + If there is an option to do so, you may want to configure your editor to + always reload the file automatically. + +The code should now look like this: + + ([1]print "hello world") + +The `[1]` that the copilot added to your expression is that expression's `tag`. +In `alv`, every expression has a tag that helps the copilot to identify the +individual expressions as you make changes to your code. The copilot will make +sure that all expressions are tagged by adding missing tags when you save the +file, but you have to watch out not to duplicate a tag when copying and pasting +code. When you duplicate code that already has tags, you can either manually +change the tags to be unique, or simply delete the whole tag (including the +square brackets) and let the copilot generate a new one for you the next time +you save the file. diff --git a/docs/index.md b/docs/index.md index 1ed709a..ec879df 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,6 +24,6 @@ you are a curious about the motivations and concepts behind `alv`, you can find more in-depth information on these topics in the ['persistent expressions' article][rationale]. -[guide]: guide.html +[guide]: guide/getting-started-guide.html [rationale]: https://s-ol.nu/alivecoding [license]: https://github.com/s-ol/alive/blob/master/LICENSE diff --git a/docs/style.css b/docs/style.css index 13f3c3b..1f81f9b 100644 --- a/docs/style.css +++ b/docs/style.css @@ -61,10 +61,10 @@ a { } ul, ol { - list-style: '- '; margin: 0.5em 0; padding-left: 0.6em; } +ul { list-style: '- '; } header { position: fixed; @@ -75,27 +75,31 @@ header { background: #eeeeee; box-sizing: border-box; } +header nav { + border-bottom: 1px solid #222222; + padding-bottom: 1em; +} +header nav > a { + margin-left: 0.6rem; +} nav { display: flex; justify-content: space-between; - - border-bottom: 1px solid #222222; - padding-bottom: 1em; } nav span, nav a { flex: 0 0 auto; + text-decoration: none; } nav > a { - margin-left: 0.6rem; padding: 0.03em 0.2em; - text-decoration: none; } nav > a.active { background: #cccccc; + text-decoration: none; } nav span a { @@ -107,6 +111,18 @@ nav .grow { flex: 1; } +.subheader { + margin: 0 -2rem; + padding: 0.5rem 2rem; + background: #cccccc; +} +.subheader nav { + margin: 0 -1.2em; +} +.subheader h1 { + margin: 0; +} + footer { font-size: 0.8em; padding: 0 2rem; |
