From aaac31f1668fe5516902ee7d2034e5c0e41667a6 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 13 Jul 2022 16:37:06 -0400 Subject: Support line breaks inside brackets in markdown BQN evaluation --- doc/arrayrepr.md | 25 +++++++++++----------- doc/block.md | 32 ++++++++++++++++++---------- doc/functional.md | 18 +++++++--------- doc/oop.md | 35 +++++++++++++----------------- doc/quick.md | 25 +++++++++++----------- docs/doc/arrayrepr.html | 28 ++++++++++++------------ docs/doc/block.html | 34 ++++++++++++++++++++---------- docs/doc/functional.html | 22 +++++++++---------- docs/doc/oop.html | 42 ++++++++++++++++++------------------ docs/doc/quick.html | 30 +++++++++++++------------- md.bqn | 55 ++++++++++++++++++++++++++++++++---------------- 11 files changed, 190 insertions(+), 156 deletions(-) diff --git a/doc/arrayrepr.md b/doc/arrayrepr.md index cc437ed1..04ce96f4 100644 --- a/doc/arrayrepr.md +++ b/doc/arrayrepr.md @@ -24,7 +24,8 @@ Array displays show only the array shape and elements. The [fill](fill.md) is an Those top-left and bottom-right corners are a distinctive part of BQN's display, as other systems almost always completely enclose the contents. BQN could add the other two corners, naturally; it just doesn't. Within the corners, elements are separated by whitespace only, and generally aligned to the top left. - ⟨2,"xy"⟩≍⟨2‿2⥊"abcd",4⟩ # Nested 2×2 array + [⟨2 , "xy"⟩ + ⟨2‿2⥊"abcd", 4 ⟩] The lack of extra separation is to make it clear that the corners enclose the whole array rather than any of its elements (elements are still distinguishable becase an individual element won't contain whitespace, except maybe between quotes). Every set of corners indicates one array. This is a good fit for the [based array model](based.md), where data doesn't have to be in an array. @@ -44,9 +45,9 @@ Up to one axis can be oriented horizontally, and then all the rest are laid out We've seen already that elements of a list are placed side by side, while the rows of a table (rank-2 array) are stacked on top of each other. - <¨ ↕5 # A list of units + <¨ ↕5 # A list of units - 2‿3‿4≍1‿0‿5 # A table + [2‿3‿4,1‿0‿5] # A table The 2-cells of a rank 3 array are *also* stacked on top of each other, but separated by a space. Below is a list of two examples. The second cell in the character array is marked with a `·` to indicate that the gap above it really separates cells as opposed to just being a row of space characters. @@ -110,16 +111,16 @@ Entries in a list are evaluated in source order, and the value will be the list BQN's separator rules give list notation a very flexible structure. You can put all the elements on one line or spread them across lines, with the option of adding blank lines between elements. A separator at the end of a line is never needed, but leading and trailing separators are allowed. - ⟨ - "e0", "e1" - ⟨ - 'e' - '2' - ⟩ - "e3", "e4", "e5" + ⟨ + "e0", "e1" + ⟨ + 'e' + '2' + ⟩ + "e3", "e4", "e5" - "e6" - ⟩ + "e6" + ⟩ #### High-rank arrays diff --git a/doc/block.md b/doc/block.md index 823007f1..e4d32fa4 100644 --- a/doc/block.md +++ b/doc/block.md @@ -20,7 +20,11 @@ Because they use [lexical scoping](lexical.md), blocks also encapsulate code. If In the simplest case a block is just a list of statements, which are executed to *evaluate* the block. A block with no special names like `𝕨` or `𝕩` is called an *immediate block*, and is evaluated as soon as it is reached. The only thing such a block does is group some statements, and create a scope for them so that definitions made there are discarded when the block finishes. Even this small amount of functionality could be useful; as an example the following program can build up an array from named components without polluting the rest of the program with those names. - updown ← { up←↕5 ⋄ down←⌽up ⋄ up∾down } + updown ← { + up ← ↕5 + down ← ⌽up + up∾down + } updown An immediate block is only ever evaluated once, and can't be used for control flow in a program. Special names can be used to define [functions and modifiers](ops.md), which have a broader range of uses. All special names are listed below: @@ -105,9 +109,10 @@ Because `𝕣` only ever refers to a 1-modifier or 2-modifer, it can never make As a program becomes larger, it often becomes necessary to name inputs to blocks rather than just using special names. It can also become difficult to identify what kind of block is being defined, as it requires scanning through the block for special names. A *block header*, which is separated from the body of a block by a colon `:`, specifies the kind of block and can declare names for the block and its inputs. - Fact ← { F n: - n × (0⊸<)◶1‿F n-1 - } + Fact_head ← { F n: + n × (0⊸<)◶1‿F n-1 + } + Fact_head 7 Its syntax mirrors an application of the block. As suggested by the positioning, the names given in a header apply only inside the block: for example `F` above is only defined inside the `{}` braces while `Fact` could be used either outside or inside. Some other possibilites are given below. @@ -175,7 +180,11 @@ Blocks can include more than one body, separated by semicolons `;`. The body use Bodies with headers come before any that don't have them. When a block is called, its headers are checked in order for compatibility with the arguments, and the first body with a compatible header is used. - CaseAdd ← { 2𝕊3:0‿5 ; 2𝕊𝕩:⟨1,2+𝕩⟩ ; 𝕊𝕩:2‿𝕩 } + CaseAdd ← { + 2𝕊3: 0‿5 ; + 2𝕊𝕩: ⟨1,2+𝕩⟩ ; + 𝕊𝕩: 2‿𝕩 + } 2 CaseAdd 3 @@ -191,12 +200,13 @@ If no header is compatible, the call results in an error. A special rule allows for convenient case-matching syntax for one-argument functions. In any function header with one argument, the function name can be omitted as long as the argument is *not* a plain identifier—it must be `𝕩` or a compound value like a list to distinguish it from an immediate block label. - Test ← { - "abc": "string" ; - ⟨2,b⟩: ⌽𝕩 ; - 5: "number" ; - 𝕩: "default" - } + Test ← { + "abc": "string" ; + ⟨2,b⟩: ⌽𝕩 ; + 5: "number" ; + 𝕩: "default" + } + Test 5 These case-style headers function exactly the same as if they were preceded by `𝕊`, and can be mixed with other kinds of headers. diff --git a/doc/functional.md b/doc/functional.md index cd7656ca..7f44f877 100644 --- a/doc/functional.md +++ b/doc/functional.md @@ -102,14 +102,13 @@ What does functional programming in BQN look like? How is it different from the First, let's look at the basics: a small program that has functions as its argument and result. The function `Lin` below gives a [linear approximation](https://en.wikipedia.org/wiki/Linear_approximation) to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, `𝕏`. - Lin ← { - v0 ← 𝕏 0 - v0 + ((𝕏 1) - v0) × ⊢ - } + Lin ← { + v0 ← 𝕏 0 + v0 + ((𝕏 1) - v0) × ⊢ + } We can pass it the [exponential](arithmetic.md#basic-arithmetic) function as an argument by giving it the name `Exp` and then referring to it in lowercase (that is, in a subject role). The result is a [train](train.md) that adds 1 to *e*-1 times the argument (we'll discuss only tacit functions here; for blocks see [lexical scoping](lexical.md)). - Lin ← { v0←𝕏0 ⋄ v0+((𝕏1)-v0)×⊢ } # (copy of above) Exp ← ⋆ Lin exp @@ -128,14 +127,13 @@ Not the most accurate approximation, though. Note also in this case that we could have used a modifier with a very similar definition to `Lin`. The modifier is identical in definition except that `𝕏` is replaced with `𝔽`. - _lin ↩ { - v0 ← 𝔽 0 - v0 + ((𝔽 1) - v0) × ⊢ - } + _lin ↩ { + v0 ← 𝔽 0 + v0 + ((𝔽 1) - v0) × ⊢ + } Its call syntax is simpler as well. In other cases, however, the function version might be preferable, for example when dealing with arrays of functions or many arguments including a function. - _lin ↩ { v0←𝔽0 ⋄ v0+((𝔽1)-v0)×⊢ } # (copy again) Exp _lin 5 ### Arrays of functions diff --git a/doc/oop.md b/doc/oop.md index bca8287b..9f007566 100644 --- a/doc/oop.md +++ b/doc/oop.md @@ -23,21 +23,21 @@ Mixins | Not really (needs `this`) An object in BQN is simply a namespace: its fields and methods are variables in the namespace, and a variable can be accessed outside of the namespace with dot syntax if it's exported with `⇐`. Unexported variables are instance-private in OOP parlance, meaning that they're only visible to the object containing them. They could be utilities, or hold state for the object. As an example, the object below implements the [Tower of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi) puzzle with five disks. You can view the state (a list of disks occupying each of the three rods) with `towerOfHanoi.View`, or move the top disk from one rod to another with `towerOfHanoi.Move`. - towerOfHanoi ← { - l ← ↕¨5‿0‿0 - View ⇐ {𝕤 - l - } - Move ⇐ {from‿to: - l ↩ Transfer´⌾(𝕩⊸⊏)⍟(≠´𝕩) l - } - # Move a disk from 𝕨 to 𝕩 - Transfer ← { - "No disk to move"!0<≠𝕨 - "Can't place larger disk on smaller one"!(0<≠)◶⟨1,𝕨<○⊑⊢⟩𝕩 - ⟨1↓𝕨, 𝕨⊏⊸∾𝕩⟩ - } - } + towerOfHanoi ← { + l ← ↕¨5‿0‿0 + View ⇐ {𝕤 + l + } + Move ⇐ {from‿to: + l ↩ Transfer´⌾(𝕩⊸⊏)⍟(≠´𝕩) l + } + # Move a disk from 𝕨 to 𝕩 + Transfer ← { + "No disk to move"!0<≠𝕨 + "Can't place larger disk on smaller one"!(0<≠)◶⟨1,𝕨<○⊑⊢⟩𝕩 + ⟨1↓𝕨, 𝕨⊏⊸∾𝕩⟩ + } + } Two variables `l` and `Transfer` aren't exported, for two different reasons. `l` encodes the state of the tower, but it's only exposed with the function `View`, which allows the internal representation to be changed freely. `Transfer` is just a utility function. While it's not dangerous to use outside of the object, there's no reason to expose it through `towerOfHanoi`'s interface. If it's wanted in another place it should be moved to a common location. @@ -45,19 +45,14 @@ Here are the results of a few applications of these functions. t ← towerOfHanoi t.View@ - ⟨ ⟨ 0 1 2 3 4 ⟩ ⟨⟩ ⟨⟩ ⟩ t.Move 0‿2 - ⟨ ⟨ 1 2 3 4 ⟩ ⟨⟩ ⟨ 0 ⟩ ⟩ t.Move 1‿2 - ! "No disk to move" t.Move 0‿1 - ⟨ ⟨ 2 3 4 ⟩ ⟨ 1 ⟩ ⟨ 0 ⟩ ⟩ t.Move 2‿1 - ⟨ ⟨ 2 3 4 ⟩ ⟨ 0 1 ⟩ ⟨⟩ ⟩ ## Classes diff --git a/doc/quick.md b/doc/quick.md index 2b3baafc..962216cc 100644 --- a/doc/quick.md +++ b/doc/quick.md @@ -41,12 +41,12 @@ Now let's see how it works. ## Case conversion - # Case conversion utilities - case ← { - diff ← -´ "Aa" - Lower ⇐ -⟜diff - Upper ⇐ Lower⁼ - } + # Case conversion utilities + case ← { + diff ← -´ "Aa" + Lower ⇐ -⟜diff + Upper ⇐ Lower⁼ + } This part of the code defines a [namespace](namespace.md) using braces `{}`, then [assigns](expression.md#assignment) it to the name `case`. There are three assignments inside the namespace too. Since BQN uses [lexical scoping](lexical.md), code outside the namespace can't access the variables `diff`, `Lower`, and `Upper` directly. Oh, and the first line is a [comment](token.md#comments). @@ -113,23 +113,22 @@ The function that does this is [Enclose](enclose.md) [Cells](rank.md), `<˘`. Th This statement consists of the name `hw` just defined, a compound function, and then the new character `↩`. This is another form of [assignment](expression.md#assignment), like `←`, but it changes the value of an existing variable instead of defining a new one. There's also some special `↩` syntax here: the expression `val Fn↩` is shorthand for `val ↩ Fn val`, avoiding the need to write the name `hw` twice (and `val Fn↩ arg` means `val ↩ val Fn arg`, like `+=` and so on from C). So we are modifying `hw` by applying this function `case.Upper⌾(⊑¨)`. hw ← <˘ 2‿∘ ⥊ "helloworld" - Upper ← -⟜(-´"Aa")⁼ - Upper⌾(⊑¨) hw + case.Upper⌾(⊑¨) hw - hw Upper⌾(⊑¨)↩ # Sets new value for hw + hw case.Upper⌾(⊑¨)↩ # Sets new value for hw That converts the first character of each string to uppercase! `case.Upper` is the case conversion function defined before, so that part makes sense. The rest of the function, `⌾(⊑¨)`, would be pronounced "[Under](under.md) the [First](pick.md#first) of [Each](map.md#one-argument-mapping)", which… pretty much makes sense too? The First Each function extracts the first element of each list in `hw`, the part that used to be `"hw"` but is now `"HW"`. ⊑¨ hw - Upper "hw" + case.Upper "hw" -The Under modifier keeps track of where that string came from and puts it *back*, to produce a new, altered array. It's kind of special, like Undo, but works on all sorts of fancy selections. It's also worth pointing out that `Upper` applies to a string here, not an individual character. That's because arithmetic is [pervasive](arithmetic.md#pervasion), so that functions made of arithmetic naturally work on arrays. Although in this case it wasn't really necessary, because it's also possible to map over the two strings and uppercase the first character of each separately: +The Under modifier keeps track of where that string came from and puts it *back*, to produce a new, altered array. It's kind of special, like Undo, but works on all sorts of fancy selections. It's also worth pointing out that `case.Upper` applies to a string here, not an individual character. That's because arithmetic is [pervasive](arithmetic.md#pervasion), so that functions made of arithmetic naturally work on arrays. Although in this case it wasn't really necessary, because it's also possible to map over the two strings and uppercase the first character of each separately: - Upper⌾⊑¨ "hello"‿"world" + case.Upper⌾⊑¨ "hello"‿"world" -Modifiers are applied from left to right, opposite to functions (1-modifiers also take the operand on the left while prefix functions have the argument on the right). So `Upper⌾⊑¨` means `(Upper⌾⊑)¨`. +Modifiers are applied from left to right, opposite to functions (1-modifiers also take the operand on the left while prefix functions have the argument on the right). So `case.Upper⌾⊑¨` means `(case.Upper⌾⊑)¨`. ### Punctuation and printing diff --git a/docs/doc/arrayrepr.html b/docs/doc/arrayrepr.html index dc0fd389..61f5dd42 100644 --- a/docs/doc/arrayrepr.html +++ b/docs/doc/arrayrepr.html @@ -33,8 +33,9 @@

