code:
WTCD 1.1

section main {
  timeStart::[]

  // Equality
  assert::[true == true]
  assert::[false == false]
  assert::[1 == 1]
  assert::[1000 == 1000]
  assert::["asd" == "asd"]
  assert::[[ 1 ] == [ 1 ]]
  assert::[[ [ 1 ] true ] == [ [ 1 ] true ]]
  assert::[[ 1 2 3 ] == [ 1 2 3 ]]
  assert::[goto dummy1 == goto dummy1]
  assert::[goto [ dummy1 dummy2 dummy1 ] == goto [ dummy1 dummy2 dummy1 ]]
  assert::[exit == exit]
  assert::[null == null]
  assert::[(choice "a" goto dummy1) == (choice "a" goto dummy1)]
  assert::[(choice "b" goto [ dummy1 dummy2 ]) == (choice "b" goto [ dummy1 dummy2 ])]
  assert::[(selection [ choice "a" goto dummy1 choice "b" goto dummy2 choice "c" exit ]) == (selection [ choice "a" goto dummy1 choice "b" goto dummy2 choice "c" exit ])]

  // Inequality
  assert::[true != false]
  assert::[false != true]
  assert::[1 != 0]
  assert::[1000 != 1]
  assert::["asd" != "asd2"]
  assert::[[ 1 2 ] != [ 1 ]]
  assert::[[ 1 ] != [ 1 2 ]]
  assert::[[ 1 2 ] != [ 1 3 ]]
  assert::[[ [ 1 ] false ] != [ [ 1 ] true ]]
  assert::[[ [ 1 ] true ] != [ [ 2 ] true ]]
  assert::[goto dummy1 != goto dummy2]
  assert::[goto [ dummy1 dummy2 dummy1 ] != goto [ dummy2 dummy1 dummy1 ]]
  assert::[goto [ dummy1 dummy2 ] != goto [ dummy1 dummy2 dummy2 ]]
  assert::[exit != goto dummy1]
  assert::[null != 1]
  assert::[(choice "a" goto dummy1) != (choice "b" goto dummy1)]
  assert::[(choice "a" goto dummy1) != (choice "a" goto dummy2)]
  assert::[(choice "b" goto [ dummy1 dummy2 ]) != (choice "b" goto [ dummy1 dummy1 ])]
  assert::[(selection [ choice "a" goto dummy1 choice "b" goto dummy2 choice "c" exit ]) != (selection [ choice "a" goto dummy1 choice "b" goto dummy1 choice "c" exit ])]

  // Selection creation with list
  {
    declare list a = [ choice "a" goto dummy1 ]
    assert::[(selection a) == selection [ choice "a" goto dummy1 ]]
  }

  // 简单算术
  assert::[(1 + 1) == 2]
  assert::[(2 - 1) == 1]
  assert::[(2 * 5) == 10]
  assert::[(8 / 2) == 4]

  assert::[(2 ** 3 ** 2) == 64]

  // 三元
  assert::[(9 > 8
    ? 9 > 8
      ? 1
      : 2
    : 9 > 8
      ? 3
      : 4) == 1 ]
  assert::[(9 > 8
    ? 9 < 8
      ? 1
      : 2
    : 9 < 8
      ? 3
      : 4) == 2 ]
  assert::[(9 < 8
    ? 9 > 8
      ? 1
      : 2
    : 9 > 8
      ? 3
      : 4) == 3 ]
  assert::[(9 < 8
    ? 9 < 8
      ? 1
      : 2
    : 9 < 8
      ? 3
      : 4) == 4 ]

  // operator assignment
  {
    declare number a = 10
    assert::[(a += 5) == 15]
    assert::[a == 15]
    assert::[(a -= 10) == 5]
    assert::[a == 5]
    assert::[(a *= 5) == 25]
    assert::[a == 25]
    assert::[(a /= 5) == 5]
    assert::[a == 5]
  }

  // yield
  assert::[{
    yield 2
    yield 3
  } == 2]

  // set yield
  assert::[{
    yield = 2
    yield = 3
  } == 3]

  // Defaults
  {
    declare number a
    assert::[ a == 0 ]
    declare boolean b
    assert::[ b == false ]
    declare string c
    assert::[ c == "" ]
    declare list d
    assert::[ d == [] ]

    declare any
    assert::[ any == null ]
    declare number null nullable
    assert::[ nullable == null ]
  }

  // Type checking
  {
    assertError::[function[] {
      declare number a = "test"
    }]
    declare number string a = "test"
    assertError::[function[] {
      declare number string list boolean b = choice "a" goto dummy1
    }]
  }

  // Function equality
  {
    declare number a = 1
    declare function fn[number b] b * a
    declare function fn2[number b] b * a
    assert::[fn == fn]
    assert::[fn != fn2] // 函数不指向同一个就算不一样
  }

  // return + yield
  {
    declare function f1[number n] n * n
    declare function f2[number n] {
      yield n * n
    }
    declare function f3[number n] {
      yield = "Wrong"
      return = n * n // Return 优先
    }
    declare function f4[number n] {
      {{{{{ return n * n }}}}}
    }
    assert[f1::[12] == 144]
    assert[f2::[12] == 144]
    assert[f3::[12] == 144]
    assert[f4::[12] == 144]
  }

  {
    declare function solveQuad[number a number b number c] {
      declare number delta = b ** 2 - 4 * a * c
      delta > 0 ? {
        return [
          (-b - delta ** 0.5) / 2 / a
          (-b + delta ** 0.5) / 2 / a
        ]
      } : delta == 0 ? {
        return [ -b / 2 / a ]
      } : {
        return []
      }
    }
    assert::[solveQuad::[1 (-3) 2] == [ 1 2 ]]
    assert::[solveQuad::[1 2 1] == [ -1 ]]
    assert::[solveQuad::[1 2 100] == []]
  }

  // Function factory
  {
    declare function maker[number base] function[number inc] {
      base += inc
      return base
    }
    declare function f1 = maker::[10]
    assert::[f1::[5] == 15]
    assert::[f1::[5] == 20]
    declare function f2 = maker::[1000]
    assert::[f2::[5] == 1005]
    assert::[f2::[5] == 1010]
    declare function f1d = f1
    assert::[f1 == f1]
    assert::[f2 == f2]
    assert::[f1 == f1d]
    assert::[f1 != f2]
    assert::[f1d != f2]
  }

  // Rest arguments
  {
    declare function rest[number a number b boolean c ...rest] {
      assert::[a == 1]
      assert::[b == 2]
      assert::[c == false]
      assert::[rest == [ 4 true "HA!" ]]
    }
    rest::[ 1 2 false 4 true "HA!" ]
  }

  // High order
  {
    declare function comp[function f function g] function[...rest] f::[g::rest]
    declare function square[number input] input * input
    declare function plusFive[number input] input + 5
    declare function squareOPlusFive = comp::[square plusFive]
    assert::[squareOPlusFive::[5] == 100]
    declare function plusFiveOSquare = comp::[plusFive square]
    assert::[plusFiveOSquare::[5] == 30]
  }

  // List spread
  assert::[[ 1 ...[ 2  3 ] 4 ] == [ 1 2 3 4 ]]
  assert::[[ 1 ...[ ...[ ...[2] ] 3 ] 4 ] == [ 1 2 3 4 ]]
  {
    declare list l1 = [ 4 5 6 ]
    assert::[[ 1 2 3 ...l1 7 8 9 ] == [ 1 2 3 4 5 6 7 8 9 ]]
    assert::[[ 1 ...l1 ...l1 ...l1 1 ] == [ 1 4 5 6 4 5 6 4 5 6 1 ]]
  }

  // Boolean
  assert::[true && true]
  assert::[(false && assert::[false]) == false] // Short circuit
  assert::[(false || false) == false]
  assert::[true || assert::[false]] // Short circuit

  // List member access
  assert::[[ 0 1 2 3 4 ].(3) == 3]
  assertError::[function[] [ 0 1 2 3 4 ].(5)]
  assert::[[ 0 1 2 null 4 ].(3) == null]
  assertError::[function[] null.(5)]

  assert::[[ 0 1 2 3 4 ].?(3) == 3]
  assert::[[ 0 1 2 3 4 ].?(5) == null]
  assert::[[ 0 1 2 null 4 ].?(3) == null]
  assertError::[function[] null.?(5)]

  assert::[[ 0 1 2 3 4 ]?.(3) == 3]
  assertError::[function[] [ 0 1 2 3 4 ]?.(5)]
  assert::[[ 0 1 2 null 4 ]?.(3) == null]
  assert::[null?.(assert::[false]) == null] // Short circuit

  assert::[[ 0 1 2 3 4 ]?.?(3) == 3]
  assert::[[ 0 1 2 3 4 ]?.?(5) == null]
  assert::[[ 0 1 2 null 4 ]?.?(3) == null]
  assert::[null?.?(assert::[false]) == null] // Short circuit

  // Null coalescing
  assert::[(1 ?! assert::[false]) == 1] // Short circuit
  assert::[(null ?! 2) == 2]
  assertError::[function[] null ?! null]

  assert::[(1 ?? assert::[false]) == 1] // Short circuit
  assert::[(null ?? 2) == 2]
  assert::[(null ?? null) == null]

  // std listSet
  {
    declare list l1 = [ 0 1 2 3 4 5 ]
    declare list l2 = listSet::[l1 3 "Nice"]
    assert::[l1 == [ 0 1 2 3 4 5 ]]
    assert::[l2 == [ 0 1 2 "Nice" 4 5 ]]
  }

  // std listForEach
  {
    declare number contentSum = 0
    declare number indexSum = 0
    listForEach::[[ 1 2 3 4 100 ] function[number item number index] {
      contentSum += item
      indexSum += index
    }]
    assert::[contentSum == 110]
    assert::[indexSum == 10]
  }

  // std listMap
  {
    declare list l1 = [ 0 1 2 4 20 ]
    declare list l2 = listMap::[l1 function[number item] item * item]
    assert::[l1 == [ 0 1 2 4 20 ]]
    assert::[l2 == [ 0 1 4 16 400 ]]
  }

  // std listCreateFilled
  {
    declare list l1 = listCreateFilled::[ 5 ]
    assert::[l1 == [ null null null null null ]]
    declare list l2 = listCreateFilled::[ 5 8 ]
    assert::[l2 == [ 8 8 8 8 8 ]]
  }

  // std listChunk
  {
    declare list l = listChunk::[[ 1 2 3 4 5 6 7 8 9 10 ] 3 ]
    assert::[l == [ [ 1 2 3 ] [ 4 5 6 ] [ 7 8 9 ] [ 10 ] ]]
  }

  // std listSplice
  {
    assert::[listSplice::[[ 0 1 2 3 ] 1 2] == [ 0 3 ]]
    assert::[listSplice::[[ 0 1 2 3 ] 1 2 [ "Wow1" "Wow2" ]] == [ 0 "Wow1" "Wow2" 3]]
  }

  // std listSlice
  {
    assert::[listSlice::[[ 0 1 2 3 4 5 ] 2] == [ 2 3 4 5 ]]
    assert::[listSlice::[[ 0 1 2 3 4 5 ] 2 4] == [ 2 3 ]]
  }

  // std listLength
  {
    assert::[listLength::[[ 0 1 2 3 4 ]] == 5]
  }

  // std listIndexOf
  {
    assert::[listIndexOf::[[ 0 1 2 3 ] 2] == 2]
    assert::[listIndexOf::[[ 0 1 2 3 ] 4] == -1]
  }

  // std listIncludes
  {
    assert::[listIncludes::[[ 0 1 2 3 ] 2] == true]
    assert::[listIncludes::[[ 0 1 2 3 ] 4] == false]
    assert::[listIncludes::[[ 0 1 [ "a" "b" ] 3 ] [ "a" "b" ]] == true]
    assert::[listIncludes::[[ 0 1 [ "a" "b" ] 3 ] [ "a" "c" ]] == false]
  }

  // std listFindIndex
  {
    assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item] item % 2 == 0] == 3]
    assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item index] index == 4] == 4]
    assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item] false] == -1]
  }

  // std listFilter
  assert::[listFilter::[[ 1 2 3 4 5 6 7 8 ] function[number a] a % 2 == 0] == [ 2 4 6 8 ]]

  // std math*
  assert::[mathMin::[ 10 1 2 3 4 ] == 1]
  assert::[mathMax::[ 1 2 3 4 0 ] == 4]
  assert::[mathFloor::[ 11.8 ] == 11]
  assert::[mathFloor::[ -11.8 ] == -12]
  assert::[mathCeil::[ 11.8 ] == 12]
  assert::[mathCeil::[ -11.8 ] == -11]

  // std string*
  assert::[stringLength::["Hello~"] == 6]
  assert::[stringFormatNumberFixed::[1.235 2] == "1.24"]
  assert::[stringFormatNumberFixed::[1.235 4] == "1.2350"]
  assert::[stringFormatNumberFixed::[1.235 6] == "1.235000"]
  assert::[stringFormatNumberPrecision::[1.235 2] == "1.2"]
  assert::[stringFormatNumberPrecision::[1.235 4] == "1.235"]
  assert::[stringFormatNumberPrecision::[1.235 6] == "1.23500"]
  assert::[stringFormatNumberPrecision::[1234 2] == "1.2e+3"]
  assert::[("test1,test2" |> stringSplit.:[","]) == ["test1" "test2"]]
  assert::[(",test1,,test2," |> stringSplit.:[","]) == ["" "test1" "" "test2" ""]]
  assert::[stringSubByLength::["0123456789" 3 2] == "34"]
  assert::[stringSubByLength::["0123456789" 3] == "3456789"]
  assert::[stringSubByLength::["0123" 0 0] == ""]
  assert::[stringSubByLength::["0123" 0 4] == "0123"]
  assertError::[$ stringSubByLength::["0123" -1 2]]
  assertError::[$ stringSubByLength::["0123" 0 5]]
  assertError::[$ stringSubByLength::["0123" 5]]
  assert::[stringSubByIndex::["0123456789" 3 6] == "345"]
  assert::[stringSubByIndex::["0123456789" 3] == "3456789"]
  assert::[stringSubByIndex::["0123" 0 0] == ""]
  assert::[stringSubByIndex::["0123" 4 4] == ""]
  assert::[stringSubByIndex::["0123" 0 4] == "0123"]
  assertError::[$ stringSubByIndex::["0123" -1 2]]
  assertError::[$ stringSubByIndex::["0123" 1 5]]
  assertError::[$ stringSubByIndex::["0123" 2 1]]

  // Partial application
  assert::[([ 0 1 2 3 ] |> listSet.:[2 "Wow"]) == [ 0 1 "Wow" 3 ]]
  assert::[("Wow" |> listSet:.[[ 0 1 2 3 ] 2]) == [ 0 1 "Wow" 3 ]]
  assert::[listSet == listSet.:[]]
  assert::[listSet == listSet:.[]]
  assert::[listSet != listSet.:[1]]
  assert::[listSet != listSet:.[1]]
  assert::[listSet.:[1] == listSet.:[1]]
  assert::[listSet:.[1] == listSet:.[1]]
  assert::[listSet:.[1] != listSet.:[1]]
  assert::[listSet.:[1] != listSet.:[2]]
  assert::[listSet:.[1] != listSet:.[2]]
  assert::[listSet:.[[ 0 1 2 3 ]].:["Wow"]:.[2]::[] == [ 0 1 "Wow" 3 ]]
  assert::[listSet.:["Wow"].:[2]:.[[ 0 1 2 3 ]]::[] == [ 0 1 "Wow" 3 ]]

  // Function shorthand
  {
    declare function fn = $100
    declare function add = $[a b] a + b
    declare function addTyped = $[number a number b] a + b
    assert::[fn::[] == 100]
    assert::[add::[1 2] == 3]
    assert::[addTyped::[1 2] == 3]
    declare number counter = 100
    declare function closure = $counter
    assert::[closure::[] == 100]
    counter = 300
    assert::[closure::[] == 300]
  }

  assert::[([ 1 2 3 4 5 ] |> listMap.:[$[v] v ** 2]) == [ 1 4 9 16 25 ]]

  // While loop
  {
    declare number i = 0
    while i < 10 {
      i += 1
    }
    assert::[i == 10]
  }
  {
    declare number i = 0
    do {
      i += 1
    } while i < 10
    assert::[i == 10]
  }
  {
    declare number i = 0
    declare number j = 0
    do {
      i += 1
    } while i < 10 then {
      j += 1
    }
    assert::[i == 10]
    assert::[j == 9]
  }

  // While loop with break
  {
    declare number i = 0
    declare number result = while i < 100 {
      (i < 50) ? null : {
        break 1000
      }
      i += 1
    }
    assert::[result == 1000]
    assert::[i == 50]
  }
  {
    declare number i = 0
    declare number result = while i < 100 {
      (i > 50) ? null : {
        break = i
      }
      i += 1
    }
    assert::[result == 50]
    assert::[i == 100]
  }

  // While loop with continue
  {
    declare number i = 0
    declare number j = 0
    while i < 100 {
      i += 1
      (i > 50) ? null : {
        continue
      }
      j += 1
    }
    assert::[j == 50]
    assert::[i == 100]
  }
  {
    declare number i = 0
    declare number j = 0
    do {
      i += 1
      (i > 50) ? null : {
        continue
      }
      j += 1
    } while i < 100
    assert::[j == 50]
    assert::[i == 100]
  }
  {
    declare number i = 0
    declare number j = 0
    do {
      i += 1
    } while j < 10 then {
      j += 1
      continue
    }
    assert::[i == 1]
    assert::[j == 10]
  }

  // Function switch
  {
    declare function normal = function switch [
      [ 1 ] 100
      [ 2 3 ] 200
      300
    ]
    assert::[normal::[1] == 100]
    assert::[normal::[2] == 200]
    assert::[normal::[3] == 200]
    assert::[normal::[4] == 300]

    declare function sugar switch [
      [ 1 ] 100
      [ 2 3 ] 200
      300
    ]
    assert::[sugar::[1] == 100]
    assert::[sugar::[2] == 200]
    assert::[sugar::[3] == 200]
    assert::[sugar::[4] == 300]
  }

  // If
  assert::[(if 5 > 3 100) == 100]
  assert::[(if 5 > 8 100) == null]
  assert::[(if 5 > 3 100 else 200) == 100]
  assert::[(if 5 > 8 100 else 200) == 200]
  assert::[(if (5 > 3) {
    yield 100
  } else {
    yield 200
  }) == 100]

  // ObJeCt OrIeNtEd PrOgRaMmInG
  {
    declare function Person[string name number age] function switch [
      #getName name
      #getAge age
      #setName $[string newName] name = newName
      #setAge $[number newAge] age = newAge
      #info name + ": " + stringFormatNumberFixed::[age 0]
    ]
    declare function a = Person::["A" 20]
    assert::[a::#getName == "A"]
    assert::[a::#getAge == 20]
    assert::[a::#info == "A: 20"]
    a::#setName::["A2"]
    a::#setAge::[40]
    assert::[a::#getName == "A2"]
    assert::[a::#getAge == 40]
    assert::[a::#info == "A2: 40"]
  }

  // Optional Invocation
  {
    declare function sum = $[number a number b] a + b
    assert::[sum?::[1 2] == 3]
    assert::[null?::[1 2] == null]
  }

  timeEnd::[]
} then exit

section dummy1 then exit
section dummy2 then exit

---<<< main >>>---
# 测试
如果你看到这行字那么就代表所有测试都通过了。

path: chapters/1.10 - META/1 - WTCD/0 - 例子/11 - 测试.wtcd


code:
WTCD 1.3
declare number x = 0
declare number y = 0
section init {
  canvasCreate::["a" 400 200]
  canvasSetFillStyle::["a" "rgba(255,0,0,0.5)"]
} then goto main
section main {
  canvasClear::["a"]
  canvasFillRect::["a" x y 20 20]
  canvasOutput::["a"]
} then selection [
  choice "x += 50" $ { x += 50 return goto main }
  choice "x -= 50" $ { x -= 50 return goto main }
  choice "y += 50" $ { y += 50 return goto main }
  choice "y -= 50" $ { y -= 50 return goto main }
]

repo_name: de/wearable

path: chapters/1.10 - META/1 - WTCD/0 - 例子/12 - Flow 模式 Canvas.wtcd

license: unknown


preferredReader: game

path: chapters/1.10 - META/1 - WTCD/0 - 例子/13 - Game 模式 Canvas.meta.json


code:
WTCD 1.3
declare number x = 0
declare number y = 0
section init {
  canvasCreate::["a" 2000 200]
} then goto main
section main {
  canvasClear::["a"]
  canvasPutImage::["a" "./image.png" x y]
  canvasOutput::["a"]
} then selection [
  choice "x += 100" $ { x += 100 return goto main }
  choice "x -= 100" $ { x -= 100 return goto main }
  choice "y += 100" $ { y += 100 return goto main }
  choice "y -= 100" $ { y -= 100 return goto main }
]

path: chapters/1.10 - META/1 - WTCD/0 - 例子/13 - Game 模式 Canvas.wtcd


code:
WTCD 1.3
section init {
  canvasCreate::["id" 800 100]
  canvasSetFont::["id" 32 "googleFonts:ZCOOL KuaiLe"]
  canvasSetStrokeStyle::["id" "rgba(0,0,0,0.8)"]
  canvasSetLineWidth::["id" 6]
  canvasStrokeText::["id" "小朋友们大家好，还记得...（wow）" 10 10 "left" "top"]
  canvasSetFillStyle::["id" "white"]
  canvasFillText::["id" "小朋友们大家好，还记得...（wow）" 10 10 "left" "top"]
  canvasOutput::["id"]
} then exit

path: chapters/1.10 - META/1 - WTCD/0 - 例子/14 - Canvas 文字.wtcd


code:
WTCD 1.3
section main {
  canvasCreate::["id" 500 500]
  canvasPutImage::["id" "./image.png" 0 0]
  canvasPutImage::["id" "./image.png" 0 100 100 100]
  canvasPutImagePart::["id" "./image.png" 10 0 30 30 0 200]
  canvasPutImagePart::["id" "./image.png" 10 0 30 30 0 300 100 100]
  canvasOutput::["id"]
} then exit

path: chapters/1.10 - META/1 - WTCD/0 - 例子/15 - canvasPutImage.wtcd


code:
WTCD 1.3
declare [
  boolean vibrator = false
]
section start then selection [
  choice "按下开关" goto press
  choice "不按" goto doNotPress
]
section press {
  vibrator = true
} then goto walkHome
section doNotPress then goto walkHome
section walkHome then vibrator
  ? goto czpClimax
  : goto czpGoToGrandmasHouse
section czpClimax then exit
section czpGoToGrandmasHouse then exit

---<<< start >>>---
# 稍微复杂点的涉及粉红色遥控器的例子
面对少女的背影，手握粉红色遥控器的你，是否要按下开关呢？

---<<< press >>>---
想到手中遥控器能给少女带来的快乐，心善的你毫不犹豫地按下了开关。

然而，出乎意料的是少女似乎丝毫没有被影响。

“可能是没电了吧...”

---<<< doNotPress >>>---
“还是不了吧...”

---<<< walkHome >>>---
这样想着，你把遥控器放进了裤子口袋里，朝家中走去。

---<<< czpClimax >>>---
突然，你远远地看到路边有一只陈志鹏正扶着墙，一脸红晕地走着。你突然明白了什么，然后愤怒地把遥控器调到了最大。

---<<< czpGoToGrandmasHouse >>>---
路上经过了一只陈志鹏。你还没来得及向他打招呼，他就说道：

“好，我要去奶奶家了。”

一脸懵逼的你只得继续向前走着。

path: chapters/1.10 - META/1 - WTCD/0 - 例子/2 - 稍微复杂点的涉及粉红色遥控器的例子.wtcd


code:
WTCD 1.0
section main then goto [ a1 b1 ]

section a1 then goto a2
section a2 then null

section b1 then goto b2
section b2 then null

---<<< main >>>---
# Goto 入栈
## main

---<<< a1 >>>---
## a1

---<<< a2 >>>---
## a2

---<<< b1 >>>---
## b1

---<<< b2 >>>---
## b2

path: chapters/1.10 - META/1 - WTCD/0 - 例子/3 - Goto 入栈.wtcd


code:
WTCD 1.1

declare number mGetName = 0
declare number mIsMale = 1
declare number mSetName = 2
declare number mSetIsMale = 3

declare function newPerson[string name boolean isMale] {
  return function[number method] switch method [
    [mGetName] name
    [mIsMale] isMale
    [mSetName] function[string newName] name = newName
    [mSetIsMale] function[boolean newIsMale] isMale = newIsMale
  ]
}

section main {
  declare function person = newPerson::["czp" true]
  print::[person::[mGetName] person::[mIsMale]]

  person::[mSetName]::["Rin"]
  person::[mSetIsMale]::[false]

  print::[person::[mGetName] person::[mIsMale]]
} then exit

---<<< main >>>---
# 面向对象（大雾）
利用引用闭包可以实现假的面向对象。

按 F12 打开浏览器控制台以查看运行结果。

path: chapters/1.10 - META/1 - WTCD/0 - 例子/4 - 面向对象（大雾）.wtcd


code:
WTCD 1.1

section main {
  // 包裹一个函数，返回一个新的函数。新的函数被调用时会打印指定的内容，并调用原先的函数。
  declare function withPrint[string msg] function[function callback] function[...args] {
    print::["开始" + msg]
    return = callback::args
    print::["结束" + msg]
  }
  declare function square[number n] n * n
  declare function wrapped = square |> withPrint::["【square 函数】"]
  print::[wrapped::[16]]

  declare function wrapped2 = square |> withPrint::["【同一个 square 函数】"]
  print::[wrapped2::[12]]
} then exit

---<<< main >>>---
# 高阶函数工厂
一个函数被调用后返回一个高阶函数的例子。

工厂是指传入一个非函数，返回一个函数的函数。高阶函数是指传入一个函数，返回一个调用时会调用传入函数的函数。

按 F12 打开浏览器控制台以查看运行结果。

path: chapters/1.10 - META/1 - WTCD/0 - 例子/5 - 高阶函数工厂.wtcd


code:
WTCD 1.1
section main then selection [
  choice "1. a" selection [
    choice "2. a" goto aa
    choice "2. b" goto ab
  ]
  choice "1. b" selection [
    choice "2. a" goto ba
    choice "2. b" goto bb
  ]
]

section aa then exit
section ab then exit
section ba then exit
section bb then exit

---<<< main >>>---
# 选项嵌套

---<<< aa >>>---
## aa

---<<< ab >>>---
## ab

---<<< ba >>>---
## ba

---<<< bb >>>---
## bb

path: chapters/1.10 - META/1 - WTCD/0 - 例子/6 - 选项嵌套.wtcd


code:
WTCD 1.1
section main {
  contentAddHeader::["动态生成内容演示"]
  contentAddHeader::["段落" 2]
  contentAddParagraph::["Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ultrices aliquet elit sed posuere. Phasellus accumsan felis massa, eu ultrices arcu hendrerit in."]

  contentAddHeader::["图片" 2]
  contentAddImage::["./chapters/META/WTCD/例子/image.png"]

  contentAddHeader::["无序列表" 2]
  contentAddUnorderedList::["一个东西" "另一个东西" "剩下一个东西"]

  contentAddHeader::["有序列表" 2]
  contentAddOrderedList::["第 1 个东西" "第 2 个东西" "第 3 个东西"]

  contentAddHeader::["表格" 2]
  contentAddTable::[
    [ "名字" "年龄" "性别" ]
    [ "琳"   "18"   "女"   ]
    [ "czp"  "？"   "男"   ]
  ]
} then exit

path: chapters/1.10 - META/1 - WTCD/0 - 例子/7 - 动态生成内容.wtcd


code:
WTCD 1.2

declare number selected

section main then selection listCreateFilled::[10] |> listMap.:[$[_ index] choice stringFormatNumberFixed::[index] ${
  selected = index
  yield goto finish
}]

section finish then exit

---<<< main >>>---
# 函数 Action 演示

---<<< finish >>>---
选择了 <$ selected $>。

path: chapters/1.10 - META/1 - WTCD/0 - 例子/8 - 函数 Action.wtcd


code:
WTCD 1.3

declare number a
section start {
  readerSetPinned::[$[] {
    a += 1
    contentAddHeader::["阿松大！" + stringFormatNumberFixed::[a]]
  }]
} then selection [
  choice "Next" goto a
]
section a then selection [ choice "Next" goto b ]
section b then selection [ choice "Next" goto c ]
section c then exit

---<<< start >>>---
# Start

---<<< a >>>---
# a

---<<< b >>>---
# b

---<<< c >>>---
# c

path: chapters/1.10 - META/1 - WTCD/0 - 例子/9 - Pinned.wtcd


code:
# 1. 概述
## 1.1 什么是 WTCD
WTCD 全名 Wearable Technology's Conditional Document，是琳为《可穿戴科技》项目设计的分支引擎所使用的语言，允许作者非常容易地编写可互动的（带有分支选项的）小说。设计之初是为了满足帕蒂卡的小说的分支需要，之后就发展成了一门完整的语言。

WTCD 是一门“四分之一”编译的语言 —— WTCD 的编译器会将源文件（`.wtcd`） 解析（parse）成 JSON 格式的 AST，然后由解释器执行（interpret）。

WTCD 是一门不是很弱类型的语言 —— 每一个变量都有指定的类型并且不能存储类型不符合的值。然而 WTCD 的编译器并不会进行类型推断，因此类型错误只会在运行时被丢出。

在 WTCD 中，通常来说代码逻辑和具体小说内容是分开的，以避免在代码之间混入大量影响理解逻辑的小说正文内容。

与大多数语言不同的是。WTCD 是一个没有分隔符的语言 —— 这意味着在 WTCD 中每一个表达式或是申明之后不需要添加分号，甚至在列表的元素之间也不需要添加逗号。需要注意的是，**没有分割符并不代表用换行作为分割**，恰恰相反，对于 WTCD 来说，换行的作用和空格或是 TAB 是完全一致的（除了单行注释，带有换行的字符串以及用 Markdown 编写的正文） —— 多个表达式之间使用空格分割或者换行分割都是可行的，因此 **WTCD 的语句之间（虽然建议，但是）不需要添加换行**。

## 1.2 Hello World!
下面是 WTCD 的 Hello World!：

```wtcd
WTCD 1.3
section helloWorld then exit

---<<< helloWorld >>>---
# Hello World!
```

[执行效果](./例子/Hello-World.html)

## 1.3 一个更~~色情~~复杂的例子
Hello World! 完全无法演示 WTCD 的任何功能，因此这里给出一个稍微复杂一些的例子：

```wtcd
WTCD 1.3
declare [
  boolean vibrator = false
]
section start then selection [
  choice "按下开关" goto press
  choice "不按" goto doNotPress
]
section press {
  vibrator = true
} then goto walkHome
section doNotPress then goto walkHome
section walkHome then vibrator
  ? goto czpClimax
  : goto czpGoToGrandmasHouse
section czpClimax then exit
section czpGoToGrandmasHouse then exit

---<<< start >>>---
# 稍微复杂点的涉及粉红色遥控器的例子
面对少女的背影，手握粉红色遥控器的你，是否要按下开关呢？

---<<< press >>>---
想到手中遥控器能给少女带来的快乐，心善的你毫不犹豫地按下了开关。

然而，出乎意料的是少女似乎丝毫没有被影响。

“可能是没电了吧...”

---<<< doNotPress >>>---
“还是不了吧...”

---<<< walkHome >>>---
这样想着，你把遥控器放进了裤子口袋里，朝家中走去。

---<<< czpClimax >>>---
突然，你远远地看到路边有一只陈志鹏正扶着墙，一脸红晕地走着。你突然明白了什么，然后愤怒地把遥控器调到了最大。

---<<< czpGoToGrandmasHouse >>>---
路上经过了一只陈志鹏。你还没来得及向他打招呼，他就说道：

“好，我要去奶奶家了。”

一脸懵逼的你只得继续向前走着。
```

[执行效果](./例子/稍微复杂点的涉及粉红色遥控器的例子.html)

## 1.5 开始编写 WTCD
尽管 WTCD 被设计为一个独立的语言，在目前阶段，WTCD 和《可穿戴科技》是捆绑在一起的。若要下载安装使用 WTCD 的编译器和解析器，你需要先安装 [Node.js](https://nodejs.org/) 和 [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)。然后，你需要使用 git 将《可穿戴科技》的 repo 克隆下来：

```
git clone https://github.com/SCLeoX/Wearable-Technology.git
```

克隆完成后，你需要在克隆下来的 repo 文件夹内使用 npm 进行安装并运行脚本 `watch`：

```
npm install
npm run watch
```

如果一切顺利，该脚本会自动打开一个浏览器窗口并显示你本地版本的《可穿戴科技》。

此时，你可以开始编写 WTCD 文档了。本教程用到的所有例子的源代码都在 `chapters/5 - Meta/1 - WTCD/1 - 例子` 中。你可以阅读并修改它们。若要创建新的文档，在 `chapters` 文件夹内创建一个文件名符合《可穿戴科技》标准，后缀为 `.wtcd` 的文件就可以了（格式为 `<显示序号> - <显示名>.wtcd`，例如 `233 - 哈哈.wtcd`）。当你修改任何 `chapters` 文件夹中的文件时，之前运行的 `watch` 脚本会自动重新编译你的修改，因此此时无需运行任何命令，只需要刷新浏览器（你可能需要使用 ctrl+F5 来强制刷新）就可以看到修改后的版本了。

另外，若要编写 WTCD 文档，我**强烈建议**你使用 [Visual Studio Code](https://code.visualstudio.com/) 并安装 [WTCD 的语法高亮插件](https://marketplace.visualstudio.com/items?itemName=WTCD.vscode-wtcd)。如果你不知道的话，Visual Studio Code 是微软写的*基本开源*轻量编辑器，非常好用。同时，WTCD 目前只提供 Visual Studio Code 的语法高亮插件，而有语法高亮可以显著提升你的编辑体验。

## 1.6 版本声明
每一个 WTCD 文档的开头都是该 WTCD 文档编写时使用 WTCD 版本。其格式如下：

```wtcd
WTCD 1.3
```

目前版本是 1.3 所以请务必使用 1.3 作为版本。

WTCD 的版本使用 2/3 Semantic Versioning：

- 版本号由两个数字组成，中间用点连接，例如：`42.2333`
- 点前面的数字是 Major Version（42）
- 点后面的数字是 Minor Version（2333）
- Breaking Change（会导致之前编写的代码无法运行的）会递增 Major Version
  - 这包括添加新的保留字或是修改现有操作符的优先级等
- 增加向后兼容的功能会递增 Minor Version
  - 这包括添加新的操作符等
- Bug Fixes 不修改版本号

为了保证代码简介，之后的例子中将不会包含版本声明。

## 1.7 注释
WTCD 允许作者在单行注释和多行注释。

```wtcd
// 单行注释例子
/*
多行
注释
例子
*/
```

单行注释以 `//` 开始持续到当前行末，多行注释则以 `/*` 开始持续到 `*/`。注释内的任何内容都不会被读取。

WTCD 不支持多行注释嵌套。

path: chapters/1.10 - META/1 - WTCD/1 - 1. 概述.md


code:
# 2. 变量、表达式及语句
## 2.1 概述
WTCD 允许定义并使用变量，运算表达式，执行语句。

## 2.2 值的类型
WTCD 有以下值类型：

- `number`：数字，支持正负和小数
- `boolean`：布尔值，只能是 `true` 或者 `false`
- `string`：字符串
- `null`：空值
- `action`： 表示一个需要可执行的操作（`goto`，`exit`，或 `selection`）
- `choice`：表示一个选项（其中记录了这个选项的文字和被选择后执行的操作）
- `list`：存储一个列表（详见第 4 章）
- `function`：存储一个函数（详见第 5 章）

本章节将会跳过 `list` 和 `function`。

## 2.3 变量
WTCD 允许定义定义和使用变量。每个变量都有固定的类型，其中可使用的有上述每一个除了 `null` 以外的类型。

### 2.3.1 定义变量
若要定义变量，则需要使用 `declare` 语句 。

```
declare <declaration>
```

与其他语句不同的是，`declare` 语句除了可以出现在**语句组**（见 2.4.3）内以外，`declare` 还可以和 `section` 的逻辑块（见 3.2）平级。

`<declaration>` 可以是单个定义或是用方括号包裹的若干个定义。每个定义采用如下格式：

```
<变量类型> <变量名> [= <初始值>]
```

#### 2.3.1.1 单一定义
举个例子：

```
declare number a = 100
declare boolean b = false
```

这两个 `declaration` 分别定义了一个类型为 `number`，初始值为 `100` 的变量 `a`，和一个类型为 `boolean`，初始值为 `false` 的变量 `b`。

#### 2.3.1.2 多个定义
上述例子中的两个定义也可以合并成一个：

```
declare [
  number a = 100
  boolean b = false
]
```

#### 2.3.1.3 代码丑化（简称请不要这么做）
因为换行在 WTCD 中与空格的作用几乎一致，这个定义也可以写成：

```
declare [ number a = 100 boolean b = false ]
```

其中大多数空格甚至能被省略：

```
declare[number a=100boolean b=false]
```

Just because you can doesn't mean you should. 尽管换行和许多空格是可选的，但是为了可读性，强烈建议不要忽略它们。

#### 2.3.1.4 自动初始化
与此同时，类型为 `number`，`boolean`，`string` 的变量的初始化可以被省略。如果省略，它们将会被自动初始化为 `0`，`false`，和 `""`（空字符串）。

因此，如下两组定义效果是一致的。

```
declare [
  number a = 0
  boolean b = false
  string c = ""
]
```

```
declare [
  number a
  boolean b
  string c
]
```

### 2.3.2 变量的赋值
变量几乎可以在所有需要值的场合使用。这里先介绍如何改变变量的值。

给出如下定义：

```
declare number a
```

那么在需要赋值的时候就可以写：

```
a = 100
```

这个操作就会把 `a` 的值设置为 100。

当然，等号右侧也可以换成一个表达式：

```
a = a + 50
```

这个操作就会让 `a` 的值自增 50。不过这个操作可以用 `+=` 操作符代替：

```
a += 50
```

具体表达式有哪些会在 **2.4 表达式** 中介绍。

这里需要注意的是，WTCD 的解释器在运行时会检查赋值给某个变量的值是否和这个变量的类型相同。举个例子，如下代码就会在运行时报错：

```
declare number a
a = true // WTCDError: Variable "a" can only hold values of type number. Received boolean false.
```

### 2.3.3 变量的作用域
WTCD 使用[静态作用域](https://zh.wikipedia.org/wiki/%E4%BD%9C%E7%94%A8%E5%9F%9F)（[Lexical Scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping)）。每一个由大括号包裹的**语句组**（见 2.4.3）将组成一个新的作用域。

每个定义的变量的作用域从定义位置开始一直延伸到当前作用域结束。最外层定义的变量是全局变量。

举个例子：

```wtcd
declare number outside = 10

{
  declare number inside = 20
  outside // 10
  inside // 20
}

outside // 10
inside // WTCDError: Cannot locate lexical scope for variable "inside".
```

```wtcd
someVariable // WTCDError: Cannot locate lexical scope for variable "someVariable".
declare number someVariable = 100
```

内层作用域定义的变量会暂时覆盖（shadow）外层作用域定义的同名变量：

```wtcd
declare number someVariable = 100
{
  declare boolean someVariable = false
  someVariable // false
}
someVariable // 100
```

在同一作用域内不能定义同名变量：

```wtcd
declare number hey = 200
declare number hey = 300 // WTCDError: Variable "hey" has already been declared within the same lexical scope.
```

## 2.4 表达式
表达式就是由能够表达一个值的式子。简单的表达式由变量，数字，或者操作符组成。而复杂的表达式可以是一个**语句组**。

### 2.4.1 常量表达式（Literal Expression）
常量表达式，就是表示常量的表达式。在 WTCD 中，常量表达式有 4 种：数字，布尔值，字符串，null。

数字，布尔值，和 null 都很好理解：

```wtcd
// 数字常量表达式
100
12.7  // 支持小数
.68   // 0. 开头的数字中的 0 可以省略
(-20) // 这个其实不是数字常量表达式，而是一个一元运算符表达式，但是为了方便理解，这里也列出来了

// 布尔值常量表达式
true
false

// null 常量表达式
null // 只能是 null
```

WTCD 的字符串常量表达式稍微复杂一些。WTCD 允许使用双引号（`"`），单引号（`'`），或者反引号（`` ` ``）来包裹字符串：

```wtcd
"Hello"
'World'
`Test`
```

WTCD 的字符串常量表达式支持使用反斜杠（`\`）进行 escape：

```wtcd
"\n" // -> 换行
"\t" // -> TAB
"\'" // -> 单引号
"\"" // -> 双引号
"\`" // -> 反引号
"\\" // -> 反斜杠
```

同时 WTCD 支持跨行字符串常量表达式：

```wtcd
"This is a
poem." // -> "This is a\npoem"
```

### 2.4.2 操作符表达式
WTCD 允许使用操作符来操作不同的值。这里先给几个例子：

```wtcd
1 + 1         // -> 2
1 + 2 * 3     // -> 7（乘法优先级更高）
(1 + 2) * 3   // -> 9（括号优先运算）
2 ** 3        // -> 8（指数）
2 ** 3 ** 2   // -> 64（注意，WTCD 的指数是左结合的）
"a" + "b"     // -> "ab"（字符串拼接）
4 < 10        // -> true
5 >= 5        // -> true
3 == 3        // -> true
3 == "3"      // -> false（无自动类型转换）
3 != 3        // -> false
5 / 2         // -> 2.5（number 可存储小数）
5 ~/ 2        // -> 2（truncate 整除）
-5 ~/ 2       // -> -2（truncate 整除永远都往零靠）
5 % 2         // -> 1（求余）
-10           // -> -10
!false        // -> true
true ? 1 : 2  // -> 1（三元/逻辑选择）
false && true // -> false
false || true // -> true
a = 3         // -> a 变成 3（变量赋值）
a += 5        // -> a 加上了3（变量累加）
```

完整的操作符请参见下表（并不需要全部记住）：

| 元数 | 操作符 | 优先级 | 作用                       | 例子                                                  |
| ---- | ------ | ------ | -------------------------- | ----------------------------------------------------- |
| 2    | ::     | 20     | 函数调用                   | `mathMax::[1 2 3]` = 3                                |
| 2    | ?::    | 20     | 可选函数调用               | `mathMax?::[1 2 3]` = 3, `null?::[1 2 3]` = null      |
| 2    | .:     | 20     | 右起偏函数调用             | `[ 0 1 2 3 ] |> listSet.:[2 "Wow"]` = [ 0 1 "Wow" 3 ] |
| 2    | :.     | 20     | 左起偏函数调用             | `"Wow" |> listSet:.[[ 0 1 2 3 ] 2]` = [ 0 1 "Wow" 3 ] |
| 2    | .      | 19     | List 成员访问              | `[ 0 1 2 3 ].(2)` = 2，`[ 0 1 2 3 ].(4)` = 报错       |
| 2    | .?     | 19     | 越界即空 List 成员访问     | `[ 0 1 2 3 ].(2)` = 2，`[ 0 1 2 3 ].?(4)` = null      |
| 2    | ?.     | 19     | 可选 List 成员访问         | `null?.(4)` = null，`[ 0 1 2 3 ]?.(4)` = 报错         |
| 2    | ?.?    | 19     | 可选越界即空 List 成员访问 | `null?.(4)` = null，`[ 0 1 2 3 ]?.?(4)` = null        |
| 1    | -      | 17     | 取负                       | `-42` = -42                                           |
| 1    | !      | 17     | 取逻辑反                   | `!true` = false, `!false` = true                      |
| 2    | **     | 16     | 指数（左结合）             | `2 ** 3` = 8, `2 ** 3 ** 2` = 64                      |
| 2    | *      | 15     | 数字乘法                   | `5 * 10` = 50                                         |
| 2    | /      | 15     | 数字除法                   | `10 / 5` = 2, `5 / 2` = 2.5                           |
| 2    | ~/     | 15     | Truncate 整除              | `5 ~/ 3` = 1, `-5 ~/ 3` = -1                          |
| 2    | %      | 15     | 求余                       | `5 % 2` = 1, `-9 % 5` = -4                            |
| 2    | +      | 14     | 数字加法或字符串拼接       | `5 + 3` = 8, `"a" + "b"` = "ab"                       |
| 2    | -      | 14     | 数字减法                   | `5 - 3` = 2, `3 - 5` = -2                             |
| 2    | <      | 12     | 判断是否小于               | `3 < 5` = true, `5 < 5` = false, `7 < 5` = false      |
| 2    | <=     | 12     | 判断是否小于或等于         | `3 <= 5` = true, `5 <= 5` = true, `7 <= 5` = false    |
| 2    | >      | 12     | 判断是否大于               | `3 > 5` = false, `5 > 5` = false, `7 > 5` = true      |
| 2    | >=     | 12     | 判断是否大于或等于         | `3 >= 5` = false, `5 >= 5` = true, `7 >= 5` = false   |
| 2    | ==     | 11     | 判断是否相等               | `5 == 5` = true, `5 == 3` = false, `"5" == 5` = false |
| 2    | !=     | 11     | 判断是否不相等             | `5 != 5` = false, `5 != 3` = true, `"5" != 5` = true  |
| 2    | &&     | 7      | 逻辑与                     | `false || true` = false, `true || true` = true        |
| 2    | \|\|   | 6      | 逻辑或                     | `false || true` = true, `false || false` = true       |
| 2    | ?!     | 6      | 不可空提供默认值           | `2 ?! 3` = 2, `null ?! 3` = 3, `null ?! null` = 报错  |
| 2    | ??     | 6      | 提供默认值                 | `2 ?? 3` = 2, `null ?? 3` = 3, `null ?? null` = null  |
| 2    | \|>    | 5      | 管道调用                   | `"Hello" |> stringLength` = 5                         |
| 2    | \|::   | 5      | 逆向调用                   | `[1 2 3] |:: max` = `max::[1 2 3]`                    |
| 3    | ? :    | 4      | 逻辑选择                   | `true ? 1 : 2` = 1, `false ? 1 : 2` = 2               |
| 2    | =      | 3      | 赋值                       | `a = 10` 把 10 赋值给 a，然后返回 10                  |
| 2    | +=     | 3      | 数字/字符串累加            | `a += 10` = `a = a + 10`, `b += "a"` = `b = b + "a"`  |
| 2    | -=     | 3      | 数字累减                   | `a -= 10` = `a = a - 10`                              |
| 2    | *=     | 3      | 数字累乘                   | `a *= 10` = `a = a * 10`                              |
| 2    | /=     | 3      | 数字累除                   | `a /= 10` = `a = a / 10`                              |
| 2    | ~/=    | 3      | 数字累 Truncate 除         | `a ~/= 10` = `a = a ~/ 10`                            |
| 2    | %=     | 3      | 数字累求余                 | `a %= 10` = `a = a % 10`                              |

### 2.4.3 语句组表达式
语句组表达式允许你在一个需要表达式的地方提供一系列的语句。你需要用大括号（`{` 和 `}`）将你的语句包裹起来。

目前，语句可以是：

- 变量定义
- 表达式
- `yield` / `yield =` / `return` / `return =`

其中，变量定义就是定义局部变量。表达式就是计算某个表达式。而 `yield` 则是指定当前语句组的返回值。

举个例子：

```wtcd
declare [
  number a = 1
  number b = 2
  number c = 3
  number squareOfSum
]
squareOfSum = {
  // 定义一个局部变量 sum 用来存储 a，b，c 的和
  declare number sum = a + b + c

  // 将该语句组的返回值设置为 sum * sum
  yield sum * sum

  // 这句话不会执行，因为 yield 会中断当前语句组的执行
  squareOfSum = 66666
}
```

运行后，`squareOfSum` 会变成 36。

除此之外 `yield` 还有一个变种：`yield =`。`yield =` 会设置当前语句组的返回值但是不会中断运行。举个例子：

```
declare [
  boolean changed = false
  number a
]
a = {
  yield = 10
  changed = true
}
```

上述例子运行后，`a` 会变成 10，而 `changed` 会变成 true，因为 `yield = 10` 并不会停止运行。

需要注意的是最后一个执行的 `yield =` 会覆盖之前执行的 `yield =`。此外 `yield` 不是一个变量，因此并不能尝试获得它的值。

path: chapters/1.10 - META/1 - WTCD/2 - 2. 变量、表达式及语句.md


code:
# 3. Section
## 3.1 概述
每个 WTCD 文档都是由若干个 Section 组成的。本章节将会介绍如何定义 section 和互相连接 section。

每一个 Section 都由一个**逻辑块**和任意自然数个**内容块**组成。

比如说，在上一章节给出的例子里就有以下 Sections：

- `start`
- `press`
- `doNotPress`
- `walkHome`
- `czpClimax`
- `czpGotoGrandmasHouse`

这些 Sections 被连接在一起形成如下结构：

![](./sectionDiagram.svg)

## 3.2 Section 逻辑
若要创建一个 Section，**逻辑块**是必不可少的。**逻辑块**决定了当运行到这个 Sections 时在输出正文内容前和输出正文内容后需要做什么。所有的**逻辑块**定义都必须在所有**内容块**之前。

### 3.2.1 逻辑块的基本结构
Section 的定义使用如下结构：

```wtcd
section <Section 名字> [先行表达式] then <后发表达式>
```

举个例子，给出如下定义：

```wtcd
section example {
  a += 1
} then {
  a += 2
}
```

其中 Section 名字是 `example`。其先行表达式为 `{ a += 1 }`，后发表达式为 `{ a += 2 }`。也就是说，当需要进入 `example` 这个 section 时，WTCD 解释器会:

1. 对 `{ a += 1 }` 求值。这个过程会让变量 a 的值加 1。
2. 根据**内容块**的定义输出。（见 3.3）
3. 对 `{ a += 2 }` 求值。这个过程会让变量 a 的值加 2。
4. 根据 `{ a += 2 }` 的返回结果决定接下来怎么做。但是因为 `{ a += 2 }` 没有指定返回值，那么就会采用默认返回值 `null`。在这里，若后发表达式返回值为 `null`，WTCD 就会什么也不做，如果此时 Section 栈（见 3.2.3）为空的话，就会结束运行。

先行表达式是可选的，如果不提供先行表达式，那么就意味着在需要执行先行表达式的时候，什么也不做。

### 3.2.2 逻辑块的互相连接
WTCD 永远从第一个定义的 Section 开始运行。

WTCD 会根据每一个逻辑块的后发表达式运算结果来决定需要做什么。后发表达式的返回值必须是 `action` 或者 `null`（见 3.2.2.2.2）。如果类型是 `action`，那么这个 `action` 就会被执行。如果是 `null`，则会什么也不做。

`action` 目前一共有 3 种：`exit`，`goto`，和 `selection`。下面来逐一介绍。

#### 3.2.2.1 Exit
因为 `exit` 比较简单，那么先介绍 `exit`。

`exit` 被执行时，会立刻终止 WTCD 的运行。举个例子：

```wtcd
section test then exit
```

在这个例子中，无论因为什么原因进入 `section test` 以后，WTCD 都会停止运行。

#### 3.2.2.2 Goto
通常来说 `goto` 也不复杂。因为当 `goto` 后面提供一个 Section 名的时候，执行 `goto` 的作用基本就是跳转到指定的 section：

```wtcd
section a then goto b
section b then exit
```

在这个例子中，当进入并执行 `section a` 之后，WTCD 会自动跳转到 `section b`。因为 `section b` 的 `then` 后面跟的是 `exit`，所以 `section b` 执行后就会停止运行。

然而，复杂的是，`goto` 允许在后面提供多个 Section。`goto` 严格来说应该被理解为“将这些 Sections 以相反顺序入栈”。或者换句话说：“依次执行这几个 Section”。现在举个提供多个 Section 的 `goto` 的例子：

```wtcd
section main then goto [ a1 b1 ]

section a1 then goto a2
section a2 then null

section b1 then goto b2
section b2 then null
```

[执行效果](./例子/Goto-入栈.html)

首先需要先了解，如果 `then` 之后的表达式返回的是 `null`，那么就代表什么也不做。

在上述例子中，可以看到各个 Section 的执行顺序为：`main -> a1 -> a2 -> b1 -> b2`。下面给出原因：

1. WTCD 从第一个 Section 开始运行，所以执行 `main`。
2. `main` 的后发表达式返回 `goto [ a1 b1 ]`，所以入栈 `b1` 和 `a1`。此时栈的状态为：`[ b1 a1 ]`。
3. 从栈中取出最后一项，是 `a1`，因此运行 `a1`。此时栈的状态为：`[ b1 ]`。
4. `a1` 的返回值是 `goto a2`，因此入栈 `a2`。此时栈的状态为：`[ b1 a2 ]`。
5. 从栈中取出最后一项，是 `a2`，因此运行 `a2`。此时栈的状态为：`[ b1 ]`。
6. `a2` 的返回值是 `null`，因此什么也不做。此时栈的状态为：`[ b1 ]`。
7. 从栈中取出最后一项，是 `b1`，因此运行 `b1`。此时栈的状态为：`[ ]`。
8. `b1` 的返回值是 `goto b2`，因此入栈 `b2`。此时栈的状态为：`[ b2 ]`。
9. 从栈中取出最后一项，是 `b2`，因此运行 `b2`。此时栈的状态为：`[ ]`。
10. `b2` 的返回值是 `null`，因此什么也不做。此时栈的状态为：`[ ]`。
11. 栈空，停止运行。

或者，另一个解释方式是，`goto` 在 WTCD 里相当于是“函数调用”。`goto a1` 相当于是调用“函数” `a1`。然而，由于所有的 `goto` 都会在 Section 的最后执行，因此 WTCD 可以在入栈目标函数前出栈，相当于是强制进行了尾递归优化。

如果还是不能理解，这边有一个 JavaScript 的类比函数你可以参考下。

```javascript
function main() {
  console.info('main');
  a1();
  b1();
}
function a1() {
  console.info('a1');
  a2();
}
function a2() {
  console.info('a2');
}
function b1() {
  console.info('b1')
  b2();
}
function b2() {
  console.info('b2');
}
main();
```

#### 3.2.2.3 Selection
除了自动执行固定的 Action 外，WTCD 允许在每一个 Section 后暂停运行，并要求由用户做出选择。若要等待用户作出选择，就需要在 `then` 后提供一个 Selection。

##### 3.2.2.3.1 创建 Selection 和 Choice
若要创建一个 Selection，则需要使用 `selection` 关键词和 `choice` 关键词。

其中，`selection` 使用如下格式：

```wtcd
selection <选项列表>
```

`choice` 用于创建一个选项。若要使用则需要使用如下格式：

```wtcd
choice <选项显示文本> <被选择后执行的 Action>
```

例如：

```wtcd
section start then selection [
  choice "去 A" then goto a
  choice "去 B" then goto b
]
section a then exit
section b then exit
```

在上述例子中，WTCD 会先执行 `start`。随后因为 `then` 之后遇到了 `selection`，脚本会停止运行，并输出两个选项供用户选择。若用户选择了“去 A”，则 Action `goto a` 会被执行，因此会跳转到 `section a`。相反，若用户选择了“去 B”，则 Action `goto b` 会被执行，因此会跳转到 `section b`。

##### 3.2.2.3.2 关于使用 `null`
在 Selection 的选项列表里面，可以提供 `null`。所有这些 `null` 都会被忽略。举个例子：

```wtcd
section main then selection [
  null
  choice "A" goto a
  null
  choice "B" goto b
  null
]
```

和

```wtcd
section main then selection [
  choice "A" goto a
  choice "B" goto b
]
```

是等效的。

至于这个特性有什么用，请看 3.2.2.4。

此外，**需要注意区分的是**，每一个 Choice 的 Action 也可以是 `null`。一旦一个 Choice 的 Action 是 `null`，那么这个按钮就是不可选的按钮。请看下述例子：

```wtcd
section main then selection [
  choice "Choice A" goto a
  choice "Choice B" null // 这个按钮连点都点不了
]
```

#### 3.2.2.4 Action 和 Choice 作为值
这里需要注意的是，Action 和 Choice 都是可以存储的值。它们各自的类型分别是：`action` 和 `choice`。

举个例子：

```wtcd
declare [
  // 定义一个存储 Action 的变量 someAction 并初始化为 goto b
  action someAction = goto b

  // 定义一个存储 Choice 的变量 someChoice1，其被选择后使用的 Action 用变量表示
  choice someChoice1 = choice "choice 1" someAction

  // 定义一个存储字符串的变量 someString
  string someString = "Some text"

  // 定义一个存储 Choice 的变量 someChoice2，choice 的显示文本使用一个涉及之前定义的字符串变量的表达式
  choice someChoice2 = choice someString + " wow!" exit

  // 定义一个存储 Selection 的变量 someSelection，它的选项由之前的变量定义
  action someSelection = selection [ someChoice1 someChoice2 ]
]

// 定义一个 section 的 then 使用变量 someSelection
section main then someSelection
```

因为其实说到底 `action`，`choice`，或者 `selection` 都只不过是和 `number` 或是 `boolean` 一样的值，所以它们也可以被用在表达式里。

举个例子，你可以使用逻辑选择操作符来选择性隐藏一个选项：

```wtcd
section main then selection [
  condition ? choice "text" goto a : null
]
```

上述例子中，选项“text”只有在 `condition` 为 `true` 的时候才会显示。

同时，你也可以选择性禁用一个选项：

```wtcd
section main then selection [
  choice "text" condition ? goto a : null
]
```

上述例子中，选项“text”都会显示，但是只有在 `condition` 为 `true` 的时候才可以被选择。

## 3.3 Section 内容
定义 Section 的内容相比逻辑而言就简单多了 。Section 的内容由**内容块**定义。所有的**内容块**都必须在所有**逻辑块**以下。

### 3.3.1 基本格式
通常来说，定义一个 Section 的固定输出内容非常简单：

```wtcd
section a then b
section b then exit

---<<< a >>>---
# Title
Hello A

---<<< b >>>---
# Another Title
Hello B
```

可以看到，在 `---<<< a >>>---` 以下，`---<<< b >>>---` 以上的就是运行 `a` 时会输出的内容。同时，在 `---<<< b >>>---` 以下的则是在运行 `b` 时输出的内容。需要注意的是，内容支持 Markdown，因此可以输出粗体，链接，图片等等。

### 3.3.2 多个内容块
其实每个 Section 可以对应零到无数个内容块。每一个内容块可以定义“边界”，即在第几次访问的时候这个内容块可能会出现。

若要定义一个边界，则需要在内容块的 Section 名后写上  `@<边界>`。其中边界可以是：

- 一个数字。例如 `main@3` 代表只有在第三次进入 `main` 时，这个内容块才会被使用。
- 一个数字加短横。例如 `main@3-` 代表只有在第三次及以后进入 `main` 时，这个内容块才会被使用。
- 短横加一个数字。例如 `main@-3` 代表只有在第三次及以前进入 `main` 时，这个内容块才会被使用。
- 一个数字，一个短横，再加一个数字。例如 `main@5-10` 代表只有在第五次到第十次进入 `main` 时，这个内容块才会被使用。

如果符合条件的内容块只有一个，那么这个内容块就会被选择。

如果没有符合条件的内容块，则不会输出任何内容。

如果有超过一个的符合条件的内容块，则会随机选择一个作为输出的内容块。

### 3.3.3 插值
WTCD 的内容块允许插入变量。插入格式为：`<$ 变量名 $>`。比如：

```wtcd
declare number money = 100
section main then exit

---<<< main >>>---
你有 <$ money $> 元钱。
```

需要注意的是，插值仅允许使用全局变量。并不允许使用表达式或者局部变量。

path: chapters/1.10 - META/1 - WTCD/3 - 3. Section.md


code:
# 4. Lists
## 4.1 概述
WTCD 允许存储有序不可变列表。其类型名为 `list`。

WTCD 的列表内的元素可以有不同的类型，因此一个长度为 1 的 `list` 可以被视为是一个可以存储任何类型值的 WTCD 变量。

## 4.2 创建新的列表
若要创建一个新的列表，请使用 `[` 和 `]` 包裹这个列表里的元素。两个元素之间仅需要空格或是换行：

```wtcd
declare list a = [ 1 "Hello" 3 ]
```

此外，在定义变量时，若使用的类型是 `list`，并不提供初始值的话，将会默认创建一个空的列表。

```wtcd
declare list empty // []
```

需要注意的是，这里有一个坑。下述代码：

```wtcd
declare list a = [ 1 -2 3 ]
```

会被 WTCD 理解为 `[ (1 - 2) 3 ]`。若要解决这个问题，则需要使用：

```wtcd
declare list a = [ 1 (-2) 3 ]
```

## 4.3 获得列表的一个成员
若要获得列表的一个成员，则需要使用成员访问操作符：`.`，`.?`，`?.`，`?.?`。

### 4.3.1 普通成员访问
通常来说，访问一个成员使用 `.` 操作符：

```wtcd
[ "a0" "a1" "a2" "a3" ] . 2 == "a2"
[ "a0" "a1" "a2" "a3" ].(2) == "a2"
```

由于小数原因，WTCD 不允许直接写 `[ "a0" "a1" "a2" "a3" ].2`。点与数字之间必须要要用空格隔开，或者用括号把数字包起来。

如果要访问的下标（Index）存在变量里，则可以直接写：
```wtcd
declare number index = 2
[ "a0" "a1" "a2" "a3" ].index == "a2"
```

需要注意的是，WTCD 的设计提倡“错误尽早发现”，因此当下标不存在（Index out of bounds）或是不正确时，普通成员访问会直接报错。需要注意的是，报错只会在下标不存在时发生。以下例子会正常返回 `null`。

```wtcd
[ "a0" null "a2" ].(1) == null
```

而这个例子会报错：

```wtcd
[ "a0" "a1" ].(2) // List does not have an element at 2. If return null is desired, use ".?" instead. List contents: list (elements = [string (value = "a0"), string (value = "a1")])
```

### 4.3.2 越界即空成员访问
当然，有时候，确实会希望当下标不存在时返回 `null`。这时候可以使用 `.?` 来访问成员。

```wtcd
[ "a0" "a1" ] .? (2) == null
```

需要注意的是，只有下标越界的时候（`index < 0` 或者 `index >= listLength::[list]`），`.?` 才会返回 `null`。如果提供的下标不是整数依然会报错。（无论如何，你都不应该在下标中使用涉及浮点数运算的操作。）

### 4.3.3 可选成员访问
还有某些时候，列表本身也可能是 `null`。如果希望在这个情况下不报错而是直接返回 `null`，可以使用 `?.`。

```wtcd
null ?. 2 == null
```

需要注意的是，`?.` 是带有 short circuit 的。也就是说，如果左侧计算结果为 `null`，那么右侧的内容根本不会被计算。

### 4.3.4 可选越界即空成员访问
如果凑巧，不但你希望在下标越界时返回 `null`，而且在列表自己是 `null` 的时候也返回 `null`，你可以使用 `?.?`。

```wtcd
null ?.? 2 == null
[ 0 1 2 ] ?.? 3 == null
```

## 4.4 改变列表
简而言之，你不能改变列表。

—— 但是你可以从一个列表创建一个稍稍有点不同的列表。

### 4.4.1 使用标准库函数
最简单的改变列表的方式是使用标准库（见第 6 章）函数（见第 5 章） `listSet`。

```wtcd
declare list a = [ 0 1 2 3 ]
declare list b = listSet::[a 2 "Wow"] // b 是 [ 0 1 "Wow" 3 ]
```

需要注意的是，正如之前所说，`listSet` 不会改变列表，你必须把返回值赋给一个之前的或是另一个变量。

```wtcd
declare list a = [ 0 1 2 3 ]

listSet::[a 2 "Wow"]
// a 依然是 [ 0 1 2 3 ]

a = listSet::[a 2 "Wow"]
// a 现在是 [ 0 1 "Wow" 3 ] 了
```

关于其他改变列表的函数，请前往 `6.1`。

### 4.4.2 使用 `...` 操作符
`...` 允许用一个数组代替一个新的数组的一部分。

举个例子：

```wtcd
declare list inner = [ 3 4 5 ]
declare list outer = [ 1 2 ...inner 6 7 ] // [ 1 2 3 4 5 6 7 ]
```

使用 `...` 可以获得一个在一个列表的基础上加了若干元素的列表。（添加元素）

```
declare list a = [ 1 2 3 ]
a = [ ...a "Back" ] // [ 1 2 3 "Back" ]
a = [ "Front" ...a ] // [ "Front" 1 2 3 "Back" ]
```

或者可以做点别的事情比如：

```
declare list a = [ 1 2 3 ]
a = [ ...a ...a ] // [ 1 2 3 1 2 3 ]
```

path: chapters/1.10 - META/1 - WTCD/4 - 4. Lists.md


code:
# 5. Functions
## 5.1 概述
WTCD 提供匿名支持引用闭包的函数。

在 WTCD 里，函数就是普通的值，因此也能被存在变量里或是被传递。若被存在变量里，其类型名为 `function`。

## 5.2 创建新的函数
若要创建函数，需要使用 `function` 关键词。其格式如下：

```
function <参数列表> <返回值>
```

举个例子：

```wtcd
function[number input] input * input
```

可以创建一个求平方的函数。

你当然可以把这个刚刚创建的函数存到变量里（不然相当于是创建了就扔了）。

```wtcd
declare function square = function[number input] input * input
```

需要注意的是，这里有两个 `function`。其中第一个 `function` 的作用是告诉 WTCD `square` 这个变量的类型是 `function`（类似 `declare number a` 里的 `number`）。之后这个 `function` 是告诉 WTCD 现在需要创建一个 `function`。

是的，我也觉得这么写很烦，所以 WTCD 有语法糖。上述定义还可以写成：

```wtcd
declare function square[number input] input * input
```

这两个写法效果是完全一致的。

若要创建一个接受多个参数的函数：

```wtcd
declare function add[number a number b] a + b
```

当然最后返回值那里也可以用代码块：

```wtcd
declare function solveQuad[number a number b number c] {
  declare number delta = b ** 2 - 4 * a * c
  delta > 0 ? {
    return [
      (-b - delta ** 0.5) / 2 / a
      (-b + delta ** 0.5) / 2 / a
    ]
  } : delta == 0 ? {
    return [ -b / 2 / a ]
  } : {
    return []
  }
}
```

## 5.3 调用函数
WTCD 提供多种调用函数的方式。

### 5.3.1 `::` 函数调用操作符
最简单的调用函数的方法是使用 `::` 操作符。使用时，`::` 左侧提供被调用函数，右侧提供一个参数列表。

举个例子：

```wtcd
square::[10] // 100
```

好的，我知道你肯定要问为什么不是 `square(10)`。那么原因是，因为 WTCD 没有分割符，如果语法是 `square(10)`，WTCD 将无法区分 `square; (10)` 和 `square(10)`。所以非常遗憾，调用函数必须用 `::`。

有意思的是，`::` 右边其实就是一个列表 —— 这里没有魔法。

因此理论上来说你可以把参数预先算好，然后到时候放进去：

```wtcd
declare list args = [ 10 ]
square::args // 100
```

当然，对于多个参数的情况，调用也是一个道理：

```wtcd
solveQuad::[1 (-3) 2] // [ 1 2 ]
```

预先计算参数也是可以的：

```wtcd
declare list args = [1 (-3) 2]
solveQuad::args // [ 1 2 ]
```

### 5.3.2 `|>` 管道调用操作符
WTCD 支持单参数管道调用。

举个例子：

```wtcd
10 |> square // 100
```

遗憾的是，如果被调用的函数接受超过 1 个参数就没办法了。

### 5.3.3 `|::` 逆向调用操作符
如果因为某些原因导致你不想把参数写在函数的后面，你可以用 `|::` 来先写参数再写函数：

```wtcd
[ 10 ] |:: square // 100
```

```wtcd
[1 (-3) 2] |:: solveQuad // [ 1 2 ]
```

## 5.4 传参
简而言之，WTCD 参数传递一律传值：WTCD 把所有参数全部复制了一遍，然后把它们当成函数内的局部变量。

```wtcd
declare function tryModify[number a] {
  a += 10
  return a
}
declare number b = 5
declare number c = tryModify::[b]

b // 5，不会受函数的影响
c // 15
```

## 5.5 返回值
虽然之前已经用过了，但是这边提一下，WTCD 的函数体内允许使用 `return`。不过你可能会问为什么不使用 `yield`。

事实上，`yield` 和 `yield =` 也是可以用的：

```wtcd
declare function square[number a] {
  yield a * a
}
```

但是，如果嵌套了多层，`return` 可以直接一次全部跳出来。

```wtcd
declare function square[number a] { // 代码块 A
  { // 代码块 B
    // 这里使用 yield a * a 将并不会设置函数的返回值，而是只会设置代码块 B 的返回值。
    return a * a // return 则会直接跳出函数
  }
}
```

当然，有了 `return`，就有 `return =`。和 `yield =` 类似，`return =` 会设置函数的返回值但是不会终止函数执行。

```wtcd
declare function test[number a] {
  return = a * 2
  print::[a]
}
test::[10] // 返回 20 并打印 10 到控制台。
```

如果 `return` 值和 `yield` 值冲突了，则会使用 `return`。

```wtcd
declare function square[number n] {
  yield = "Wrong"
  return = n * n // Return 优先
}
square::[12] // 144
```

## 5.6 默认参数
WTCD 支持默认参数。简而言之就是允许你定义的函数被调用时若一部分参数没有提供，或是提供的值是 `null`，则使用提供的默认值。

```wtcd
declare function greet[string name = "朋友"] {
  print::["你好，" + name]
}
greet::[] // 你好，朋友
greet::["陈志鹏"] // 你好，陈志鹏
```

如果要忽略之前的参数，但是提供后面的参数，可以在前面的参数使用 `null`。

```wtcd
declare function fn[number a = 1 number b = 2] {
  print::[a + b]
}
fn::[null 10] // 11
fn::[10] // 12
fn::[10 10] // 20
```

## 5.7 Rest / 可变参数
WTCD 支持可变参数。简而言之，就是指定一个参数接受多出来的参数。如果除了这个参数以外，别的什么也没提供，那么相当于是所有的参数都会给这个参数。

WTCD 仅支持让最后一个参数接受所有多出来的参数。若要这么做，请忽略该参数的类型定义并在其前方写上 `...`。该参数的类型会自动变成 `list`。

```wtcd
declare function sum[...items] {
  declare number value
  listForEach::[items function[number item] {
    value += item
  }]
  return value
}
print::[sum::[1 2 3]]
```

## 5.8 闭包
> This is where the magic lies. -- Tepis

> I want you to make a contract with me and become a Magical Girl! -- Kyubey

简而言之，WTCD 的闭包允许函数内的语句使用函数外定义的变量。

先举个简单的例子：

```wtcd
declare number value = 100
declare function printValue[] {
  print::[value]
}
printValue::[] // 打印 100
value = 200
printValue::[] // 打印 200
```

虽然从这个例子来看好像没有什么特殊的，但是仔细观察发现，函数内使用的 `value` 是在函数外部的。并且可以发现，当这个 `value` 发生改变时，`printValue` 的值也会改变。

这个就是最简单的闭包。

下面来看第二个例子。

```wtcd
declare function printValue = {
  declare number value = 100 // 定义 value
  yield function[] {
    print::[value]
  }
} // value 所在的作用域在此结束
printValue::[] // 打印 100
```

需要注意的是，`value` 这个变量不在外层作用域里面。我们调用的函数 `printValue` 是从内层有 `value` 的作用域里面通过 `yield` 传递出来的。

也就是说，因为一些原因，即使 `value` 的作用域已经结束了，`value` 还在某个地方存在着。

事实上，在定义函数的时候，WTCD 会把涉及到的函数作用域以外的变量的值全部打包和函数放在一起。因此也就是说这个传回来的 `printValue` 函数在 WTCD 内部实际上还保存了 `value` 的值。这就是为什么 `value` 本身存在的空间已经挂了但是依然能够被 `printValue` 访问的原因。

不但如此，WTCD 的闭包捕获的变量都是保存引用的。也就是说，不但你可以访问之前存在的值，你甚至可以修改它们。

```wtcd
declare function printValue = {
  declare number value = 1 // 定义 value
  yield function[] {
    print::[value]
    value += 1
  }
} // value 所在的作用域在此结束
printValue::[] // 打印 1
printValue::[] // 打印 2
printValue::[] // 打印 3
printValue::[] // 打印 4
```

除此之外，WTCD 捕获的变量都是互相隔离的。意味着，你两次捕获由同一段代码定义的两个作用域所产生的捕获是互相隔离的。

```wtcd
declare function makeCounter[] {
  declare number n = 0
  return function[] {
    n += 1
    return n
  }
}
declare [
  function counter1 = makeCounter::[]
  function counter2 = makeCounter::[]
]
print::[counter1::[]] // 1
print::[counter1::[]] // 2
print::[counter1::[]] // 3
print::[counter2::[]] // 1
print::[counter1::[]] // 4
print::[counter2::[]] // 2
```

因为 `makeCounter` 被调用了两次。每次调用都会产生一个新的 `n`。所以两个 `counter` 捕获的是不同的变量，因此能够独立互不干涉地工作。

闭包是函数式编程的重要组件之一。只有用好闭包才能在 WTCD 繁荣。

path: chapters/1.10 - META/1 - WTCD/5 - 5. Functions.md


code:
# 6. 标准库
WTCD 提供一系列标准库用来做一些正常做不到或是即使做到了也非常复杂的操作。

## 6.1 `list*`
### 6.1.1 `listSet`
接受 3 个参数：`list sourceList`，`number index`，`任意 value`。

返回一个列表，返回的列表是 `sourceList` 中 `index` 被替换为 `value` 后的列表。该函数不会修改 `sourceList`。

```wtcd
listSet::[[ 0 1 2 3 4 5 ] 3 "Nice"] == [ 0 1 2 "Nice" 4 5 ]
```

### 6.1.2 `listForEach`
接受 2 个参数：`list targetList`，`function fn`。

对 `targetList` 中每一项运行 `fn`。`fn` 的第一个参数是这一项的值，第二个参数是这一项的下标。

```wtcd
declare number contentSum = 0
declare number indexSum = 0
listForEach::[[ 1 2 3 4 100 ] function[number item number index] {
  contentSum += item
  indexSum += index
}]
contentSum // 110
indexSum // 10
```

### 6.1.3 `listMap`
接受 2 个参数：`list targetList`，`function fn`。

对 `targetList` 中每一项运行 `fn`。`fn` 的第一个参数是这一项的值，第二个参数是这一项的下标。然后，将所有 `fn` 的运行返回值合在一起，形成一个新的列表并返回。

```wtcd
listMap::[[ 0 1 2 4 20 ] function[number item] item * item] == [ 0 1 4 16 400 ]
```

### 6.1.4 `listCreateFilled`
接受 1 - 2 个参数：`number count`，`任意 value = null`。

创建一个列表，这个列表有 `count` 个元素，每个元素的值都是 `value`。

```wtcd
listCreateFilled::[5 8] == [ 8 8 8 8 8 ]
```

### 6.1.5 `listChunk`
接受 2 个参数：`list targetList`，`number chunkSize`。

将 `targetList` 分成若干个列表，每个列表的长度为 `chunkSize`。

```wtcd
listChunk::[[ 1 2 3 4 5 6 7 8 9 10 ] 3 ] == [ [ 1 2 3 ] [ 4 5 6 ] [ 7 8 9 ] [ 10 ] ]
```

### 6.1.6 `listFilter`
接受 2 个参数：`list targetList`，`function fn`。

对 `targetList` 中每一项运行 `fn`。`fn` 的第一个参数是这一项的值，第二个参数是这一项的下标，并要求返回一个布尔值。然后，将所有返回 true 的项目合在一起形成一个新的列表并返回。

```wtcd
listFilter::[[ 1 2 3 4 5 6 7 8 ] function[number a] a % 2 == 0] == [ 2 4 6 8 ]
```

### 6.1.7 `listSplice`
接受 3 - 4 个参数：`list source`，`number start`，`number length`，`list newItems = []`。

从 `source` 的 index = `start` 的元素开始，删除 `length` 个元素，并用 `newItems` 中的元素代替。返回修改后的列表。（不会修改原始列表）

```wtcd
listSplice::[[ 0 1 2 3 ] 1 2] == [ 0 3 ]
listSplice::[[ 0 1 2 3 ] 1 2 [ "Wow1" "Wow2" ]] == [ 0 "Wow1" "Wow2" 3]
```

### 6.1.8 `listSlice`
接受 2 - 3 个参数：`list source`，`number start`，`number end = listLength::[source]`。

复制从 `source` 的 index = `start`（含）开始到 index = `end`（不含）的元素。

```wtcd
    assert::[listSlice::[[ 0 1 2 3 4 5 ] 2] == [ 2 3 4 5 ]]
    assert::[listSlice::[[ 0 1 2 3 4 5 ] 2 4] == [ 2 3 ]]
```

### 6.1.9 `listLength`
接受 1 个参数：`list targetList`。

返回 `targetList` 的元素数量。

```wtcd
listLength::[[ 0 1 2 3 4 ]] == 5
```

### 6.1.10 `listIndexOf`
接受 2 个参数：`list targetList`，`target`。

找 `targetList` 中第一个 == `target` 的元素，返回其 index。若无法找到，返回 -1。

```wtcd
assert::[listIndexOf::[[ 0 1 2 3 ] 2] == 2]
assert::[listIndexOf::[[ 0 1 2 3 ] 4] == -1]
```

### 6.1.11 `listIncludes`
接受 2 个参数：`list targetList`，`target`。

判断 `targetList` 中是否有 == `target` 的元素。

```wtcd
assert::[listIncludes::[[ 0 1 2 3 ] 2] == true]
assert::[listIncludes::[[ 0 1 2 3 ] 4] == false]
```

### 6.1.12 `listFindIndex`
接受 2 个参数：`list targetList`，`function predicate`。

对 `targetList` 中每一项依次运行 `fn`。`fn` 的第一个参数是这一项的值，第二个参数是这一项的下标，并要求返回一个布尔值。当任意一个 `predicate` 返回 true 时，返回该项的 index。如果没有一项能使 `predicate` 返回 true，否则返回 -1。

```wtcd
assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item] item % 2 == 0] == 3]
assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item index] index == 4] == 4]
assert::[listFindIndex::[[ 1 3 5 8 9 11 14 ] $[item] false] == -1]
```

## 6.2 `math*`
### 6.2.1 `mathMin`
接受 1 个及以上的数字。返回其中最小的值。

```wtcd
mathMin::[10 1 2 3 4] == 1
```

### 6.2.2 `mathMax`
接受 1 个及以上的数字。返回其中最大的值。

```wtcd
mathMax::[1 2 3 4 0] == 4
```

### 6.2.3 `mathFloor`
接受 1 个参数：`number input`。返回小于等于 `input` 的最大整数。

```wtcd
mathFloor::[11.8] == 11
mathFloor::[-11.8] == -12
```

### 6.2.4 `mathCeil`
接受 1 个参数：`number input`。返回大于于等于 `input` 的最小整数。

```wtcd
mathFloor::[11.8] == 12
mathFloor::[-11.8] == -11
```

## 6.3 `string*`
### 6.3.1 `stringLength`
接受 1 个参数：`string input`。返回 `input` 的长度。

```wtcd
stringLength::["Hello~"] == 6
```

### 6.3.2 `stringFormatNumberFixed`
接受 1 个参数：`number target`，`number digits = 0`。返回将 `target` 四舍五入到 `digits` 位小数后的字符串。

```wtcd
stringFormatNumberFixed::[1.235 2] == "1.24"
```

### 6.3.3 `stringFormatNumberPrecision`
接受 2 个参数：`number target`，`number digits`。返回将 `target` 四舍五入到保留 `digits` 位数字后的字符串。

```wtcd
stringFormatNumberPrecision::[1.235 2] == "1.2"
stringFormatNumberPrecision::[1234 2] == "1.2e+3"
```

### 6.3.4 `stringSplit`
接受 2 个参数：`string str`，`string separator`。返回用 `separator` 分割 `str` 产生的列表。

```wtcd
stringSplit::["a,b" ","] == ["a" "b"]
```

### 6.3.5 `stringSubByLength`
接受 2 - 3 个参数：`string str`，`number startIndex`，`number length = 剩余长度`。

返回 `str` 从 `startIndex`（含）开始 `length`（含）个字符的子串。

### 6.3.6 `stringSubByIndex`
接受 2 - 3 个参数：`string str`，`number startIndex`，`number endIndex = 字符串长度`。

返回 `str` 从 `startIndex`（含）开始直到 `endIndex`（不含）的子串。

## 6.4 `random*`
### 6.4.1 `random`
接受 0 - 2 个参数：`number low = 0`，`number high = 1`。

返回一个大于等于 `low`，小于 `high` 的随机浮点数。

### 6.4.2 `randomInt`
接受 0 - 2 个参数：`number low = 0`，`number high = 1`。

返回一个大于等于 `low`，小于 `high` 的随机整数。

### 6.4.3 `randomBoolean`
接受 0 - 1 个参数：`boolean trueChance = 0.5`。

返回一个有 `trueChance` 为 `true` 的随机布尔值。

### 6.4.4 `randomBiased`
**实验性，尚未确认**

接受 0 - 4 个参数：`number low = 0`，`number high = 1`，`number bias = (low + high) / 2`，`number influence = 3`。

返回一个大于等于 `low`，小于 `high`，偏向于 `bias`，影响系数为 `influence` 的随机浮点数。

## 6.5 `content*`
### 6.5.1 `contentAddParagraph`
接受 1 个参数：`string content`。

创建并插入一个内容为 `content` 的自然段。

### 6.5.2 `contentAddImage`
接受 1 个参数：`string src`。

创建并插入一个地址为 `src` 的图片。

### 6.5.3 `contentAddUnorderedList`
接受任意数量个字符串参数。

然后用它们来产生并插入一个无序列表。

### 6.5.4 `contentAddOrderedList`
接受任意数量个字符串参数。

然后用它们来产生并插入一个有序列表。

### 6.5.5 `contentAddHeader`
接受 1 - 2 个参数：`string content`，`number level = 1`。

创建并插入一个级别为 `level`，内容为 `content` 的标题。

### 6.5.6 `contentAddTable`
接受 1 个及以上的参数。每一个参数都是长度相同的列表。每一个列表对应表格的一行。第一行作为表头。

```wtcd
contentAddTable::[
  [ "名字" "年龄" "性别" ]
  [ "琳"   "18"   "女"   ]
  [ "czp"  "？"   "男"   ]
]
```

### 6.6.6 `contentAddHorizontalRule`
创建并插入一条水平分割线。

## 6.6 Debug
### 6.6.1 `print`
接受任意数量个参数。将这些值打印到浏览器控制台。

### 6.6.2 `assert`
接受 1 个参数：`boolean isOk`。

如果 `isOk` 不是 true，立刻产生一个异常（中断执行）。

### 6.6.3 `assertError`
接受 1 个参数：`function fn`。

调用 `fn`。如果 `fn` 没有产生 WTCD 错误，则产生一个异常。（断言这个提供的函数在运行时会报错。）

### 6.6.4 `timeStart`
接受 0 - 1 个参数：`string timerName = "default"`。

启动一个名为 `timerName` 的计时器。

### 6.6.5 `timeEnd`
接受 0 - 1 个参数：`string timerName = "default"`。

结束名为 `timerName` 的计时器，并将计时结果打印到浏览器控制台。

## 6.7 `reader*`
### 6.7.1 `readerSetPinned`
接受 1 个参数：`function pinnedFunction`。

设置 `pinnedFunction` 为当前的常驻函数。常驻函数是指在每次需要用户做出选择的时候被调用的函数。在这个函数内调用 `content*` 会把创建的内容添加到一个特殊的常驻区域。常驻区域的显示方式则是由具体的阅读器决定的。

### 6.7.2 `readerUnsetPinned`
不接受参数。取消当前常驻函数。

### 6.7.3 `readerSetStateDesc`
接受 1 个参数：`string stateDesc`。

将当前的状态描述文本设置为 `stateDesc`。状态描述文本通常被用在支持多存档的阅读器的存档选择界面中来帮助区分存档。

### 6.7.4 `readerUnsetStateDesc`
不接受参数。取消当前状态描述文本。

## 6.8 `canvas*`
### 6.8.1 `canvasCreate`
接受 3 个参数：`string id`，`number width`，`number height`。

创建一个 ID 为 `id`，宽度为 `width`，高度为 `height` 的画布。需要注意的是，这个画布是不会显示出来的。

如果需要显示这个画布，则需要使用 `canvasOutput`。

### 6.8.2 `canvasOutput`
接受 1 个参数：`string id`。

将 ID 为 `id` 的画布显示出来。需要注意的是，这个画布一旦被显示就会被克隆。因此之后对该画布的修改不会影响到已经显示出来的版本。

### 6.8.3 `canvasClear`
接受 1 个参数：`string id`。

将 ID 为 `id` 的画布清空。

### 6.8.4 `canvasPutImage`
接受 4 - 6 个参数：`string id`，`string path`，`number x`，`number y`，`number width = -1`，`number height = -1`。

将位于 `path` 的图片绘制到 ID 为 `id` 的画布上。使用 `x` 和 `y` 可以控制绘制的位置（左上角）。使用 `width` 和 `height` 可以控制绘制出来的尺寸。如果忽略，那么就会使用图片的原始尺寸。

需要注意的是，`width` 和 `height` 要么同时提供，要么都不提供。不可以只提供一个。

### 6.8.5 `canvasPutImagePart`
接受 8 - 10 个参数：`string id`，`string path`，`number sourceX`，`number sourceY`，`number sourceWidth`，`number sourceHeight`，`number destX`，`number destY`，`number destWidth = -1`，`number destHeight = -1`。

将位于 `path` 的图片的一部分绘制到 ID 为 `id` 的画布上。使用 `sourceX`，`sourceY`，`sourceWidth`，`sourceHeight` 可以控制需要绘制的部分在来源图片的位置。使用 `destX` 和 `destY` 可以控制在画布的位置（左上角）。使用 `destWidth` 和 `destHeight` 可以控制绘制出来的尺寸。如果忽略，那么就会使用图片的来源部分的尺寸。

需要注意的是，`destWidth` 和 `destHeight` 要么同时提供，要么都不提供。不可以只提供一个。

### 6.8.6 `canvasSetFont`
接受 3 个参数：`string id`，`number size`，`string fontIdentifier`。

设置 ID 为 `id` 的画布的字体为尺寸是 `size`px，类型是 `fontIdentifier` 的字体。

`fontIdentifier` 的解释方法由使用的 WTCD 阅读器指定。在《可穿戴科技》中，这个 `fontIdentifier` 的值为 `googleFonts:<在 Google Fonts 上的字体名>`。例如：`googleFonts:ZCOOL KuaiLe`。

### 6.8.7 `canvasSetFillStyle`
接受 2 个参数：`string id`，`string fillStyle`。

设置 ID 为 `id` 的画布的填充方式为 `fillStyle`。

### 6.8.8 `canvasFillText`
接受 4 - 6 个参数：`string id`，`string text`，`number x`，`number y`，`string hAlign = "start"`，`string vAlign = "alphabetic"`。

在 ID 为 `id` 的画布的位于（`x`, `y`）的位置，绘制以 `hAlign` 横向对齐，以 `vAlign` 纵向对齐的文本 `text`。

请使用 `canvasSetFont` 来选择字体，使用 `canvasSetFillStyle` 来选择填充方式。

`hAlign` 可采取的值可以在[这里](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign)找到。

`vAlign` 可采取的值可以在[这里](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline)找到。

### 6.8.9 `canvasFillRect`
接受 5 个参数：`string id`，`number x`，`number y`，`number width`，`number height`。

在 ID 为 `id` 的画布的位于（`x`, `y`）的位置绘制一个 `width`px x `height`px 的长方形。

### 6.8.10 `canvasSetStrokeStyle`
接受 2 个参数：`string id`，`string strokeStyle`。

设置 ID 为 `id` 的画布的描线方式为 `strokeStyle`。

### 6.8.11 `canvasSetLineWidth`
接受 2 个参数：`string id`，`number lineWidth`。

设置 ID 为 `id` 的画布的描线粗细为 `lineWidth`px。

### 6.8.12 `canvasStrokeText`
接受 4 - 6 个参数：`string id`，`string text`，`number x`，`number y`，`string hAlign = "start"`，`string vAlign = "alphabetic"`。

类似 `canvasFillText`，但是只是描边。所以请使用 `canvasSetStrokeStyle` 来选择描边方式。

path: chapters/1.10 - META/1 - WTCD/6 - 6. 标准库.md


code:
# 7. 流程控制
## 7.1 概述
目前 WTCD 的传统流程控制只原生提供了 `switch`。

## 7.2 `switch`
`switch` 语句语法如下：

```
switch <valueExpr> [
  <matchList1> <matchExpr1>
  <matchList2> <matchExpr2>
  ...
  [defaultExpr]
]
```

举个例子：

```wtcd
declare number value = // ...
declare number result = switch value [
  [ 1 5 10 ] 200
  [ 3 7 ] 300
  400
]
```

在上述例子中，如果 `value` 的值是 1 或 5 或 10，则 `result` 会变成 200。如果 `value` 的值是 3 或 7，则 `result` 会变成 300。否则 `result` 会变成 400。

在 WTCD 中，如果没有提供最后的 `defaultExpr`，那么在如果没有匹配时就会报错。

此外，WTCD 的 switch 时没有 fall through 的。也就是说，上一个 `case` 执行完后不会进入下一个 `case`，因此也没有必要使用 `break`。

另外，每一个 `matchList` 都可以是一个表达式。上述 `switch` 语句还可以写成：

```wtcd
declare number value = // ...
declare list list1 = [ 1 5 10 ]
declare list list2 = [ 3 7 ]
declare number result = switch value [
  list1 200
  list2 300
  400
]
```

## 7.3 `if`
WTCD 目前不支持 `if`，但是可以用 `?` `:` 实现。

### 7.3.1 实现 if then
```wtcd
(condition) ? {
  // Then
} : null
```

### 7.3.2 实现 if then else
```wtcd
(condition) ? {
  // Then
} : {
  // Else
}
```

path: chapters/1.10 - META/1 - WTCD/7 - 7. 流程控制.md


code:
# 文档债
文档债 = 因为时间原因 + 懒导致没有及时编写的文档。

- while 循环
- if 语句
- 复合类型
- Partial application
- $ 语法糖
- function switch 语法糖
- 函数 Action
- tag
- pin

path: chapters/1.10 - META/1 - WTCD/9997 - 文档债.md


code:
# 变化日志
## 1.3 (WIP)
- 添加如下标准库函数
  - `mathCeil`
  - `stringSplit`
  - `stringSubByLength`
  - `stringSubByIndex`
  - `contentAddHorizontalRule`
  - `readerSetPinned`
  - `readerUnsetPinned`
  - `readerSetStateDesc`
  - `readerUnsetStateDesc`
  - `canvasCreate`
  - `canvasOutput`
  - `canvasClear`
  - `canvasPutImage`
  - `canvasPutImagePart`
  - `canvasSetFont`
  - `canvasSetFillStyle`
  - `canvasFillText`
  - `canvasFillRect`
  - `canvasSetStrokeStyle`
  - `canvasSetLineWidth`
  - `canvasStrokeText`

## 1.2
- 允许一个变量存储多个类型的值
- 添加 `while` 支持
- 添加 `if` 支持
- 添加 `null` 的变量类型
- 添加如下标准库函数
  - `contentAddTable`
  - `listFilter`
  - `listSplice`
  - `listSlice`
  - `listLength`
  - `listIndexOf`
  - `listIncludes`
  - `listFindIndex`
  - `stringFormatNumberFixed`
  - `stringFormatNumberPrecision`
- 添加如下操作符
  - `.:`
  - `:.`
- 添加函数创建语法糖 `$`
- 添加 `function switch` 语法糖
- 添加函数 Action 支持

## 1.1
- 添加 `list` 支持
- 添加 `function` 支持
- 存储 `selection` 的变量类型由 `selection` 变为 `action`
  - 为了向前兼容，目前 `selection` 为 `action` 的 alias，并且将在下一个大版本移除
- 添加 `switch` 支持
- 添加标准库
- 添加如下操作符
  - `::`
  - `.`
  - `.?`
  - `?.`
  - `?.?`
  - `**`
  - `?!`
  - `??`
  - `|>`
  - `|::`

path: chapters/1.10 - META/1 - WTCD/9998 - 变化日志.md


code:
# 计划功能
WTCD 目前还在开发初期阶段，因此还有许多功能被计划实现但是还没有实现。这里给出目前计划中的功能。需要注意的是，具体的语法还没有确定，这里给出的只是目前的想法

## 使用 `::` 代表递归调用自身
因为 WTCD 的函数都是匿名函数，所以可能需要一个特殊语法来处理递归。

## `while` 循环
`while` 循环有必要，估计在下个版本就有了。

## `if` 语句
虽然 `if` 可以用 `?` `:`，但是有原生的 `if` 看起来可能舒服一点。

## 引用赋值与引用比较
这个实现起来并不复杂，但是因为在设计初期没有考虑这个，所以不知道会不会影响什么别的功能。引用赋值大概是说让两个变量变成同一个东西。

举个例子：

```wtcd
declare [
  number a = 100
  number b
]
b =& a // 将 a 的引用给 b

a = 200
a // 200
b // 200

b = 300
a // 300
b // 300
```

因为没有想到什么好的方法可以解除引用，所以目前看来引用赋值是永久的...

类似的，引用比较可以判断两个变量是否是同一个引用。

```
declare [
  number a = 100
  number b = 100
]

a ==& b // false

a =& b

a ==& b // true
```

# 已实装
- 标准库
- 函数
- 列表

path: chapters/1.10 - META/1 - WTCD/9999 - 计划功能.md


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="27cm" height="7cm" viewBox="306 411 531 133" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g>
    <rect style="fill: #ffffff" x="308" y="463" width="47.6" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="308" y="463" width="47.6" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="331.8" y="486.8">
      <tspan x="331.8" y="486.8">start</tspan>
    </text>
  </g>
  <g>
    <rect style="fill: #ffffff" x="419.074" y="417" width="51.85" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="419.074" y="417" width="51.85" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="444.999" y="440.8">
      <tspan x="444.999" y="440.8">press</tspan>
    </text>
  </g>
  <g>
    <rect style="fill: #ffffff" x="417.176" y="505" width="85.65" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="417.176" y="505" width="85.65" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="460.001" y="528.8">
      <tspan x="460.001" y="528.8">doNotPress</tspan>
    </text>
  </g>
  <g>
    <rect style="fill: #ffffff" x="540.674" y="458" width="80.65" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="540.674" y="458" width="80.65" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="580.999" y="481.8">
      <tspan x="580.999" y="481.8">walkHome</tspan>
    </text>
  </g>
  <g>
    <rect style="fill: #ffffff" x="678.95" y="412" width="78.1" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="678.95" y="412" width="78.1" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="718" y="435.8">
      <tspan x="718" y="435.8">czpClimax</tspan>
    </text>
  </g>
  <g>
    <rect style="fill: #ffffff" x="677.05" y="505" width="159.9" height="38"/>
    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="677.05" y="505" width="159.9" height="38"/>
    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="757" y="528.8">
      <tspan x="757" y="528.8">czpGotoGrandmasHouse</tspan>
    </text>
  </g>
  <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="757" y="524">
    <tspan x="757" y="524"></tspan>
  </text>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 409.429,436.119 A 61.3144,61.3144 0 0 0 355.6,482"/>
    <polygon style="fill: #000000" points="416.843,435.852 406.533,440.177 409.359,435.354 407.197,430.199 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="416.843,435.852 406.533,440.177 409.359,435.354 407.197,430.199 "/>
  </g>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 355.6,482 A 73.9165,73.9165 0 0 0 407.591,522.692"/>
    <polygon style="fill: #000000" points="414.945,523.843 404.62,528.13 407.464,523.318 405.321,518.155 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="414.945,523.843 404.62,528.13 407.464,523.318 405.321,518.155 "/>
  </g>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 533.871,470.061 A 137.21,137.21 0 0 0 470.926,436"/>
    <polygon style="fill: #000000" points="539.166,475.349 528.73,471.337 534.108,469.811 536.114,464.593 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="539.166,475.349 528.73,471.337 534.108,469.811 536.114,464.593 "/>
  </g>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 532.963,482.894 A 107.12,107.12 0 0 0 502.826,524"/>
    <polygon style="fill: #000000" points="538.838,478.276 533.479,488.089 532.679,482.556 527.773,479.877 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="538.838,478.276 533.479,488.089 532.679,482.556 527.773,479.877 "/>
  </g>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 669.509,433.113 A 74.3817,74.3817 0 0 0 621.324,477"/>
    <polygon style="fill: #000000" points="676.741,431.345 667.633,437.83 669.331,432.504 666.088,427.95 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="676.741,431.345 667.633,437.83 669.331,432.504 666.088,427.95 "/>
  </g>
  <g>
    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 621.324,477 A 52.6233,52.6233 0 0 0 667.44,523.741"/>
    <polygon style="fill: #000000" points="674.819,524.144 665.162,529.778 667.334,524.628 664.517,519.799 "/>
    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="674.819,524.144 665.162,529.778 667.334,524.628 664.517,519.799 "/>
  </g>
</svg>

path: chapters/1.10 - META/1 - WTCD/sectionDiagram.svg


code:
# 人名请求
目前而言，可穿戴科技的所有出场角色的人名都是读者的网名/真实姓名。这里列出了目前正在请求的人名。如果你愿意贡献自己的名字，请发送到讨论组或者私聊琳。（讨论组链接请到主页 -> 订阅/讨论组 -> Telegram 讨论组）

当然，如果你希望自己被写入小说但是不愿意用自己的真实网名，你也可以编造一个出来。而且，每个人可以以不同的名字多次出现噢。

**目前没有正在请求的人名**

# 历史请求
## 主线
- 需要被琳踩的男性 2 位，性格偏 M。（已找到 2/2）
- 需要某戴鸭舌帽的半反派男性 1 位，领导向。（已找到 1/1）
- 需要某理着粉红色双马尾的半反派女性 1 位，技术向。（已找到 1/1）

## 道具集
- 需要男用可穿戴科技的穿戴者 1 位。（已找到 1/1）
- 需要参加某冒险活动的女性 1 位。（已找到 1/1）

# 撤回
如果你事后后悔了，希望把自己的名字从小说中取消掉，请联系作者琳。她会尽快使用全文替换功能将你的名字全部替换掉。不过，因为本小说使用 git 进行版本管理，项目的历史中可能会依然存在你的名字。

**所有人都可以请求撤回，这包括在本文档创建之前就被写入小说的读者（哪怕你是完全未经同意就被写进去的）。**

path: chapters/1.10 - META/1 - 人名请求.md


code:
# 协助打标签
首先十分感谢你对协助打标签有兴趣。因为标签系统刚刚做好，之前的文章都没有标签。如果你愿意帮之前的文章打标签，请跟随以下指引：

1. 选择一篇你想要协助打标签的文章。通读全文，尽量将文章的所有剧情记在脑海中。
2. 打开[《可穿戴科技》标签工具](#/page/tagging-tool)。
3. 从上往下，一条一条标签确认，并标记所有文章拥有的标签。这一步请务必认真仔细。是的，每一个标签都要看，不要只是匆匆扫过去。如果你没有这个耐心，给文章打标签还是算了。标签工具会自动保存，因此如果一不小心关掉了，重新打开不会丢失当前进度。
4. 复制标签工具最底部的输出，然后将结果复制到本指引的评论区，记得附带上文章名和章节号。（你也可以把结果复制到 [Telegram 群](https://t.me/joinchat/Dt8_WlJnmEwYNbjzlnLyNA)并 @ 琳。）

帮助打标签后，你可能会被加到鸣谢列表。如果你不希望被加到鸣谢列表，请注明。

path: chapters/1.10 - META/1.1 - 协助打标签.md


code:
# 标签规格文档（TagsSpec.txt）
《可穿戴科技》允许作者给文章添加标签（Tag）。标签可以帮助查找或推荐文章。为了使标签更统一，《可穿戴科技》预先制定了一系列可能会用到的标签。所有标签，以及它们的定义都被写入了这个标签规格文档中。该文件位于项目根目录下。文件名是 `TagsSpec.txt`。

## 文件格式
该文件格式的设计目标是极度容易编写和理解。其解析器的源代码位于 `src/builder/tagsSpecParser.ts`。

下面描述一下该文件的详细格式：

### 每两个标签之间用一个空行隔开
例如：

```
遥控

舔舐

舔足
```

定义了三个标签：`遥控`，`舔舐`，和`舔足`。

## 在标签后使用中文括号（`（`，`）`）可添加用正斜杠（`/`）划分的标签变种
例如：

```
舔足（女/伪娘/男/扶她）

阴茎刺激（伪娘/男/扶她）

现代
```

定义了三个标签：

- 标签`舔足`有四个变种：`舔足（女）`，`舔足（伪娘）`，`舔足（男）`，和`舔足（扶她）`。
- 标签`阴茎刺激`有三个变种：`阴茎刺激（伪娘）`，`阴茎刺激（男）`，和`阴茎刺激（扶她）`。
- 标签`现代`则没有变种。

## 在标签最后添加一个或多个中文感叹号（`！`）可以付给该标签优先级
感叹号越多，优先级越高。

高优先级的关键词会在计算相似度的时候采用更高的权重。同时，当在某些字数有限制的场合（例如 Twitter 推送），高优先级的标签会优先展现。

例如：

```
舔舐

遥控！

舔足（女/伪娘/男/扶她）！！
```

定义了三个标签：`遥控`，`舔舐`，和`舔足`。其中舔舐为标准优先度，遥控为一级优先度，舔足为二级优先度。

### 不用空行隔开的会变成该标签的解释
例如：

```
自慰（女/伪娘/男/扶她）
自己刺激自己获得快感的行为。

自虐（女/伪娘/男/扶她）
自己刺激自己获得痛感/痛苦的行为。
也包括故意通过给自己施加大量快感以至于超过忍受极限而感到痛苦的行为。
```

定义了两个标签：`自慰`和`自虐`。其中`自慰`有一个解释项，`自慰`有两个解释项。

### 解释项中可以用方括号插入对其他标签的引用
例如：

```
舔舐（女/伪娘/男/扶她）
使用舌头舔身体/物体。

口交（女/伪娘/男/扶她）
使用舌头刺激对方（或者自己）的阴道/阴蒂/阴茎/肛门，其他部位不算。
如果有舔的动作，请选择【舔舐】。
```

其中`口交`的第二个解释项附带了一个`舔舐`的引用。

### 在解释项前写一个加号可以将该解释项内引用的标签设置为前置标签
例如：

```
舔舐（女/伪娘/男/扶她）
使用舌头舔身体/物体。

舔足（女/伪娘/男/扶她）
使用舌头舔脚（可以隔着丝袜）。
+需选择【舔舐】
```

其中`舔足`的前置条件就是`舔舐`。

需要注意的是，如果本标签和前置标签都有变种，则只会依赖对应的变种。

例如：

```
性器官刺激（女/伪娘/男/扶她）

阴蒂刺激（女/扶她）
+需选择【性器官刺激】
```

在这个情况下，如果选择了`阴蒂刺激（女）`，则只需要选择前置`性器官刺激`的对应变种`性器官刺激（女）`。正是因此，如果一个标签和其前置都有变种的话，那么其变种必须是其前置变种的子集。当然，如果一个标签和它的前置其中任意一个没有变种的话，则没有这个限制。

### 在一个解释项前添加一个等号可以将该解释项变为用中文逗号（`，`）分隔的同义词列表
例如：

```
含有伪娘
参与某一场色情活动的人中含有一个及以上的伪娘。
=伪娘，女装
```

其中为`含有伪娘`这个标签添加了两个同义词：`伪娘`和`女装`。同时，`伪娘，女装`这一句话将不再作为`含有伪娘`的解释项。

**请注意：同义词仅使用于搜索时将用户输入的标签转换为《可穿戴科技》标签。同义词并不是真的标签。**

path: chapters/1.10 - META/10 - 阅读器技术文档/1 - 标签规格文档.md


path: chapters/1.10 - META/10 - 阅读器技术文档/2 - 推荐算法.md


code:
# 撰稿须知及简易 Markdown 教程
> 如果你在 2021 年 5 月 16 日之前阅读过本须知，你可以转到这里查看[更新内容](./撰稿规则更新.html)。

撰稿是指读者编写文章并发布在《可穿戴科技》的行为。

在撰写你为《可穿戴科技》编写的稿件之前，请务必通读该页面所有内容（5 ~ 10 分钟）。

## 0. 《可穿戴科技》不是普通的文章发布平台
在开始之前，请明白一点：《可穿戴科技》不是普通的文章发布平台，也不是文章转载平台。**你写的文章不是发布在《可穿戴科技》，而是变成了《可穿戴科技》的一部分**。你可以把《可穿戴科技》想象成一个由多个作者共同编写的文集，撰稿就是加入其他作者共同编写《可穿戴科技》。

这意味着为《可穿戴科技》撰稿要求《可穿戴科技》必须是你的文章的**唯一**首发地。所有在其他地方发布必须是以转载的形式进行，并在转载时在标明作者是你的基础上，写上来源《可穿戴科技》。

需要澄清的是，尽管这么说，**你依然是你撰写的稿件的作者并持有其完整版权**。如果你想要事后转到其他地方作为首发，你只需要通知一下琳将你的文章从《可穿戴科技》移除即可。

与此同时，因为《可穿戴科技》是以 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 协议发布的，你撰写的文章作为《可穿戴科技》的一部分，也将以这个协议发布。然而，由于你依然是你编写的文章的版权持有者，如果你之后把你的文章从《可穿戴科技》撤出了，你可以届时使用任何你想用的协议重新发布。

如果你选择发布在其他平台（比如你的博客），你可以选择联系琳并交换友情链接。

## 1. 文章内容要求
- 文章内容不需要与可穿戴科技有关，但必须含有显著的色情内容。
- 撰稿内容没有硬性字数限制，讲清楚就好。
- 所有参与色情活动的角色不得为儿童。
  - 如果指明了年龄，年龄必须为 18 岁及以上，但是你可以写 18 岁高中生的色情故事。
  - 这条规则是为了防止法律问题。
- 不能含有过分血腥猎奇内容。
  - 所有主线里面出现的玩法都可以出现。
  - 不允许的内容包括但不限于：固体排泄物食用，肢解，食人，恋尸等。
  - 如果对于某个玩法是否属于血腥猎奇有疑问，欢迎询问琳。
  - 这条规则是为了照顾大多数读者的口味。人的性癖是自由的，我不会批判你喜欢的内容，但是我要保证大多数读者不会被雷到。

## 2. 撰稿类型
撰稿一般情况下分成四大类：**普通撰稿**，**道具集撰稿**，**小作品撰稿**，及**微小说撰稿**。

- **普通撰稿**：本类目包含最标准的色情小说，即描述一个色情故事的小说。
  - 普通撰稿的文章将会被放置于[根目录内](#/menu/章节选择/所有章节)。
- **道具集撰稿**：本类目包含的内容通常是关于某个色情道具的描述。
  - 相较于普通撰稿，道具集撰稿不需要有完整的故事。此外题材不一定要是可穿戴的。
  - 小道具，大型器械，甚至是抽象概念都是可以的。
  - 为了体现道具集的非线性，你可以为你的作品选择一个正整数序号（请不要选一个超出 [JavaScript 限制](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)的）。如果你的作品叫做“强制灌食器”，你选择的序号是 50，请将文件名保存为“50 - 强制灌食器.md”。
  - 道具集撰稿的文章将会被放置于[文件夹“道具集”内](#/menu/章节选择/所有章节/道具集)。
- **小作品撰稿**：本类目包含的内容以生草文或创意文为主。
  - **该分类仅对之前撰稿过的作者开放**。
  - 此类型的撰稿只要与色情沾边即可。
  - 小作品撰稿的文章将会被放置于[文件夹“小作品”内](#/menu/章节选择/所有章节/小作品)。
- **微小说撰稿**：本类目包含篇幅特别短（1500 字以下）的色情小说。
  - 微小说撰稿的文章将会被放置于[文件夹“微小说集”内](#/menu/章节选择/所有章节/微小说集)。

此外，如果你预计你的文章字数很多，你可以选择分成多个章节。如果需要分章节，请私聊琳。

如果你要编写的内容不属于这三大类，请私聊琳，她会帮你想想看有没有啥别的地方可以放。

在正式开始撰稿之前，请先阅读一下这篇[色情小说编写基本排坑指引](./色情小说编写基本排坑指引.html)，里面列出了色情小说作者经常会犯的一些错误。

此外，虽然不强制，**但是强烈建议在开始撰稿前，尤其是第一次撰稿前，私聊通知琳**（在 Telegram 上面发条消息就好了，比如：“琳姐，我有个绝妙的点子，想撰写一篇文。我的点子大概是：xxx，你看成不？”）。琳会根据经验尽可能帮你避免一些其他的可能会踩的坑。

## 3. 简易 Markdown 教程
在《可穿戴科技》撰稿，你需要使用 [Markdown](https://zh.wikipedia.org/wiki/Markdown)。

如果你不知道 Markdown，Markdown 是一门非常简单的标记语言。因此，请花几分钟阅读这篇专门为《可穿戴科技》撰稿人编写的[简易 Markdown 教程](./简易-Markdown-教程.html)。如果有什么不懂的，欢迎私聊琳询问。

## 4. 基本排版规则
学会了 Markdown，这边还有几个比较重要的排版规则。其中很多规则都是从[中文文案排版指北](https://github.com/mzlogin/chinese-copywriting-guidelines)继承来的。这些规则都不复杂，但是遵循这些规则可以让你的文章看起来舒服许多。

### 中文英文之间需要空格
当你的小说中需要使用英文单词的时候，请确保其前后都有一个空格。

```
正确：于是，琳的放置 Play 开始了。
错误：于是，琳的放置Play开始了。
```

### 英文单位整体前后加空格，中文单位数字前后加空格
尽管在英文语境中，数字和英文单位之间通常是要加空格的（[来源](https://physics.nist.gov/cuu/Units/checklist.html)）。但是在中文语境中，数字和英文单位之间添加空格会看起来十分奇怪。因此，在这里，数字和英文单位视为一个整体：它们之间不加空格，而数字前、单位后都加空格。

```
正确：琳眼看着 200cc 的液体被缓缓注入了自己的体内。
错误：琳眼看着 200 cc 的液体被缓缓注入了自己的体内。
错误：琳眼看着200cc的液体被缓缓注入了自己的体内。
```

然而，当单位是中文的时候，不使用空格又变得很奇怪了。因此中文单位前则一律都使用空格，数字前也加空格，但是单位后不加空格。

```
正确：琳眼看着 200 毫升的液体被缓缓注入了自己的体内。
错误：琳眼看着 200毫升的液体被缓缓注入了自己的体内。
错误：琳眼看着200毫升 的液体被缓缓注入了自己的体内。
错误：琳眼看着 200毫升 的液体被缓缓注入了自己的体内。
```

### 全角标点前后一律不加空格
全角标点就是在中文输入法下打出的标点（`。`、`，`、`！`等）。这些标点因为本身已经看上去非常空旷了，它们前后无论发生什么都不需要添加空格。这一条覆盖前两条的指示。

```
正确：瞬间，琳捂着肚子瘫倒在了地上。
错误：瞬间， 琳捂着肚子瘫倒在了地上。
错误：瞬间 ，琳捂着肚子瘫倒在了地上 。
```

```
正确：实话实说，2000cc 已经是她的极限了。
错误：实话实说， 2000cc 已经是她的极限了。
正确：实话实说，2000 毫升已经是她的极限了。
错误：实话实说， 2000 毫升已经是她的极限了。
```

## 5. 注明作者
在提交前，请首先确保你的名字写在文章的标题前。假如你是李华，请这样写：

```markdown
作者：李华

# 文章标题
正文第一自然段...
```

## 6. 提交
将你的文章发布到《可穿戴科技》需要在 GitLab.com 发起一个 Merge Request。因此，你首先需要确保你已经登录了 GitLab.com。如果你没有 GitLab.com 账号，请到[这里](https://gitlab.com/users/sign_in#register-pane)注册一个。

这之后，根据你的撰稿类型，进入相应的章节文件夹：

- 普通撰稿进入[此文件夹](https://gitlab.com/SCLeo/wearable-technology/-/tree/master/chapters)。
- 道具集撰稿进入[此文件夹](https://gitlab.com/SCLeo/wearable-technology/-/tree/master/chapters/1.1%20-%20%E9%81%93%E5%85%B7%E9%9B%86)。
- 小作品撰稿进入[此文件夹](https://gitlab.com/SCLeo/wearable-technology/-/tree/master/chapters/1.2%20-%20%E5%B0%8F%E4%BD%9C%E5%93%81)。
- 微小说撰稿进入[此文件夹](https://gitlab.com/SCLeo/wearable-technology/-/tree/master/chapters/1.3%20-%20%E5%BE%AE%E5%B0%8F%E8%AF%B4%E9%9B%86)

如下图，点击上方加号，然后点击 `New file`。

![](./提交-1.png)

此时，GitLab.com 会说“Forking in progress”。请耐心等候片刻。

Forking 结束后，你需要把你的文章复制进 GitLab 的编辑器中。

**请注意，你的文件名必须是这个格式：编号、空格、短横（`-`）、空格、名字、`.md`！** 请务必再次注意，短横（`-`）前后有空格，文件结尾必须是 `.md`。如果稍有写错，这个文件将无法被《可穿戴科技》网页构建脚本正确读取。

- 如果你的撰稿类型是普通、小作品或者微小说，那么编号就是下一篇的编号（具体是多少琳会告诉你），名字就是你的小说的名字。举个例子：`2 - 新能源.md`。
- 如果你的撰稿类型是道具集，那么编号就是你的道具编号，名字就是你的道具名字。举个例子：`50 - 强制灌食器.md`。

![](./提交-2.png)

这之后，你需要开启一个合并请求，即请求琳将你编写的文章加入到《可穿戴科技》中。

![](./提交-3.png)

这样，一个 Merge Request 就会被创建。你现在只需要等待琳阅读你的杰作然后批准就好了。如果琳一直没有操作，你可以在群聊/私聊里面喊她一下。

path: chapters/1.10 - META/2 - 撰稿须知及简易 Markdown 教程.md


code:
# 隐藏
# 撰稿规则更新
# 2021.5.16
- 开放“微小说撰稿”。微小说撰稿接受篇幅特别短（1500 字以下）的色情小说。

## 2021.5.6
- 为了防止新作者误以为“支线”是“主线”的分支，现在起，取消“支线”的概念。之前所有的支线撰稿直接从支线文件夹移出至根目录，并与主线文件夹同级。
- 取消撰稿硬性字数限制。现在起，所有类型撰稿只要讲清楚了就可以。
- 开放“小作品撰稿”。**小作品撰稿仅对之前撰稿过的作者开放**。小作品撰稿以生草文或创意文为主。

path: chapters/1.10 - META/2.0.1 - 撰稿规则更新.md


code:
作者：琳 缇佩斯，0 级骨钉

# 色情小说编写基本排坑指引
本文收录了常见的，编写色情小说时容易踩的坑。即使在读了本文后 —— 尤其是第一次写色情小说时 —— 依然强烈建议在动笔前将你的点子发给琳看一下。

## 第一大坑：不够色（WTP001）
关于这点，希望大家能明白一点。色情小说和其他小说不一样。色情小说是为了满足人类的性欲的。在色情小说中，色情是核心。剧情、人物、风格都是为了色情服务的。这一点千万不能倒过来。

当然，这不是说剧情就不重要了。好的剧情能更出色地衬托你的色情内容。如果完全没有剧情，反而会让人觉得故事发生在真空里，丧失代入感。

当你在编写一篇短篇的时候（比如道具集），你应该从一个色情点出发 —— 比如想到了一个很棒的玩法 —— 然后围绕着这个色情点展开故事。同时要注意故事部分尽量不要过分复杂，以免喧宾夺主。

编写长篇也是一个道理。你应该先构思几个色情点，然后再想办法用故事把它们串起来。

相反地，**千万不要先编了一个正常的故事，再想办法往故事里面添加色情要素**。这样基本是行不通的。

## 第二大坑：文爱记录（WTP002）
这个坑是指把文爱记录当成色情小说发表的。这也包括几个人通过互动的方式共同一篇色情小说（A：我把你的手铐住了 B：我开始努力挣扎）。这是一个非常糟糕的主意。

造成这类文章很糟糕的原因有很多：

首先，文爱因为即时性，导致双方在决定玩法的时候考虑的时间很短，以至于想出来的玩法都非常平庸，或者都是一些烂大街的玩法。

其次，还是因为即时性，文爱时所使用的语言通常都严重缺乏描写，都是一些干巴巴的动作描写。心理描写之类的都会非常缺乏。

再则，文爱时因为对方的行为是另一个人控制的，你并不能预测接下来发生的事情，因此也不能对后续色情内容的发展做出有效规划，以至于不太可能勾勒出那种真正让人心头一震的色情故事。

文爱记录类型的色情小说的缺点还有很多别的。这里不列举了。总之，请避免编写此类文章。

## 第三大坑：自嗨（WTP003）
自嗨是指一篇文章的时候自己感觉很色，但是写出来读者看了效果不太好。

写小说，尤其是色情小说，一定要注重读者的感受。*Moby Dick* 出名不是因为作者一意孤行，而是因为后来人慢慢觉得这部小说有价值了。

目前看来，造成自嗨的主要原因是过分着重描写自己想写的内容，而忽略了组成色情故事的其他要素，以至于读者读起来一头雾水，完全无法带入。

比如有的文章中，两个人明明刚刚才碰到，没有什么互动或者一起的剧情也不多，但是莫名其妙地关系就变得相当好了。这就是典型的自嗨：在作者脑海中，两个人的关系已经变得很好了，但是文字上却完全没有表现出来。

## 第四大坑：万能性（WTP004）
万能性是在小说中使用了一些过分万能的道具/设定。

万能性会使精巧的设计失去意义。

万能性的例子有很多，比如一些过分灵活的魔法都具有很强的万能性。但是所有万能性中最常见的例子就是机械臂。

比如，当你有了机械臂以后，做什么事情都可以通过机械臂完成了 —— 原本可能是用活塞控制的按摩棒，或是嵌在鞋底的转轮，现在都可以用机械臂来做到了。

去除万能性的方法也很简单，那就是想办法用别的更局限的方法来解决同样的问题。比如需要一个上下活动的按摩棒，用机械臂当然可以完成，但是如果不用机械臂，而是用一个简单的活塞，岂不是也可以。或者再比如，需要将女主的四肢张开，用四条机械臂当然可以完成，但是如果不用机械臂，而是四个铐环，岂不是也可以。将文章中的机械臂都替换以后，你的文章就会变得更加有意思，而不是一味的依赖这个万能性道具/设定来做所有的事情。

当然，这条规则不是绝对的。万能性可以当作一个“逃生舱口（escape hatch）”。如果**实在想不出来**不用机械臂怎么解决问题，或者说需要解决的问题并不重要，那么适量地使用一些万能性也是可以的。

## 第五大坑：沉迷于设定（WTP005）
相信很多人都有过这种想法：设定一个或大或小的世界观或者小舞台，然后填充进去海量的细节和复杂的规则，并且有着一副写出一篇“传世之作”的雄心壮志。然而当你准备下笔，开始描绘这个故事的时候，却发现迟迟动不了笔，甚至连开启这个故事的想法都没有了。

久而久之，这个点子就被抛进了角落里，只有偶尔想起来时，才意识到自己曾经想过写这样一篇东西。

过度的设定往往会让人忽略情节的发展，毕竟一篇 h 文终究要有情节来推动。而繁复的设定与自由的情节畅想是冲突的，当你真正要写一篇故事时，那些设定大多都会变成累赘。或是在设定的限制下无法尽情地构思剧情。

所以如果想要写一篇完整的文，最大的建议就是先考虑情节和人物，再补齐设定。当你心中有了一个足够精彩的故事或是足够 h 的玩法，那些设定根本不是重点。

对于新手而言，另一个建议就是刚开始写文不要把世界观放的那么大，可以先以自己最喜欢的 h 要素构思一篇不太长的文，在大脑中想象你要写下的人物、故事、**结局**和穿插的 h 情节，然后再将它写下来。有了第一次的经验，之后的创作之路会通常许多。

## 第六大坑：过度随意的表述（WTP006）
过度随意的表述是指使用平时聊天的口吻来编写色情小说，包括但不限于：大量使用括号，网络用词（例如：233），滥用标点符号（例如：句子结束不用句号），玩梗过度，等。

当表述过度随意时，读者可能会觉得自己在和作者交流，而不是在探索作者创造出来的世界。如果你在写博客，这当然没问题。然而，如果是在写色情小说，这将会导致读者出戏，难以代入，从而降低色情程度。

path: chapters/1.10 - META/2.1 - 色情小说编写基本排坑指引.md


code:
# 隐藏
# 简易 Markdown 教程
Markdown 是一门非常简单的标记语言。其语法非常简单，因此你只需要花费几分钟就能学会。考虑到网上 Markdown 的简单教程不多，这里介绍一下 Markdown 的基本使用。

Markdown 说白了就是给一个 txt 文本文件加一点井号，星号，或是其他标点符号来告诉阅读器哪几行字是标题，哪几行字要加粗之类的。

举个例子，如果你有一个文本文件 `a.txt`：

```
陈志鹏爱吃玻璃。他真的非常非常喜欢吃玻璃。对于陈志鹏来说，吃玻璃是这世界上最美好的事情。他完全无法想象没有玻璃吃的生活。
```

然后你把这个文本文件的后缀改成 `.md`，使之变成 `a.md`，恭喜你，你已经创建了一个合法的 Markdown 文件。

然而，这个 Markdown 文件一点意思也没有，因为它被用户打开的时候，什么特殊样式都没有。为了给你的 Markdown 文档加点特色，接下来讲 4 个最基础的操作。

## 自然段
无论是正常写作还是在互联网上写作，你都需要把你的文章分割成多个自然段。如果你要新建一个自然段，请在两段话之间加一个空行。举个例子：

```markdown
陈志鹏爱吃玻璃。他真的非常非常喜欢吃玻璃。对于陈志鹏来说，吃玻璃是这世界上最美好的事情。他完全无法想象没有玻璃吃的生活。

直到有一天，玻璃涨价了。
```

这样，你就有两个自然段了。请注意一个空行的意思是按两下回车。只按一下回车或者按三下回车都是错误的。

同时，请务必注意，在规范的写作里是几乎没有纯换行的（换行，但是不进入下一个自然段）。~~因此，Markdown 不支持纯换行。~~*更正：Markdown 支持通过在行尾添加两个空格的方式添加纯换行，但是请避免使用这个功能。* 不过无论如何，请永远不要只使用一个回车。**比如下述例子就是错误的，它渲染出来的结果是两行文字会出现在同一行，完全没有换行。**

```markdown
陈志鹏爱吃玻璃。
上一句话是真的。
```

## 加粗
如果你想把这句话中的`陈志鹏`三个字加粗，因为这只是一个平凡的文本文件，所以你肯定不能像 Word 一样去点工具栏上的 **B**。在 Markdown，如果你要加粗一个词，你只需要在这个词两边加上两个星号，比如：

```markdown
**陈志鹏**爱吃玻璃。
```

这样，当读者在阅读你这篇文章的时候就会发现陈志鹏三个字变成粗体了。怎么样，是不是很简单？

## 标题
几乎每篇文章都有标题，甚至子标题。若要添加主标题，请依次输入井号、**空格**、标题内容。举个例子，如果你要添加一个内容为“陈志鹏的兴趣爱好”的标题，你只需要这么写：

```markdown
# 陈志鹏的兴趣爱好
陈志鹏爱吃玻璃。他真的非常非常喜欢吃玻璃。对于陈志鹏来说，吃玻璃是这世界上最美好的事情。他完全无法想象没有玻璃吃的生活。

直到有一天，玻璃涨价了。
```

需要注意的是
1. 井号后面有一个空格。
2. 标题和正文第一个自然段之间没有空行。

如果你要添加子标题（比主标题字小一点的标题，用来创建树状结构），你可以使用多个井号。井号数量越多，这个标题越小（最多六个）。举个例子：

```markdown
# 陈志鹏的兴趣爱好
## 第一章
陈志鹏爱吃玻璃。他真的非常非常喜欢吃玻璃。对于陈志鹏来说，吃玻璃是这世界上最美好的事情。他完全无法想象没有玻璃吃的生活。

## 第二章
直到有一天，玻璃涨价了。
```

需要注意的是，一级标题（`# 陈志鹏的兴趣爱好`）和二级标题（`## 第一章`）之间没有空行，而二级标题（`## 第二章`）之前有空行。简而言之就是：

1. 标题和标题之间没有空行。
2. 标题过渡到自然段没有空行。
3. 自然段过渡到标题有空行。
4. 自然段和自然段之间有空行。

或者说，只有在每个自然段之后都才有空行。

## 链接
有时候你可能会希望添加超链接。在 Markdown 里，超链接的格式长这样：`[链接的文字](链接网址)`。举个例子：

```markdown
点[这里](https://www.google.com)来打开谷歌。
```

这样，“这里”两个字就会变成可以点的超链接，点击后就会打开谷歌。

## 总结
Markdown 本身功能也不强大，但是除了这边介绍的还是有很多其他功能的。然而学会了这边列出的四个基本功能，编写色情小说应该是足够了。通常情况下，编写 Markdown 使用任何文本编辑器就绰绰有余了。如果你希望可视化，可以试一下以下几个网页版的 Markdown 编辑器：

- [Arya - 在线 Markdown 编辑器](https://markdown.lovejade.cn/)
- [StackEdit](https://stackedit.io/)
- [Dillinger](https://dillinger.io/)

如果想要学习其他的 Markdown 语法，你可以查看这篇[发布在掘金上的教程](https://juejin.im/post/5ad6e09e518825557b4d451c)。

path: chapters/1.10 - META/2.2 - 简易 Markdown 教程.md


code:
# 隐私政策
简而言之，《可穿戴科技》不会收集任何个人信息。

《可穿戴科技》的作者琳非常想知道她的小说有多少人读了，以满足她的虚荣心。与此同时，琳非常重视隐私保护，因此她决定不使用任何现成的数据统计服务（Google Analytics 等），而是自己编写了一个开源的，极度重视隐私保护的数据统计系统。

这个统计系统只会记录在何时哪个章节被访问了，而不会记录你的任何信息。

未来，《可穿戴科技》可能会记录你是通过哪个镜像站访问的，以便琳去感谢镜像站的提供者，但是目前还没有实装。

你可以在[这里](https://github.com/SCLeoX/wt_analytics)找到统计系统服务端的源代码。

path: chapters/1.10 - META/3 - 隐私政策.md


code:
作者：琳 缇佩斯（金牌得主），0 级骨钉（银牌得主），城岭 樱（铜牌得主）

# 第一届西塔杯评选 - 已结束
西塔杯是《可穿戴科技》计划于每年年底举行的年度最佳已完结文章评选活动。《可穿戴科技》内所有已完结的文章都会参与评选。

以下为前十评选结果：

| 名次 | 作品 | 平均分 | 作者 |
|------|------|--------|------|
| 1    | [长期贞操带实验第七次定期报告](./../长期贞操带实验第七次定期报告/第一幕.html) | 4.433 | [琳 缇佩斯](#/page/author/琳%20缇佩斯) |
| 2    | [长期贞操带实验定期报告（番外）](./../长期贞操带实验定期报告（番外）.html) | 4.215 | [0 级骨钉](#/page/author/0%20级骨钉) |
| 3    | [新能源：美妙的旅程](./../新能源：美妙的旅程.html) | 4.094 | [0 级骨钉](#/page/author/0%20级骨钉) |
| 4    | [城岭樱的游街仪式](./../城岭樱的游街仪式.html) | 4.092 | [城岭 樱](#/page/author/城岭%20樱) |
| 5    | [新能源](./../新能源.html) | 4.054 | [琳 缇佩斯](#/page/author/琳%20缇佩斯) |
| 6    | [可穿戴科技的其他版本 · Flexible (Episode 1)](./../道具集/可穿戴科技的其他版本-·-Flexible-(Episode-1).html) | 3.905 | [Darkside](#/page/author/Darkside) |
| 7    | [疼痛实验记录](./../疼痛实验记录/第一次调研报告.html) | 3.828 | [城岭 樱](#/page/author/城岭%20樱) |
| 8    | [可穿戴装置 γ 型介绍](./../道具集/可穿戴装置-γ-型介绍.html) | 3.814 | [伶 若萱](#/page/author/伶%20若萱) |
| 9    | [现代女仆管理系统](./../道具集/现代女仆管理系统.html) | 3.795 | [琳 缇佩斯](#/page/author/琳%20缇佩斯) |
| 10    | [梦野与千夏](./../梦野与千夏.html) | 3.772 | [0 级骨钉](#/page/author/0%20级骨钉) |

获胜作者（由排名最高的作品名次决定）：

- 金牌：琳 缇佩斯
- 银牌：0 级骨钉
- 铜牌：城岭 樱

## 奖品
因为琳获得了第一名，并且目前来看《Cyberpunk 2077》风评不佳，奖品改为赠送第二名及第三名等价 3A 游戏各一份。

~~投票分数最高的文章作者将获得 Steam 版《Cyberpunk 2077》一份。如果第一名作者无法联系到，则顺延至下一位。~~

# 以下为原评选描述
## 评选时间
参与评选的稿件将截止于北京时间 2020 年 12 月 3 日 23 时。所有于该时间点前完成的稿件都将参与评选。截稿时间过后将会开始时长 28 天（4 个星期）的投票期。投票将于北京时间 2020 年 12 月 31 日 23 时结束。投票结果将在投票期过后尽快公布。

## 参与条件
所有在截稿期（北京时间 2020 年 12 月 3 日 23 时）之前提交的**已完结的**文章都会自动参与。这意味着《可穿戴科技》主线将不会参与评选。如果你希望参加，但是还没有被《可穿戴科技》收录的文章，请参考[撰稿须知及简易 Markdown 教程](./撰稿须知及简易-Markdown-教程.html)。

path: chapters/1.10 - META/4 - 第一届西塔杯评选.md


code:
作者：琳 缇佩斯

# 琳的“足疗”馆
因生活所迫，琳开了一家“足疗”馆。这家足疗馆和别的足疗馆并不一样。在琳的“足疗”馆里，客人会付钱躺在床上，然后让服务人员 —— 琳 —— 穿着丝袜或是别的东西踩他们。怎么样，你一定也很想被琳踩吧？如果是这样的话，赶紧下载价目表并在感兴趣的项目前打勾吧~

[琳的“足疗”馆价目表](./琳的“足疗”馆价目表.pdf)

path: chapters/1.2 - 小作品/1 - 琳的“足疗馆”.md


code:
作者：琳 缇佩斯

# 戴着可穿戴科技参加 AP 考试
今天考 AP。

监考老师：Good afternoon. According to College Board regulations, the following electronic equipment is prohibited: phones, smartwatches or any other wearable technology...

我：*Raises hand

监考老师：Do you have a question?

我：Yes... Did you say wearable technology?

监考老师：I did say that. You do have to take off any of your wearable technology.

我：...Here? In front of everyone?

监考老师：Yes.

我：*Slowly takes off my skirt

...

我：I don't have the key... The chastity belt won't come off...

监考老师：...If that is the case, you can keep it...

我：Oh, thanks!

监考老师：...And you can pull your skirt back up...

...

考试中，

我：*moans

监考老师：Are you okay?

我：S... Sorry, there are v... vibrators inside me... I am okay...

我：*orgasms and moans loudly immediately

其他考生：*stare at me

监考老师：If you are okay, can you try to keep your voice down? Others are trying to focus here.

其他考生：We are fine. She can make whatever noise she wants.

...

考试结束，

College Board：*Disqualifies everyone's score

其他考生：Worth it.

path: chapters/1.2 - 小作品/2 - 戴着可穿戴科技参加 AP 考试.md


code:
作者：acted咕咕喵

# 《喵喵，新闻！》
2333年第一期修订1

## 导览
### 头条新闻
1. 尖端技术的背后，秋晴大学校内秘闻
2. 光明世界的暗部，秩维之外的暗流涌动
3. 独家特报，前秋晴大学**优秀**学子开除内幕

### 今日美食
红茶与蛋糕搭配的下午茶

## 头条新闻
### 尖端技术的背后，秋晴大学校内秘闻。
秋晴大学，是闻名于世界的全科 911 特别学习院校。以其著名的开放、自由和高学术成就而闻名于世界。其管理水平也是全世界各地争相追求的目标。井井有条的课程秩序，宿舍同学彬彬有礼的待人处事为人之态度。也侧面印证了这所高等学府的魅力和水准。

其宿舍管理，是在招生官网上首页大幅宣传的一颗璀璨明珠。明面上，学校以“方便管理”和“拉平社会差距”为理由，所有在校生均享受且必须在学校的免费公立宿舍中居住。其宽阔如总统套房的空间大小，独立厕所、卫浴；全屋全自动清洁系统。无处不彰显着人性关怀和资源丰富的能力所在。

然而，在本报唯一记者咕咕喵的探寻中，竟发现其远远不止这么简单。

通过部分技术手段，我们得以访问其封闭已久的高速内部网络。从智能爬虫的检索结果而言，这所高等学府，说其作为新时代的高等 RBQ 培养所完全不为过。全校大部分师生在入宿之时，即收到多种色情卡片及视频。从各种方面，各种 XP， 学校网站完全可以满足。无论是已经绒布球化久了的淫荡绒布球，或是连才自慰都没了解过的正直的人，都有专业的背后团队运作以将其引导至穿着名为“可穿戴科技”的装置。

而在“可穿戴科技”的背后，本报还发现了学校和这个名为“第七次定期”组织的具体联系。凡是其组织成员，均可轻松获取到该学校号称宇宙第一的防卫系统的全部通行权限。从自动保卫到秩维，对持有该组织证明文件的人员竟全部放行。由此可见，这所大学，绝不仅仅是为了培养一般的人才而设立。

本报记者同时隐秘的对部分“可穿戴科技”的受害者进行了一定的跟踪。成功获取到了一台可穿戴科技公司发来的设备。通过对设备的逆向工程研究发现，其宣称的“AI 人工智能控制”的背后是一个庞大的传销性质的组织，通过控制拉人进入可穿戴科技计划以减少设备对自己的惩罚。~~（本来就是自己想被惩罚却祈求饶恕，也真是有意思呢）~~

在取材的过程中，本报记者遭到了秩维的驱逐。但有很多疑似可穿戴科技工作人员和等级较高的受害者其对本报记者的猫娘形态疑似有极其深刻的兴趣。看来涩情果然是臭味相投，连猫娘都不放过的高等学府，实质一定是淫荡学府。

### “光明”世界的暗部，秩维之外的暗流涌动
在可穿戴科技的总部，阴暗的小巷子中。时长传出骇人听闻的惨叫声。同时，哪怕再高明的清洁手段，也难以掩盖血渍和亡灵的飘荡。

明面上如“学园都市”一样安全的先进社区，其背后究竟隐藏了多少奥秘，可能无人知晓。但请跟随本报记者咕咕喵的脚步，一起初探这惊人的黑幕吧！

在 SVR-1 街道上，看似为贫民区的地方很少有秩维的出现，哪怕出现了死人，亦是由死者的“家属”亲自将其送出贫民区，在棺椁中封存好，才交由秩维处理。然而，秩维似乎故意的忽略去通过实人认证或打开棺椁来确认尸体的状态。

本报记者和隔壁~~某文~~记者借来了高清透视摄像机后，成功捕捉到了其棺椁的内幕——其内部根本不是一具，而是多具不成样子的血浆和肢体组成的血腥肉块。

通过本报记者的深入探访，恶意借贷组织“不可穿戴科技”——一家不起眼但是负债率很高的网贷企业。通过向未成年少女乃至幼女发放高额高利贷款和诱导他们穿戴类似“可穿戴科技”的多种昂贵型号来控制奴隶。最终的目的无非两个：以彻底毁灭这个人并做成人棍供部分恶趣味的人玩耍。

让事态更加有意思的是，在河童重工的帮助下，本报记者成功追踪到了某可穿戴科技佩戴者女仆也出入于本街区，甚至亲手将两位恶意要债嫌疑犯（姑且称之为“嫌疑犯”，毕竟这俩实际上也是罪行累累）通过一白色钝物虐杀。

在整个制度体系下，淫荡和暴行充斥于整个社会。其背后的可穿戴科技和诸多大中小黑恶势力错综复杂的交汇，可能不仅是简单的同流合污……

### 独家特报，前秋晴大学**优秀**学子开除内幕
在秋晴大学前，一队人正驻足观看全新的处分通知：琳·缇佩斯，因在校内举止不雅，有伤风化，被处以开除学籍的处分通告。

从少女们红色的脸庞和震动着的裙摆可以看出，对于这所“淫荡”高校而言，单单对一个学生的驱逐令是不可能有成效的。但理事会和德育处如此坚决和速度的决定，也侧面印证了黑幕的存在。

在本报记者的求实精神追问下，一个匿名学生缓缓透露出：琳·缇佩斯，实际上完全不是在这个社区土生土长的，而是在小学毕业时举家搬迁来到这里的！这对于这个对全国严格保密的地区而言，可谓是一个重要信息。

学生和家长分离、高强度的隐私保护虽然在这个城市是一种常态，但作为外来人士的琳，不可能没有在外部留下的蛛丝马迹。在本报记者的综合探寻下，无论是琳的身世，还是和他一直在一起的晴雪惠，或是目前可能和她在一起的某秋姓博士（该博士的身份更加耐人寻味，但受制于顺着网线砍过来的秩维，调查只能到此为止了）

就当前本报记者探寻之力，成功破解了可穿戴科技的网络定位系统来看，该二人目前正关押于一个废弃仓库中。然而就定位而言，已经超过一个月没有移动了。因此也不排除他们身上的可穿戴科技被移除了——而这种权限只有可穿戴科技的高等级管理人士才有。结论显而易见：她们正被拘束在某个地方。

而联系近期发生在“某街区的恐怖袭击，对秩维人员使用了非致命性武器”的新闻来看，晴雪慧的休学、琳·缇佩斯的消失、石倚的居家……种种迹象都表明，这些看似孤立的事件，必然是紧密相连的。

如果让喵来指明的话，那么可能性也只有一个：陈志朋

虽然表面上是一个年轻的猥琐学生，但其不但在此次事件中完全没有受到任何处分，反而消掉了几门不及格科目的补考。再加上，从其通讯分析来看，和琳、秋、晴之间不断的短小、固定时间、高度加密的通讯。

如果说加害者是晴雪慧、秋博士的话，也看得出，其与琳之间亲密无间，交换遥控的关系。是不可能如此的。因此，本报记者大胆猜想：琳的入境，是陈志朋一手操办而成。而无论是晴还是秋，不过是陈志朋安插在琳·缇佩斯周围的耳目而已。只有这样，这一系列看似无关实则紧密链接，一步步将琳·缇佩斯推入 rbq 深渊的操作，才能得以实现！

## 今日美食
> 无论如何，我们还是成功采访到了石倚女仆来为我们讲解红茶和蛋糕的完美搭配。
> “那么，事不宜迟。请问如何搭配蛋糕和红茶才能做出美味的下午茶呢？”

“从茶叶的选取来看，我们要精选大吉岭等英国红茶。这种红茶茶味较轻，更能凸显出 xu，啊不，食品的风味。”

“每天冲泡红茶时，要注意水温不要太热，否则会出现沉底的现象。而且，会丧失很多茶叶中的营养物质。因此，最接近人体温度的 37 摄氏度是最适合冲泡红茶的。同理，调制用茶制成的蛋糕也是如此……”

> 无论怎么说，总觉得这种茶里面，有一种罪恶的味道呢。。。

path: chapters/1.2 - 小作品/3 - 《喵喵，新闻！》.md


code:
- 作者：NanayaSakuya
- 标签：含有女性，未来

# 魔镜
“七夜，看看我最新做出的东西。”咲夜拿出了一个直径有二十厘米的圆形物件，上面有着繁复的花纹。

“好家伙，你集成了多少东西啊。”七夜接过来看，发现是一面镜子。

“不清楚，反正我每天的补水量增加了一倍。我们部门有一个试着供能，短短十几秒爱液流了一大摊，预计能装满一个纸杯。”

“您这可得注意一下身体啊。”

“顺便，我把它叫做魔镜，你该懂怎么用了吧。”

“我来试试。”

七夜饶有兴致地对魔镜说道：“魔镜啊魔镜，谁是这个世界上最 M 的人？”

预料之外的，七夜看到的是一个绿色头发的双马尾少女。

“这是年轻时候的琳？”看着镜子里身穿 Ribbon 型中控，不停在受苦的绿色双马尾少女，咲夜有些迷惑。

“长得是蛮像，但琳不是银白色头发吗？怎么是绿色的，不过 M 度倒是依然这么高。还有，你加的术式为什么还会检索到过去的人啊？”

“前几天刚试出来的，要不要教你？顺带一提我还……”

“技术内容请去技术群。”镜子里的琳突然说道。

白光一闪，镜子恢复成原状，两人的嘴里则出现了口球。

“……？”

“……？”

path: chapters/1.2 - 小作品/4 - 魔镜.md