doc/tutorial/basics: generate testscript cases

And add generator for Hugo docs. The resulting
file are to be mounted in cuelang.org

Change-Id: Ie1b8f1c5ff68b304d4d3d35a7fb5c6cec1577c98
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3264
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/doc/tutorial/basics/aliases.txt b/doc/tutorial/basics/aliases.txt
new file mode 100644
index 0000000..e3af18e
--- /dev/null
+++ b/doc/tutorial/basics/aliases.txt
@@ -0,0 +1,37 @@
+cue eval alias.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Aliases"
+description = ""
+
+-- text.md --
+An alias defines a local macro.
+
+A typical use case is to provide access to a shadowed field.
+
+Aliases are not members of a struct. They can be referred to only within the
+struct, and they do not appear in the output.
+
+-- alias.cue --
+A = a  // A is an alias for a
+a: {
+    d: 3
+}
+b: {
+    a: {
+        // A provides access to the outer "a" which would
+        // otherwise be hidden by the inner one.
+        c: A.d
+    }
+}
+
+-- expect-stdout-cue --
+a: {
+    d: 3
+}
+b: {
+    a: {
+        c: 3
+    }
+}
diff --git a/doc/tutorial/basics/bottom.txt b/doc/tutorial/basics/bottom.txt
new file mode 100644
index 0000000..a3fb295
--- /dev/null
+++ b/doc/tutorial/basics/bottom.txt
@@ -0,0 +1,35 @@
+cue eval -i bottom.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Bottom / Error"
+description = ""
+
+-- text.md --
+Specifying duplicate fields with conflicting values results in an error
+or bottom.
+_Bottom_ is a special value in CUE, denoted `_|_`, that indicates an
+error such as conflicting values.
+Any error in CUE results in `_|_`.
+Logically all errors are equal, although errors may be associated with
+metadata such as an error message.
+
+Note that an error is different from `null`: `null` is a valid value,
+whereas `_|_` is not.
+
+-- bottom.cue --
+a: 4
+a: 5
+
+l: [ 1, 2 ]
+l: [ 1, 3 ]
+
+list: [0, 1, 2]
+val: list[3]
+
+-- expect-stdout-cue --
+list: [0, 1, 2]
+a: _|_ // conflicting values 4 and 5
+l: [1, _|_, // conflicting values 2 and 3
+]
+val: _|_ // index 3 out of bounds
diff --git a/doc/tutorial/basics/bytes.txt b/doc/tutorial/basics/bytes.txt
new file mode 100644
index 0000000..edf7d03
--- /dev/null
+++ b/doc/tutorial/basics/bytes.txt
@@ -0,0 +1,24 @@
+cue export bytes.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Bytes"
+description = ""
+
+-- text.md --
+CUE distinguishes between a `string` and a `bytes` type.
+Bytes are converted to base64 when emitting JSON.
+Byte literals are defined with single quotes.
+The following additional escape sequences are allowed in byte literals:
+
+    \xnn   // arbitrary byte value defined as a 2-digit hexadecimal number
+    \nnn   // arbitrary byte value defined as a 3-digit octal number
+<!-- jba: this contradicts the spec, which has \nnn (no leading zero) -->
+
+-- bytes.cue --
+a: '\x03abc'
+
+-- expect-stdout-cue --
+{
+    "a": "A2FiYw=="
+}
diff --git a/doc/tutorial/basics/coalesce.txt b/doc/tutorial/basics/coalesce.txt
new file mode 100644
index 0000000..783e6c9
--- /dev/null
+++ b/doc/tutorial/basics/coalesce.txt
@@ -0,0 +1,44 @@
+cue eval coalesce.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Null Coalescing"
+description = ""
+
+-- text.md --
+<!-- jba: the terms here are confusing. "Null coalescing" is actually not
+  that, but then there is something called "actual null coalescing."
+  
+  Just say that because _|_ | X evaluates to X, you can use disjunction
+  to represent fallback values.
+  
+  And then you can use that to effectively type-check with a default value.
+-->
+
+With null coalescing we really mean error, or bottom, coalescing.
+The defaults mechanism for disjunctions can also be
+used to provide fallback values in case an expression evaluates to bottom.
+
+In the example the fallback values are specified
+for `a` and `b` in case the list index is out of bounds.
+
+To do actual null coalescing one can unify a result with the desired type
+to force an error.
+In that case the default will be used if either the lookup fails or
+the result is not of the desired type.
+
+-- coalesce.cue --
+list: [ "Cat", "Mouse", "Dog" ]
+
+a: *list[0] | "None"
+b: *list[5] | "None"
+
+n: [null]
+v: *n[0]&string | "default"
+
+-- expect-stdout-cue --
+list: ["Cat", "Mouse", "Dog"]
+a: "Cat"
+b: "None"
+n: [null]
+v: "default"
diff --git a/doc/tutorial/basics/commas.txt b/doc/tutorial/basics/commas.txt
new file mode 100644
index 0000000..3035ebc
--- /dev/null
+++ b/doc/tutorial/basics/commas.txt
@@ -0,0 +1,31 @@
+cue export commas.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Commas are Optional after Fields"
+description = ""
+
+-- text.md --
+Commas are optional at the end of fields.
+This is also true for the last field.
+The convention is to omit them.
+
+<!-- Side Note -->
+_CUE borrows a trick from Go to achieve this: the formal grammar still
+requires commas, but the scanner inserts commas according to a small set
+of simple rules._
+
+-- commas.cue --
+{
+    one: 1
+    two: 2
+
+    "two-and-a-half": 2.5
+}
+
+-- expect-stdout-cue --
+{
+    "one": 1,
+    "two": 2,
+    "two-and-a-half": 2.5
+}
diff --git a/doc/tutorial/basics/commaslists.txt b/doc/tutorial/basics/commaslists.txt
new file mode 100644
index 0000000..fe4c127
--- /dev/null
+++ b/doc/tutorial/basics/commaslists.txt
@@ -0,0 +1,24 @@
+cue export commas2.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Commas are Still Required in Lists"
+description = ""
+
+-- text.md --
+Commas are still required as separators in lists.
+The last element of a list may also have a comma.
+
+-- commas2.cue --
+[
+    1,
+    2,
+    3,
+]
+
+-- expect-stdout-cue --
+[
+    1,
+    2,
+    3
+]
diff --git a/doc/tutorial/basics/comments.txt b/doc/tutorial/basics/comments.txt
new file mode 100644
index 0000000..7f21d7b
--- /dev/null
+++ b/doc/tutorial/basics/comments.txt
@@ -0,0 +1,20 @@
+cue export comments.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Comments"
+description = ""
+
+-- text.md --
+CUE supports C-style line comments.
+
+-- comments.cue --
+// a doc comment
+one: 1
+two: 2 // a line comment
+
+-- expect-stdout-cue --
+{
+    "one": 1,
+    "two": 2
+}
diff --git a/doc/tutorial/basics/conditional.txt b/doc/tutorial/basics/conditional.txt
new file mode 100644
index 0000000..f348874
--- /dev/null
+++ b/doc/tutorial/basics/conditional.txt
@@ -0,0 +1,27 @@
+cue eval conditional.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Conditional Fields"
+description = ""
+
+-- text.md --
+Field comprehensions can also be used to
+add a single field conditionally.
+
+Converting the resulting configuration to JSON results in an error
+as `justification` is required yet no concrete value is given.
+
+-- conditional.cue --
+price: number
+
+// Require a justification if price is too high
+if price > 100 {
+    justification: string
+}
+
+price: 200
+
+-- expect-stdout-cue --
+price:         200
+justification: string
diff --git a/doc/tutorial/basics/curly.txt b/doc/tutorial/basics/curly.txt
new file mode 100644
index 0000000..66ae11d
--- /dev/null
+++ b/doc/tutorial/basics/curly.txt
@@ -0,0 +1,24 @@
+cue export curly.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Curly Braces"
+description = ""
+
+-- text.md --
+The outer curly braces may be omitted for the top-level struct.
+CUE also allows both, which has a specific meaning.
+[We will come back to that later](emit.md).
+
+-- curly.cue --
+one: 1
+two: 2
+
+"two-and-a-half": 2.5
+
+-- expect-stdout-cue --
+{
+    "one": 1,
+    "two": 2,
+    "two-and-a-half": 2.5
+}
diff --git a/doc/tutorial/basics/cycle.txt b/doc/tutorial/basics/cycle.txt
new file mode 100644
index 0000000..6b59407
--- /dev/null
+++ b/doc/tutorial/basics/cycle.txt
@@ -0,0 +1,33 @@
+cue eval -i -c cycle.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Reference Cycles"
+description = ""
+
+-- text.md --
+CUE can handle many types of cycles just fine.
+Because all values are final, a field with a concrete value of, say `200`,
+can only be valid if it is that value.
+So if it is unified with another expression, we can delay the evaluation of
+this until later.
+
+By postponing that evaluation, we can often break cycles.
+This is very useful for template writers that may not know what fields
+a user will want to fill out.
+
+-- cycle.cue --
+// CUE knows how to resolve the following:
+x: 200
+x: y + 100
+y: x - 100
+
+// If a cycle is not broken, CUE will just report it.
+a: b + 100
+b: a - 100
+
+-- expect-stdout-cue --
+x: 200
+y: 100
+a: _|_ // cycle detected
+b: _|_ // cycle detected
diff --git a/doc/tutorial/basics/cycleref.txt b/doc/tutorial/basics/cycleref.txt
new file mode 100644
index 0000000..25552fb
--- /dev/null
+++ b/doc/tutorial/basics/cycleref.txt
@@ -0,0 +1,29 @@
+cue eval cycleref.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Cycles in Fields"
+description = ""
+
+-- text.md --
+Also, we know that unifying a field with itself will result in the same value.
+Thus if we have a cycle between some fields, all we need to do is ignore
+the cycle and unify their values once to achieve the same result as
+merging them ad infinitum.
+
+-- cycleref.cue --
+labels: selectors
+labels: {app: "foo"}
+
+selectors: labels
+selectors: {name: "bar"}
+
+-- expect-stdout-cue --
+labels: {
+    name: "bar"
+    app:  "foo"
+}
+selectors: {
+    name: "bar"
+    app:  "foo"
+}
diff --git a/doc/tutorial/basics/defaults.txt b/doc/tutorial/basics/defaults.txt
new file mode 100644
index 0000000..310e2f6
--- /dev/null
+++ b/doc/tutorial/basics/defaults.txt
@@ -0,0 +1,29 @@
+cue eval defaults.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Default Values"
+description = ""
+
+-- text.md --
+Elements of a disjunction may be marked as preferred.
+If there is only one mark, or the users constraints a field enough such that
+only one mark remains, that value is the default value.
+
+In the example, `replicas` defaults to `1`.
+In the case of `protocol`, however, there are multiple definitions with
+different, mutually incompatible defaults.
+In that case, both `"tcp"` and `"udp"` are preferred and one must explicitly
+specify either `"tcp"` or `"udp"` as if no marks were given.
+
+-- defaults.cue --
+// any positive number, 1 is the default
+replicas: uint | *1
+
+// the default value is ambiguous
+protocol: *"tcp" | "udp"
+protocol: *"udp" | "tcp"
+
+-- expect-stdout-cue --
+replicas: 1
+protocol: "tcp" | "udp" | *_|_
diff --git a/doc/tutorial/basics/disjunctions.txt b/doc/tutorial/basics/disjunctions.txt
new file mode 100644
index 0000000..b6b85c6
--- /dev/null
+++ b/doc/tutorial/basics/disjunctions.txt
@@ -0,0 +1,25 @@
+-- frontmatter.toml --
+title = "Disjunctions"
+description = ""
+
+-- text.md --
+Disjunctions, or sum types, define a new type that is one of several things.
+
+In the example, `conn` defines a `protocol` field that must be one of two
+values: `"tcp"` or `"udp"`.
+It is an error for a concrete `conn`
+to define anything else than these two values.
+
+-- disjunctions.cue --
+conn: {
+    address:  string
+    port:     int
+    protocol: "tcp" | "udp"
+}
+
+lossy: conn & {
+    address:  "1.2.3.4"
+    port:     8888
+    protocol: "udp"
+}
+
diff --git a/doc/tutorial/basics/duplicates.txt b/doc/tutorial/basics/duplicates.txt
new file mode 100644
index 0000000..dce9b26
--- /dev/null
+++ b/doc/tutorial/basics/duplicates.txt
@@ -0,0 +1,38 @@
+cue eval dup.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Duplicate Fields"
+description = ""
+
+-- text.md --
+CUE allows duplicated field definitions as long as they don't conflict.
+
+For values of basic types this means they must be equal.
+
+For structs, fields are merged and duplicated fields are handled recursively.
+
+For lists, all elements must match accordingly
+([we discuss open-ended lists later](lists.md).)
+
+-- dup.cue --
+a: 4
+a: 4
+
+s: {
+    x: 1
+}
+s: {
+    y: 2
+}
+
+l: [ 1, 2 ]
+l: [ 1, 2 ]
+
+-- expect-stdout-cue --
+a: 4
+s: {
+    x: 1
+    y: 2
+}
+l: [1, 2]
diff --git a/doc/tutorial/basics/emit.txt b/doc/tutorial/basics/emit.txt
new file mode 100644
index 0000000..fc841a0
--- /dev/null
+++ b/doc/tutorial/basics/emit.txt
@@ -0,0 +1,22 @@
+cue eval emit.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Emit Values"
+description = ""
+
+-- text.md --
+By default all top-level fields are emitted when evaluating a configuration.
+Embedding a value at top-level will cause that value to be emitted instead.
+
+Emit values allow CUE configurations, like JSON,
+to define any type, instead of just structs, while keeping the common case
+of defining structs light.
+
+-- emit.cue --
+"Hello \(who)!"
+
+who: "world"
+
+-- expect-stdout-cue --
+"Hello world!"
diff --git a/doc/tutorial/basics/fieldcomp.txt b/doc/tutorial/basics/fieldcomp.txt
new file mode 100644
index 0000000..d32cda2
--- /dev/null
+++ b/doc/tutorial/basics/fieldcomp.txt
@@ -0,0 +1,44 @@
+cue eval fieldcomp.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Field Comprehensions"
+description = ""
+
+-- text.md --
+CUE also supports comprehensions for fields.
+
+One cannot refer to generated fields with references.
+Instead, one must use indexing.
+
+-- fieldcomp.cue --
+import "strings"
+
+a: [ "Barcelona", "Shanghai", "Munich" ]
+
+{
+    for k, v in a {
+        "\( strings.ToLower(v) )": {
+            pos:     k + 1
+            name:    v
+            nameLen: len(v)
+        }
+    }
+}
+
+-- expect-stdout-cue --
+barcelona: {
+    name:    "Barcelona"
+    pos:     1
+    nameLen: 9
+}
+shanghai: {
+    name:    "Shanghai"
+    pos:     2
+    nameLen: 8
+}
+munich: {
+    name:    "Munich"
+    pos:     3
+    nameLen: 6
+}
diff --git a/doc/tutorial/basics/fieldname.txt b/doc/tutorial/basics/fieldname.txt
new file mode 100644
index 0000000..256c115
--- /dev/null
+++ b/doc/tutorial/basics/fieldname.txt
@@ -0,0 +1,29 @@
+cue export fieldname.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Quotes are Optional for Field Names"
+description = ""
+
+-- text.md --
+JSON objects are called structs in CUE.
+An object member is called a field.
+
+
+Double quotes may be omitted from field names if their name contains no
+special characters and does not start with a number:
+
+-- fieldname.cue --
+{
+    one: 1,
+    two: 2,
+
+    "two-and-a-half": 2.5
+}
+
+-- expect-stdout-cue --
+{
+    "one": 1,
+    "two": 2,
+    "two-and-a-half": 2.5
+}
diff --git a/doc/tutorial/basics/fold.txt b/doc/tutorial/basics/fold.txt
new file mode 100644
index 0000000..f39c9f7
--- /dev/null
+++ b/doc/tutorial/basics/fold.txt
@@ -0,0 +1,21 @@
+cue export fold.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Folding of Single-Field Structs"
+description = ""
+
+-- text.md --
+CUE allows a shorthand for structs with single members.
+
+-- fold.cue --
+outer middle inner: 3
+
+-- expect-stdout-cue --
+{
+    "outer": {
+        "middle": {
+            "inner": 3
+        }
+    }
+}
diff --git a/doc/tutorial/basics/foldany.txt b/doc/tutorial/basics/foldany.txt
new file mode 100644
index 0000000..efa1519
--- /dev/null
+++ b/doc/tutorial/basics/foldany.txt
@@ -0,0 +1,28 @@
+cue export foldany.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Folding all Fields"
+description = ""
+
+-- text.md --
+This also works if a struct has more than one member.
+
+In general, any JSON object can be expressed as a collection of
+path-leaf pairs without using any curly braces.
+
+-- foldany.cue --
+outer middle1 inner: 3
+outer middle2 inner: 7
+
+-- expect-stdout-cue --
+{
+    "outer": {
+        "middle1": {
+            "inner": 3
+        },
+        "middle2": {
+            "inner": 7
+        }
+    }
+}
diff --git a/doc/tutorial/basics/hidden.txt b/doc/tutorial/basics/hidden.txt
new file mode 100644
index 0000000..520cf97
--- /dev/null
+++ b/doc/tutorial/basics/hidden.txt
@@ -0,0 +1,26 @@
+cue export hidden.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Hidden Fields"
+description = ""
+
+-- text.md --
+A non-quoted field name that starts with an underscore (`_`) is not
+emitted from the output.
+To include fields in the configuration that start with an underscore
+put them in quotes.
+
+Quoted and non-quoted fields share the same namespace unless they start
+with an underscore.
+
+-- hidden.cue --
+"_foo": 2
+_foo:   3
+foo:    4
+
+-- expect-stdout-cue --
+{
+    "_foo": 2,
+    "foo": 4
+}
diff --git a/doc/tutorial/basics/imports.txt b/doc/tutorial/basics/imports.txt
new file mode 100644
index 0000000..c7176cd
--- /dev/null
+++ b/doc/tutorial/basics/imports.txt
@@ -0,0 +1,34 @@
+cue eval imports.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Imports"
+description = ""
+
+-- text.md --
+A CUE file may import definitions from builtin or user-defined packages.
+A CUE file does not need to be part of a package to use imports.
+
+The example here shows the use of builtin packages.
+
+This code groups the imports into a parenthesized, "factored" import statement.
+
+You can also write multiple import statements, like:
+
+```
+import "encoding/json"
+import "math"
+```
+
+But it is good style to use the factored import statement.
+
+-- imports.cue --
+import (
+	"encoding/json"
+	"math"
+)
+
+data: json.Marshal({ a: math.Sqrt(7) })
+
+-- expect-stdout-cue --
+data: "{\"a\":2.6457513110645907}"
diff --git a/doc/tutorial/basics/instances.txt b/doc/tutorial/basics/instances.txt
new file mode 100644
index 0000000..2fe7df2
--- /dev/null
+++ b/doc/tutorial/basics/instances.txt
@@ -0,0 +1,4 @@
+-- frontmatter.toml --
+title = "Modules"
+description = ""
+
diff --git a/doc/tutorial/basics/interpolation.txt b/doc/tutorial/basics/interpolation.txt
new file mode 100644
index 0000000..a741d1f
--- /dev/null
+++ b/doc/tutorial/basics/interpolation.txt
@@ -0,0 +1,21 @@
+cue eval interpolation.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Interpolation"
+description = ""
+
+-- text.md --
+String and bytes literals support interpolation.
+
+Any valid CUE expression may be used inside the escaped parentheses.
+Interpolation may also be used in multiline string and byte literals.
+
+-- interpolation.cue --
+"You are \( cost - budget ) dollars over budget!"
+
+cost:   102
+budget: 88
+
+-- expect-stdout-cue --
+"You are 14 dollars over budget!"
diff --git a/doc/tutorial/basics/interpolfield.txt b/doc/tutorial/basics/interpolfield.txt
new file mode 100644
index 0000000..963828c
--- /dev/null
+++ b/doc/tutorial/basics/interpolfield.txt
@@ -0,0 +1,27 @@
+cue eval -i genfield.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Interpolation of Field Names"
+description = ""
+
+-- text.md --
+String interpolations may also be used in field names.
+
+One cannot refer to generated fields with references.
+
+-- genfield.cue --
+sandwich: {
+    type:            "Cheese"
+    "has\(type)":    true
+    hasButter:       true
+    butterAndCheese: hasButter && hasCheese
+}
+
+-- expect-stdout-cue --
+sandwich: {
+    type:            "Cheese"
+    hasButter:       true
+    butterAndCheese: _|_ // reference "hasCheese" not found
+    hasCheese:       true
+}
diff --git a/doc/tutorial/basics/json.txt b/doc/tutorial/basics/json.txt
new file mode 100644
index 0000000..ba5d6df
--- /dev/null
+++ b/doc/tutorial/basics/json.txt
@@ -0,0 +1,4 @@
+-- frontmatter.toml --
+title = "JSON sugar and other Goodness"
+description = ""
+
diff --git a/doc/tutorial/basics/listcomp.txt b/doc/tutorial/basics/listcomp.txt
new file mode 100644
index 0000000..d87f1e0
--- /dev/null
+++ b/doc/tutorial/basics/listcomp.txt
@@ -0,0 +1,19 @@
+cue eval listcomp.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "List Comprehensions"
+description = ""
+
+-- text.md --
+Lists can be created with list comprehensions.
+
+The example shows the use of `for` loops and `if` guards.
+
+-- listcomp.cue --
+[ x*x for x in items if x rem 2 == 0]
+
+items: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
+
+-- expect-stdout-cue --
+[4, 16, 36, 64]
diff --git a/doc/tutorial/basics/lists.txt b/doc/tutorial/basics/lists.txt
new file mode 100644
index 0000000..3ecf475
--- /dev/null
+++ b/doc/tutorial/basics/lists.txt
@@ -0,0 +1,41 @@
+cue eval -i lists.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Lists"
+description = ""
+
+-- text.md --
+Lists define arbitrary sequences of CUE values.
+A list can be closed or open ended.
+Open-ended lists may have some predefined elements, but may have
+additional, possibly typed elements.
+
+In the example we define `IP` to be a list of `4` elements of type `uint8`, which
+is a predeclared value of `>=0 & <=255`.
+`PrivateIP` defines the IP ranges defined for private use.
+Note that as it is already defined to be an `IP`, the length of the list
+is already fixed at `4` and we do not have to specify a value for all elements.
+Also note that instead of writing `...uint8`, we could have written `...`
+as the type constraint is already already implied by `IP`.
+
+The output contains a valid private IP address (`myIP`)
+and an invalid one (`yourIP`).
+
+-- lists.cue --
+IP: 4 * [ uint8 ]
+
+PrivateIP: IP
+PrivateIP: [10, ...uint8] | [192, 168, ...] | [172, >=16 & <=32, ...]
+
+myIP: PrivateIP
+myIP: [10, 2, 3, 4]
+
+yourIP: PrivateIP
+yourIP: [11, 1, 2, 3]
+
+-- expect-stdout-cue --
+IP: [uint8, uint8, uint8, uint8]
+PrivateIP: [10, uint8, uint8, uint8] | [192, 168, uint8, uint8] | [172, >=16 & <=32 & uint8, uint8, uint8]
+myIP: [10, 2, 3, 4]
+yourIP: _|_ // empty disjunction: [((10 & (int & >=0 & int & <=255)) & 11),((int & >=0 & int & <=255) & 1),((int & >=0 & int & <=255) & 2),((int & >=0 & int & <=255) & 3)]
diff --git a/doc/tutorial/basics/numberlit.txt b/doc/tutorial/basics/numberlit.txt
new file mode 100644
index 0000000..c6da95d
--- /dev/null
+++ b/doc/tutorial/basics/numberlit.txt
@@ -0,0 +1,25 @@
+cue export numlit.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Number Literals"
+description = ""
+
+-- text.md --
+CUE adds a variety of sugar for writing numbers.
+
+-- numlit.cue --
+[
+    1_234,       // 1234
+    5M,          // 5_000_000
+    1.5Gi,       // 1_610_612_736
+    0x1000_0000, // 268_435_456
+]
+
+-- expect-stdout-cue --
+[
+    1234,
+    5000000,
+    1610612736,
+    268435456
+]
diff --git a/doc/tutorial/basics/numbers.txt b/doc/tutorial/basics/numbers.txt
new file mode 100644
index 0000000..645ecb1
--- /dev/null
+++ b/doc/tutorial/basics/numbers.txt
@@ -0,0 +1,35 @@
+cue eval -i numbers.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Numbers"
+description = ""
+
+-- text.md --
+CUE defines two kinds of numbers.
+Integers, denoted `int`, are whole, or integral, numbers.
+Floats, denoted `float`, are decimal floating point numbers.
+
+An integer literal (e.g. `4`) can be of either type, but defaults to `int`.
+A floating point literal (e.g. `4.0`) is only compatible with `float`.
+
+In the example, the result of `b` is a `float` and cannot be
+used as an `int` without conversion.
+
+-- numbers.cue --
+a: int
+a: 4 // type int
+
+b: number
+b: 4.0 // type float
+
+c: int
+c: 4.0
+
+d: 4  // will evaluate to type int (default)
+
+-- expect-stdout-cue --
+a: 4
+b: 4.0
+c: _|_ // conflicting values int and 4.0 (mismatched types int and float)
+d: 4
diff --git a/doc/tutorial/basics/operators.txt b/doc/tutorial/basics/operators.txt
new file mode 100644
index 0000000..75051d6
--- /dev/null
+++ b/doc/tutorial/basics/operators.txt
@@ -0,0 +1,30 @@
+cue eval -i op.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Operators"
+description = ""
+
+-- text.md --
+CUE supports many common arithmetic and boolean operators.
+
+The operators for division and remainder are different for `int` and `float`.
+For `float` CUE supports the `/` and `%`  operators with the usual meaning.
+For `int` CUE supports both Euclidean division (`div` and `mod`)
+and truncated division (`quo` and `rem`).
+
+-- op.cue --
+a: 3 / 2   // type float
+b: 3 div 2 // type int: Euclidean division
+
+c: 3 * "blah"
+d: 3 * [1, 2, 3]
+
+e: 8 < 10
+
+-- expect-stdout-cue --
+a: 1.5
+b: 1
+c: "blahblahblah"
+d: [1, 2, 3, 1, 2, 3, 1, 2, 3]
+e: true
diff --git a/doc/tutorial/basics/packages.txt b/doc/tutorial/basics/packages.txt
new file mode 100644
index 0000000..812d5a8
--- /dev/null
+++ b/doc/tutorial/basics/packages.txt
@@ -0,0 +1,34 @@
+cue eval a.cue b.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Packages"
+description = ""
+
+-- text.md --
+A CUE file is a standalone file by default.
+A `package` clause allows a single configuration to be split across multiple
+files.
+
+The configuration for a package is defined by the concatenation of all its
+files, after stripping the package clauses and not considering imports.
+
+Duplicate definitions are treated analogously to duplicate definitions within
+the same file.
+The order in which files are loaded is undefined, but any order will result
+in the same outcome, given that order does not matter.
+
+-- a.cue --
+package config
+
+foo: 100
+bar: int
+
+-- b.cue --
+package config
+
+bar: 200
+
+-- expect-stdout-cue --
+foo: 100
+bar: 200
diff --git a/doc/tutorial/basics/rangedef.txt b/doc/tutorial/basics/rangedef.txt
new file mode 100644
index 0000000..1ef0b89
--- /dev/null
+++ b/doc/tutorial/basics/rangedef.txt
@@ -0,0 +1,45 @@
+cue eval -i range.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Predefined Ranges"
+description = ""
+
+-- text.md --
+CUE numbers have arbitrary precision.
+Also there is no unsigned integer type.
+
+CUE defines the following predefined identifiers to restrict the ranges of
+integers to common values.
+
+```
+uint      >=0
+uint8     >=0 & <=255
+int8      >=-128 & <=127
+uint16    >=0 & <=65536
+int16     >=-32_768 & <=32_767
+rune      >=0 & <=0x10FFFF
+uint32    >=0 & <=4_294_967_296
+int32     >=-2_147_483_648 & <=2_147_483_647
+uint64    >=0 & <=18_446_744_073_709_551_615
+int64     >=-9_223_372_036_854_775_808 & <=9_223_372_036_854_775_807
+int128    >=-170_141_183_460_469_231_731_687_303_715_884_105_728 &
+              <=170_141_183_460_469_231_731_687_303_715_884_105_727
+uint128   >=0 & <=340_282_366_920_938_463_463_374_607_431_768_211_455
+```
+
+-- range.cue --
+positive: uint
+byte:     uint8
+word:     int32
+
+{
+    a: positive & -1
+    b: byte & 128
+    c: word & 2_000_000_000
+}
+
+-- expect-stdout-cue --
+a: _|_ // invalid value -1 (out of bound int & >=0)
+b: 128
+c: 2000000000
diff --git a/doc/tutorial/basics/ranges.txt b/doc/tutorial/basics/ranges.txt
new file mode 100644
index 0000000..c908892
--- /dev/null
+++ b/doc/tutorial/basics/ranges.txt
@@ -0,0 +1,39 @@
+cue eval -i bounds.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Bounds"
+description = ""
+
+-- text.md --
+Bounds define a lower bound, upper bound, or inequality for a certain value.
+They work on numbers, strings, bytes, and and null.
+
+The bound is defined for all values for which the corresponding comparison
+operation is define.
+For instance `>5.0` allows all floating point values greater than `5.0`,
+whereas `<0` allows all negative numbers (int or float).
+
+-- bounds.cue --
+rn: >=3 & <8        // type int | float
+ri: >=3 & <8 & int  // type int
+rf: >=3 & <=8.0     // type float
+rs: >="a" & <"mo"
+
+{
+    a: rn & 3.5
+    b: ri & 3.5
+    c: rf & 3
+    d: rs & "ma"
+    e: rs & "mu"
+
+    r1: rn & >=5 & <10
+}
+
+-- expect-stdout-cue --
+a:  3.5
+b:  _|_ // conflicting values ri and 3.5 (mismatched types int and float)
+c:  3
+d:  "ma"
+e:  _|_ // invalid value "mu" (out of bound <"mo")
+r1: >=5 & <8
diff --git a/doc/tutorial/basics/regexp.txt b/doc/tutorial/basics/regexp.txt
new file mode 100644
index 0000000..ffbf892
--- /dev/null
+++ b/doc/tutorial/basics/regexp.txt
@@ -0,0 +1,34 @@
+cue eval -i regexp.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Regular expressions"
+description = ""
+
+-- text.md --
+The `=~` and `!~` operators can be used to check against regular expressions.
+
+The expression `a =~ b` is true if `a` matches `b`, while
+`a !~ b` is true if `a` does _not_ match `b`.
+
+Just as with comparison operators, these operators may be used
+as unary versions to define a set of strings.
+
+-- regexp.cue --
+a: "foo bar" =~ "foo [a-z]{3}"
+b: "maze" !~ "^[a-z]{3}$"
+
+c: =~"^[a-z]{3}$" // any string with lowercase ASCII of length 3
+
+d: c
+d: "foo"
+
+e: c
+e: "foo bar"
+
+-- expect-stdout-cue --
+a: true
+b: true
+c: =~"^[a-z]{3}$"
+d: "foo"
+e: _|_ // invalid value "foo bar" (does not match =~"^[a-z]{3}$")
diff --git a/doc/tutorial/basics/scopes.txt b/doc/tutorial/basics/scopes.txt
new file mode 100644
index 0000000..ba12f98
--- /dev/null
+++ b/doc/tutorial/basics/scopes.txt
@@ -0,0 +1,35 @@
+cue eval scopes.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "References and Scopes"
+description = ""
+
+-- text.md --
+A reference refers to the value of the field defined within nearest
+enclosing scope.
+
+If no field matches the reference within the file, it may match a top-level
+field defined in any other file of the same package.
+
+If there is still no match, it may match a predefined value.
+
+-- scopes.cue --
+v: 1
+a: {
+    v: 2
+    b: v // matches the inner v
+}
+a: {
+    c: v // matches the top-level v
+}
+b: v
+
+-- expect-stdout-cue --
+v: 1
+a: {
+    v: 2
+    b: 2
+    c: 1
+}
+b: 1
diff --git a/doc/tutorial/basics/script_test.go b/doc/tutorial/basics/script_test.go
new file mode 100644
index 0000000..4523fb1
--- /dev/null
+++ b/doc/tutorial/basics/script_test.go
@@ -0,0 +1,25 @@
+package basics
+
+import (
+	"flag"
+	"os"
+	"testing"
+
+	"cuelang.org/go/cmd/cue/cmd"
+	"github.com/rogpeppe/testscript"
+)
+
+var update = flag.Bool("update", false, "update the test files")
+
+func TestScript(t *testing.T) {
+	testscript.Run(t, testscript.Params{
+		Dir:           "",
+		UpdateScripts: *update,
+	})
+}
+
+func TestMain(m *testing.M) {
+	os.Exit(testscript.RunMain(m, map[string]func() int{
+		"cue": cmd.Main,
+	}))
+}
diff --git a/doc/tutorial/basics/selectors.txt b/doc/tutorial/basics/selectors.txt
new file mode 100644
index 0000000..0e29ce2
--- /dev/null
+++ b/doc/tutorial/basics/selectors.txt
@@ -0,0 +1,27 @@
+cue eval selectors.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Accessing Fields"
+description = ""
+
+-- text.md --
+Selectors access fields within a struct using the `.` notation.
+This only works if a field name is a valid identifier and it is not computed.
+For other cases one can use the indexing notation.
+
+-- selectors.cue --
+a: {
+    b: 2
+    "c-e": 5
+}
+v: a.b
+w: a["c-e"]
+
+-- expect-stdout-cue --
+a: {
+    b:     2
+    "c-e": 5
+}
+v: 2
+w: 5
diff --git a/doc/tutorial/basics/stringlit.txt b/doc/tutorial/basics/stringlit.txt
new file mode 100644
index 0000000..80c1cd8
--- /dev/null
+++ b/doc/tutorial/basics/stringlit.txt
@@ -0,0 +1,33 @@
+cue export stringlit.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "String Literals"
+description = ""
+
+-- text.md --
+CUE strings allow a richer set of escape sequences than JSON.
+
+CUE also supports multi-line strings, enclosed by a pair of triple quotes `"""`.
+The opening quote must be followed by a newline.
+The closing quote must also be on a newline.
+The whitespace directly preceding the closing quote must match the preceding
+whitespace on all other lines and is removed from these lines.
+
+Strings may also contain [interpolations](interpolation.md).
+
+-- stringlit.cue --
+// 21-bit unicode characters
+a: "\U0001F60E" // 😎
+
+// multiline strings
+b: """
+    Hello
+    World!
+    """
+
+-- expect-stdout-cue --
+{
+    "a": "😎",
+    "b": "Hello\nWorld!"
+}
diff --git a/doc/tutorial/basics/stringraw.txt b/doc/tutorial/basics/stringraw.txt
new file mode 100644
index 0000000..817fced
--- /dev/null
+++ b/doc/tutorial/basics/stringraw.txt
@@ -0,0 +1,38 @@
+cue eval stringraw.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "\"Raw\" Strings"
+description = ""
+
+-- text.md --
+CUE does not support raw strings in the strictest sense.
+Instead it allows modifying the escape delimiter by requiring
+an arbitrary number of hash `#` signs after the backslash by
+enclosing a string literal in an equal number of hash signs on either end.
+
+This works for normal and interpolated strings.
+Quotes do not have to be escaped in such strings.
+
+-- stringraw.cue --
+msg1: #"The sequence "\U0001F604" renders as \#U0001F604."#
+
+msg2: ##"""
+    A regular expression can conveniently be written as:
+
+        #"\d{3}"#
+
+    This construct works for bytes, strings and their
+    multi-line variants.
+    """##
+
+-- expect-stdout-cue --
+msg1: "The sequence \"\\U0001F604\" renders as 😄."
+msg2: """
+        A regular expression can conveniently be written as:
+        
+            #\"\\d{3}\"#
+        
+        This construct works for bytes, strings and their
+        multi-line variants.
+        """
diff --git a/doc/tutorial/basics/sumstruct.txt b/doc/tutorial/basics/sumstruct.txt
new file mode 100644
index 0000000..5ec64e6
--- /dev/null
+++ b/doc/tutorial/basics/sumstruct.txt
@@ -0,0 +1,26 @@
+-- frontmatter.toml --
+title = "Disjunctions of Structs"
+description = ""
+
+-- text.md --
+Disjunctions work for any type.
+
+In this example we see that a `floor` of some specific house
+has an exit on level 0 and 1, but not on any other floor.
+
+-- sumstruct.cue --
+// floor defines the specs of a floor in some house.
+floor: {
+    level:   int  // the level on which this floor resides
+    hasExit: bool // is there a door to exit the house?
+}
+
+// constraints on the possible values of floor.
+floor: {
+    level: 0 | 1
+    hasExit: true
+} | {
+    level: -1 | 2 | 3
+    hasExit: false
+}
+
diff --git a/doc/tutorial/basics/templates.txt b/doc/tutorial/basics/templates.txt
new file mode 100644
index 0000000..779854b
--- /dev/null
+++ b/doc/tutorial/basics/templates.txt
@@ -0,0 +1,46 @@
+cue eval templates.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Templates"
+description = ""
+
+-- text.md --
+<!-- jba: this is not in the spec, aside from the TemplateLabel grammar rule. -->
+
+One of CUE's most powerful features is templates.
+A template defines a value to be unified with each field of a struct.
+
+The template's identifier (in angular brackets) is bound to name of each
+of its sibling fields and is visible within the template value
+that is unified with each of the siblings.
+
+-- templates.cue --
+// The following struct is unified with all elements in job.
+// The name of each element is bound to Name and visible in the struct.
+job <Name>: {
+    name:     Name
+    replicas: uint | *1
+    command:  string
+}
+
+job list command: "ls"
+
+job nginx: {
+    command:  "nginx"
+    replicas: 2
+}
+
+-- expect-stdout-cue --
+job: {
+    list: {
+        name:     "list"
+        replicas: 1
+        command:  "ls"
+    }
+    nginx: {
+        name:     "nginx"
+        replicas: 2
+        command:  "nginx"
+    }
+}
diff --git a/doc/tutorial/basics/tut_test.go b/doc/tutorial/basics/tut_test.go
index 29336a6..c69326d 100644
--- a/doc/tutorial/basics/tut_test.go
+++ b/doc/tutorial/basics/tut_test.go
@@ -15,7 +15,7 @@
 package basics
 
 import (
-	"bytes"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -23,13 +23,14 @@
 	"testing"
 
 	"cuelang.org/go/internal/cuetest"
+	"github.com/rogpeppe/testscript/txtar"
 )
 
 func TestTutorial(t *testing.T) {
-	// t.Skip()
+	t.Skip()
 
 	err := filepath.Walk(".", func(path string, f os.FileInfo, err error) error {
-		if strings.HasSuffix(path, ".md") {
+		if strings.HasSuffix(path, ".md") && !strings.Contains(path, "/") {
 			t.Run(path, func(t *testing.T) { simulateFile(t, path) })
 		}
 		return nil
@@ -45,16 +46,45 @@
 		t.Fatalf("failed to open file %q: %v", path, err)
 	}
 
-	dir, err := ioutil.TempDir("", "tutbasics")
-	if err != nil {
-		t.Fatal(err)
+	if path == "Readme.md" {
+		return
 	}
-	defer os.Remove(dir)
+
+	archive := &txtar.Archive{}
+
+	addFile := func(filename string, body string) {
+		archive.Files = append(archive.Files, txtar.File{
+			Name: filename,
+			Data: []byte(strings.TrimSpace(body) + "\n\n"),
+		})
+	}
+
+	var (
+		baseName = path[:len(path)-len(".md")]
+		txtFile  = baseName + ".txt"
+	)
+
+	defer func() {
+		err = ioutil.WriteFile(txtFile, txtar.Format(archive), 0644)
+	}()
 
 	c := cuetest.NewChunker(t, b)
 
-	// collect files
-	for c.Find("<!-- CUE editor -->") {
+	c.Find("\n")
+	c.Next("_", "_")
+	section := c.Text()
+	sub := strings.ToLower(strings.Fields(section)[0])
+	sub = strings.TrimRight(sub, ",")
+
+	c.Next("# ", "\n")
+	addFile("frontmatter.toml", fmt.Sprintf(`title = %q
+description = ""
+`, c.Text()))
+
+	for i := 0; c.Find("<!-- CUE editor -->"); i++ {
+		if i == 0 {
+			addFile("text.md", c.Text())
+		}
 		if !c.Next("_", "_") {
 			continue
 		}
@@ -63,9 +93,7 @@
 		if !c.Next("```", "```") {
 			t.Fatalf("No body for filename %q in file %q", filename, path)
 		}
-		b := bytes.TrimSpace(c.Bytes())
-
-		ioutil.WriteFile(filepath.Join(dir, filename), b, 0644)
+		addFile(filename, c.Text())
 	}
 
 	if !c.Find("<!-- result -->") {
@@ -75,15 +103,21 @@
 	if !c.Next("`$ ", "`") {
 		t.Fatalf("No command for result section in file %q", path)
 	}
-	command := c.Text()
+	archive.Comment = []byte(c.Text())
+	archive.Comment = append(archive.Comment, `
+cmp stdout expect-stdout-cue
+
+`...)
 
 	if !c.Next("```", "```") {
-		t.Fatalf("No body for result section in file %q", path)
+		return
 	}
 	gold := c.Text()
 	if p := strings.Index(gold, "\n"); p > 0 {
 		gold = gold[p+1:]
 	}
 
-	cuetest.Run(t, dir, command, &cuetest.Config{Golden: gold})
+	gold = strings.TrimSpace(gold) + "\n"
+	// TODO: manually adjust file type and stderr.
+	addFile("expect-stdout-cue", gold)
 }
diff --git a/doc/tutorial/basics/types.txt b/doc/tutorial/basics/types.txt
new file mode 100644
index 0000000..94792ee
--- /dev/null
+++ b/doc/tutorial/basics/types.txt
@@ -0,0 +1,64 @@
+cue eval types.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Basic Types"
+description = ""
+
+-- text.md --
+CUE defines the following basic types
+
+```
+null bool string bytes int float
+```
+in addition to the error type mentioned in the previous section.
+
+CUE does not distinguish between types and values.
+A field value can be a type (using one of the above names), a concrete value,
+or, in case of composite types (lists and structs), anything in between.
+
+In the example, `point` defines an arbitrary point, while `xaxis` and `yaxis`
+define the points on the respective lines.
+We say that `point`, `xaxis`, and `yaxis` are abstract points, as these
+points are underspecified.
+Such abstract values cannot be represented as JSON,
+which requires all values to be concrete.
+
+The only concrete point is `origin`.
+The `origin` is defined to be both on the x-axis and y-axis, which means it
+must be at `0, 0`.
+Here we see constraints in action:
+`origin` evalutes to `0, 0`, even though we did not specify its coordinates
+explicitly.
+
+-- types.cue --
+point: {
+    x: number
+    y: number
+}
+
+xaxis: point
+xaxis x: 0
+
+yaxis: point
+yaxis y: 0
+
+origin: xaxis & yaxis
+
+-- expect-stdout-cue --
+point: {
+    x: number
+    y: number
+}
+xaxis: {
+    x: 0
+    y: number
+}
+yaxis: {
+    x: number
+    y: 0
+}
+origin: {
+    x: 0
+    y: 0
+}
diff --git a/doc/tutorial/basics/unification.txt b/doc/tutorial/basics/unification.txt
new file mode 100644
index 0000000..0742194
--- /dev/null
+++ b/doc/tutorial/basics/unification.txt
@@ -0,0 +1,56 @@
+cue eval -i unification.cue
+cmp stdout expect-stdout-cue
+
+-- frontmatter.toml --
+title = "Order is Irrelevant"
+description = ""
+
+-- text.md --
+As mentioned before, values of duplicates fields are combined.
+This process is called unification.
+Unification can also be written explicitly with the `&` operator.
+
+There is always a single unique result, possibly bottom,
+for unifying any two CUE values.
+
+Unification is commutative, associative, and idempotent.
+In other words, order doesn't matter and unifying a given set of values
+in any order always gives the same result.
+
+-- unification.cue --
+a: { x: 1, y: 2 }
+b: { y: 2, z: 3 }
+c: { x: 1, z: 4 }
+
+q: a & b & c
+r: b & c & a
+s: c & b & a
+
+-- expect-stdout-cue --
+a: {
+    x: 1
+    y: 2
+}
+b: {
+    y: 2
+    z: 3
+}
+c: {
+    x: 1
+    z: 4
+}
+q: {
+    x: 1
+    y: 2
+    z: _|_ // conflicting values 3 and 4
+}
+r: {
+    x: 1
+    y: 2
+    z: _|_ // conflicting values 3 and 4
+}
+s: {
+    x: 1
+    y: 2
+    z: _|_ // conflicting values 4 and 3
+}