From ff1453f2e2d086a984905bbb85a89712ea3c78bb Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Fri, 30 Oct 2020 16:08:14 -0400 Subject: Generate expression displays from compiled bytecode, not an ad-hoc parser --- docs/tutorial/expression.html | 150 +++++++++++++++++++++++++----------------- md.bqn | 5 +- tutorial/evalexp.bqn | 46 ++++++++----- tutorial/expression.md | 4 ++ 4 files changed, 129 insertions(+), 76 deletions(-) diff --git a/docs/tutorial/expression.html b/docs/tutorial/expression.html index cea81292..22da2272 100644 --- a/docs/tutorial/expression.html +++ b/docs/tutorial/expression.html @@ -119,37 +119,37 @@ (4÷3) × π × 23 - - - - - - - - - + + + + + + + + + - 4 - ÷ - 3 - × - π - × - 2 - 3 + + 2 + × + π + × + 3 + ÷ + 4 - 4 - ÷ - 3 - × - π - × - 2 - 3 + + 2 + × + π + × + 3 + ÷ + 4 @@ -172,46 +172,46 @@ (3 + 2×√2) - 1+√2 - - - - - - - - - - - - + + + + + + + + + + + + - - 3 - + - 2 - × - - 2 - - - 1 - + - 2 + + + + 1 + - + 2 + + × + 2 + + + 3 + - - 3 - + - 2 - × - - 2 - - - 1 - + - 2 + + + + 1 + - + 2 + + × + 2 + + + 3 + @@ -351,6 +351,38 @@ + 3 4 + + + + 3 ט+ 4 + + + + + + + + + + 4 + + + + ˜ + × + 3 + + + 4 + + + + ˜ + × + 3 + + + + +

This ordering is more consistent with the fact that the operand of a 1-modifier goes to its left. If we tried going from right to left we'd end up with ×(˜+), which uses ˜ as an operand to . But a modifier can't be used as an operand. To make it work we'd essentially have to give 1-modifiers a higher precedence than 2-modifiers.

In fact, the rules for modifiers are exactly the same as those for functions, but reversed. So why is there a distinction between 1- and 2-modifiers, when for functions we can look to the left to see whether there is a left argument? The reason is that it's natural to follow a 1-modifier by a subject or function that isn't supposed to be its operand. Using an example from the last section, +˜ 3 has a subject to the right of the 1-modifier ˜. Even worse, +˜ ÷ 3 looks the same syntactically as + ÷ 3, but it's two functions +˜ and ÷ applied to 3 while the version with Atop is a single function +÷ applied to 3. So the two-layer system of functions and modifiers forces modifiers to have a fixed number of operands even every function (including those derived by applying modifiers) can be called with one or two arguments.

Remember that 1-modifiers are all superscripts? The characters for 2-modifiers use a different rule: each contains an unbroken circle (that is, lines might touch it but not go through it). The 2-modifiers in BQN are the combinators ∘○⊸⟜⌾, the sort-of-combinators ⊘◶⍟, and the not-at-all-combinators ⎉⚇⎊. And the functions that make that unbroken circle rule necessary are written ⌽⍉. Since every primitive is a function, 1-modifier, or 2-modifier, you can always tell what type (and role) it has: a superscript is a 1-modifier, an unbroken circle makes it a 2-modifier, and otherwise it's a function.