Array displays show only the array shape and elements. The fill is an inferred property and the display never indicates or depends on it.

Corners

Those top-left and bottom-right corners are a distinctive part of BQN's display, as other systems almost always completely enclose the contents. BQN could add the other two corners, naturally; it just doesn't. Within the corners, elements are separated by whitespace only, and generally aligned to the top left.

-↗️
    2,"xy"22"abcd",4  # Nested 2×2 array
-┌─             
+↗️
    [⟨2         , "xy"
+     22"abcd", 4   ⟩]
+┌─             
 ╵ 2      "xy"  
   ┌─     4     
   ╵"ab         
@@ -69,14 +70,14 @@
 

High-rank layout

We've seen already that elements of a list are placed side by side, while the rows of a table (rank-2 array) are stacked on top of each other.

-↗️
    <¨ 5        # A list of units
+↗️
    <¨ 5          # A list of units
 ┌─                               
 · ┌·    ┌·    ┌·    ┌·    ┌·     
   · 0   · 1   · 2   · 3   · 4    
       ┘     ┘     ┘     ┘     ┘  
                                 ┘
 
-    234105  # A table
+    [234,105]  # A table
 ┌─       
 ╵ 2 3 4  
   1 0 5  
@@ -181,16 +182,17 @@
                ┘
 

