Merge branch 'main' into i/353/fix-header-levels

This commit is contained in:
tinnus-napbus 2023-04-29 19:13:22 +12:00 committed by GitHub
commit 4ce8dc0267
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 641 additions and 15 deletions

View File

@ -165,7 +165,7 @@ Since we're being quite thorough in this article, let's summarize every single a
| `@uw` | unsigned base64 | `0w_____._____` | `0wbnC.8haTg` | |
| `@ux` | unsigned hexadecimal | `0x____.____` | `0x5f5.e138` | |
You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^5{% /math %}, etc.)
You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^4{% /math %}, etc.)
We also include two other literal syntaxes which don't resolve to atoms:

View File

@ -5,11 +5,14 @@ weight = 3
insert_anchor_links = "right"
+++
## [`%ahoy`](/guides/additional/app-workbook/ahoy)
## [Building a CLI App with `%rpn`](/guides/additional/app-workbook/rpn)
## [`%dbug`](/guides/additional/app-workbook/dbug)
## [Debugging Wrapper with `%dbug`](/guides/additional/app-workbook/dbug)
## [`%flap`](/guides/additional/app-workbook/flap)
## [Hosting a Website with `%feature`](/guides/additional/app-workbook/feature)
## [`%feature`](/guides/additional/app-workbook/feature)
## [Serving a JS Game with `%flap`](/guides/additional/app-workbook/flap)
## [Ship Monitoring with `%ahoy`](/guides/additional/app-workbook/ahoy)
## [Styled Text with `%track7`](/guides/additional/app-workbook/track7)

View File