diff --git a/md.bqn b/md.bqn index 68213774..92c7090a 100644 --- a/md.bqn +++ b/md.bqn @@ -161,7 +161,10 @@ blobURL ← repoURL∾"/blob/master/" # Environments _getCodeExec ← {𝕗⋄⍎} -_getSvgExec ← {𝕗⋄JoinLines⍟(1<≡)∘(⟨"svg.bqn","⍎"⟩ •Import "dzref")} +_getSvgExec ← {𝕗 + Compile ← (↕62)⊸(•Import "src/c.bqn") # for tutorial/evalexp.bqn + JoinLines⍟(1<≡)∘(⟨"svg.bqn","⍎"⟩ •Import "dzref") +} ################################ diff --git a/tutorial/evalexp.bqn b/tutorial/evalexp.bqn index 17c8c0db..f25144ac 100644 --- a/tutorial/evalexp.bqn +++ b/tutorial/evalexp.bqn @@ -15,25 +15,39 @@ Shadow ← { } DrawEval←{ - ix←𝕩⊏˜i←/𝕩≠' ' - f←0=b←(op←'('=ix)-')'=ix - l←op<<⟜«fn←ix∊"+-×÷⋆√" - g←⍋+`b - ei←¯1∾˜b{((𝕨<0)/𝕩)⌾((𝕨>0)⊸/)𝕩}○(g⊸⊏)»+`f - o←ei⊏˜f/↕∘≠⊸-⌾(g⊸⊏)(1+»l)(⊢+fn×-)1-2×l - g⊏˜↩⍋g⊏l - rev←⍋+`¯1↓(¯1∾g)(⊣⍋⊸⊏⊏˜⟜⍋¬⊏˜)⍋+`⊸+1∾g⊏l∨op - m←1+⌈´d←+`⊸×⌾(rev⊸⊏)fn + b‿const‿blk‿i ← Compile 𝕩 + ba‿bc‿bo‿bp ← '0'-˜⟨ # For each instruction, number of: + "11111000000000010000022000" # Arguments + "000//232323223102303200121" # Stack values consumed + "11111111111111011101111111" # Stack values output + "00000110101000001101000100" # Position determiner + ⟩ + m ← { # Mask of instruction starts + a ← ba(⊣⊏˜≠⊸>×⊢)b + na← ∾⟜≠ (≠-(1+↕∘≠)⊸(»·⌈`×)⌾⌽) <⟜128 b + n ← 1+{(𝕩=⌜a) +˝∘× >⊏⟜na⍟𝕩↕≠a}↕3 + ! ∧´ ↕∘≠⊸< n + Se←{(⊏˜𝕨)Se 1¨⌾((𝕩/𝕨)⊸⊏)𝕩}⍟(0=¯1⊑⊢) + (≠↑∾⟜≠Se 1∾0¨) n + } + mb‿mi ← m⊸/¨ b‿i + mi↓˜↩¯1 + + r←+`(mb⊏bo)-na←mb⊏bc + ! 1=¯1⊑r + p←((¯1↓r)⍋⊸⊏○⍋(⊏⟜r+⊒))⊸⊏/na + j←¯1↓mi⊏˜⊏˜⍟≠{s←𝕩⊏˜g←⍋𝕩⋄(g/˜(⊒s)=s⊏mb⊏bp)⌾((⍷s)⊸⊏)↕≠mb}p + je←j∾¯1.25 + d←+`⊸×f←0 wh×⟨i,d⟩ - off← (o ⊏ ∾⟜(whׯ1.25‿m))⊸- pt - pd ← pt (<(wh×0.6‿0.1)⊸+)⊸∾⟜⌽˘ off - dim← (wh×⟨≠𝕩,2+m⟩)+2‿0×pad - tp ← <˘pt+to+⎉1(0‿1×⌜˜f/¬fn)×off + pd ← <⊸∾⟜⌽˘˜˝ q ⊏ ⍉wh×0.6‿0.1+je≍d + dim← (wh×⟨≠𝕩,2+⌈´d⟩)+2‿0×pad (512‿0⊸⌈⊸(⊣∾˜(t+pad)-˜-˜÷2˙)dim+2×t) SVG gr Enc ∾⥊¨⟨ <"rect" Elt rc∾(Pos-pad)∾"width"‿"height"≍˘FmtNum¨dim <"text" Enc Highlight 𝕩 - (<"path" Elt ps∾"d"≍○<·∾"Mvh"∾⟜Fmt¨⊢)˘ pd - + This ordering is more consistent with the fact that the operand of a 1-modifier goes to its left. If we tried going from right to left we'd end up with `×(˜∘+)`, which uses `˜` as an operand to `∘`. But a modifier can't be used as an operand. To make it work we'd essentially have to give 1-modifiers a higher precedence than 2-modifiers. In fact, the rules for modifiers are exactly the same as those for functions, but reversed. So why is there a distinction between 1- and 2-modifiers, when for functions we can look to the left to see whether there is a left argument? The reason is that it's natural to follow a 1-modifier by a subject or function that isn't supposed to be its operand. Using an example from the last section, `+˜ 3` has a subject to the right of the 1-modifier `˜`. Even worse, `+˜ ÷ 3` looks the same syntactically as `+∘ ÷ 3`, but it's two functions `+˜` and `÷` applied to `3` while the version with Atop is a single function `+∘÷` applied to `3`. So the two-layer system of functions and modifiers forces modifiers to have a fixed number of operands even every function (including those derived by applying modifiers) can be called with one or two arguments. -- cgit v1.2.3