BQN's separator rules give list notation a very flexible structure. You can put all the elements on one line or spread them across lines, with the option of adding blank lines between elements. A separator at the end of a line is never needed, but leading and trailing separators are allowed.

-

-  "e0", "e1"
-  
-    'e'
-    '2'
-  
-  "e3", "e4", "e5"
+↗️
    
+      "e0", "e1"
+      
+        'e'
+        '2'
+      
+      "e3", "e4", "e5"
 
-  "e6"
-
+      "e6"
+    
+⟨ "e0" "e1" "e2" "e3" "e4" "e5" "e6" ⟩
 

High-rank arrays

Higher-rank arrays can be written with [], an array notation that indicates each element is to be used as a cell of its result. It's identical to forming a list and applying Merge ([] is the same as >).

diff --git a/docs/doc/block.html b/docs/doc/block.html index a79791df..ec9e4ed9 100644 --- a/docs/doc/block.html +++ b/docs/doc/block.html @@ -20,7 +20,11 @@

Headerless blocks

In the simplest case a block is just a list of statements, which are executed to evaluate the block. A block with no special names like 𝕨 or 𝕩 is called an immediate block, and is evaluated as soon as it is reached. The only thing such a block does is group some statements, and create a scope for them so that definitions made there are discarded when the block finishes. Even this small amount of functionality could be useful; as an example the following program can build up an array from named components without polluting the rest of the program with those names.