@ -1,6 +1,6 @@
+++
title = "%ahoy Ship Monitoring"
weight = 10
title = "Ship Monitoring"
weight = 194
+++
The `%ahoy` desk by [~midden-fabler](https://urbit.org/ids/~midden-fabler) provides a number of agents to automatically monitor ship activity such as breaching and network uptime. This tutorial examines the `%ahoy` agent specifically with some slight simplifications to demonstrate how an Urbit-native app can be constructed. You will see how to render a front-end using Sail, employ the `++abet` nested core design pattern, construct CLI generators, and set wakeup timers using [Behn](https://developers.urbit.org/reference/glossary/behn).

View File

@ -1,6 +1,6 @@
+++
title = "%dbug Debugging Wrapper"
weight = 40
title = "Debugging Wrapper"
weight = 42
+++

View File

@ -1,6 +1,6 @@
+++
title = "%feature Page Hosting"
weight = 160
title = "Host a Website"
weight = 85
+++
[`%feature`](https://github.com/hanfel-dovned/Feature) by [~hanfel-dovned](https://urbit.org/ids/~hanfel-dovned) hosts a simple HTML page from an Urbit ship at an associated URL. This tutorial examines how it uses the middleware [`%schooner`](https://github.com/dalten-collective/schooner/) library by Quartus to return a web page when contacted by a web browser. You will learn how a basic site hosting app can handle HTTP requests and render a page using an `%html` mark.

View File

@ -1,10 +1,8 @@
+++
title = "%flap JS Client"
weight = 60
title = "Serving a JS Game"
weight = 193
+++
## Introduction
In this tutorial, we will take an off-the-shelf JavaScript game which runs in the browser and connect it to an Urbit back-end. This page assumes that you have completed some version of Hoon School and App School, whether the [live courses](/courses) or the [written docs](/guides/core/hoon-school/A-intro). Our goal is to show you one way of directly serving client code from an Urbit ship as server.
_Flappy Bird_ is an "insanely irritating, difficult and frustrating game which combines a super-steep difficulty curve with bad, boring graphics and jerky movement" ([Huffington Post](https://web.archive.org/web/20140205084251/http://www.huffingtonpost.com/2014/02/03/flappy-bird-tips_n_4717406.html)). We are going to implement `%flap`, a _Flappy Bird_ leaderboard using ~paldevs `%pals` peer tracking agent. The approach given in this tutorial will apply to any game which is primarily run in the browser and has some persistent state to retain across sessions or communicate between players at discrete intervals. Direct player-v.-player games will require other techniques to implement.

View File

@ -0,0 +1,189 @@
+++
title = "Building a CLI App"
weight = 28
+++
We will utilize the basic calculator app logic from the [parsing guide](/guides/additional/parsing#recursive-parsers) to produce a linked calculator agent `%rpn` supporting the following operators by the appropriate parsers:
- numbers (as `@rs` without `.` dot prefix) (`royl-rs:so`)
- `+` lus, addition (`lus`)
- `-` hep, subtraction (`hep`)
- `*` tar, multiplication (`tar`)
- `/` fas, division (`fas`)
- `.` dot, display top of stack (`dot`)
We will leave all regular Gall arms as their defaults, but of course poking, subscribing, and peeking should be supported in a full application.
## Agent Logic
**`/sur/rpn.hoon`**
We just need to define the expected operators that will show up in the stack. These are `@t` text constants.
```hoon
|%
+$ op $? [%op %add]
[%op %sub]
[%op %mul]
[%op %div]
[%op %sho]
==
+$ num @rs
+$ command ?(@rs op)
--
```
(`+$command` doesn't really feel like the right name here, but we're pattern-matching with the demo `/app/shoe.hoon`.)
**`/lib/rpn.hoon`**
These are the parsing rules that the CLI agent will use. We could include these directly in the agent file but we'll post them to a library file.
```hoon
|%
++ num royl-rs:so
++ op-add (cook |=(p=@ ?:(=('+' p) op+%add ~)) lus)
++ op-sub (cook |=(p=@ ?:(=('-' p) op+%sub ~)) hep)
++ op-mul (cook |=(p=@ ?:(=('*' p) op+%mul ~)) tar)
++ op-div (cook |=(p=@ ?:(=('/' p) op+%div ~)) fas)
++ op-sho (cook |=(p=@ ?:(=('.' p) op+%sho ~)) dot)
++ ops ;~(pose op-add op-sub op-mul op-div op-sho)
--
```
**`/app/rpn.hoon`**
```hoon
++ state-0
$: %0
stack=(list ?(@rs op:rpn))
==
```
**`++command-parser`**
We want this arm to wait until `RETURN` is pressed so we `++stag` the value with `|` `FALSE`/`%.n`.
```hoon
++ command-parser
|= =sole-id:shoe
^+ |~(nail *(like [? command:rpn]))
%+ stag |
(cook command:rpn ;~(pose num:rpnlib ops:rpnlib))
```
**`++on-command`**
This arm pushes values onto the stack, displays the stack, then checks to parse for the result of an operation.
```hoon
++ on-command
|= [=sole-id:shoe =command:rpn]
^- (quip card _this)
=/ old-stack (weld stack ~[command])
=/ new-stack (process:rpnlib old-stack)
:_ this(stack new-stack)
:~ [%shoe ~ sole+klr+~[(crip "{<old-stack>} →")]]
[%shoe ~ sole+klr+~[[[`%br ~ `%g] (crip "{<new-stack>}") ~]]]
==
```
For this we add a helper arm to `/lib/rpn.hoon` which takes each entry, makes sure it is a `@rs` atom, and carries out the operation. (This could probably be made more efficient.)
**`/lib/rpn.hoon`**
```hoon
/- rpn
:: * * *
++ process
|= stack=(list command:rpn)
^- (list command:rpn)
~| "Failure processing operation on stack {<stack>}"
?~ stack !!
?- `command:rpn`(snag 0 (flop stack))
[%op %add]
=/ augend ;;(@rs `command:rpn`(snag 1 (flop stack)))
=/ addend ;;(@rs `command:rpn`(snag 2 (flop stack)))
(flop (weld ~[(add:rs augend addend)] (slag 3 (flop stack))))
::
[%op %sub]
=/ minuend ;;(@rs `command:rpn`(snag 1 (flop stack)))
=/ subtrahend ;;(@rs `command:rpn`(snag 2 (flop stack)))
(flop (weld ~[(sub:rs minuend subtrahend)] (slag 3 (flop stack))))
::
[%op %mul]
=/ multiplicand ;;(@rs `command:rpn`(snag 1 (flop stack)))
=/ multiplier ;;(@rs `command:rpn`(snag 2 (flop stack)))
(flop (weld ~[(mul:rs multiplicand multiplier)] (slag 3 (flop stack))))
::
[%op %div]
=/ numerator ;;(@rs `command:rpn`(snag 1 (flop stack)))
=/ denominator ;;(@rs `command:rpn`(snag 2 (flop stack)))
(flop (weld ~[(div:rs numerator denominator)] (slag 3 (flop stack))))
::
[%op %sho]
~& > "{<(snag 1 (flop stack))>}"
(flop (slag 1 (flop stack)))
::
@rs
stack
==
```
### Linking
After a `%sole` agent has been `|install`ed, it should be registered for Dojo to cycle input to it using `|link`.
```hoon
|link %rpn
```
Now `Ctrl`+`X` allows you to switch to that app and evaluate expressions using it.
```hoon
gall: booted %rpn
> 50
~ →
~[.50]
> 25
~[.50] →
~[.50 .25]
> -
~[.50 .25] →
~[.-25]
> 5
~[.-25] →
~[.-25 .5]
> /
~[.-25 .5] →
~[.-0.19999999]
> 5
~[.-0.19999999] →
~[.-0.19999999 .5]
> *
~[.-0.19999999 .5] →
~[.-0.99999994]
> 1
~[.-0.99999994] →
~[.-0.99999994 .1]
> /
~[.-0.99999994 .1] →
~[.-1]
```
## Exercises
- Extend the calculator app to support modulus as `%` cen.
- Extend the calculator app so it instead operates on `@rd` values. Either use `++cook` to automatically convert the input values from a `1.23`-style input to the `.~1.23` `@rd` style or build a different input parser from the entries in `++royl:so`.
- Extend the calculator app so that it can support named variables (using `@tas`) with `=` tis. What new data structure do you need? For convenience, expose the result of the last operation as `ans` (a feature of TI graphing calculators and MATLAB, among other programs).
- The calculator app stack isn't really a proper CS stack with push and pop operations. Refactor it to use such a type.

View File

@ -0,0 +1,174 @@
+++
title = "Styled Text"
weight = 199
+++
In this tutorial, we examine how to produce `styx` styled text strings and output them to the terminal from an agent.
## `%shoe` CLI Session Manager
`%shoe` is responsible to manage attached agent sessions. It adds a few arms to the standard Gall agent, namely:
- `++command-parser` is the input parser, similar to the work we were carrying out just above. This parses every input and only permits valid keystrokes (think of Dojo real-time parsing).
- `++tab-list` provides autocompletion options. We can ignore for now.
- `++on-command` is called whenever a valid command is run. This produces the actual effects.
- `++can-connect` supports `|link` connexion to the app.
- `++on-connect` provides particular session support when a user connects. We can ignore for now.
- `++on-disconnect` provides particular session support when a user disconnects. We can ignore for now.
To get started with text parsers and CLI agents, we need to focus on `++command-parser` and `++on-command`. But first, the agent's structure and state:
The agent will adopt a two-stage process, wherein a value is put on the stack then the stack is checked for any valid operations.
### `++command-parser`
The input parser can simply accept whole words or single inputs, or parse complex expressions (as Dojo does with Hoon).
This results in a noun of `+$command-type` based on the specific application. The example `/app/shoe.hoon` agent defines:
```hoon
+$ command
$? %demo
%row
%table
==
```
and later uses this as:
```hoon
++ command-parser
|= =sole-id:shoe
^+ |~(nail *(like [? command]))
%+ stag &
(perk %demo %row %table ~)
```
where the unfamiliar parser components are:
- `++stag` adds a label, here `&` pam `TRUE`/`%.y` to indicate that the command should be run immediately when it matches. (We won't want this below so we will `++stag` a `|` `FALSE`/`%.n`.)
- `++perk` parses a fork in the type.
### `++on-command`
This arm accepts a session ID and a command resulting from `++command-parser`. It produces a regular `(quip card _this)` so you can modify agent state and produce effects here.
## `%sole` Effects
`%sole` is responsible for producing effects. If you want to yield effects to the command line from your CLI agent (which you often do), this is a great place to work.
`%sole-effect`s are head-tagged by time and produce a variety of terminal effects, from text to bells, colors, and other screen effects.
## `$styx` Styled Text String
A `klr` effect uses a `styx`, or styled text string. The relevant data structures are in `/sys/lull.hoon`:
```hoon
+$ deco ?(~ %bl %br %un) :: text decoration
+$ stye (pair (set deco) (pair tint tint)) :: decos/bg/fg
+$ styl %+ pair (unit deco) :: cascading style
(pair (unit tint) (unit tint)) ::
+$ styx (list $@(@t (pair styl styx))) :: styled text
+$ tint $@ ?(%r %g %b %c %m %y %k %w %~) :: text color
[r=@uxD g=@uxD b=@uxD] :: 24bit true color
```
- `$deco` is a text decoration, here `%bl` blinking, `%br` bright (bold), and `%un` underlined.
- `$tint` is a color, either explicitly the terminal red/green/blue/cyan etc. or a 24-bit true color value.
- `$stye` composes these into a style which will be applied to a string.
- `$styl` similarly composes styles together.
- `$styx` pairs styles with cords.
This means that composing styled text correctly can require explicitly nesting statements in rather a complicated way.
For instance, to produce a bold string with hex color `#123456`, we could produce the `sole-effect`:
```hoon
^- sole-effect:sole
:- klr
^- styx
~[[[`%br ~ `[r=0x12 g=0x34 b=0x56]] 'Hello Mars!' ~]]
```
- [~ropdeb-sormyr, "Styled output - requirements and progress" ~2016.8.2 Urbit fora post](https://github.com/urbit/fora-posts/blob/0238536650dfc284f14295d350f9acada0341480/archive/posts/~2016.8.2..21.19.29..2ab8~.md)
## Agent Logic
Here is an agent that will accept a single character and produce a line with varying random colors of that character.
**`/app/track7.hoon`**
```hoon
/+ default-agent, dbug, shoe, sole
|%
+$ versioned-state
$% state-0
==
+$ state-0 %0
+$ card card:agent:shoe
+$ command @t
--
%- agent:dbug
=| state-0
=* state -
^- agent:gall
%- (agent:shoe command)
^- (shoe:shoe command)
|_ =bowl:gall
+* this .
default ~(. (default-agent this %|) bowl)
leather ~(. (default:shoe this command) bowl)
++ on-init on-init:default
++ on-save !>(state)
++ on-load
|= old=vase
^- (quip card _this)
`this(state !<(state-0 old))
++ on-poke on-poke:default
++ on-peek on-peek:default
++ on-arvo on-arvo:default
++ on-watch on-watch:default
++ on-leave on-leave:default
++ on-agent on-agent:default
++ on-fail on-fail:default
++ command-parser
|= =sole-id:shoe
^+ |~(nail *(like [? command]))
(stag & (boss 256 (more gon qit)))
++ on-command
|= [=sole-id:shoe =command]
^- (quip card _this)
:_ this
^- (list card)
:~ :+ %shoe ~
^- shoe-effect:shoe
:- %sole
^- sole-effect:sole :- %klr
^- styx
=/ idx 0
=| fx=styx
=/ rng ~(. og eny:bowl)
|-
?: =(80 idx) fx
=^ huer rng (rads:rng 256)
=^ hueg rng (rads:rng 256)
=^ hueb rng (rads:rng 256)
%= $
idx +(idx)
fx `styx`(weld fx `styx`~[[[`%br ~ `[r=`@ux`huer g=`@ux`hueg b=`@ux`hueb]] command ~]])
== ==
++ can-connect
|= =sole-id:shoe
^- ?
?| =(~zod src.bowl)
(team:title [our src]:bowl)
==
++ on-connect on-connect:leather
++ on-disconnect on-disconnect:leather
++ tab-list tab-list:leather
--
```

View File

@ -0,0 +1,239 @@
+++
title = "Generators"
weight = 73
+++
Generator files provide a way for users to interact with code "scripts" through the Dojo prompt. There are
three basic kinds of generators:
1. Bare or naked generators, standalone computations that can accept input and carry out a single calculation.
2. `%say` generators, scripts which can utilize the full system knowledge (`now`, `our`, `eny`) and accept
optional input arguments.
3. `%ask` generators, scripts driven by interactive prompts.
(Threads have some commonalities with generators, _q.v._)
Generators are a Dojo concept, although they can also be applied to agents (such as `+dbug`). This guide will
show you how to build and invoke all kinds of generators.
## Bare Generators
A basic generator is a gate, a core with a `$` buc arm and a sample.
The Dojo will supply the sample directly to the core in the `$` buc arm.
A bare generator must be a gate but can have more complicated internal structure, as with all Hoon code. It does
not know about entropy `eny`, ship identity `our`, or the timestamp `now`.
**`/gen/add-one.hoon`**
```hoon
|= n=@ud
=<
(add-one n)
|%
++ add-one
|= a=@ud
^- @ud
(add a 1)
--
```
Invoke as `+add-one 5`.
You could in principle use a `|*` bartar wet gate as well, but other cores don't pattern-match to what Dojo expects.
## `%say` Generators
A `%say` generator can have zero, many, or optional arguments, unlike a bare generator. It can also have access to
system variables like `now`, `our`, and `eny`.
For instance, the following generator can be run with no arguments:
**`/gen/say.hoon`**
```hoon {% copy=true %}
:- %say
|= *
:- %noun
(add 40 2)
```
```hoon
> +say
42
```
A `%say` generator is structurally a head-tagged cell of a gate which returns a head-tagged cell of a mark and a value
(or a `cask`).
The head tag over the entire generator is always `%say`. The `cask` tag is most commonly `%noun`.
We use `%say` generators when we want to provide something else in Arvo, the Urbit operating system, with metadata about
the generator's output. This is useful when a generator is needed to pipe data to another program, a frequent occurrence.
To that end, `%say` generators use `mark`s to make it clear, to other Arvo computations, exactly what kind of data their
output is. A `mark` is akin to a MIME type on the Arvo level. A `mark` describes the data in some way, indicating that
it's an `%atom`, or that it's a standard such as `%json`, or even that it's an application-specific data structure like
`%talk-command`.
The gate sample follows this pattern, with undesired elements stubbed out by `*`:
```hoon
|= $: :: environment
$: now=@da :: timestamp
eny=@uvJ :: entropy
bec=beak :: clay beak
==
:: :: unnamed args
$=
$: arg=@ :: required arguments
==
~
==
:: :: named args
$=
$: named-arg=@ :: optional arguments
==
~
==
==
```
The Dojo will modify the sample by inserting `%~` (constant null) at the end of each collection, since the Dojo adapts
the input arguments into a list (either the unnamed/required argument list or the named/optional argument list).
### Zero arguments
`/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments:
```
> +vats
%base
/sys/kelvin: [%zuse 414]
base hash ends in: drceb
%cz hash ends in: drceb
app status: running
pending updates: ~::
> +vats, =verb %.n
%base
/sys/kelvin: [%zuse 414]
base hash ends in: drceb
%cz hash ends in: drceb
app status: running
pending updates: ~
> +vats, =filt %suspended
```
### Optional arguments
Let's look at an example that uses all three parts.
**`/gen/dice.hoon`**
```hoon {% copy=true %}
:- %say
|= [[now=@da eny=@uvJ bec=beak] [n=@ud ~] [bet=@ud ~]]
:- %noun
[(~(rad og eny) n) bet]
```
This is a very simple dice program with an optional betting functionality. In the code, our sample specifies faces on all
of the Arvo data, meaning that we can easily access them. We also require the argument `[n=@ud ~]`, and allow the
_optional_ argument `[bet=@ud ~]`.
We can run this generator like so:
```hoon
> +dice 6, =bet 2
[4 2]
> +dice 6
[5 0]
> +dice 6
[2 0]
> +dice 6, =bet 200
[0 200]
> +dice
nest-fail
```
Notice how the `,` com works to separate arguments and how the name of the optional argument must be included.
We get a different value from the same generator between runs, something that isn't possible with a bare generator. Another
novelty is the ability to choose to not use the second argument.
- [Hoon School, “1.9 Generators”](/guides/core/hoon-school/generators)
## `%ask` Generators
We use an `%ask` generator when we want to create an interactive program that prompts for inputs as it runs, rather than
expecting arguments to be passed in at the time of initiation.
Like `%say` generators, `%ask` generators are head-tagged cells of gates, but with `%ask`.
The code below is an `%ask` generator that checks if the user inputs `"blue"` when prompted [per a classic Monty Python
scene](https://www.youtube.com/watch?v=L0vlQHxJTp0).
**`/gen/axe.hoon`**
```hoon {% copy=true mode="collapse" %}
/- sole
/+ generators
=, [sole generators]
:- %ask
|= *
^- (sole-result (cask tang))
%+ print leaf+"What is your favorite color?"
%+ prompt [%& %prompt "color: "]
|= t=tape
%+ produce %tang
?: =(t "blue")
:~ leaf+"Oh. Thank you very much."
leaf+"Right. Off you go then."
==
:~ leaf+"Aaaaagh!"
leaf+"Into the Gorge of Eternal Peril with you!"
==
```
Run the generator from the Dojo:
```hoon
> +axe
What is your favorite color?
: color:
```
Instead of simply returning something, your Dojo's prompt changed from `~sampel-palnet:dojo>` to `~sampel-palnet:dojo: color:`,
and now expects additional input. Let's give it an answer:
```hoon
: color: red
Into the Gorge of Eternal Peril with you!
Aaaaagh!
```
`%ask` generators return `sole-effect`s. For more information on these, consult the [guide on command-line agents](/guides/additional/cli).
`%ask` generators can also accept arguments, although this is uncommon.
## Generators for Agents
Generators can furthermore interact specifically with agents.
The Hood/Helm tooling like `|install` are generators automatically routed by Dojo to the correct agent.
The [`+dbug` agent](/guides/additional/app-workbook/dbug) is invoked against an agent to display internal state.
Any app can implement generators to wrap raw pokes (see [`%ahoy`](/guides/additional/app-workbook/ahoy) for instance).

View File

@ -40,3 +40,26 @@ is the same limb as `+`, `..b` is the same wing as `+1.foo`.
~zod:dojo> moo.bat.a
2
```
## Wing Resolution
There are two common syntaxes used to resolve a wing path into the
current subject: `.` dot and `:` col.
- `.` dot syntax, as `c.b.a`, resolves the wing path into the subject
at the right hand using Nock 0 (or possibly Nock 9 or Nock 10
depending on the expression).
```hoon
> !,(*hoon c.b.a)
[%wing p=~[%c %b %a]]
```
- The `:` col operator expands to a `=>` tisgar to resolve the wing path
against its right-hand side as the subject. This can be a Nock 7
or possibly optimized by the compiler to a Nock 0.
```hoon
> !,(*hoon c:b:a)
[%tsgl p=[%wing p=~[%c]] q=[%tsgl p=[%wing p=~[%b]] q=[%wing p=~[%a]]]]
```