-↗️
    updown  { up5  downup  updown }
+↗️
    updown  {
+      up  5
+      down  up
+      updown
+    }
     updown
 ⟨ 0 1 2 3 4 4 3 2 1 0 ⟩
 
@@ -139,9 +143,11 @@

Because 𝕣 only ever refers to a 1-modifier or 2-modifer, it can never make sense to refer to it as a function, and the uppercase letter is not recognized by BQN. To allow 𝕣 to be spelled as a 1-modifier _𝕣 or 2-modifier _𝕣_, it's tokenized as an ordinary identifier character, so it has to be separated from adjacent letters or numbers with a space.

Block headers

As a program becomes larger, it often becomes necessary to name inputs to blocks rather than just using special names. It can also become difficult to identify what kind of block is being defined, as it requires scanning through the block for special names. A block header, which is separated from the body of a block by a colon :, specifies the kind of block and can declare names for the block and its inputs.

-
Fact  { F n:
-  n × (0<)1F n-1
-}
+↗️
    Fact_head  { F n:
+      n × (0<)1F n-1
+    }
+    Fact_head 7
+5040
 

Its syntax mirrors an application of the block. As suggested by the positioning, the names given in a header apply only inside the block: for example F above is only defined inside the {} braces while Fact could be used either outside or inside. Some other possibilites are given below.

# A dyadic function that refers to itself as Func
@@ -197,7 +203,11 @@
 ⟨ 2 'a' 'b' ⟩
 

Bodies with headers come before any that don't have them. When a block is called, its headers are checked in order for compatibility with the arguments, and the first body with a compatible header is used.

-↗️
    CaseAdd  { 2𝕊3:05 ; 2𝕊𝕩:1,2+𝕩 ; 𝕊𝕩:2𝕩 }
+↗️
    CaseAdd  {
+      2𝕊3: 05 ;
+      2𝕊𝕩: 1,2+𝕩 ;
+       𝕊𝕩: 2𝕩
+    }
 
     2 CaseAdd 3
 ⟨ 0 5 ⟩
@@ -214,12 +224,14 @@
 

Case headers

A special rule allows for convenient case-matching syntax for one-argument functions. In any function header with one argument, the function name can be omitted as long as the argument is not a plain identifier—it must be 𝕩 or a compound value like a list to distinguish it from an immediate block label.

-
Test  {
-  "abc": "string" ;
-  2,b: 𝕩       ;
-  5:     "number" ;
-  𝕩:     "default"
-}
+↗️
    Test  {
+      "abc": "string" ;
+      2,b: 𝕩       ;
+      5:     "number" ;
+      𝕩:     "default"
+    }
+    Test 5
+"number"
 

These case-style headers function exactly the same as if they were preceded by 𝕊, and can be mixed with other kinds of headers.

Predicates

diff --git a/docs/doc/functional.html b/docs/doc/functional.html index ea4d18ca..f3eea496 100644 --- a/docs/doc/functional.html +++ b/docs/doc/functional.html @@ -70,14 +70,13 @@

What does functional programming in BQN look like? How is it different from the typical APL style of manipulating functions with operators?

Working with roles

First, let's look at the basics: a small program that has functions as its argument and result. The function Lin below gives a linear approximation to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, 𝕏.

-
Lin  {
-  v0  𝕏 0
-  v0 + ((𝕏 1) - v0) × 
-}
+↗️
    Lin  {
+      v0  𝕏 0
+      v0 + ((𝕏 1) - v0) × 
+    }
 

We can pass it the exponential function as an argument by giving it the name Exp and then referring to it in lowercase (that is, in a subject role). The result is a train that adds 1 to e-1 times the argument (we'll discuss only tacit functions here; for blocks see lexical scoping).

-↗️
    Lin  { v0𝕏0  v0+((𝕏1)-v0)×⊢ }  # (copy of above)
-    Exp  
+↗️
    Exp  
     Lin exp
 1+1.718281828459045×⊢
 
@@ -95,14 +94,13 @@ 148.4131591025766

Note also in this case that we could have used a modifier with a very similar definition to Lin. The modifier is identical in definition except that 𝕏 is replaced with 𝔽.

-
_lin  {
-  v0  𝔽 0
-  v0 + ((𝔽 1) - v0) × 
-}
+↗️
    _lin  {
+      v0  𝔽 0
+      v0 + ((𝔽 1) - v0) × 
+    }
 

Its call syntax is simpler as well. In other cases, however, the function version might be preferable, for example when dealing with arrays of functions or many arguments including a function.

-↗️
    _lin  { v0𝔽0  v0+((𝔽1)-v0)×⊢ }  # (copy again)
-    Exp _lin 5
+↗️
    Exp _lin 5
 9.591409142295225
 

Arrays of functions

diff --git a/docs/doc/oop.html b/docs/doc/oop.html index 5915f033..4102ddc1 100644 --- a/docs/doc/oop.html +++ b/docs/doc/oop.html @@ -66,39 +66,39 @@

Objects

An object in BQN is simply a namespace: its fields and methods are variables in the namespace, and a variable can be accessed outside of the namespace with dot syntax if it's exported with . Unexported variables are instance-private in OOP parlance, meaning that they're only visible to the object containing them. They could be utilities, or hold state for the object. As an example, the object below implements the Tower of Hanoi puzzle with five disks. You can view the state (a list of disks occupying each of the three rods) with towerOfHanoi.View, or move the top disk from one rod to another with towerOfHanoi.Move.

-
towerOfHanoi  {
-  l  ¨500
-  View  {𝕤
-    l
-  }
-  Move  {fromto:
-    l  Transfer´(𝕩)(´𝕩) l
-  }
-  # Move a disk from 𝕨 to 𝕩
-  Transfer  {
-    "No disk to move"!0<≠𝕨
-    "Can't place larger disk on smaller one"!(0<≠)1,𝕨<⊑⊢𝕩
-    1𝕨, 𝕨𝕩
-  }
-}
+↗️
    towerOfHanoi  {
+      l  ¨500
+      View  {𝕤
+        l
+      }
+      Move  {fromto:
+        l  Transfer´(𝕩)(´𝕩) l
+      }
+      # Move a disk from 𝕨 to 𝕩
+      Transfer  {
+        "No disk to move"!0<≠𝕨
+        "Can't place larger disk on smaller one"!(0<≠)1,𝕨<⊑⊢𝕩
+        1𝕨, 𝕨𝕩
+      }
+    }
 

Two variables l and Transfer aren't exported, for two different reasons. l encodes the state of the tower, but it's only exposed with the function View, which allows the internal representation to be changed freely. Transfer is just a utility function. While it's not dangerous to use outside of the object, there's no reason to expose it through towerOfHanoi's interface. If it's wanted in another place it should be moved to a common location.

Here are the results of a few applications of these functions.

-
    t  towerOfHanoi
+↗️
    t  towerOfHanoi
     t.View@
-  0 1 2 3 4  ⟨⟩ ⟨⟩ 
+⟨ ⟨ 0 1 2 3 4 ⟩ ⟨⟩ ⟨⟩ ⟩
 
     t.Move 02
-  1 2 3 4  ⟨⟩  0  
+⟨ ⟨ 1 2 3 4 ⟩ ⟨⟩ ⟨ 0 ⟩ ⟩
 
     t.Move 12
-! "No disk to move"
+Error: No disk to move
 
     t.Move 01
-  2 3 4   1   0  
+⟨ ⟨ 2 3 4 ⟩ ⟨ 1 ⟩ ⟨ 0 ⟩ ⟩
 
     t.Move 21
-  2 3 4   0 1  ⟨⟩ 
+⟨ ⟨ 2 3 4 ⟩ ⟨ 0 1 ⟩ ⟨⟩ ⟩
 

Classes

The object above is a singleton: there's just one of it, at least in the scope it occupies. It's often more useful to have a class that can be used to create objects. What we'll call a "class" is a namespace function, that is, a function that contains and so returns a namespace. It's very easy to convert a singleton object to a class: just add a no-op 𝕤 line to force it to be a function, and call it with @ when needed.

diff --git a/docs/doc/quick.html b/docs/doc/quick.html index 28ff1248..8a13379b 100644 --- a/docs/doc/quick.html +++ b/docs/doc/quick.html @@ -39,12 +39,13 @@

If you save it with the name hello.bqn and have BQN installed, the script can be run with $ bqn hello.bqn from a shell. Because of the #! line at the top, $ ./hello.bqn also works if bqn is in your path and hello.bqn is executable. It can also be run from another BQN file in the same directory, or REPL started there, using •Import "hello.bqn". Or just copy-paste it into the online REPL.

Now let's see how it works.

Case conversion

-
# Case conversion utilities
-case  {
-  diff  -´ "Aa"
-  Lower  -diff
-  Upper  Lower
-}
+↗️
    # Case conversion utilities
+Error: Empty program
+    case  {
+      diff  -´ "Aa"
+      Lower  -diff
+      Upper  Lower
+    }
 

This part of the code defines a namespace using braces {}, then assigns it to the name case. There are three assignments inside the namespace too. Since BQN uses lexical scoping, code outside the namespace can't access the variables diff, Lower, and Upper directly. Oh, and the first line is a comment.

The value diff is the result of applying a function -´ to the argument "Aa". Function application is always written just by placing a function next to its arguments like this—a prefix application if there's one argument, infix if there are two, and that's the most arguments you can have. This doesn't limit BQN's capabilities because it's easy to pass a list as an argument. In fact, "Aa" is a string, which means a list of characters. Characters are written with single quotes, so it's a list of 'A' and 'a'.

@@ -138,27 +139,26 @@
hw case.Upper(¨)
 

This statement consists of the name hw just defined, a compound function, and then the new character . This is another form of assignment, like , but it changes the value of an existing variable instead of defining a new one. There's also some special syntax here: the expression val Fn is shorthand for val Fn val, avoiding the need to write the name hw twice (and val Fn arg means val val Fn arg, like += and so on from C). So we are modifying hw by applying this function case.Upper(¨).

-↗️
    hw  <˘ 2  "helloworld"
-    Upper  -(-´"Aa")
+↗️
    hw  <˘ 2  "helloworld"
 
-    Upper(¨) hw
+    case.Upper(¨) hw
 ⟨ "Hello" "World" ⟩
 
-    hw Upper(¨)  # Sets new value for hw
+    hw case.Upper(¨)  # Sets new value for hw
 ⟨ "Hello" "World" ⟩
 

That converts the first character of each string to uppercase! case.Upper is the case conversion function defined before, so that part makes sense. The rest of the function, (¨), would be pronounced "Under the First of Each", which… pretty much makes sense too? The First Each function extracts the first element of each list in hw, the part that used to be "hw" but is now "HW".

-↗️
    ¨ hw
+↗️
    ¨ hw
 "HW"
 
-    Upper "hw"
+    case.Upper "hw"
 "HW"
 
-

The Under modifier keeps track of where that string came from and puts it back, to produce a new, altered array. It's kind of special, like Undo, but works on all sorts of fancy selections. It's also worth pointing out that Upper applies to a string here, not an individual character. That's because arithmetic is pervasive, so that functions made of arithmetic naturally work on arrays. Although in this case it wasn't really necessary, because it's also possible to map over the two strings and uppercase the first character of each separately:

-↗️
    Upper¨ "hello""world"
+

The Under modifier keeps track of where that string came from and puts it back, to produce a new, altered array. It's kind of special, like Undo, but works on all sorts of fancy selections. It's also worth pointing out that case.Upper applies to a string here, not an individual character. That's because arithmetic is pervasive, so that functions made of arithmetic naturally work on arrays. Although in this case it wasn't really necessary, because it's also possible to map over the two strings and uppercase the first character of each separately:

+↗️
    case.Upper¨ "hello""world"
 ⟨ "Hello" "World" ⟩
 
-

Modifiers are applied from left to right, opposite to functions (1-modifiers also take the operand on the left while prefix functions have the argument on the right). So Upper¨ means (Upper)¨.

+

Modifiers are applied from left to right, opposite to functions (1-modifiers also take the operand on the left while prefix functions have the argument on the right). So case.Upper¨ means (case.Upper)¨.

Punctuation and printing

The variable hw is modified one more time, then printed, producing the output Hello, World!

•Out hw   ⥊⍉ [hw, ", ""!"]  # Hello, World!
diff --git a/md.bqn b/md.bqn
index fd325102..44895ca2 100644
--- a/md.bqn
+++ b/md.bqn
@@ -213,18 +213,25 @@ Markdown ← {filename𝕊𝕩:
     # If every line is indented by at least 4 additional spaces, we will
     # execute each one and insert the results.
     addRslt ← ∧´ ' ' = ∾ 4 (⌊⟜≠ ↑ ⊢)¨ 𝕩
-    # Don't show assignment results by default
-    ShowRslt ← {
-      depth ← +` "(){}⟨⟩[]" (⊣(≠⊸>ׯ1⋆2|⊢)⊐) 𝕩
-      𝕩 /˜↩ ¬ ∨`⌾⌽ (0=depth) ∧ (∧`𝕩≠'#') ∧ 𝕩∊"⋄,"  # Just the last statement
-      g ← 𝕩∊"←↩"
-      (⊑g⊐1) (<⟜(≠g))◶⟨1,¬(" "∾∾idChars)∧´∘∊˜↑⟩ 𝕩
+    r‿ri ← {
+      ¬addRslt ? ⋈˜⟨⟩ ;
+      # Find the top-level separators sep
+      m ← NotCommentOrString code
+      depth ← +` m × "(){}⟨⟩[]" (⊣(≠⊸>×·¬⊸-2|⊢)⊐) code
+      sep ← (0=depth) ∧ m ∧ code∊"⋄,"∾lf
+      # Divide code
+      gr ← (1+´sl) ∾˜ (⊢-˜+`׬) sl←sep∧lf=code
+      # Don't show assignment results by default
+      ShowRslt ← {
+        c ← (¬ ∨`⌾⌽ 𝕨) / 𝕩
+        g ← c∊"←↩"
+        (⊑g⊐1) (<⟜(≠g))◶⟨1,¬(" "∾∾idChars)∧´∘∊˜↑⟩ c
+      }
+      ShowErr ← lf∾˜"span class='Error'"Html"Error: "∾(∧`lf⊸≠)⊸/⎊•Repr
+      E ← ShowRslt ⊣◶⟨"",(⥊∾⟜lf⎉1)∘Fmt∘⊢⟩ CodeExec∘⊢
+      r ← (gr⊔sep) E⎊(ShowErr∘•CurrentError⊢)⍟(0<≠∘⊢)¨ gr⊔code
+      ⟨r, /sl∾1⟩
     }
-    ShowErr ← lf∾˜"span class='Error'"Html"Error: "∾(∧`lf⊸≠)⊸/⎊•Repr
-    r‿ri ← addRslt◶(⋈˜⟨⟩)‿{
-      ⟨ (ShowRslt ⊣◶⟨"",(⥊∾⟜lf⎉1)∘Fmt∘⊢⟩ CodeExec)⎊(ShowErr∘•CurrentError)⍟(0<≠)¨ 𝕩
-        1 -˜ +` 1 + ≠¨ 𝕩   ⟩ # Don't forget the trailing newline
-    } 𝕩
 
     Link ← {
       c ← tryURL ∾ Base64 ¯1 ↓ JoinLines 4↓¨𝕩
@@ -598,12 +605,8 @@ hlchars‿classTag ← {
   classTag ← ""‿"" ∾ > {⟨"",""⟩}¨ 1↓classes
   chars‿classTag
 }
-GetHighlights ← {
-  # Find each character's group, sending unknowns to 1 and # to 0.
-  col ← (1-˜≠hlchars) (⊢-⊣×≤) hlchars FindGroup 𝕩
-  col-↩ 4×(𝕩='.')>«𝕩∊'0'+↕10 # Namespace dot: 5→1
 
-  # Locate comments and strings.
+CommentStringLocations ← {
   c ← 𝕩='#'
   le← / (𝕨 ⊢⊘∨ 𝕩=lf) ∾ 1
   # Line endings (le) end every comment (/c) on the line, so take a copy
@@ -616,12 +619,28 @@ GetHighlights ← {
   cse ← ∾ ⟨ 2+s ⋄  1↓d ⋄ ce ⟩ # Corresponding end indices
   # If 𝕨 is given, filter out strings with ends in different divisions.
   {css‿cse <∘=○(⊏⟜(+`0∾𝕩))´⊸(/¨)↩} 𝕨
-  # Now b is a table of (start,end) pairs
+  # Table of (start,end) pairs
   b ← css Trace cse
+  # Return the table, and a mask of which rows are comments
+  ⟨b, (⊏˘b)⊏c⟩
+}
+NotCommentOrString ← {
+  i‿c ← 𝕨 CommentStringLocations 𝕩
+  i +↩ (¬c) ×⌜ 0‿1  # Strings include ending character; comments don't
+  1 ≠` (≠𝕩) ↑ 2|/⁼⥊i
+}
+
+GetHighlights ← {
+  # Find each character's group, sending unknowns to 1 and # to 0.
+  col ← (1-˜≠hlchars) (⊢-⊣×≤) hlchars FindGroup 𝕩
+  col-↩ 4×(𝕩='.')>«𝕩∊'0'+↕10 # Namespace dot: 5→1
+
+  # Table of start/end pairs, and which are comments
+  b‿c ← 𝕨 CommentStringLocations 𝕩
   # Given a list of pairs, get a mask indicating included regions
   ToMask ← (≠`∨⊢) (≠𝕩)↑/⁼∘∾
   # Split rows and group into text‿comments
-  tc ← ((⊏˘b)⊏c) ∾⟜2⊸⊔ <˘b
+  tc ← c ∾⟜2⊸⊔ <˘b
   # Color with "String" and "Comment"
   col ⌈↩ +´ (2‿1-˜≠classTag) × ToMask¨ tc
 
-- 
cgit v1.2.3