├── Makefile ├── README.md ├── README.txt ├── apl.c ├── apl.lua ├── apl385.ttf ├── finnaplidiom.lua ├── help.lua ├── lua-apl.c ├── lua-apl.html ├── lua-apl.txt ├── lua-apl.xmodmap ├── prog-guide.html └── test.lua /Makefile: -------------------------------------------------------------------------------- 1 | apl_core.so: apl.c 2 | cc -shared apl.c -l lapack -o apl_core.so 3 | 4 | # -------------------------------------------------------------------- 5 | # 6 | # Relevant to distribution 7 | 8 | PACKAGE = apl.c apl.lua help.lua test.lua finnaplidiom.lua apl385.ttf lua-apl.xmodmap lua-apl.c lua-apl.html prog-guide.html README.md Makefile 9 | 10 | zip: $(PACKAGE) 11 | zip lua-apl.zip $(PACKAGE) 12 | 13 | #-------------------------------------------------------------------------- 14 | # 15 | # What follows below is relevant to the GitHub repository 16 | 17 | 18 | all: apl_core.so lua-src/lua lua-apl.html prog-guide.html README.md 19 | 20 | GITFILES = apl.c apl.lua help.lua test.lua finnaplidiom.lua apl385.ttf lua-apl.xmodmap lua-apl.c lua-apl.txt lua-apl.html prog-guide.html README.txt README.md Makefile 21 | 22 | lua-src/lua: lua-apl.c 23 | cp lua-apl.c lua-src/lua.c 24 | make -C lua-src linux 25 | 26 | README.md: README.txt Makefile 27 | pandoc -t markdown_github README.txt -o README.md 28 | 29 | lua-apl.html: lua-apl.txt Makefile 30 | pandoc -s lua-apl.txt -o lua-apl.html 31 | 32 | prog-guide.html: prog-guide.txt Makefile 33 | pandoc -s prog-guide.txt -o prog-guide.html 34 | 35 | commit: $(GITFILES) 36 | git add $(GITFILES) 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lua⋆APL 2 | ======= 3 | 4 | © Dirk Laurie 2013 Lua-style MIT licence 5 | 6 | Lua⋆APL is Lua powered by APL. (That's an APL pun.) If the symbol between Lua and APL in Lua⋆APL does not look like a five-pointed star, chances are the rest will look unintelligible too. Get yourself a proper APL-enabled screen font as described in [UTF-8 essentials](#utf-8-essentials). 7 | 8 | The module table contains mostly Lua functions mapped to APL names, by and large doing what the corresponding APL functions would do, but operating on Lua numbers, strings, tables and functions. 9 | 10 | A version which checks that its host Lua satisfies `_VERSION=="Lua 5.2"` has been frozen but is available as a branch on GitHub. Future development will rely on Lua 5.3. 11 | 12 | March 2015: GNU APL is now mature enough to allow the writing of a Lua interface to it. See . Future versions of Lua⋆APL will aspire to be compatible with GNU APL. 13 | 14 | Package contents 15 | ---------------- 16 | 17 | This is what you should find in `lua-apl.zip`. 18 | 19 | apl.lua -- Lua code for the module 20 | help.lua -- Module 'help' required by apl.lua 21 | apl.c -- Supporting routines in C 22 | lua-apl.c -- Replacement for lua.c allowing immediate 23 | evaluation and display of APL expressions 24 | Makefile -- Gnu makefile for Linux systems 25 | 26 | apl385.ttf -- A public-domain APL font by Adrian Smith 27 | lua-apl.xmodmap -- APL key mappings for X 28 | 29 | README.md -- What you are reading now 30 | lua-apl.html -- User's manual 31 | prog-guide.html -- Programmer's guide (i.e. for this software itself) 32 | 33 | test.lua -- Tests a large selection of features 34 | finnaplidiom.lua -- A Lua module containing the FinnAPL idiom library. 35 | 36 | External dependencies 37 | --------------------- 38 | 39 | lpeg -- Roberto Ierusalimschy's LPeg package 40 | lapack -- Well-known linear algebra package, must be 41 | available to the C compiler as `-l lapack`. 42 | 43 | Installation of the Lua module 44 | ------------------------------ 45 | 46 | On a Linux system, make sure that you have `lpeg` and `lapack` installed, and type `make` in the module directory. Copy `apl_core.so`, `help.lua` and `apl.lua` to where your `package.cpath` and `package.path` will find it. 47 | 48 | On a non-Linux system, if you get it running, tell me how and and I will put the instructions in with due acknowledgment. 49 | 50 | UTF-8 essentials 51 | ---------------- 52 | 53 | 1. Get a UTF-8 font with decent APL glyphs. `apl385.ttf` is recommended. On Linux systems you put it in `$HOME/.fonts` and select it from your application's font selector. I'm told it is even easier on Windows systems. 54 | 55 | 2. Configure an APL keyboard. I don't mean a physical APL keyboard, I mean that you must have a reasonably easy way to type APL characters, for example via the keyboard's Level-3 and Level-4 character sets, associated with the AltGr key. 56 | 57 | On systems that use X-windows you can run `xmodmap lua-apl.xmodmap`, after of course having first backed up your present settings by `xmodmap -pke > original.xmodmap`. 58 | 59 | On my keyboard, doing that produces the following layout: 60 | 61 | ~ ⍬ ! ⌶ @ ⍫ # ⍒ $ ⍋ % ⌽ ^ ⍉ & ⊖ * ⍟ ( ⍱ ) ⍲ _ − + ⌹ 62 | ` ⋄ 1 ¨ 2 ¯ 3 < 4 ≤ 5 = 6 ≥ 7 > 8 ≠ 9 ∨ 0 ∧ - × = ÷ 63 | 64 | Q W E ⍷ R T U Y I ⍸ O ⍥ P { ⊣ } ⊢ | ⍙ 65 | q w ⍵ e ∊ r ⍴ t ∼ u ↓ y ↑ i ⍳ o ○ p ⋆ [ ← ] → \ ⍀ 66 | 67 | A S ⌷ D F ≡ G ⍒ H ⍋ J ⍤ K L ⍞ : ⍂ " ⌻ 68 | a ⍺ s ⌈ d ⌊ f _ g ∇ h ∆ j ∘ k l ⎕ ; ⊢ ' ⊣ 69 | 70 | Z X C ⍝ V B ⍎ N ⍕ M < > ∵ ? 71 | z ⊂ x ⊃ c ∩ v ∪ b ⊥ n ⊤ m ∣ , ⍪ , ⍀ / ⌿ 72 | 73 | In each 2x2 matrix, right column requires AltGr, top row requires Shift. The four-symbol combinations should come out the same on your keyboard too, but their positions probably will not. 74 | 75 | Some characters are available on both keyboards, but beware: AltGr `∼∧⋆−∣` may look the same but are non-ASCII. There are no commonly accepted non-ASCII alternatives for `<>+=.,!?/\`, otherwise I would have used them too. The difference matters only at the Lua level: inside an APL expression, you may use either of the look-alike characters. 76 | 77 | The above layout pays some respect to tradition, with changes mostly being additions. 78 | 79 | Installing the APL interpreter 80 | ------------------------------ 81 | 82 | This is optional: you can do everything almost as easily without it. 83 | 84 | 1. Copy the `src` subdirectory of the 5.2.2 Lua source to your module directory. 85 | 2. Optionally, customize `src/Makefile`. For example, I like to put 86 | 87 | MYCFLAGS=-DLUA\_PROMPT='" "' -DLUA\_PROMPT2='""' 88 | 89 | so that my session will look like an APL session instead of a Lua session. 90 | 3. Copy the supplied `lua-apl.c` to replace `src/lua.c` 91 | 4. While inside `src`, do `make linux` or `make mingw` or whatever is appropriate for your system. 92 | 5. Copy the Lua executable `src/lua` you have just made to your execution path, changing its name to `lua-apl` or `lua-apl.exe`. 93 | 94 | Make sure that `apl.lua`, `help.lua` and `apl-core.so` or `apl-core.dll` are properly installed and your keyboard is APL-enabled, and do `lua-apl`. 95 | 96 | Quick start 97 | ----------- 98 | 99 | We'll do this as a transcript of an interactive session at a terminal running `bash`. The resulting experience has quite a realistic APL-like look and feel. 100 | 101 | The first few lines show how to use APL as a desk calculator, and what you would need to do if you prefer not to compile the interpreter. 102 | 103 | The next few lines show how to define Lua functions in APL. 104 | 105 | The last few lines show how to get interactive help. 106 | 107 | $ lua-apl 108 | Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio 109 | Lua⋆APL 0.4.0 © Dirk Laurie 2013 110 | Lua⋆APL 0.4.0 (Lua code) © Dirk Laurie 2013 111 | Bug reports are welcome. You'll find me on Lua-L. 112 | If you can't remember the README, do this: 113 | help'start' 114 | In Lua mode, you will need `apl:import()` first. 115 | -- 116 | 117 | n=10 -- this is straight Lua 118 | (n,n)⍴(n+1)↑1 119 | 1 0 0 0 0 0 0 0 0 0 120 | 0 1 0 0 0 0 0 0 0 0 121 | 0 0 1 0 0 0 0 0 0 0 122 | 0 0 0 1 0 0 0 0 0 0 123 | 0 0 0 0 1 0 0 0 0 0 124 | 0 0 0 0 0 1 0 0 0 0 125 | 0 0 0 0 0 0 1 0 0 0 126 | 0 0 0 0 0 0 0 1 0 0 127 | 0 0 0 0 0 0 0 0 1 0 128 | 0 0 0 0 0 0 0 0 0 1 129 | return apl"(n,n)⍴(n+1)↑1"() 130 | -- The above is what the previous line looks like on replay on 131 | -- systems with readline history. Changing your input to that is 132 | -- in fact all that the interactive interpreter does. 133 | 134 | f=apl"(n,n)⍴(n+1)↑1" -- the same code as a function definition 135 | n=3 -- redefining the global variable 136 | =f() -- executing it 137 | 1 0 0 138 | 0 1 0 139 | 0 0 1 140 | g=apl"(⍵,⍵)⍴(⍵+1)↑1" -- replacing the global variable by an argument 141 | =g(5) -- executing it, with an unnecessary space 142 | stdin:1: unexpected symbol near '=' 143 | =g(5) -- executing it, without that space 144 | 1 0 0 0 0 145 | 0 1 0 0 0 146 | 0 0 1 0 0 147 | 0 0 0 1 0 148 | 0 0 0 0 1 149 | h=apl"⍺÷⍵" -- a function of two arguments 150 | return h(5,8) -- ⍵ is the first, ⍺ the second (explanation below) 151 | 1.6 152 | =lua(g) -- Show the Lua code that is actually executed 153 | return Reshape(Take(1,Add(1,_w)),Attach2(_w,_w)) 154 | 155 | help(apl) 156 | Contents: Abs Add And Assign Attach Attach1 Attach2 Binom Ceil Circ 157 | Compress Compress1 Compress2 Copy Deal Decode Define Disclose Div Down 158 | Drop Each Enclose Encode Execute Exp Expand Expand1 Expand2 Fact Find 159 | Floor Format Has Inner Length Ln Log MatDiv MatInv Max Min Mod Mul Nand 160 | Nor Not Or Outer Output Pass Pi Pow Range Ravel Recip Reduce Reduce1 161 | Reduce2 Reshape Reverse Reverse1 Reverse2 Roll Rotate Rotate1 Rotate2 162 | Same Scan Scan1 Scan2 Shape Sign Sub Take TestEq TestGE TestGT TestLE 163 | TestLT TestNE ToString Transpose Unm Up _act _rct help import lua 164 | register util 165 | help"APL" 166 | Contents: ! + , . / < = > ? \ ¨ × ÷ ↑ ↓ ∇ ∊ − ∘ ∣ ∧ 167 | ∨ ∼ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊖ ⊤ ⊥ ⋆ ⌈ ⌊ ⌹ ⌽ ⌿ ⍀ ⍉ ⍋ ⍎ ⍒ ⍕ ⍟ ⍪ ⍱ ⍲ ⍳ ⍴ ⎕ ○ 168 | help"⍳" 169 | Range: ⍳⍵ → first ⍵ integers starting at 1 170 | ⍺⍳⍵ → first ⍵ integers starting at ⍺ 171 | Find: ⍺⍳⍵ → position of first occurrence of ⍵ in ⍺; not found is #⍺+1 172 | 173 | The above is probably not quite the same as what the current version would 174 | give (that applies to all the other examples too) but you get the idea. 175 | 176 | If you know no APL 177 | ------------------ 178 | 179 | APL functions are *niladic* (no arguments), *monadic* (one argument on the right of the name) or *dyadic* (two arguments, one to the left and one to the right of the name). In function definitions, the left and right arguments have the reserved names `⍺` and `⍵` respectively; a niladic function has neither. The distinction is only made at runtime. Unlike Lua, APL can overload function names to call different functions when called monadically to when called dyadically. 180 | 181 | As shown above, you can use `help` to find out the Lua names of APL symbols or vice versa. 182 | 183 | An APL function can called from Lua by giving `⍵` as first and `⍺` as second argument, i.e. `3÷5` and `apl.Div(5,3)` do the same thing. This is confusing at first, but quite logical: the argument that might not be there is `⍺`, so it must come second. 184 | 185 | Functions cannot be called niladically from APL. You need to give at least `⍵`, even when you know that it will be ignored. 186 | 187 | More details are given in the HTML files on the repository. 188 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Lua⋆APL 2 | ======= 3 | 4 | © Dirk Laurie 2013 Lua-style MIT licence 5 | 6 | Lua⋆APL is Lua powered by APL. (That's an APL pun.) If the symbol 7 | between Lua and APL in Lua⋆APL does not look like a five-pointed 8 | star, chances are the rest will look unintelligible too. Get yourself 9 | a proper APL-enabled screen font as described in [UTF-8 essentials]. 10 | 11 | The module table contains mostly Lua functions mapped to APL names, 12 | by and large doing what the corresponding APL functions would do, 13 | but operating on Lua numbers, strings, tables and functions. 14 | 15 | A version which checks that its host Lua satisfies `_VERSION=="Lua 5.2"` 16 | has been frozen but is available as a branch on GitHub. Future development 17 | will rely Lua 5.3. 18 | 19 | Package contents 20 | ---------------- 21 | 22 | This is what you should find in `lua-apl.zip`. 23 | 24 | apl.lua -- Lua code for the module 25 | help.lua -- Module 'help' required by apl.lua 26 | apl.c -- Supporting routines in C 27 | lua-apl.c -- Replacement for lua.c allowing immediate 28 | evaluation and display of APL expressions 29 | Makefile -- Gnu makefile for Linux systems 30 | 31 | apl385.ttf -- A public-domain APL font by Adrian Smith 32 | lua-apl.xmodmap -- APL key mappings for X 33 | 34 | README.md -- What you are reading now 35 | lua-apl.html -- User's manual 36 | prog-guide.html -- Programmer's guide (i.e. for this software itself) 37 | 38 | test.lua -- Tests a large selection of features 39 | finnaplidiom.lua -- A Lua module containing the FinnAPL idiom library. 40 | 41 | External dependencies 42 | --------------------- 43 | 44 | lpeg -- Roberto Ierusalimschy's LPeg package 45 | lapack -- Well-known linear algebra package, must be 46 | available to the C compiler as `-l lapack`. 47 | Installation of the Lua module 48 | ------------------------------ 49 | 50 | On a Linux system, make sure that you have `lpeg` and `lapack` 51 | installed, and type `make` in the module directory. Copy `apl_core.so`, 52 | `help.lua` and `apl.lua` to where your `package.cpath` and 53 | `package.path` will find it. 54 | 55 | On a non-Linux system, if you get it running, tell me how and and I will 56 | put the instructions in with due acknowledgment. 57 | 58 | UTF-8 essentials 59 | ---------------- 60 | 61 | 1. Get a UTF-8 font with decent APL glyphs. `apl385.ttf` is recommended. 62 | On Linux systems you put it in `$HOME/.fonts` and select it from your 63 | application's font selector. I'm told it is even easier on Windows systems. 64 | 65 | 2. Configure an APL keyboard. I don't mean a physical APL keyboard, 66 | I mean that you must have a reasonably easy way to type APL characters, 67 | for example via the keyboard's Level-3 and Level-4 character sets, 68 | associated with the AltGr key. 69 | 70 | On systems that use X-windows you can run `xmodmap lua-apl.xmodmap`, 71 | after of course having first backed up your present settings by 72 | `xmodmap -pke > original.xmodmap`. 73 | 74 | On my keyboard, doing that produces the following layout: 75 | 76 | 77 | ~ ⍬ ! ⌶ @ ⍫ # ⍒ $ ⍋ % ⌽ ^ ⍉ & ⊖ * ⍟ ( ⍱ ) ⍲ _ − + ⌹ 78 | ` ⋄ 1 ¨ 2 ¯ 3 < 4 ≤ 5 = 6 ≥ 7 > 8 ≠ 9 ∨ 0 ∧ - × = ÷ 79 | 80 | Q W E ⍷ R T U Y I ⍸ O ⍥ P { ⊣ } ⊢ | ⍙ 81 | q w ⍵ e ∊ r ⍴ t ∼ u ↓ y ↑ i ⍳ o ○ p ⋆ [ ← ] → \ ⍀ 82 | 83 | A S ⌷ D F ≡ G ⍒ H ⍋ J ⍤ K L ⍞ : ⍂ " ⌻ 84 | a ⍺ s ⌈ d ⌊ f _ g ∇ h ∆ j ∘ k l ⎕ ; ⊢ ' ⊣ 85 | 86 | Z X C ⍝ V B ⍎ N ⍕ M < > ∵ ? 87 | z ⊂ x ⊃ c ∩ v ∪ b ⊥ n ⊤ m ∣ , ⍪ , ⍀ / ⌿ 88 | 89 | 90 | In each 2x2 matrix, right column requires AltGr, top row requires Shift. 91 | The four-symbol combinations should come out the same on your keyboard 92 | too, but their positions probably will not. 93 | 94 | Some characters are available on both keyboards, but beware: 95 | AltGr `∼∧⋆−∣` may look the same but are non-ASCII. There are no 96 | commonly accepted non-ASCII alternatives for `<>+=.,!?/\`, 97 | otherwise I would have used them too. The difference matters only 98 | at the Lua level: inside an APL expression, you may use either of 99 | the look-alike characters. 100 | 101 | The above layout pays some respect to tradition, with changes 102 | mostly being additions. 103 | 104 | ## Installing the APL interpreter 105 | 106 | This is optional: you can do everything almost as easily without it. 107 | 108 | 1. Copy the `src` subdirectory of the 5.2.2 Lua source to your module 109 | directory. 110 | 2. Optionally, customize `src/Makefile`. For example, I like to put 111 | 112 | MYCFLAGS=-DLUA_PROMPT='" "' -DLUA_PROMPT2='""' 113 | 114 | so that my session will look like an APL session instead of a Lua 115 | session. 116 | 3. Copy the supplied `lua-apl.c` to replace `src/lua.c` 117 | 4. While inside `src`, do `make linux` or `make mingw` or whatever is 118 | appropriate for your system. 119 | 5. Copy the Lua executable `src/lua` you have just made to your 120 | execution path, changing its name to `lua-apl` or `lua-apl.exe`. 121 | 122 | Make sure that `apl.lua`, `help.lua` and `apl-core.so` or `apl-core.dll` 123 | are properly installed and your keyboard is APL-enabled, and do `lua-apl`. 124 | 125 | Quick start 126 | ----------- 127 | 128 | We'll do this as a transcript of an interactive session at a terminal 129 | running `bash`. The resulting experience has quite a realistic APL-like 130 | look and feel. 131 | 132 | The first few lines show how to use APL as a desk calculator, and what 133 | you would need to do if you prefer not to compile the interpreter. 134 | 135 | The next few lines show how to define Lua functions in APL. 136 | 137 | The last few lines show how to get interactive help. 138 | 139 | $ lua-apl 140 | Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio 141 | Lua⋆APL 0.4.0 © Dirk Laurie 2013 142 | Lua⋆APL 0.4.0 (Lua code) © Dirk Laurie 2013 143 | Bug reports are welcome. You'll find me on Lua-L. 144 | If you can't remember the README, do this: 145 | help'start' 146 | In Lua mode, you will need `apl:import()` first. 147 | -- 148 | 149 | n=10 -- this is straight Lua 150 | (n,n)⍴(n+1)↑1 151 | 1 0 0 0 0 0 0 0 0 0 152 | 0 1 0 0 0 0 0 0 0 0 153 | 0 0 1 0 0 0 0 0 0 0 154 | 0 0 0 1 0 0 0 0 0 0 155 | 0 0 0 0 1 0 0 0 0 0 156 | 0 0 0 0 0 1 0 0 0 0 157 | 0 0 0 0 0 0 1 0 0 0 158 | 0 0 0 0 0 0 0 1 0 0 159 | 0 0 0 0 0 0 0 0 1 0 160 | 0 0 0 0 0 0 0 0 0 1 161 | return apl"(n,n)⍴(n+1)↑1"() 162 | -- The above is what the previous line looks like on replay on 163 | -- systems with readline history. Changing your input to that is 164 | -- in fact all that the interactive interpreter does. 165 | 166 | f=apl"(n,n)⍴(n+1)↑1" -- the same code as a function definition 167 | n=3 -- redefining the global variable 168 | =f() -- executing it 169 | 1 0 0 170 | 0 1 0 171 | 0 0 1 172 | g=apl"(⍵,⍵)⍴(⍵+1)↑1" -- replacing the global variable by an argument 173 | =g(5) -- executing it, with an unnecessary space 174 | stdin:1: unexpected symbol near '=' 175 | =g(5) -- executing it, without that space 176 | 1 0 0 0 0 177 | 0 1 0 0 0 178 | 0 0 1 0 0 179 | 0 0 0 1 0 180 | 0 0 0 0 1 181 | h=apl"⍺÷⍵" -- a function of two arguments 182 | return h(5,8) -- ⍵ is the first, ⍺ the second (explanation below) 183 | 1.6 184 | =lua(g) -- Show the Lua code that is actually executed 185 | return Reshape(Take(1,Add(1,_w)),Attach2(_w,_w)) 186 | 187 | help(apl) 188 | Contents: Abs Add And Assign Attach Attach1 Attach2 Binom Ceil Circ 189 | Compress Compress1 Compress2 Copy Deal Decode Define Disclose Div Down 190 | Drop Each Enclose Encode Execute Exp Expand Expand1 Expand2 Fact Find 191 | Floor Format Has Inner Length Ln Log MatDiv MatInv Max Min Mod Mul Nand 192 | Nor Not Or Outer Output Pass Pi Pow Range Ravel Recip Reduce Reduce1 193 | Reduce2 Reshape Reverse Reverse1 Reverse2 Roll Rotate Rotate1 Rotate2 194 | Same Scan Scan1 Scan2 Shape Sign Sub Take TestEq TestGE TestGT TestLE 195 | TestLT TestNE ToString Transpose Unm Up _act _rct help import lua 196 | register util 197 | help"APL" 198 | Contents: ! + , . / < = > ? \ ¨ × ÷ ↑ ↓ ∇ ∊ − ∘ ∣ ∧ 199 | ∨ ∼ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊖ ⊤ ⊥ ⋆ ⌈ ⌊ ⌹ ⌽ ⌿ ⍀ ⍉ ⍋ ⍎ ⍒ ⍕ ⍟ ⍪ ⍱ ⍲ ⍳ ⍴ ⎕ ○ 200 | help"⍳" 201 | Range: ⍳⍵ → first ⍵ integers starting at 1 202 | ⍺⍳⍵ → first ⍵ integers starting at ⍺ 203 | Find: ⍺⍳⍵ → position of first occurrence of ⍵ in ⍺; not found is #⍺+1 204 | 205 | The above is probably not quite the same as what the current version would 206 | give (that applies to all the other examples too) but you get the idea. 207 | 208 | If you know no APL 209 | ------------------ 210 | 211 | APL functions are _niladic_ (no arguments), _monadic_ (one argument 212 | on the right of the name) or _dyadic_ (two arguments, one to the left 213 | and one to the right of the name). In function definitions, the left 214 | and right arguments have the reserved names `⍺` and `⍵` respectively; 215 | a niladic function has neither. The distinction is only made at runtime. 216 | Unlike Lua, APL can overload function names to call different functions 217 | when called monadically to when called dyadically. 218 | 219 | As shown above, you can use `help` to find out the Lua names of APL 220 | symbols or vice versa. 221 | 222 | An APL function can called from Lua by giving `⍵` as first and `⍺` 223 | as second argument, i.e. `3÷5` and `apl.Div(5,3)` do the same thing. 224 | This is confusing at first, but quite logical: the argument that might 225 | not be there is `⍺`, so it must come second. 226 | 227 | Functions cannot be called niladically from APL. You need to give at 228 | least `⍵`, even when you know that it will be ignored. 229 | 230 | More details are given in the HTML files on the repository. 231 | -------------------------------------------------------------------------------- /apl.c: -------------------------------------------------------------------------------- 1 | /* apl.c (c) 2012-2013 Dirk Laurie 2 | 3 | Core routines for apl.lua 4 | Same license as Lua 5.2.2 (c) 1994-2012 Lua.org, PUC-Rio 5 | 6 | */ 7 | 8 | /* on Linux compile with `cc -shared apl.c -o apl_core.so` */ 9 | 10 | /* on Windows, define the symbols 'LUA_BUILD_AS_DLL' and 'LUA_LIB' and 11 | * compile and link with stub library lua52.lib (for lua52.dll) 12 | * generating apl_core.dll. If necessary, generate lua52.lib and lua52.dll 13 | * by compiling Lua sources with 'LUA_BUILD_AS_DLL' defined. 14 | */ 15 | 16 | /* Functions with prefix "block" receive a range (which may go up or 17 | * down) as second and third arguments. These functions were initially 18 | * developed in collaboration with John Hind for the `xtable` library. 19 | * Functions with prefix "apl" follow the conventions for APL tables. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "lua.h" 26 | #include "lauxlib.h" 27 | #include "lualib.h" 28 | #include "math.h" 29 | 30 | /* For debugging. I've put these back too often to omit them altogether. */ 31 | static void stackprint(lua_State *L, int from) { 32 | int top=lua_gettop(L); 33 | printf("Stack:"); 34 | while (from<=top) { 35 | printf(" %s",luaL_tolstring(L,from++,NULL)); 36 | lua_pop(L,1); } 37 | printf("\n"); 38 | } 39 | static void arrayprint(lua_State *L, int from, int to) { 40 | printf("Array:"); 41 | while (from<=to) { lua_rawgeti(L,1,from++); 42 | printf(" %s",luaL_tolstring(L,-1,NULL)); 43 | lua_pop(L,2); } 44 | printf("\n"); 45 | } 46 | /* */ 47 | 48 | #define store(tbl,item,idx) lua_pushvalue(L,item); lua_rawseti(L,tbl,idx) 49 | #define move(a,from,to) lua_rawgeti(L,a,from); lua_rawseti(L,a,to) 50 | #define swap(a,x,y) lua_rawgeti(L,a,x); lua_rawgeti(L,a,y); \ 51 | lua_rawseti(L,a,x); lua_rawseti(L,a,y); 52 | 53 | /* get(tbl,a,b) */ 54 | static int block_get(lua_State *L) { 55 | int a=luaL_checkint(L,2), b=luaL_checkint(L,3), inc, count; 56 | luaL_checktype(L,1,LUA_TTABLE); 57 | inc = a<=b ? 1 : -1; 58 | count = (b-a)*inc+1; 59 | lua_settop(L,1); 60 | if (!lua_checkstack(L,count)) luaL_error( 61 | L, "stack overflow: 'get' needs %d values",count); 62 | for(;;a+=inc) { lua_rawgeti(L,1,a); if(a==b) break; } 63 | return count; 64 | } 65 | 66 | /* set(tbl,a,b,...) */ 67 | static int block_set(lua_State *L) { 68 | int a=luaL_checkint(L,2), b, top=lua_gettop(L), count=top-3, 69 | inc, item=4; 70 | luaL_checktype(L,1,LUA_TTABLE); 71 | b = (lua_isnoneornil(L,3)) ? a+count-1 : luaL_checkint(L,3); 72 | if (count<1) return 0; 73 | inc = a<=b ? 1 : -1; 74 | if (top==4) for(;;a+=inc) { store(1,top,a); if(a==b) break; } 75 | else for(;;a+=inc) { 76 | if (item>top) item=4; 77 | store(1,item++,a); 78 | if(a==b) break; 79 | } 80 | lua_pushvalue(L,1); 81 | return 1; 82 | } 83 | 84 | /* move(tbl,a1,b1,a2[,b2]) */ 85 | static int block_move(lua_State *L) { 86 | int a1=luaL_checkint(L,2), b1=luaL_checkint(L,3), 87 | a2=luaL_checkint(L,4), b2, inc1, inc2; 88 | luaL_checktype(L,1,LUA_TTABLE); 89 | inc1 = a1<=b1 ? 1 : -1; 90 | if (lua_isnoneornil(L,5)) { inc2=inc1; b2=a2+b1-a1; } 91 | else { 92 | b2=luaL_checkint(L,5); 93 | inc2 = a2<=b2 ? 1 : -1; 94 | luaL_argcheck(L,(b1-a1)*inc1==(b2-a2)*inc2,5, 95 | "source and destination must have the same length"); 96 | } 97 | lua_settop(L,1); 98 | if (inc1!=inc2) { /* reverse direction */ 99 | for(;(a1-b2)*inc1<0;a1+=inc1,a2+=inc2) { move(1,a1,a2); } 100 | for(;(b1-a2)*inc1>0;b1-=inc1,b2-=inc2) { move(1,b1,b2); } 101 | for(;(a2-a1)*inc1>0;a1+=inc1,a2+=inc2) { swap(1,a1,a2); } 102 | } 103 | else if ((a2-a1)*inc1<0) 104 | for (;;a1+=inc1,a2+=inc2) { move(1,a1,a2); if (a1==b1) break; } 105 | else for (;;b1-=inc1,b2-=inc2) { move(1,b1,b2); if (a1==b1) break; } 106 | lua_pushvalue(L,1); 107 | return 1; 108 | } 109 | 110 | /* transpose(tbl,m,n,tblT) */ 111 | static int block_transpose(lua_State *L) { 112 | int i,j, m=luaL_checkint(L,2), n=luaL_checkint(L,3); 113 | luaL_checktype(L,1,LUA_TTABLE); luaL_checktype(L,4,LUA_TTABLE); 114 | luaL_argcheck(L,!lua_compare(L,1,4,LUA_OPEQ),1, 115 | "not supported: in-place transposition"); 116 | for (i=0; i=0,2,"must be a non-negative integer"); 328 | if (!lua_isnoneornil(L,3)) { 329 | m=len; n=luaL_checkint(L,3); len=m*n; 330 | luaL_argcheck(L,n>=0,3,"must be a non-negative integer"); 331 | } 332 | lua_settop(L,1); 333 | core_new(L,len,1); 334 | if (m>=0) { 335 | lua_pushstring(L,"rows"); lua_pushinteger(L,m); lua_rawset(L,2); 336 | lua_pushstring(L,"cols"); lua_pushinteger(L,n); lua_rawset(L,2); 337 | } 338 | return 1; 339 | } 340 | 341 | /* iota(n) */ 342 | static int apl_iota(lua_State *L) { 343 | int i=0, j, len=luaL_checkint(L,1); 344 | double x=lua_tonumber(L,1); 345 | if (len>x) printf("Lua 'truncated' %.10g to %d\n",x,len); 346 | luaL_argcheck(L,len>=0,1,"must be a non-negative integer"); 347 | if (!lua_isnoneornil(L,2)) { i=luaL_checkint(L,2)-1; } 348 | lua_settop(L,0); 349 | lua_pushnil(L); 350 | core_new(L,len,1); 351 | if (i!=0) { 352 | int t=lua_gettop(L); for (j=1; j<=len; j++) { 353 | lua_rawgeti(L,t,j); 354 | lua_pushinteger(L,lua_tointeger(L,-1)+i); 355 | lua_rawseti(L,t,j); 356 | lua_pop(L,1); 357 | } 358 | } 359 | return 1; 360 | } 361 | 362 | /* check compatibility of shapes */ 363 | static int check_compat(lua_State *L, int a1, int a2, int *m, int *n) { 364 | int l1=-1,m1=-2,n1=-3, l2=-4,m2=-5,n2=-6,k=1,l=1; 365 | if (!lua_istable(L,a1) || !lua_istable(L,a2)) return 1; /* scalar */ 366 | apl_getshapeinfo(a1,l1,m1,n1); 367 | apl_getshapeinfo(a2,l2,m2,n2); 368 | if (l1>=0 && l2>=0) { k=l1; l=l2; } /* two vectors */ 369 | else { 370 | if (l1<0 && l2<0) { /* two matrices */ 371 | k=m1; l=m2; 372 | if (k==l) { k=n1; l=n2; } 373 | } 374 | else if (n1==1&&l2>=0) { k=m1; l=l2; } /* one-column matrix and vector */ 375 | else if (m1==1&&l2>=0) { k=n1; l=l2; } /* one-row matrix and vector */ 376 | else if (l1>=0&&n2==1) { k=m2; l=l1; } /* vector and one-column matrix */ 377 | else if (l1>=0&&m2==1) { k=n2; l=l1; } /* vector and one-row matrix */ 378 | else { k=l1; l=l2; } /* vector and incompatible matrix */ 379 | } 380 | if (k==l) return 1; 381 | if (m&&n) { *m=k; *n=l; } 382 | return 0; 383 | } 384 | 385 | /* compat(a1,a2) 386 | Returns true if 387 | - either a1 or a2 is scalar 388 | - a1 and a2 are both vectors of the same length 389 | - a1 and a2 have identical shapes 390 | - either a1 or a2 is a vector and the other a one-row or one-column 391 | matrix of the same length 392 | Returns false,m,n otherwise, where m and n are the two numbers that 393 | break compatibility 394 | */ 395 | static int apl_compat(lua_State *L) { 396 | int m,n,res; 397 | res = check_compat(L,1,2,&m,&n); 398 | lua_pushboolean(L,res); 399 | if (res) return 1; 400 | lua_pushinteger(L,m); lua_pushinteger(L,n); 401 | return 3; 402 | } 403 | 404 | /* each(f,a): applies f termwise to every item in a */ 405 | static int apl_each(lua_State *L) { 406 | int i, n, tbl=lua_istable(L,2); 407 | luaL_checktype(L,1,LUA_TFUNCTION); 408 | luaL_argcheck(L,!lua_isnoneornil(L,2),2,"nil not allowed"); 409 | lua_settop(L,2); 410 | if (!tbl) { 411 | lua_call(L,1,1); 412 | return 1; 413 | } 414 | n=luaL_len(L,2); 415 | core_new(L,n,0); 416 | for (i=1; i<=n; i++) { 417 | lua_pushvalue(L,1); 418 | lua_rawgeti(L,2,i); 419 | if (lua_isnoneornil(L,-1)) { 420 | lua_rawseti(L,3,i); lua_settop(L,3); continue; 421 | } 422 | lua_call(L,1,1); 423 | lua_rawseti(L,3,i); 424 | } 425 | apl_cloneshape(L,1,2,3); 426 | return 1; 427 | } 428 | 429 | #define f 1 430 | #define a1 2 431 | #define a2 3 432 | #define r 4 433 | #define x1 4 434 | #define x2 5 435 | /* both(f,a1,a2,x1,x2): 436 | * Returns f(a1,a2) when a1 and a2 are both non-tables, ignoring the 437 | other arguments. 438 | * Otherwise applies f termwise, and stores the results in a fresh table, 439 | which is returned after copying the shape from a1, or if a1 is not 440 | a table, from a2. 441 | * If x1 and x2 both equal 0, the shapes of a1 and a2 must be compatible. 442 | * If either equals 1, the corresponding argument is allowed to be 443 | a scalar, or consist of a single value, which will be used every time. 444 | * If either equals 2, the corresponding argument is used every time even 445 | if it is a table. 446 | */ 447 | static int apl_both(lua_State *L) { 448 | int both1=lua_tointeger(L,x1), both2=lua_tointeger(L,x2), 449 | i, n, n1=1, n2=1, s=0, tbl1=lua_istable(L,a1), tbl2=lua_istable(L,a2); 450 | luaL_checktype(L,f,LUA_TFUNCTION); 451 | luaL_argcheck(L,!lua_isnoneornil(L,a1),a1,"nil not allowed"); 452 | luaL_argcheck(L,!lua_isnoneornil(L,a2),a2,"nil not allowed"); 453 | lua_settop(L,r-1); 454 | if ((!tbl1 && !tbl2) || (both1==2 && both2==2)) { /* both scalar */ 455 | lua_call(L,2,1); 456 | return 1; 457 | } 458 | if (tbl1 && both1!=2) n1=luaL_len(L,a1); 459 | if (tbl2 && both2!=2) n2=luaL_len(L,a2); 460 | n = n1>n2?n1:n2; 461 | core_new(L,n,0); 462 | if (!(n1&&n2)) return 1; /* nothing to do */ 463 | if (n1!=1 && both1==1) both1=0; 464 | if (n2!=1 && both2==1) both2=0; 465 | if (tbl1 && both2==1) { s=a2; tbl2=0; } 466 | else if (tbl2 && both1==1) { s=a1; tbl1=0; } 467 | else if (both1!=2 && both2!=2) { 468 | luaL_argcheck(L,check_compat(L,a1,a2,NULL,NULL),a2, 469 | "shapes are incompatible"); 470 | } 471 | if (s && lua_istable(L,s)) { /* replace singleton table by its one item */ 472 | lua_rawgeti(L,s,1); lua_replace(L,s); 473 | } 474 | for (i=1; i<=n; i++) { 475 | lua_pushvalue(L,f); 476 | if (tbl1 && both1!=2) lua_rawgeti(L,a1,i); else lua_pushvalue(L,a1); 477 | if (lua_isnoneornil(L,-1)) { 478 | lua_rawseti(L,r,i); lua_settop(L,r); continue; 479 | } 480 | if (tbl2 && both2!=2) lua_rawgeti(L,a2,i); else lua_pushvalue(L,a2); 481 | if (lua_isnoneornil(L,-1)) { 482 | lua_rawseti(L,r,i); lua_settop(L,r); continue; 483 | } 484 | lua_call(L,2,1); 485 | lua_rawseti(L,r,i); 486 | } 487 | apl_cloneshape(L,tbl2,a2,r); /* rows and cols from a2 */ 488 | apl_cloneshape(L,tbl1,a1,r); /* a1 overrides a2 */ 489 | return 1; 490 | } 491 | #undef f 492 | #undef r 493 | #undef a1 494 | #undef a2 495 | #undef x1 496 | #undef x2 497 | 498 | void apl_array(lua_State *L,double *s,int l) { 499 | int i, t; 500 | core_new(L,l,0); 501 | t=lua_gettop(L); 502 | for (i=0; i0 && n>0, 1, "nonempty APL matrix required"); 517 | l = m0 then helptext=concat(helptext,'\n') 103 | else -- try function body 104 | for k=start,stop do 105 | helptext[#helptext+1] = fcode[k] 106 | end 107 | fcode = helptext 108 | if #helptext>0 then 109 | helptext=concat(helptext,'\n') 110 | helptext = helptext and helptext:match(docstring_pattern) 111 | else helptext=nil 112 | end 113 | -- last resort: full code if it is short enough 114 | if not helptext and (#fcode<=shortenough) then 115 | helptext=concat(fcode,'\n') 116 | end 117 | end 118 | end 119 | end 120 | end 121 | return helptext 122 | end 123 | 124 | -- assumes validity of UTF8 encoding 125 | local utflen = utf8len or 126 | function (s) return #s:gsub("[\192-\239][\128-\191]*",'.') end 127 | 128 | local fold 129 | fold = function(s) 130 | --- Primitive word-wrap function. 131 | if utflen(s)<=72 then return s end 132 | local n=74 133 | while n>60 do n=n-1; if s:find("^%s",n) then break end end 134 | return s:sub(1,n-1)..'\n '..fold(s:sub(n+1)) 135 | end 136 | 137 | local topics = function (tbl,prefix) 138 | local t={} 139 | for k in pairs(tbl) do if type(k)=='string' then 140 | t[#t+1]=(prefix or '')..k 141 | end end 142 | sort(t) 143 | return concat(t,' ') 144 | end 145 | 146 | local help = function(fct,...) 147 | --- help(), help(arg), help(arg1,arg2) 148 | -- help(): Prints short help. 149 | -- help(function): Prints the docstring of `arg`, if any. 150 | -- help(table): Prints `help` field, if any; else contents. 151 | -- help(string): Prints help on the topic, if any. 152 | -- help"all": Prints available topics. 153 | -- help(topic,false): Removes topic from "all" 154 | -- help(arg1,"newhelp"): Redefines what you will get from `help(arg1)` 155 | -- help(arg1,0), help(arg1,nil) (or any second argument except `false` 156 | -- or a string): don't print help, return it as a string instead 157 | if select('#',...)>1 then 158 | print('Too many arguments: try `help(help)`'); return 159 | end 160 | local helptext 161 | local redefine = select('#',...)==1 and type(select(1,...))=='string' 162 | local kill = select(1,...)==false 163 | local printme = select('#',...)==0 164 | if kill then longhelp[fct]=nil 165 | elseif fct==nil then 166 | if redefine then shorthelp=... else helptext=shorthelp end 167 | elseif fct=='all' then 168 | if redefine then print"help cannot be redefined for 'all'"; return 169 | else helptext='Help available via `help"topic"` on these topics:\n '.. 170 | fold(topics(longhelp)) 171 | end 172 | elseif redefine then longhelp[fct]=... 173 | elseif longhelp[fct] then helptext=longhelp[fct] 174 | elseif type(fct)=="table" then 175 | if type(fct.help)=='string' then helptext=fct.help 176 | elseif type(fct.help)=='function' then helptext=fct.help(fct) 177 | else helptext=fold("Contents: "..topics(fct)) 178 | end 179 | elseif type(fct)=='function' then helptext=docstring(fct) or nohelp 180 | else print(helpless:format(fct)); return 181 | end 182 | if printme then print(helptext) else return helptext end 183 | end 184 | 185 | return help 186 | 187 | end 188 | -------------------------------------------------------------------------------- /lua-apl.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** lua-apl.c 3 | ** Lua stand-alone interpreter with APL fallback 4 | ** See Copyright Notice in lua.h 5 | ** patched from $Id: lua.c,v 1.206 2012/09/29 20:07:06 roberto Exp $ 6 | ** by Dirk Laurie 2013 7 | */ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define lua_c 16 | 17 | #include "lua.h" 18 | 19 | #include "lauxlib.h" 20 | #include "lualib.h" 21 | 22 | #define LUA_APL_VERSION "Lua⋆APL 0.4.0" 23 | #define LUA_APL_COPYRIGHT " © Dirk Laurie 2013" 24 | 25 | #if !defined(LUA_PROMPT) 26 | #define LUA_PROMPT "> " 27 | #define LUA_PROMPT2 ">> " 28 | #endif 29 | 30 | #if !defined(LUA_PROGNAME) 31 | #define LUA_PROGNAME "lua" 32 | #endif 33 | 34 | #if !defined(LUA_MAXINPUT) 35 | #define LUA_MAXINPUT 512 36 | #endif 37 | 38 | #if !defined(LUA_INIT) 39 | #define LUA_INIT "LUA_INIT" 40 | #endif 41 | 42 | #define LUA_INITVERSION \ 43 | LUA_INIT "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR 44 | 45 | 46 | /* 47 | ** lua_stdin_is_tty detects whether the standard input is a 'tty' (that 48 | ** is, whether we're running lua interactively). 49 | */ 50 | #if defined(LUA_USE_ISATTY) 51 | #include 52 | #define lua_stdin_is_tty() isatty(0) 53 | #elif defined(LUA_WIN) 54 | #include 55 | #include 56 | #define lua_stdin_is_tty() _isatty(_fileno(stdin)) 57 | #else 58 | #define lua_stdin_is_tty() 1 /* assume stdin is a tty */ 59 | #endif 60 | 61 | 62 | /* 63 | ** lua_readline defines how to show a prompt and then read a line from 64 | ** the standard input. 65 | ** lua_saveline defines how to "save" a read line in a "history". 66 | ** lua_freeline defines how to free a line read by lua_readline. 67 | */ 68 | #if defined(LUA_USE_READLINE) 69 | 70 | #include 71 | #include 72 | #include 73 | #define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) 74 | #define lua_saveline(L,idx) \ 75 | if (lua_rawlen(L,idx) > 0) /* non-empty line? */ \ 76 | add_history(lua_tostring(L, idx)); /* add it to history */ 77 | #define lua_freeline(L,b) ((void)L, free(b)) 78 | 79 | #elif !defined(lua_readline) 80 | 81 | #define lua_readline(L,b,p) \ 82 | ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ 83 | fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ 84 | #define lua_saveline(L,idx) { (void)L; (void)idx; } 85 | #define lua_freeline(L,b) { (void)L; (void)b; } 86 | 87 | #endif 88 | 89 | 90 | 91 | 92 | static lua_State *globalL = NULL; 93 | 94 | static const char *progname = LUA_PROGNAME; 95 | 96 | 97 | 98 | static void lstop (lua_State *L, lua_Debug *ar) { 99 | (void)ar; /* unused arg. */ 100 | lua_sethook(L, NULL, 0, 0); 101 | luaL_error(L, "interrupted!"); 102 | } 103 | 104 | 105 | static void laction (int i) { 106 | signal(i, SIG_DFL); /* if another SIGINT happens before lstop, 107 | terminate process (default action) */ 108 | lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); 109 | } 110 | 111 | 112 | static void print_usage (const char *badoption) { 113 | luai_writestringerror("%s: ", progname); 114 | if (badoption[1] == 'e' || badoption[1] == 'l') 115 | luai_writestringerror("'%s' needs argument\n", badoption); 116 | else 117 | luai_writestringerror("unrecognized option '%s'\n", badoption); 118 | luai_writestringerror( 119 | "usage: %s [options] [script [args]]\n" 120 | "Available options are:\n" 121 | " -e stat execute string " LUA_QL("stat") "\n" 122 | " -i enter interactive mode after executing " LUA_QL("script") "\n" 123 | " -l name require library " LUA_QL("name") "\n" 124 | " -v show version information\n" 125 | " -E ignore environment variables\n" 126 | " -- stop handling options\n" 127 | " - stop handling options and execute stdin\n" 128 | , 129 | progname); 130 | } 131 | 132 | 133 | static void l_message (const char *pname, const char *msg) { 134 | if (pname) luai_writestringerror("%s: ", pname); 135 | luai_writestringerror("%s\n", msg); 136 | } 137 | 138 | 139 | static int report (lua_State *L, int status) { 140 | if (status != LUA_OK && !lua_isnil(L, -1)) { 141 | const char *msg = lua_tostring(L, -1); 142 | if (msg == NULL) msg = "(error object is not a string)"; 143 | l_message(progname, msg); 144 | lua_pop(L, 1); 145 | /* force a complete garbage collection in case of errors */ 146 | lua_gc(L, LUA_GCCOLLECT, 0); 147 | } 148 | return status; 149 | } 150 | 151 | 152 | /* the next function is called unprotected, so it must avoid errors */ 153 | static void finalreport (lua_State *L, int status) { 154 | if (status != LUA_OK) { 155 | const char *msg = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1) 156 | : NULL; 157 | if (msg == NULL) msg = "(error object is not a string)"; 158 | l_message(progname, msg); 159 | lua_pop(L, 1); 160 | } 161 | } 162 | 163 | 164 | static int traceback (lua_State *L) { 165 | const char *msg = lua_tostring(L, 1); 166 | if (msg) 167 | luaL_traceback(L, L, msg, 1); 168 | else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ 169 | if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ 170 | lua_pushliteral(L, "(no error message)"); 171 | } 172 | return 1; 173 | } 174 | 175 | 176 | static int docall (lua_State *L, int narg, int nres) { 177 | int status; 178 | int base = lua_gettop(L) - narg; /* function index */ 179 | lua_pushcfunction(L, traceback); /* push traceback function */ 180 | lua_insert(L, base); /* put it under chunk and args */ 181 | globalL = L; /* to be available to 'laction' */ 182 | signal(SIGINT, laction); 183 | status = lua_pcall(L, narg, nres, base); 184 | signal(SIGINT, SIG_DFL); 185 | lua_remove(L, base); /* remove traceback function */ 186 | return status; 187 | } 188 | 189 | static void print_version (void) { 190 | luai_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); 191 | luai_writeline(); 192 | luai_writestring(LUA_APL_VERSION, strlen(LUA_APL_VERSION)); 193 | luai_writestring(LUA_APL_COPYRIGHT, strlen(LUA_APL_COPYRIGHT)); 194 | luai_writeline(); 195 | } 196 | 197 | 198 | static int getargs (lua_State *L, char **argv, int n) { 199 | int narg; 200 | int i; 201 | int argc = 0; 202 | while (argv[argc]) argc++; /* count total number of arguments */ 203 | narg = argc - (n + 1); /* number of arguments to the script */ 204 | luaL_checkstack(L, narg + 3, "too many arguments to script"); 205 | for (i=n+1; i < argc; i++) 206 | lua_pushstring(L, argv[i]); 207 | lua_createtable(L, narg, n + 1); 208 | for (i=0; i < argc; i++) { 209 | lua_pushstring(L, argv[i]); 210 | lua_rawseti(L, -2, i - n); 211 | } 212 | return narg; 213 | } 214 | 215 | 216 | static int dofile (lua_State *L, const char *name) { 217 | int status = luaL_loadfile(L, name); 218 | if (status == LUA_OK) status = docall(L, 0, 0); 219 | return report(L, status); 220 | } 221 | 222 | 223 | static int dostring (lua_State *L, const char *s, const char *name) { 224 | int status = luaL_loadbuffer(L, s, strlen(s), name); 225 | if (status == LUA_OK) status = docall(L, 0, 0); 226 | return report(L, status); 227 | } 228 | 229 | 230 | static int dolibrary (lua_State *L, const char *name) { 231 | int status; 232 | lua_getglobal(L, "require"); 233 | lua_pushstring(L, name); 234 | status = docall(L, 1, 1); /* call 'require(name)' */ 235 | if (status == LUA_OK) 236 | lua_setglobal(L, name); /* global[name] = require return */ 237 | return report(L, status); 238 | } 239 | 240 | 241 | static const char *get_prompt (lua_State *L, int firstline) { 242 | const char *p; 243 | lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); 244 | p = lua_tostring(L, -1); 245 | if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); 246 | return p; 247 | } 248 | 249 | /* mark in error messages for incomplete statements */ 250 | #define EOFMARK "" 251 | #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) 252 | 253 | static int incomplete (lua_State *L, int status) { 254 | if (status == LUA_ERRSYNTAX) { 255 | size_t lmsg; 256 | const char *msg = lua_tolstring(L, -1, &lmsg); 257 | if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { 258 | lua_pop(L, 1); 259 | return 1; 260 | } 261 | } 262 | return 0; /* else... */ 263 | } 264 | 265 | /* Guess, based on its first line, whether a chunk is APL. 266 | For minimal changes to a line causing a wrong guess, put `∘` (APL, 267 | don't print), `⍕` (APL, please print), `=` (Lua, please print) in 268 | front of it. 269 | */ 270 | static int guess_apl(char* b) { 271 | int k; 272 | /* Possible characters at the start of an executable Lua statement */ 273 | char *Lua="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz("; 274 | /* Words that could be present when an APL string literal is used in Lua */ 275 | char *c, *Lua_Marker[] = {"apl","Define","Execute","help","return","print"}; 276 | /* Skip blanks */ 277 | while (*b==' ') b++; 278 | if (strlen(b)<2) return 0; 279 | /* If it is a Lua comment, has '=' shortcut, or contains a Lua marker: Lua */ 280 | for (k=0; k127) return 1; 288 | return 0; 289 | } 290 | 291 | static int pushline (lua_State *L, int firstline) { 292 | char buffer[LUA_MAXINPUT]; 293 | char *b = buffer; 294 | size_t l; 295 | const char *prmt = get_prompt(L, firstline); 296 | int readstatus = lua_readline(L, b, prmt); 297 | lua_pop(L, 1); /* remove result from 'get_prompt' */ 298 | if (readstatus == 0) 299 | return 0; /* no input */ 300 | l = strlen(b); 301 | if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ 302 | b[l-1] = '\0'; /* remove it */ 303 | if (firstline && guess_apl(b)) /* does this look like raw APL ? */ 304 | lua_pushfstring(L, "return apl\"%s\"()", b); 305 | else if (firstline && b[0] == '=') /* first line starts with `=' ? */ 306 | lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ 307 | else 308 | lua_pushstring(L, b); 309 | lua_freeline(L, b); 310 | return 1; 311 | } 312 | 313 | 314 | static int loadline (lua_State *L) { 315 | int status; 316 | lua_settop(L, 0); 317 | if (!pushline(L, 1)) 318 | return -1; /* no input */ 319 | for (;;) { /* repeat until gets a complete line */ 320 | size_t l; 321 | const char *line = lua_tolstring(L, 1, &l); 322 | status = luaL_loadbuffer(L, line, l, "=stdin"); 323 | if (!incomplete(L, status)) break; /* cannot try to add lines? */ 324 | if (!pushline(L, 0)) /* no more input? */ 325 | return -1; 326 | lua_pushliteral(L, "\n"); /* add a new line... */ 327 | lua_insert(L, -2); /* ...between the two lines */ 328 | lua_concat(L, 3); /* join them */ 329 | } 330 | lua_saveline(L, 1); 331 | lua_remove(L, 1); /* remove line */ 332 | return status; 333 | } 334 | 335 | 336 | static void dotty (lua_State *L) { 337 | int status; 338 | const char *oldprogname = progname; 339 | progname = NULL; 340 | while ((status = loadline(L)) != -1) { 341 | if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); 342 | report(L, status); 343 | if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */ 344 | luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); 345 | lua_getglobal(L, "print"); 346 | lua_insert(L, 1); 347 | if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != LUA_OK) 348 | l_message(progname, lua_pushfstring(L, 349 | "error calling " LUA_QL("print") " (%s)", 350 | lua_tostring(L, -1))); 351 | } 352 | } 353 | lua_settop(L, 0); /* clear stack */ 354 | luai_writeline(); 355 | progname = oldprogname; 356 | } 357 | 358 | 359 | static int handle_script (lua_State *L, char **argv, int n) { 360 | int status; 361 | const char *fname; 362 | int narg = getargs(L, argv, n); /* collect arguments */ 363 | lua_setglobal(L, "arg"); 364 | fname = argv[n]; 365 | if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) 366 | fname = NULL; /* stdin */ 367 | status = luaL_loadfile(L, fname); 368 | lua_insert(L, -(narg+1)); 369 | if (status == LUA_OK) 370 | status = docall(L, narg, LUA_MULTRET); 371 | else 372 | lua_pop(L, narg); 373 | return report(L, status); 374 | } 375 | 376 | 377 | /* check that argument has no extra characters at the end */ 378 | #define noextrachars(x) {if ((x)[2] != '\0') return -1;} 379 | 380 | 381 | /* indices of various argument indicators in array args */ 382 | #define has_i 0 /* -i */ 383 | #define has_v 1 /* -v */ 384 | #define has_e 2 /* -e */ 385 | #define has_E 3 /* -E */ 386 | 387 | #define num_has 4 /* number of 'has_*' */ 388 | 389 | 390 | static int collectargs (char **argv, int *args) { 391 | int i; 392 | for (i = 1; argv[i] != NULL; i++) { 393 | if (argv[i][0] != '-') /* not an option? */ 394 | return i; 395 | switch (argv[i][1]) { /* option */ 396 | case '-': 397 | noextrachars(argv[i]); 398 | return (argv[i+1] != NULL ? i+1 : 0); 399 | case '\0': 400 | return i; 401 | case 'E': 402 | args[has_E] = 1; 403 | break; 404 | case 'i': 405 | noextrachars(argv[i]); 406 | args[has_i] = 1; /* go through */ 407 | case 'v': 408 | noextrachars(argv[i]); 409 | args[has_v] = 1; 410 | break; 411 | case 'e': 412 | args[has_e] = 1; /* go through */ 413 | case 'l': /* both options need an argument */ 414 | if (argv[i][2] == '\0') { /* no concatenated argument? */ 415 | i++; /* try next 'argv' */ 416 | if (argv[i] == NULL || argv[i][0] == '-') 417 | return -(i - 1); /* no next argument or it is another option */ 418 | } 419 | break; 420 | default: /* invalid option; return its index... */ 421 | return -i; /* ...as a negative value */ 422 | } 423 | } 424 | return 0; 425 | } 426 | 427 | 428 | static int runargs (lua_State *L, char **argv, int n) { 429 | int i; 430 | for (i = 1; i < n; i++) { 431 | lua_assert(argv[i][0] == '-'); 432 | switch (argv[i][1]) { /* option */ 433 | case 'e': { 434 | const char *chunk = argv[i] + 2; 435 | if (*chunk == '\0') chunk = argv[++i]; 436 | lua_assert(chunk != NULL); 437 | if (dostring(L, chunk, "=(command line)") != LUA_OK) 438 | return 0; 439 | break; 440 | } 441 | case 'l': { 442 | const char *filename = argv[i] + 2; 443 | if (*filename == '\0') filename = argv[++i]; 444 | lua_assert(filename != NULL); 445 | if (dolibrary(L, filename) != LUA_OK) 446 | return 0; /* stop if file fails */ 447 | break; 448 | } 449 | default: break; 450 | } 451 | } 452 | return 1; 453 | } 454 | 455 | 456 | static int handle_luainit (lua_State *L) { 457 | const char *name = "=" LUA_INITVERSION; 458 | const char *init = getenv(name + 1); 459 | if (init == NULL) { 460 | name = "=" LUA_INIT; 461 | init = getenv(name + 1); /* try alternative name */ 462 | } 463 | if (init == NULL) return LUA_OK; 464 | else if (init[0] == '@') 465 | return dofile(L, init+1); 466 | else 467 | return dostring(L, init, name); 468 | } 469 | 470 | 471 | static int pmain (lua_State *L) { 472 | int argc = (int)lua_tointeger(L, 1); 473 | char **argv = (char **)lua_touserdata(L, 2); 474 | int script; 475 | int args[num_has]; 476 | args[has_i] = args[has_v] = args[has_e] = args[has_E] = 0; 477 | if (argv[0] && argv[0][0]) progname = argv[0]; 478 | script = collectargs(argv, args); 479 | if (script < 0) { /* invalid arg? */ 480 | print_usage(argv[-script]); 481 | return 0; 482 | } 483 | if (args[has_v]) print_version(); 484 | if (args[has_E]) { /* option '-E'? */ 485 | lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ 486 | lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); 487 | } 488 | /* open standard libraries */ 489 | luaL_checkversion(L); 490 | lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ 491 | luaL_openlibs(L); /* open libraries */ 492 | lua_gc(L, LUA_GCRESTART, 0); 493 | if (!args[has_E] && handle_luainit(L) != LUA_OK) 494 | return 0; /* error running LUA_INIT */ 495 | /* execute arguments -e and -l */ 496 | if (!runargs(L, argv, (script > 0) ? script : argc)) return 0; 497 | /* execute main script (if there is one) */ 498 | if (script && handle_script(L, argv, script) != LUA_OK) return 0; 499 | if (args[has_i]) /* -i option? */ 500 | dotty(L); 501 | else if (script == 0 && !args[has_e] && !args[has_v]) { /* no arguments? */ 502 | if (lua_stdin_is_tty()) { 503 | print_version(); 504 | dotty(L); 505 | } 506 | else dofile(L, NULL); /* executes stdin as a file */ 507 | } 508 | lua_pushboolean(L, 1); /* signal no errors */ 509 | return 1; 510 | } 511 | 512 | int main (int argc, char **argv) { 513 | int status, result, k; 514 | char *myarg[argc+5], *arg[]={ 515 | "-l","apl", 516 | "-e","apl:import()", 517 | "-i"}; 518 | if (argc>1) { 519 | printf("Error: lua-apl does not accept extra command-line arguments\n"); 520 | return EXIT_FAILURE; 521 | } 522 | lua_State *L = luaL_newstate(); /* create state */ 523 | if (L == NULL) { 524 | l_message(argv[0], "cannot create state: not enough memory"); 525 | return EXIT_FAILURE; 526 | } 527 | /* add arguments to load and initialize module `apl` */ 528 | myarg[0]=strdup(argv[0]); 529 | for (k=0; k<5; k++) myarg[k+1]=strdup(arg[k]); 530 | argc+=5; 531 | /* call 'pmain' in protected mode */ 532 | lua_pushcfunction(L, &pmain); 533 | lua_pushinteger(L, argc); /* 1st argument */ 534 | lua_pushlightuserdata(L, myarg); /* 2nd argument */ 535 | status = lua_pcall(L, 2, 1, 0); 536 | result = lua_toboolean(L, -1); /* get result */ 537 | finalreport(L, status); 538 | lua_close(L); 539 | return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; 540 | } 541 | 542 | -------------------------------------------------------------------------------- /lua-apl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 |

Lua⋆APL User's Manual

16 |

© Dirk Laurie 2013 Lua-style MIT licence

17 |

Read what it says in the README about installation and UTF-8.

18 |

APL is a functional language with extensive array processing abilities, originally designed by Ken Iverson in the 1960's. Lua⋆APL is an extension of Lua that gives access to the power of APL (the APL asterisk means Power) without leaving the comfort and security of Lua.

19 |

There are three modes of usage, which can be thought of as analogous to the C API, scripting language, and standalone executable of Lua.

20 |
21 |
Library mode:
22 |

APL functionality is offered as a Lua library, which makes no use of the APL character set except in its help information. You type in Lua commands like:

23 |
Reshape(Take(1,Add(1,n)),Attach2(n,n))
24 |
25 |
Lua mode:
26 |

Functions callable from Lua can be written in APL. This is achieved via a function apl rather like Lua's load, which generates intermediate Lua code consisting of chained calls to the APL library. You type in Lua commands like:

27 |
apl"(n,n)⍴(n+1)↑1"()
28 |

The apl function invokes an APL compiler to translate this to the string of calls shown above.

29 |
30 |
APL mode:
31 |

Bare APL code can be entered directly at the terminal. You type in a mixture of Lua and APL commands like:

32 |
n=10
 33 | (n,n)⍴(n+1)↑1
34 |

The APL interpreter decides the second line must be APL code and modifies it to the Lua mode code shown above. If it seems to be Lua code, the interpreter does not do the translation.

35 |

This works because APL code has such a distinctive appearance. Even a primitive guessing algorithm can identify APL code with a high rate of accuracy. Code deemed to be APL is translated to load and execute as in Lua mode, but the result is printed by default as typical APL systems do. The effect is to give an APL-like look-and-feel. If you already know APL, jump straight to the section on APL mode and try it out.

36 |
37 |
38 |

The code examples shown here, even the ones in the section on Lua mode, use the three-space prompt of the Lua⋆APL standalone rather than the conventional Lua prompt, but all code except that shown in the section on APL mode works on a vanilla Lua interpreter too.

39 |

In this document, we do not consistently use "APL" and "Lua⋆APL" as denoting different things. "APL" is a fairly vague term, meaning more or less "a typical APL implementation" or even "the APL way of thinking", whereas "Lua⋆APL" is a specific term for this implementation, which of course also falls under the shadowy APL umbrella.

40 |

Words like "standard" refer to features that are typically found in almost every APL dialect since about 1980, well summarized in the Wikipedia article. "Non-standard" in the case of Lua⋆APL means that I am using an unusual APL symbol without making any attempt even to find out how modern APL implementations use it (the latest APL that I take into account is APL2, and even in that case compatibility across the board is not aimed at). Please contact me if you feel that some other symbol for the same concept, or some other concept for the same symbol, is actually semi-standard by now.

41 |

If you would like to know much more about APL than you currently do, there are several well-organized websites of modern commercial APLs, e.g. http://www.microapl.com/apl_help.

42 |

When I wrote this line, the version number displayed by the start-up message was 0.4.0. As long as the first number is 0, anything might still change.

43 |

Library mode

44 |

In this mode, APL is a distant inspiration only, but since the other modes make use of it, it is documented quite extensively here.

45 |

Actually, I do not expect anybody to stop at library mode, but it is the easiest place to start for Lua programmers who do not know any APL; eventually one needs know about it, if only because some library functions have optional extra parameters that are fixed at default values when called from APL. It is best learnt in an interactive session because an extensive amount of interactive help is available.

46 |

I'm assuming you loaded the module by

47 |
   apl=require'apl'
 48 |    apl:import'*'
49 |

That call to apl:import adds the 90-plus items of the module to the global environment. Almost all of them are quite distinctively named, but if you instinctively recoil in horror, muttering "pollution" under your breath, by all means omit that line and prepare to type numerous instances of apl., or (in a script) replace it by assigning the ones you actually use to local variables. Better, actually: just hold your horses till you get to Lua mode; no large-scale imports necessary there.

50 |

A thorough knowledge of what the library offers is not needed if you already know APL and plan to use mostly APL mode. If you already know APL, by all means skip straight to APL mode and only read this part later. If you don't want to build the APL interpreter, skip to Lua mode.

51 |

The module apl loads a C module apl_core, documented in the Programmer's Guide.

52 |

The module returns a table with keys named by the following convention:

53 |
    54 |
  • CamelCase: APL functions (mostly mapped to APL symbols later)
  • 55 |
  • lowercase: Lua functions (not usually called from APL code)
  • 56 |
  • _CAPS: needed by the system or providing information
  • 57 |
  • _lowercase: control variables
  • 58 |
59 |

"APL functions" of course does not imply that they are anything else than just Lua functions. It merely means that these are mostly the functions that lie at the heart of APL, closely following the original design of Iverson. There is a simple mapping between Lua postfix notation and APL infix notation, for example:

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
LuaAPL
_w
_a
func(_w,_a)⍺ func ⍵
Range(_w)⍳⍵
Find(_w,_⍺)⍺⍳⍵
Shape(_w)⍴⍵
Reshape(_w,_a)⍺⍴⍵
Decode(_w,_a)⍺⊤⍵
Add(_w,_a)⍺+⍵
Sub(_w,_a)⍺-⍵
Mul(_w,_a)⍺×⍵
Div(_w,_a)⍺÷⍵
131 |

In total, over 90 of these functions are provided in library mode. The APL symbols used for them, even those which in Lua denote arithmetic operators, must be thought of as names, not as as special characters, even though those names are not used in library mode.

132 |

One and the same APL name often does duty for two Lua names. This is possible because APL infix syntax allows the compiler to distinguish between monadic and dyadic functions (see Valence) and select the appropriate function. That distinction is possible in Lua too, but only at run time, when one can test whether the second argument of a function is present and not nil.

133 |

APL syntax makes the distinction already at compile time, so that overloading of the Lua names of the functions is not necessary. Instead, the names have been chosen to be descriptive of what the functions do.

134 |

Certain semi-standard descriptive names are often used in APL documentation, such as http://en.wikipedia.org/wiki/APL_syntax_and_symbols. These have been retained in some cases (Ravel, Reshape etc), abbreviated in others (Exp rather than Exponential etc), and occasionally replaced by totally different names (Pow rather than Exponentiation). Functions corresponding to Lua arithmetic metamethods have been given basically the same name (Unm for unary minus, etc.) but there's no rule, really; I've done what feels programmer-friendly to me.

135 |

The documentation, in particular the interactive help, freely uses both the Lua names and APL symbols simultaneously, so you will acquire a smattering of APL more or less painlessly even while using library mode only.

136 |

APL types and Lua types

137 |

An APL-like type classification consisting of functions, operators, scalars, vectors and matrices is implemented via Lua values.

138 |

The APL terms "function" and "operator" are used in a different sense than in Lua. The symbols for arithmetic, comparison etc are called "operators" in Lua and the parser treats them by strict precedence rules. In APL, those symbols are names of functions (many of them are actually three-byte names that only look like a single character thanks to UTF-8), there is no notion of precedence, and "operator" means a function that acts on functions.

139 |
    140 |
  • APL functions are Lua functions of one or two variables, returning one value. In the documentation, the names ⍺,⍵ and _a,_w are used respectively when the function is called from APL or from Lua. The left argument in APL infix notation becomes the second argument _a in Lua postfix notation. In APL, the parser can see whether is omitted; in Lua, providing _a as nil is equivalent to omitting it.

  • 141 |
  • APL operators are also Lua functions of two variables, but:

    142 |
      143 |
    1. Their arguments and return value are functions.

    2. 144 |
    3. In the documentation, the first argument is called f and the second g in the documentation of both APL and Lua.

    4. 145 |
    5. The left argument f in APL infix notation becomes the first argument f in Lua postfix notation and must always be present; the (right) second argument g is usually absent, Inner being the only function in apl-lib that uses it.

    6. 146 |
    7. Operators have a higher priority than functions.

    8. 147 |
  • 148 |
  • APL scalars are Lua numbers and strings. Other Lua types are treated as scalar. In particular, a Lua function can be an APL scalar. APL needs to be told at the lexical level (via register) which names will be used to denote functions.

  • 149 |
  • APL arrays (vectors and matrices both) are Lua index-1 tables. The ordering of matrix elements is row-wise. Lua⋆APL arrays do not have a type as in classic APL: each element is a Lua value and carries its own type. In particular, an empty array has no type.

  • 150 |
151 |

APL arrays share a common metatable, which provides __tostring, __index __newindex and __lt (the last of these does a lexicographic comparison). Numeric keys in the simplest case obey the same conventions as the Lua table library: the positive integer keys of A are expected to form a solid block from 1 to #A. The full truth is more complicated; see Array operations and Indexing.

152 |

String keys are used to achieve the extra functionality of APL arrays:

153 |
154 |
A.apl_len
155 |
Optional field. If present, this is the value returned by the Lua # operator. 156 |
157 |
A.rows, A.cols
158 |
Number of rows and columns (matrix only). 159 |
160 |
161 |

It is possible to synthesize an APL array in vanilla Lua. However, it is not recommended, and never necessary, for two reasons:

162 |
    163 |
  • In almost all cases, the APL library will accept a Lua table as a substitute for an APL vector.
  • 164 |
  • All tables returned by the library are APL arrays. In particular, the function Copy will convert a Lua array to an APL array.
  • 165 |
166 |

For example, to define a matrix (see Formatting for _format) one calls Reshape with a Lua table and the desired shape as arguments; to convert it to an APL vector one calls Ravel.

167 |
   apl._format = 'raw'     
168 |    A = Reshape({11,12,13,21,22,23},{2,3})   
169 |    print(A)
170 | [11,12,13;21,22,23]
171 |    print(Ravel(A))
172 | (11,12,13,21,22,23) 
173 |

The rank of an array is the number of indices it takes to reference an element from it. APL allows rank 0 (scalar), rank 1 (vector) and rank 2 (matrix). APL does not distinguish between row vectors and column vectors; if you need to do that, use a one-row or one-column matrix.

174 |

Shape is the APL analogue of Lua's type, defined as follows:

175 |
    176 |
  • number: Shape(a) = {}
  • 177 |
  • vector: Shape(x) = {#x}
  • 178 |
  • matrix: Shape(A) = {A.rows,A.cols}
  • 179 |
  • string: Shape(s) = #s
  • 180 |
  • other scalar: Shape(other) = nil
  • 181 |
182 |

It should be stressed that APL matrices are not tables of tables, they are simple tables that carry shape information.

183 |

Lua userdata values are also APL scalars. If equipped with the right metamethods, they might work inside APL expressions, but this possibility is unexplored.

184 |

Formatting

185 |

APL normally uses a pretty-printing output format.

186 |
   print(Reshape({11,12,13,21,22,23},{2,3}))
187 | 11 12 13
188 | 21 22 23
189 |    
190 |

This is produced by the Format function.

191 |

While nice-looking, this format makes it hard to notice the subtle distinctions that we will need to make in this manual, and therefore we have in many of the examples used raw format. The Lua⋆APL Format function in general takes two arguments. The second argument "raw" selects one-line output with the following conventions:

192 |
    193 |
  • undelimited: scalars
  • 194 |
  • delimited by braces: {Lua,table}
  • 195 |
  • delimited by parentheses: (APL,vector)
  • 196 |
  • delimited by brackets: [APL,matrix;semicolons,separate,rows]
  • 197 |
198 |

If the second argument is absent, the _format field in the module table is interrogated. Since the module table is not read-only, the user can assign something (e.g. "raw") to apl._format,

199 |

Not-a-number is printed as NaN and nil as an underscore.

200 |
   print(Format{1,nil,0/0,"abc"})
201 | {1,_,NaN,'abc'}
202 |

The Format function works recursively until the result is a string.

203 |

Array operations

204 |

The whole point of using APL is so that one never (well, hardly ever) needs to use an explicit table index. The vast majority of its functions accept array arguments and return array values.

205 |

The APL library handles nils in a table gracefully in the case of all scalar functions (i.e. functions that apply term-by-term to array arguments).

206 |
   x=Copy{1,2,nil,4}; y=Reverse(x)
207 |    print(Add(x,y))
208 | {5,_,_,5}
209 |

The actual storage of a very sparse vector will be compact, using mostly the hash part of a Lua table, but there is no true optimized support for sparse vectors. The functions simply iterate over the known length of the array and skip nils.

210 |

Indexing

211 |

APL arrays have a fixed size, which is frozen for a given Lua table tbl by setting its metatable to the APL array metatable. Or rather, it is the set of indices with non-nil values that is frozen. The __index and __newindex metamethods come into play whenever an attempt is made to access any index except those which are actually in use. Conversely those metamethods are not invoked in the case of a valid index.

212 |
    213 |
  • A nonexistent numeric or string index is illegal.
  • 214 |
  • If you assign nil to a numeric or string index, that index becomes unavailable permanently, no matter what apl_len is, except via rawget and rawset.
  • 215 |
  • A legal numeric index to a matrix indexes it as a vector, in row-major order (i.e. matrices are stored row by row).
  • 216 |
  • A table-valued index to a vector should either be an APL vector (Lua array acceptable) or an APL matrix (Lua array with rows and cols set acceptable). The result will have the same shape as the index.
  • 217 |
  • A table-valued index to a matrix should be a pair of items, each of which may be a number or a Lua array (APL vectors and matrices are acceptable, but lose their magic powers) which select the rows and columns. The result will have rank determined by the index types, and shape determined by the lengths of the lists.
  • 218 |
  • A function-valued index should be an iterator that returns a valid numeric index until it returns nil. The array being indexed is treated as a vector, and the result is always a vector.
  • 219 |
220 |

The two metamethods are available in the library as Get and Set, and can be invoked explicitly, even with ordinary non-APL tables. You should only do that with a function-valued or table-valued index, because you are bypassing the Lua indexing mechanism. Other indices are passed to routines normally called only for nonexistent indices; the routines know that a scalar index that gets this far must be invalid, and throw an error.

221 |
   x={1,2,3,"A","B","C"}
222 |    y={10,20,30,A='a',B='b',C='c'}
223 |    print(Get(x,{5,2,4}))
224 | ('B',2,'A')
225 |    Set(y,Get(x,{5,2,4}),{100,200,300})
226 |    print(y)
227 | table: 0x93e4e90
228 |    print(Copy(y),y.A,y.B)
229 | (10,200,30)     300     100
230 |    Get(y,1)  -- index not a function or a table: trouble!
231 | ./apl-lib.lua:520: bad argument #2 to 'core_index' (index out of range)
232 |

Nested arrays

233 |

The elements of an APL array may be themselves be arrays, since the inner structure of an element is opaque to APL, i.e. APL thinks of anything stored in an array as scalar.

234 |
   A={{1,2},{3,4},{5,6}}; print(Format(A))
235 | {{1,2},{3,4},{5,6}}
236 |    B=Copy(A); print(B)         
237 | ({1,2},{3,4},{5,6})            
238 |    print(A[1]==B[1])
239 | true
240 |

That true says that A[1] and B[1] are actually one and the same table: the elements are still the original Lua tables. This is not quite what is usually wanted from a routine called Copy. The operator Each applies a function term-by-term to every element of a table and returns a new array with the same shape as the original.

241 |
   C=Each(Copy)(A); print(C)
242 | ((1,2),(3,4),(5,6))
243 |    print(A[1]==C[1])
244 | false
245 |

The elements are now newly minted APL arrays, as is the containing array.

246 |

Each works equally well for monadic and for dyadic functions. The APL compiler can tell the difference.

247 |
   print(Each(Reshape)({{1,2,3,4},{5,6,7,8}},{{2,2}}))
248 | ([1,2;3,4],[5,6;7,8])
249 |

In this example, the two arguments are both nested arrays, so they look to APL like a two-element and a one-element array. A dyadic function created by Each expects two arrays of compatible shape for term-by-term operations, but will accept a one-element argument to act as a constant value. You can override that permission by supplying extra parameters to Each (this feature is only available when calling Each from Lua; the APL operator ¨ does not allow the possibility.).

250 |
   print(Each(Add)({{1,2,3,4},{5,6,7,8}},{1}))
251 | (2,3,4,5) (6,7,8,9)
252 |    help(Each)
253 | Each(f): f¨ → make a term-by-term function from f
254 | Each(f,x_w,x_a): (no APL equivalent) Fine-tune constant arguments.
255 |    x_w=x_a=0: the shapes of _w and _a must be compatible.
256 |    x_w=1, x_a=1: _w,_a may be a singleton, which will be used every time.
257 |    x_w=2, x_a=2: _w,_a is used as such every time even if it is a table.
258 |    print(Each(Add,1,0)({{1,2,3,4},{5,6,7,8}},{1}))
259 | ./apl-lib.lua:497: bad argument #3 to 'both' (shapes are incompatible)
260 |

A vector of vectors of the same length can be combined into a matrix, and a matrix can be converted to a nested array. These operations are so commonly needed that the APL2 notations (Enclose) and (Disclose) have been borrowed (but not with the exact same meaning), and the non-standard APL function Rerank has have been invented as an umbrella for them in library mode.

261 |
   help"⊂"
262 | Enclose: ⊂⍵ makes an array of rows from a matrix or concatenates a vector.
263 | Depends on _join.
264 |    help"⊃"
265 | Disclose: ⊃⍵ makes a matrix from an array of rows, or a string vector from
266 | a string. If ⍵ is a matrix, each ⍵[i] is treated as a vector and padded to 
267 | the maximum length using zeros or empty strings. If ⍵ is a string, ⍵ is
268 | split into substrings, depending on _split.
269 |    help(Rerank)
270 | Rerank: Rerank(_w,_a): array of one rank higher (_a>0) or lower (_a<0),
271 |    stacking rows (±1) or columns (±2). See Disclose/Enclose.
272 |

The behaviour of Enclose and Disclose on strings is discussed under Strings.

273 |

Axis-dependent functions and operators

274 |

Several APL vector functions generalize to matrices by operating either on the rows or on the columns. In APL notation, the distinction is made by using different symbols (⊖ ⌿ ⍀ ⍪ are the row-wise counterparts to ⌽ / \ ,). There are typically three different functions at issue here.

275 |
   help(Attach)
276 | Attach(⍵,⍺): ⍺,⍵ → elements of ⍺ followed by elements of ⍵
277 |    help(Attach1)
278 | Attach1(⍵,⍺): ⍺⍪⍵ → rows of ⍺ followed by rows of ⍵
279 |    help(Attach2)
280 | Attach2(⍵,⍺): ⍺,⍵ → columns of ⍺ followed by columns of ⍵
281 |    print(Attach({1,2},{3}))
282 | (3,1,2)
283 |    print(Attach1({1,2},{3}))
284 | [3,0;1,2]
285 |    print(Attach2({1,2},{3}))
286 | [3,1,2]
287 |

As you can see, Attach1 pads short rows to the length of the longest list. Attach2 pads short columns, but vectors count as one-row matrices.

288 |

In Lua⋆APL, the principle is "act on rows when the axis is 1, act on colums when the axis is 2", and the implementation is to convert the matrix to a nested array, do something to that nested array, and convert back.

289 |

In the case of Rotate, there is a possibility of confusion here. Rotate1(A,1) must rotate the rows of A up by 1, no question. What does Rotate1(A,{2,1,3,4}) do, rotate each row of A individually by the specified amount? If so, Rotate1(A,{1,1,1,1}) would rotate every row left by 1, i.e. it would rotate the columns left by 1. That would violate the APL principle that a singleton does the same as a constant vector. So we must violate the Lua⋆APL principle for the sake of the greater cause: Rotate1(A,{2,1,3,4}) rotates each column of A individually by the specified amount.

290 |
   A=Copy{1,2,3,4;5,6,7,8;9,10,11,12;rows=3,cols=4}; print(A)
291 |  1  2  3  4
292 |  5  6  7  8
293 |  9 10 11 12
294 |    print(Rotate1(A,{2,1,3,4}))
295 |  9  6  3  8
296 |  1 10  7 12
297 |  5  2 11  4
298 |    
299 |

High-level matrix functions

300 |

In the case where A is a non-singular square matrix, MatDiv(A,b) returns the unique solution of a system of linear equations and MatInv(A) to the matrix inverse.

301 |

For a singular or non-square matrix, the numerical rank is determined. MatDiv(A,b) returns the minimum-norm least-squares solution, and MatInv(A) the pseudo-inverse, for that rank.

302 |

The determination of rank depends on non-negative numbers known as the singular values of A (see any text on matrix computations) and on the comparison tolerances (see Global control variables). Specifically, if _act is nonzero, singular values less than _act count as zero; if _rct is nonzero, singular values less than _rct*s[1] count as zero. The rank equals the number of nonzero singular values.

303 |

In library mode, the function SVD has been provided for the convenience of those who know the theory. Its return value is a Lua table containing a vector S of singular values and nested arrays U and V of left and right singular vectors respectively. At this point library mode becomes just too inconvenient, so we resort to Lua mode.

304 |
   A=Reshape(Range(12),{3,4}); print(A)
305 |  1  2  3  4
306 |  5  6  7  8
307 |  9 10 11 12
308 |    U=USV.U; V=USV.V; S=USV.S; print(S)
309 | 25.43684 1.722612 2.681529e¯16
310 |    B=apl"+/SרU(∘.×)¨V"(); print(B)
311 |  1  2  3  4
312 |  5  6  7  8
313 |  9 10 11 12
314 |    
315 |

Strings

316 |

Lua⋆APL does not have character arrays, any more than Lua itself has them. A string is a scalar. If you need to operate on individual bytes of a string, the Lua function string.byte combines well with the library.

317 |
   tobytes = function(s) return {s:byte(1,-1)} end
318 |    frombytes = function(s) return string.char(unpack(s)) end
319 |    M=Each(tobytes){"The","quick","brown","fox"}; print(M)
320 | ({84,104,101},{113,117,105,99,107},{98,114,111,119,110},{102,111,120})
321 |    print(Each(frombytes)(M))
322 | ('The','quick','brown','fox')
323 |

Lua⋆APL does have string arrays, and support for them is provided by the functions Disclose and Enclose. These functions normally act respectively on nested arrays and matrices, but Disclose(str) splits a string into UTF-8 codepoints and Enclose(vec) concatenates a vector. More precisely, Disclose returns the result of apl.util.utfchar and Enclose the result of table.concat.

324 |

This support can be customized: see Splitting and joining.

325 |

Global control variables

326 |

APL traditionally makes use of some control variables that affect its overall behaviour. Which these are, differs from one APL implementation to the next, so they are sometimes called "system variables". In Lua⋆APL, their names start with an underscore, and they are stored in the module table.

327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 |
_actAbsolute comparison tolerance.
_rctRelative comparision tolerance.
_formatDefault format for monadic Format.
_splitString splitting function.
_joinTable concatenation function.
356 |

Comparison tolerance

357 |

Lua⋆APL does a straightforward comparison only in the case of functions that don't test for equality (TestNE, TestGT, TestLT). Functions that give 1 in the equality case (TestEq, TestGE, TestLE) actually test for approximate equality. Either of abs(_a-_w)<apl._act or abs(_a-_w)<apl._rct*abs(_w) also counts as equality if the corresponding control variable is defined,

358 |

The philosophical implications are mind-blowing (TestEq(x,y) may not give the same result as TestEq(y,x), etc) but the practical effect is that with the default settings, the equality tests do what most sane people (that excludes numerical analysts, of course) expect them to. If you don't want them, you can always set those tolerances to zero or nil, or just do the complementary test (1-TestNE(x,y) etc).

359 |
   s=1/7; t=s+s+s+s+s+s+s; print(1==t)
360 | false
361 |    print(TestGE(1,t))
362 | 1
363 |    apl._act=0; apl._rct=0
364 |    print(TestGE(1,t))
365 | 0
366 |

Splitting and joining

367 |

The routines called by Disclose and Enclose for splitting and joining by default are apl.util.utfchar and table.concat respectively. You can change that by setting your own monadic functions in the global control variables apl._split and apl._join; to revert to default, set them to nil.

368 |

_split is only called when Disclose receives a string; _join is called for any table that is not a matrix, and the usual Lua coercion of numbesr to strings will be applied.

369 |

List of Lua⋆APL functions

370 |

The current version may give a slightly different result, so do the following yourself. For most of these, you can get interactive help.

371 |
   help(apl)
372 | Contents: Abs Add And Assign Attach Attach1 Attach2 Binom Ceil Circ
373 |     Compress Compress1 Compress2 Copy Deal Decode Define Disclose Div Down
374 |     Drop Each Enclose Encode Execute Exp Expand Expand1 Expand2 Fact Find
375 |     Floor Format Get Has Inner Length Ln Log MatDiv MatInv Max Min Mod Mul
376 |     Nand Nor Not Or Outer Output Pass Pi Pow Range Ravel Recip Reduce
377 |     Reduce1 Reduce2 Rerank Reshape Reverse Reverse1 Reverse2 Roll Rotate
378 |     Rotate1 Rotate2 SVD Same Scan Scan1 Scan2 Set Shape Sign Sub Take TestEq
379 |     TestGE TestGT TestLE TestLT TestNE ToString Transpose Unm Up _act _rct
380 |     help import lua register util  
381 |    help(Transpose)
382 | Transpose: ⍉⍵ → matrix transpose of ⍵
383 |

apl.util contains utility functions. They are lightly documented by help, even if the help is merely the Lua code.

384 |
   help(apl.util)
385 | Contents: argcheck arr both checksize checktype compat each filler get
386 |     invert iota is is_int is_not replace rho set shape start sum utfchar
387 |     utflen
388 |

Lua mode

389 |

I'm assuming you loaded the module by

390 |
   apl=require"apl"
391 |    apl:import()
392 |

That call to apl:import adds help and lua to the global environment.

393 |

This is the mode in which to write programs, but makes quite a convenient interactive session too. If you prefer the control and safety of a family car to the breathless excitement of an open-top sports model, you will be happier with Lua mode than APL mode.

394 |

The compiler works as follows:

395 |
    396 |
  1. APL source is split into independent chunks by the (diamond) separator, each of which is compiled on its own. This implies that you are not allowed to use the diamond inside an APL string. You are also not allowed to use the single-quote inside. If you really need those two characters inside a string, create it using Lua and refer to it inside APL by its global name.

  2. 397 |
  3. An APL chunk is lexed, parsed and translated to Lua code in one step by a single LPeg grammar. I thank Roberto Ierusalimschy from the bottom of my heart for this absolutely ideal tool.

  4. 398 |
  5. The result of the previous step is a chunk of Lua code, which looks almost exactly like what you would code in library mode, and a number identifying the position in the APL code at which the compiler could not continue. If that is before the end of the string, the compilation failed, and the APL code is displayed with an arrow pointing to that position and an error message. This is maybe only slightly more helpful than saying there is an error somewhere, but at least it gives a starting point.

    399 |
       ⍺∘.×(⍺←⍳5)
    400 | ./apl-compiler.lua:116: APL syntax error
    401 | ⍺∘.×(⍺←⍳5)
    402 |  ↑
    403 |

    There is nothing wrong with the outer product to which the arrow points. The real error is that assignment to local variables is only allowed at the start of a statement, but the compiler makes no clever second-guessing of common mistakes. All that the arrow says is that by itself would have been legal APL code, but no substring that goes beyond the arrow is legal.

  6. 404 |
  7. If the compilation of all the APL chunks succeeded, the Lua chunks produced by the compiler are concatenated; if there was only one chunk, return is put in front of it; the result is tacked onto a standard preamble and the whole lot is processed by load, with the APL runtime environment as fourth argument.

  8. 405 |
  9. If load succeeds (which it should, otherwise there is a compiler bug that should be reported), the original APL code is set as the help string for the Lua function, which is returned. The Lua code can be recovered by apl.lua.

  10. 406 |
407 |

All this is done by calling apl (it is a table, yes, but a callable table), which returns an anonymous function that can be stored or executed.

408 |
   sorted_down=apl"⍵[⍒⍵]"
409 |    print(lua(sorted_down))
410 | return _w[Down(_w)]
411 |    x=apl"10?100"()
412 |    print(x)
413 | (49,23,96,93,19,89,67,48,66,35)
414 |    print(sorted_down(x))
415 | (96,93,89,67,66,49,48,35,23,19)
416 |

The APL runtime environment has mostly the same keys and values as apl, but it is a different table. You have read-write access to the module table; you have no access to the APL runtime environment. There are certain advantages to this.

417 |
   print(apl.Deal(100,10))
418 | (85,41,79,81,92,24,38,3,34,60)
419 |    apl.Deal = function() print"Your APL system is now stoned" end
420 |    print(apl.Deal(100,10))
421 | Your APL system is now stoned
422 |    print(Execute"10?100")
423 | (49,23,96,93,19,89,67,48,66,35)
424 |

I'm not saying that a sufficiently sneaky hacker can't access it — after all, this is open-source code — merely that you can't easily clobber it by accident.

425 |

It is possible to write quite long stretches of APL this way, but it is even harder to find a computing task that genuinely requires a long stretch of APL code. What one does need fairly often is a way to separate expressions that belong to the same chunk. The reason is that assigment to the local variables and is only allowed at the beginning of a statement.

426 |

The statement separator is used for thus purpose. Also available is the APL comment symbol , which makes everything up to and including the next linebreak invisible to the compiler.

427 |
   sievestep=apl[[
428 |       ⍺←⍺×⍳⌊(⍴⍵)÷⍺    ⋄  ⍝ multiples of ⍺ not past end of ⍵
429 |       ⍵[⍺]←0          ⋄                 ⍝ assign 0 to those
430 |       ←⍵      ⍝ explicit return required in multi-statement 
431 |    ]]
432 |    print(lua(sievestep))
433 | _a=Mul(Range(Floor(Div(_a,Shape(_w)))),_a); _w[_a]=0; return _w 
434 |

You can see from the Lua listing why there is a restriction on where you can assign to a local variable: it gets its own separate Lua statement. On the other hand, assignment into a table, such as the APL or Lua global environment, is implemented as a function call, which can appear anywhere.

435 |

However, program logic has not been implemented in APL. You will need mixed-language programming, using Lua for the logic. If the original environment that was active when the library was loaded is still in effect, APL code can see global the Lua variables: see Namespaces. However, locals and upvalues are invisible, so the process needs some care when used from inside a function.

436 |

The canonical APL way is to do everything via named or anonymous functions. Your data is passed in as arguments and , values are returned. If you pass a table, that table itself is mutable.

437 |
   function primes(n)
438 |    local x=apl"⍳⍵"(n)
439 |    local step=apl"⍵[1↓⍺×⍳⌊(⍴⍵)÷⍺]←0 ⋄ ←⍵"
440 |    for i=2,math.sqrt(n) do 
441 |       local p=x[i]
442 |       if p and p>0 then step(x,p) end 
443 |    end
444 |    x[1]=0
445 |    return apl"(⍵>0)/⍵"(x)
446 |    end
447 |    print(primes(100))
448 | 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
449 |

Valence

450 |

In Lua, the number of arguments to a function is no big deal. You supply more arguments than the function was defined with? The others are ignored. You supply fewer arguments than the function was defined with? The missing ones are given the value nil. This behaviour lies at the very heart of the Lua way of thinking,

451 |

In APL, the number of arguments to a function is absolutely central. Funtions are niladic (no arguments), monadic (one argument) or dyadic (two arguments). At present, niladic functions can only be executed from Lua; Lua⋆APL syntax does not make provision for them. Functions may be ambivalent, i.e. take either one or two arguments, deducible from the way the expression is formed, and in such a case may call different Lua functions.

452 |

The APL syntax allows the valence of a function to be determined from its context in the expression. Just like Lua knows the difference between unary minus and subtraction, APL knows the difference between monadic and dyadic use of a function symbol — in fact, about 20 APL symbols can be used for either a monadic or a dyadic function, and in almost all cases, those functions are implemented by different Lua functions.

453 |

Operators are even more complicated. In principle there are numerous possibilities for the valence of an operator, its arguments and its result. The original APL operators solve the difficulty by taking only the valence of the operator into account, in effect treating the arguments and result as ambivalent.

454 |
455 |
Monadic operators
456 |

A monadic operator acts on a function (which is the left argument of the operator) to form another function.

457 |
458 |
Dyadic operators
459 |

A dyadic operator acts on a pair of functions to form a single function.

460 |
461 |
462 |

Other implementations are not always as catholic as the above suggests; some APLs do not allow operators to act on user-defined functions.

463 |

Lua*APL, on the other hand, is very permissive. A monadic operator is simply one that acts on one function, a dyadic operator is one that acts on two functions. The resulting function could be either case be monadic or dyadic; we'll know when it is used. In cases where the same name could denote either a monadic or a dyadic function, the dyadic function takes precedence.

464 |

Lua⋆APL goes even further: it accepts functions created by operators as arguments to another operator. In such cases, parentheses must be used.

465 |

It is occasionally useful to have a dummy left argument to a dyadic operator. The jot is traditionally used for that purpose. It is treated as a function Pass by Lua⋆APL. Pass is ambivalent and returns nothing. It can be used to suppress output.

466 |
   a←1+!10
467 | 3628801
468 |    ∘a←1+!10  -- nothing is printed
469 |

Namespaces

470 |

Functions compiled by the APL have _ENV set to the to the APL runtime environment. This environment is not visible to the Lua user.

471 |

You have three main mechanisms for getting values back from compiled APL code.

472 |
    473 |
  • Return values of APL statements.
  • 474 |
  • Global assignment.
  • 475 |
  • Printing to the console (see [Functions added at compiler level]).
  • 476 |
477 |

Three namespaces are visible from an APL function.

478 |
    479 |
  • The local namespace of a function. This contains only the names _a and _w, which start out as the values of the left and right arguments, but can be assigned to. They are referred to in the APL code as and respectively.

  • 480 |
  • The APL global namespace. This contains the functions currently known to APL under their Lua names. The underscore, though alphabetic in Lua, has a special meaning during APL assigment and is therefore best avoided. The APL namespace is a field _V in the APL runtime environment.

  • 481 |
  • The Lua global namespace, which is set as the __index field of the metatable of _V. The APL global namespace takes precedence when retrieving a value, but if it does not contain that name, the Lua namespace is tried next. Assignment into the Lua namespace from APL is achieved by putting an underscore in front of the name. That underscore is not part of the real name; it merely indicates which namesapce to use.

  • 482 |
483 |

Several other namespaces are used internally by the APL compiler. These are collectively referred to as the APL registry, and contain the same functions as the APL runtime environment, but keyed by their APL names.

484 |

The APL compiler assumes that a name refers to a variable unless it has been added to the APL registry. The easiest way is to assign a newly defined function to an APL name. The newly defined function will be defined as an ambivalent function (use Lua's runtime distinction if necessary) and can be used wherever a built-in APL function can.

485 |
   run=function(str) print(apl(str)()) end
486 |    cf=function(_w,_a) return _w+1/_a end
487 |    run"2 cf 3"
488 | ./apl-compiler.lua:113: APL syntax error
489 | 2 cf 3
490 |   ↑
491 |    run"cf←cf"    
492 | cf
493 |    run"2 cf 3"
494 | 3.5
495 |    run"cf/⌽2 1 2 1 1 4 1 1 6 1 1 8"
496 | 2.718281835206
497 |

For direct assignment to work, the name must be a valid Lua name too.

498 |

More flexibility is offered by the apl.register function. You can define new operators, use a different APL name (which need not be a valid Lua name) from the Lua name), and define help. See help(apl.register).

499 |

Some functions that interact with the compiler

500 |

Note that and are used in a non-standard way.

501 |
502 |
(Assign)
503 |

Returns its right argument after assigning it to the left argument. This can be used inside expressions, except when the left argument is a parameter ( or ). In that case, it must occur at the very start of the expression. When used monadically (also only allowed at the start of an expression), forces a return instruction to be generated.

504 |

If the right argument is a Lua function, Assign registers it for use as a function in APL expressions under the name specified by the left argument.

505 |
506 |
(Define)
507 |

This is the only Lua⋆APL function that returns a Lua function as a first-class value. It can take either a string (which is then compiled) or a function (which is simply returned).

508 |
   =Deal(100,10)  
509 | Your APL system is now stoned
510 |    print(lua(Define"∇?"))  -- see comment below
511 | return Deal
512 |    Deal=Execute"∇?"        -- recovery!
513 |    =Deal(100,10)
514 | (85,41,79,81,92,24,38,3,34,60)
515 |

Note that apl"∇?" does not return Deal, it returns a niladic function whose body is return Deal; that's why we nead Execute.

516 |
517 |
518 |

(Execute) ~ DoesDefine` and returns the value of the function thus constructed after calling it with no arguments.

519 |
520 |
, (Read,Do,Print)
521 |

Although implemented as Lua functions, these are not really functions from the APL point of view, they are variables with special semantics. ⍞←x or ⎕←x causes the value x to be printed. When used as a value, not being assigned to, stands for the string you will be prompted to type in and for the result of executing that string.

522 |
523 |
lua
524 |

Returns the Lua code to which a function has been compiled.

525 |
526 |
register
527 |

Registers a Lua function for use by the compiler. See Extending Lua⋆APL. The interface may change more quickly than this manual. help(register) should give up-to-date instructions.

528 |
529 |
530 |

APL functions not documented here

531 |

The Wikipedia article does an excellent job, and Lua⋆APL is designed to be downwards compatible with the sections "Monadic Functions", "Dyadic Functions" and "Operators" from a snapshot of that page made on 12 April 2013.

532 |

There are all sorts of little incompatibilities between one APL system and the next, and Lua⋆APL is no exception. These mainly deal with extra features that are not quite standard. An APL reference manual for whatever implementation would nevertheless not be totally useless.

533 |

The interactive help is the only documentation that is intended to be kept in sync with the code. You can print out a complete quick reference as follows:

534 |
   apl:import()
535 |    for k in help(apl,1):match":(.*)":gmatch"%S+" do 
536 |       if k:match"^_" or k=="NaN" then h=help(k,1) 
537 |       else h=help(apl[k],1)         
538 |       end
539 |       if (not h or h:match"^That's nil") then 
540 |          print ("No help on'"..k.."', report this please")
541 |       else print(h)
542 |       end
543 |    end
544 |

Complete syntax of Lua⋆APL

545 |

The actual grammar is spcified in LPeg in apl-compiler.c and may by now be a little different from what you read below. It allows neutral whitespace and specifies the Lua code to which a pattern match is translated. Both of these have here been suppressed in the service of legibility.

546 |

The capitalized names are terminals which, for the same reason, I prefer to describe in English.

547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 |
Param or
StringA Lua string delimited by single-quotes
VarA Lua name or a single UTF-8 character
VectorAPL-formatted numbers separated by blanks
571 |

String and Var are subject to exceptions.

572 |
    573 |
  • As currently implemented, a String may not contain a single-quote or a statement separator .
  • 574 |
  • Var is any name that has not been listed as a function, an operator, or a parameter. Monadic and dyadic functions and operators have names just like the names allowed for variables, which are only special because they appear in lists kept by the compiler. Those lists are not static. The user can add new items to them at run time (see Extending Lua⋆APL); once a name has been added to a list, it cannot be removed.
  • 575 |
576 |

Users familiar with BNF should have no difficulty reading this grammar, with the following hints:

577 |
    578 |
  • * means that the two elements are adjacent
  • 579 |
  • + means that the two elements are alternatives, tried in left-to-right order
  • 580 |
581 |

For example, an expression can consist of just a single variable name (since a variable is a value, and a value is a left argument), but this possibility is only tried when all the other possibilities for an expression have failed.

582 |

A simplified version of that grammar is given below. The full grammar, complete with compilation captures and special cases, is contained in the table apl_expr in the module file lua.apl.

583 |
statement = '←'*expr 
584 |   + Param*'←'*expr 
585 |   + Param*"["*indices*"]"*'←'*expr
586 |   + expr;
587 | expr = '∇'*func_expr
588 |   + Var*'←'*expr
589 |   + Var*"["*indices*"]"*'←'*expr 
590 |   + leftarg*dyadic_func*expr 
591 |   + monadic_func*expr
592 |   + leftarg;
593 | dyadic_func = ambivalent + Dyadic_function;
594 | monadic_func = ambivalent + Monadic_function;
595 | func_expr = '('*(dyadic_func+Monadic_function)*')'
596 |   + Dyadic_function + Monadic_function;
597 | ambivalent = func_expr*Monadic_operator
598 |    + func_expr*Dyadic_operator*func_expr;
599 | leftarg = value + '('*expr*')';
600 | value = Vector + String +
601 |    Var*'['*indices*']' + Var + 
602 |    Param*'['*indices*']' + Param;
603 | index = expr+"";
604 | indices = index*';'*index + expr;
605 |

Extending Lua⋆APL

606 |

One can extend APL for particular applications. Here is an example.

607 |

In Matlab one finds a very convenient function named diag, which converts between a diagonal matrix and a vector. Let's code a simplified version of it in Lua and teach APL to use the function. There's a very convenient unused UTF-8 character available for the name.

608 |
   do
609 |    local rho = apl.util.rho
610 |    local argcheck = apl.util.argcheck
611 |    local shape = apl.util.shape
612 |    local min = math.min 
613 | 
614 |    local function diag(A)
615 |       local m,n = shape(A)
616 |       if not m then return A end
617 |       if n then 
618 |          n=min(m,n)
619 |          res=rho(0,n)
620 |          for k=1,m do res[k]=A[{k,k}] end
621 |          return res
622 |       end
623 |       res=rho(0,m,m)
624 |       for k=1,m do res[{k,k}]=A[k] end
625 |       return res
626 |    end
627 | 
628 |    apl.register(1,diag,'⍂','Diag')
629 |    end -- do
630 | 
631 |    print(apl"⍂1 2 3 4 5"())
632 | 1 0 0 0 0
633 | 0 2 0 0 0
634 | 0 0 3 0 0
635 | 0 0 0 4 0
636 | 0 0 0 0 5
637 |    print(apl"⍂(⍳6)∘.×⍳6"())
638 | 1 4 9 16 25 36
639 |

Notes:

640 |

The functions in apl.util are listed in the List of Lua⋆APL functions.

641 |

APL mode

642 |

The APL standalone interpreter gives an approximation to APL's look-and-feel. Here is a sample session.

643 |
$ lua-apl 
644 | Lua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio
645 | Lua⋆APL 0.3.0 © Dirk Laurie 2013
646 | apl-lib 0.3.0 © Dirk Laurie 2013
647 | Bug reports are welcome. You'll find me on Lua-L.
648 | If you can't remember the README, do this:
649 |   apl:import(); help'start'
650 | --
651 |    apl:import()
652 |    help'APL'
653 | Contents:  ! + , . / < = > ? \ ¨ × ÷ ← ↑ ↓ ∇ ∊ − ∘ ∣
654 | ∧ ∨ ∼ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊖ ⊤ ⊥ ⋆ ⌈ ⌊ ⌹ ⌽ ⌿ ⍀ ⍉ ⍋ ⍎ ⍒ ⍕ ⍟ ⍪ ⍱ ⍲ ⍳ ⍴ ⎕ ○ 
655 |    help'⌽'
656 | Reverse2(⍵): ⌽⍵ → columns of ⍵ in reverse order
657 | Rotate2(⍵,⍺): ⍺⌽⍵ → elements in rows of ⍵ rotated left by ⍺ or right by -⍺
658 |    ⍺←⍳5 ⋄ ←A←⍺∘.×⍺
659 |  1  2  3  4  5
660 |  2  4  6  8 10
661 |  3  6  9 12 15
662 |  4  8 12 16 20
663 |  5 10 15 20 25
664 |    5 ¯5↑A[2 3;3 2]
665 | 0 0 0 6 4
666 | 0 0 0 9 6
667 | 0 0 0 0 0
668 | 0 0 0 0 0
669 | 0 0 0 0 0
670 |    1 3 2 4 5⌽A
671 |  2  3  4  5  1
672 |  8 10  2  4  6
673 |  9 12 15  3  6
674 | 20  4  8 12 16
675 |  5 10 15 20 25
676 |

This is pretty standard APL, but some points deserve mention:

677 |
    678 |
  • The diamond separator is supported. It is necessary in this case because ⍺∘.×(⍺←⍳5) is invalid in Lua⋆APL.
  • 679 |
  • The parameter names ( and ) are available for temporary values. Other assignments go into the APL global namespace, or even the Lua global namespace (see Namespaces).
  • 680 |
  • Every input line translates to a single APL function. If that line contains a separator, you must explicitly put in a unary assignment if you want the function to return something. If there is no separator, the value of the sole APL expression is returned.
  • 681 |
  • The jot () is mapped to the Lua function Pass, which throws away all its arguments and returns nothing. This can be used to suppress printout of one-expression inputs. Implementing jot as a function also removes the need for the compiler to have a separate syntax for the outer product.
  • 682 |
683 |

Some other important points have not been illustrated in the short sample.

684 |
    685 |
  • The APL-enabled Lua interpreter pre-empts all the command-line parameters of the standard Lua interpreter. You cannot redirect to its input, and script files cannot be processed. For that, you must use the compiler explicitly from Lua.

  • 686 |
  • Heuristics are used to guess whether an input chunk is APL. These change so often that I don't document them outside lua-apl.c any more. Ideally, it should not bother anybody: it is very hard to invent a character string that could parse to either correct APL or correct Lua, so it ought to be very easy to see at a glance, even a computer's glance, which it is.

    687 |

    The comments in lua-apl.c (search for "guess") include tips on how to override a wrong guess.

  • 688 |
  • If you replay input history (assuming your Lua has that feature) you will notice that your input has been changed to what you would have typed in Lua mode, e.g. 2 3⌽A turns into return apl"2 3⌽A"().

  • 689 |
690 | 691 | 692 | -------------------------------------------------------------------------------- /lua-apl.txt: -------------------------------------------------------------------------------- 1 | 4 | 5 | Lua⋆APL User's Manual 6 | ===================== 7 | 8 | © Dirk Laurie 2013 Lua-style MIT licence 9 | 10 | Read what it says in the README about installation and UTF-8. 11 | 12 | APL is a functional language with extensive array processing abilities, 13 | originally designed by Ken Iverson in the 1960's. Lua⋆APL is an extension 14 | of Lua that gives access to the power of APL (the APL asterisk `⋆` means 15 | `Power`) without leaving the comfort and security of Lua. 16 | 17 | There are three modes of usage, which can be thought of as analogous to 18 | the C API, scripting language, and standalone executable of Lua. 19 | 20 | Library mode: 21 | ~ APL functionality is offered as a Lua library, which 22 | makes no use of the APL character set except in its help 23 | information. You type in Lua commands like: 24 | 25 | Reshape(Take(1,Add(1,n)),Attach2(n,n)) 26 | 27 | Lua mode: 28 | ~ Functions callable from Lua can be written in APL. This is 29 | achieved via a function `apl` rather like Lua's `load`, which 30 | generates intermediate Lua code consisting of chained calls to the APL 31 | library. You type in Lua commands like: 32 | 33 | apl"(n,n)⍴(n+1)↑1"() 34 | 35 | The `apl` function invokes an APL compiler to translate this to 36 | the string of calls shown above. 37 | 38 | APL mode: 39 | ~ Bare APL code can be entered directly at the terminal. You type 40 | in a mixture of Lua and APL commands like: 41 | 42 | n=10 43 | (n,n)⍴(n+1)↑1 44 | 45 | The APL interpreter decides the second line must be APL code and 46 | modifies it to the Lua mode code shown above. If it seems to be Lua 47 | code, the interpreter does not do the translation. 48 | 49 | This works because APL code has such a distinctive appearance. Even 50 | a primitive guessing algorithm can identify APL code with a high 51 | rate of accuracy. Code deemed to be APL is translated to load and 52 | execute as in Lua mode, but the result is printed by default as 53 | typical APL systems do. The effect is to give an APL-like 54 | look-and-feel. If you already know APL, jump straight to the 55 | section on [APL mode] and try it out. 56 | 57 | The code examples shown here, even the ones in the section on Lua mode, 58 | use the three-space prompt of the Lua⋆APL standalone rather than the 59 | conventional Lua prompt, but all code except that shown in the section 60 | on [APL mode] works on a vanilla Lua interpreter too. 61 | 62 | In this document, we do not consistently use "APL" and "Lua⋆APL" as 63 | denoting different things. "APL" is a fairly vague term, meaning more or 64 | less "a typical APL implementation" or even "the APL way of thinking", 65 | whereas "Lua⋆APL" is a specific term for this implementation, which of 66 | course also falls under the shadowy APL umbrella. 67 | 68 | Words like "standard" refer to features that are typically found in 69 | almost every APL dialect since about 1980, well summarized in the 70 | [Wikipedia article](http://en.wikipedia.org/wiki/APL_syntax_and_symbols). 71 | "Non-standard" in the case of Lua⋆APL means that I am using an unusual 72 | APL symbol without making any attempt even to find out how modern APL 73 | implementations use it (the latest APL that I take into account is APL2, 74 | and even in that case compatibility across the board is not aimed at). 75 | Please contact me if you feel that some other symbol for the same concept, 76 | or some other concept for the same symbol, is actually semi-standard by now. 77 | 78 | If you would like to know much more about APL than you currently do, 79 | there are several well-organized websites of modern commercial APLs, 80 | e.g. . 81 | 82 | When I wrote this line, the version number displayed by the start-up 83 | message was `0.4.0`. As long as the first number is `0`, anything might 84 | still change. 85 | 86 | Library mode 87 | ============ 88 | 89 | In this mode, APL is a distant inspiration only, but since the other 90 | modes make use of it, it is documented quite extensively here. 91 | 92 | Actually, I do not expect anybody to stop at library mode, but it 93 | is the easiest place to start for Lua programmers who do not know any 94 | APL; eventually one needs know about it, if only because some library 95 | functions have optional extra parameters that are fixed at default 96 | values when called from APL. It is best learnt in an interactive session 97 | because an extensive amount of interactive help is available. 98 | 99 | I'm assuming you loaded the module by 100 | 101 | apl=require'apl' 102 | apl:import'*' 103 | 104 | That call to `apl:import` adds the 90-plus items of the module to the 105 | global environment. Almost all of them are quite distinctively named, 106 | but if you instinctively recoil in horror, muttering "pollution" under 107 | your breath, by all means omit that line and prepare to type numerous 108 | instances of `apl.`, or (in a script) replace it by assigning the ones 109 | you actually use to local variables. Better, actually: just hold your 110 | horses till you get to Lua mode; no large-scale imports necessary there. 111 | 112 | A thorough knowledge of what the library offers is not needed if you 113 | already know APL and plan to use mostly APL mode. If you already know 114 | APL, by all means skip straight to [APL mode] and only read this part 115 | later. If you don't want to build the APL interpreter, skip to [Lua 116 | mode]. 117 | 118 | The module `apl` loads a C module `apl_core`, documented in the 119 | Programmer's Guide. 120 | 121 | The module returns a table with keys named by the following convention: 122 | 123 | - `CamelCase`: APL functions (mostly mapped to APL symbols later) 124 | - `lowercase`: Lua functions (not usually called from APL code) 125 | - `_CAPS`: needed by the system or providing information 126 | - `_lowercase`: control variables 127 | 128 | "APL functions" of course does not imply that they are anything else 129 | than just Lua functions. It merely means that these are mostly the 130 | functions that lie at the heart of APL, closely following the original 131 | design of Iverson. There is a simple mapping between Lua postfix 132 | notation and APL infix notation, for example: 133 | 134 | Lua APL 135 | ------------------- -- ----------------- 136 | `_w` `⍵` 137 | `_a` `⍺` 138 | `func(_w,_a)` `⍺ func ⍵` 139 | `Range(_w)` `⍳⍵` 140 | `Find(_w,_⍺)` `⍺⍳⍵` 141 | `Shape(_w)` `⍴⍵` 142 | `Reshape(_w,_a)` `⍺⍴⍵` 143 | `Decode(_w,_a)` `⍺⊤⍵` 144 | `Add(_w,_a)` `⍺+⍵` 145 | `Sub(_w,_a)` `⍺-⍵` 146 | `Mul(_w,_a)` `⍺×⍵` 147 | `Div(_w,_a)` `⍺÷⍵` 148 | ------------------- -- ----------------- 149 | 150 | In total, over 90 of these functions are provided in library mode. The 151 | APL symbols used for them, even those which in Lua denote arithmetic 152 | operators, must be thought of as names, not as as special characters, 153 | even though those names are not used in library mode. 154 | 155 | One and the same APL name often does duty for two Lua names. This is 156 | possible because APL infix syntax allows the compiler to distinguish 157 | between monadic and dyadic functions (see [Valence]) and select the 158 | appropriate function. That distinction is possible in Lua too, but only 159 | at run time, when one can test whether the second argument of a function 160 | is present and not nil. 161 | 162 | APL syntax makes the distinction already at compile time, so that 163 | overloading of the Lua names of the functions is not necessary. Instead, 164 | the names have been chosen to be descriptive of what the functions do. 165 | 166 | Certain semi-standard descriptive names are often used in APL 167 | documentation, such as 168 | . These have been 169 | retained in some cases (`Ravel`, `Reshape` etc), abbreviated in others 170 | (`Exp` rather than `Exponential` etc), and occasionally replaced by 171 | totally different names (`Pow` rather than `Exponentiation`). Functions 172 | corresponding to Lua arithmetic metamethods have been given basically 173 | the same name (`Unm` for unary minus, etc.) but there's no rule, really; 174 | I've done what feels programmer-friendly to me. 175 | 176 | The documentation, in particular the interactive help, freely uses both 177 | the Lua names and APL symbols simultaneously, so you will acquire a 178 | smattering of APL more or less painlessly even while using library mode 179 | only. 180 | 181 | APL types and Lua types 182 | ----------------------- 183 | 184 | An APL-like type classification consisting of functions, operators, 185 | scalars, vectors and matrices is implemented via Lua values. 186 | 187 | The APL terms "function" and "operator" are used in a different sense 188 | than in Lua. The symbols for arithmetic, comparison etc are called 189 | "operators" in Lua and the parser treats them by strict precedence rules. 190 | In APL, those symbols are names of functions (many of them are actually 191 | three-byte names that only look like a single character thanks to UTF-8), 192 | there is no notion of precedence, and "operator" means a function that 193 | acts on functions. 194 | 195 | - APL functions are Lua functions of one or two variables, returning 196 | one value. In the documentation, the names `⍺,⍵` and `_a,_w` 197 | are used respectively when the function is called from APL or from 198 | Lua. The _left_ argument `⍺` in APL infix notation becomes the 199 | _second_ argument `_a` in Lua postfix notation. In APL, the parser 200 | can see whether `⍺` is omitted; in Lua, providing `_a` as nil is 201 | equivalent to omitting it. 202 | 203 | - APL operators are also Lua functions of two variables, but: 204 | 205 | 1. Their arguments and return value are functions. 206 | 207 | 2. In the documentation, the first argument is called `f` and 208 | the second `g` in the documentation of both APL and Lua. 209 | 210 | 3. The _left_ argument `f` in APL infix notation becomes the 211 | _first_ argument `f` in Lua postfix notation and must always 212 | be present; the (right) second argument `g` is usually absent, 213 | `Inner` being the only function in `apl-lib` that uses it. 214 | 215 | 4. Operators have a higher priority than functions. 216 | 217 | - APL scalars are Lua numbers and strings. Other Lua types are treated 218 | as scalar. In particular, a Lua function can be an APL scalar. 219 | APL needs to be told at the lexical level (via `register`) which 220 | names will be used to denote functions. 221 | 222 | - APL arrays (vectors and matrices both) are Lua index-1 tables. The 223 | ordering of matrix elements is row-wise. Lua⋆APL arrays do not have 224 | a type as in classic APL: each element is a Lua value and carries 225 | its own type. In particular, an empty array has no type. 226 | 227 | APL arrays share a common metatable, which provides `__tostring`, 228 | `__index` `__newindex` and `__lt` (the last of these does a 229 | lexicographic comparison). Numeric keys in the simplest case obey the 230 | same conventions as the Lua table library: the positive integer keys of 231 | `A` are expected to form a solid block from 1 to `#A`. The full truth is 232 | more complicated; see [Array operations] and [Indexing]. 233 | 234 | String keys are used to achieve the extra functionality of APL arrays: 235 | 236 | `A.apl_len` 237 | : Optional field. If present, this is the value returned by the Lua 238 | `#` operator. 239 | `A.rows, A.cols` 240 | : Number of rows and columns (matrix only). 241 | 242 | It is possible to synthesize an APL array in vanilla Lua. However, it is 243 | not recommended, and never necessary, for two reasons: 244 | 245 | - In almost all cases, the APL library will accept a Lua table as 246 | a substitute for an APL vector. 247 | - All tables returned by the library are APL arrays. In particular, 248 | the function `Copy` will convert a Lua array to an APL array. 249 | 250 | For example, to define a matrix (see [Formatting] for `_format`) 251 | one calls `Reshape` with a Lua table and the desired shape as 252 | arguments; to convert it to an APL vector one calls `Ravel`. 253 | 254 | apl._format = 'raw' 255 | A = Reshape({11,12,13,21,22,23},{2,3}) 256 | print(A) 257 | [11,12,13;21,22,23] 258 | print(Ravel(A)) 259 | (11,12,13,21,22,23) 260 | 261 | The _rank_ of an array is the number of indices it takes to reference 262 | an element from it. APL allows rank 0 (scalar), rank 1 (vector) and 263 | rank 2 (matrix). APL does not distinguish between row vectors and column 264 | vectors; if you need to do that, use a one-row or one-column matrix. 265 | 266 | `Shape` is the APL analogue of Lua's `type`, defined as follows: 267 | 268 | - number: `Shape(a) = {}` 269 | - vector: `Shape(x) = {#x}` 270 | - matrix: `Shape(A) = {A.rows,A.cols}` 271 | - string: `Shape(s) = #s` 272 | - other scalar: `Shape(other) = nil` 273 | 274 | It should be stressed that APL matrices are not tables of tables, 275 | they are simple tables that carry shape information. 276 | 277 | Lua userdata values are also APL scalars. If equipped with the right 278 | metamethods, they might work inside APL expressions, but this 279 | possibility is unexplored. 280 | 281 | Formatting 282 | ---------- 283 | 284 | APL normally uses a pretty-printing output format. 285 | 286 | print(Reshape({11,12,13,21,22,23},{2,3})) 287 | 11 12 13 288 | 21 22 23 289 | 290 | This is produced by the `Format` function. 291 | 292 | While nice-looking, this format makes it hard to notice the subtle 293 | distinctions that we will need to make in this manual, and therefore 294 | we have in many of the examples used raw format. The Lua⋆APL `Format` 295 | function in general takes two arguments. The second argument `"raw"` 296 | selects one-line output with the following conventions: 297 | 298 | - undelimited: scalars 299 | - delimited by braces: {Lua,table} 300 | - delimited by parentheses: (APL,vector) 301 | - delimited by brackets: [APL,matrix;semicolons,separate,rows] 302 | 303 | If the second argument is absent, the `_format` field in the module 304 | table is interrogated. Since the module table is not read-only, the user 305 | can assign something (e.g. `"raw"`) to `apl._format`, 306 | 307 | Not-a-number is printed as `NaN` and nil as an underscore. 308 | 309 | print(Format{1,nil,0/0,"abc"}) 310 | {1,_,NaN,'abc'} 311 | 312 | The `Format` function works recursively until the result is a string. 313 | 314 | Array operations 315 | ---------------- 316 | 317 | The whole point of using APL is so that one never (well, hardly ever) 318 | needs to use an explicit table index. The vast majority of its functions 319 | accept array arguments and return array values. 320 | 321 | The APL library handles nils in a table gracefully in the case of all 322 | scalar functions (i.e. functions that apply term-by-term to array 323 | arguments). 324 | 325 | x=Copy{1,2,nil,4}; y=Reverse(x) 326 | print(Add(x,y)) 327 | {5,_,_,5} 328 | 329 | The actual storage of a very sparse vector will be compact, using mostly 330 | the hash part of a Lua table, but there is no true optimized support for 331 | sparse vectors. The functions simply iterate over the known length of 332 | the array and skip nils. 333 | 334 | ###Indexing 335 | 336 | APL arrays have a fixed size, which is frozen for a given Lua table 337 | `tbl` by setting its metatable to the APL array metatable. Or rather, 338 | it is the set of indices with non-nil values that is frozen. The 339 | `__index` and `__newindex` metamethods come into play whenever an 340 | attempt is made to access any index except those which are actually 341 | in use. Conversely those metamethods are _not_ invoked in the case of 342 | a valid index. 343 | 344 | - A nonexistent numeric or string index is illegal. 345 | - If you assign nil to a numeric or string index, that index 346 | becomes unavailable permanently, no matter what `apl_len` is, 347 | except via `rawget` and `rawset`. 348 | - A legal numeric index to a matrix indexes it as a vector, in row-major 349 | order (i.e. matrices are stored row by row). 350 | - A table-valued index to a vector should either be an APL vector (Lua 351 | array acceptable) or an APL matrix (Lua array with `rows` and `cols` 352 | set acceptable). The result will have the same shape as the index. 353 | - A table-valued index to a matrix should be a pair of items, each of 354 | which may be a number or a Lua array (APL vectors and matrices are 355 | acceptable, but lose their magic powers) which select the rows and 356 | columns. The result will have rank determined by the index types, and 357 | shape determined by the lengths of the lists. 358 | - A function-valued index should be an iterator that returns a valid 359 | numeric index until it returns nil. The array being indexed is 360 | treated as a vector, and the result is always a vector. 361 | 362 | The two metamethods are available in the library as `Get` and `Set`, 363 | and can be invoked explicitly, even with ordinary non-APL tables. 364 | You should only do that with a function-valued or table-valued 365 | index, because you are bypassing the Lua indexing mechanism. Other 366 | indices are passed to routines normally called only for nonexistent 367 | indices; the routines know that a scalar index that gets this far 368 | must be invalid, and throw an error. 369 | 370 | x={1,2,3,"A","B","C"} 371 | y={10,20,30,A='a',B='b',C='c'} 372 | print(Get(x,{5,2,4})) 373 | ('B',2,'A') 374 | Set(y,Get(x,{5,2,4}),{100,200,300}) 375 | print(y) 376 | table: 0x93e4e90 377 | print(Copy(y),y.A,y.B) 378 | (10,200,30) 300 100 379 | Get(y,1) -- index not a function or a table: trouble! 380 | ./apl-lib.lua:520: bad argument #2 to 'core_index' (index out of range) 381 | 382 | ### Nested arrays 383 | 384 | The elements of an APL array may be themselves be arrays, since the 385 | inner structure of an element is opaque to APL, i.e. APL thinks of 386 | anything stored in an array as scalar. 387 | 388 | A={{1,2},{3,4},{5,6}}; print(Format(A)) 389 | {{1,2},{3,4},{5,6}} 390 | B=Copy(A); print(B) 391 | ({1,2},{3,4},{5,6}) 392 | print(A[1]==B[1]) 393 | true 394 | 395 | That `true` says that `A[1]` and `B[1]` are actually one and the same 396 | table: the elements are still the original Lua tables. This is not quite 397 | what is usually wanted from a routine called `Copy`. The operator `Each` 398 | applies a function term-by-term to every element of a table and returns 399 | a new array with the same shape as the original. 400 | 401 | C=Each(Copy)(A); print(C) 402 | ((1,2),(3,4),(5,6)) 403 | print(A[1]==C[1]) 404 | false 405 | 406 | The elements are now newly minted APL arrays, as is the containing 407 | array. 408 | 409 | `Each` works equally well for monadic and for dyadic functions. The 410 | APL compiler can tell the difference. 411 | 412 | print(Each(Reshape)({{1,2,3,4},{5,6,7,8}},{{2,2}})) 413 | ([1,2;3,4],[5,6;7,8]) 414 | 415 | In this example, the two arguments are both nested arrays, so they 416 | look to APL like a two-element and a one-element array. A dyadic 417 | function created by `Each` expects two arrays of compatible shape for 418 | term-by-term operations, but will accept a one-element argument 419 | to act as a constant value. You can override that permission by 420 | supplying extra parameters to `Each` (this feature is only available 421 | when calling `Each` from Lua; the APL operator `¨` does not allow 422 | the possibility.). 423 | 424 | print(Each(Add)({{1,2,3,4},{5,6,7,8}},{1})) 425 | (2,3,4,5) (6,7,8,9) 426 | help(Each) 427 | Each(f): f¨ → make a term-by-term function from f 428 | Each(f,x_w,x_a): (no APL equivalent) Fine-tune constant arguments. 429 | x_w=x_a=0: the shapes of _w and _a must be compatible. 430 | x_w=1, x_a=1: _w,_a may be a singleton, which will be used every time. 431 | x_w=2, x_a=2: _w,_a is used as such every time even if it is a table. 432 | print(Each(Add,1,0)({{1,2,3,4},{5,6,7,8}},{1})) 433 | ./apl-lib.lua:497: bad argument #3 to 'both' (shapes are incompatible) 434 | 435 | A vector of vectors of the same length can be combined into a matrix, 436 | and a matrix can be converted to a nested array. These operations are 437 | so commonly needed that the APL2 notations `⊂` (Enclose) and `⊃` (Disclose) 438 | have been borrowed (but not with the exact same meaning), and the 439 | non-standard APL function `Rerank` has have been invented as an 440 | umbrella for them in library mode. 441 | 442 | help"⊂" 443 | Enclose: ⊂⍵ makes an array of rows from a matrix or concatenates a vector. 444 | Depends on _join. 445 | help"⊃" 446 | Disclose: ⊃⍵ makes a matrix from an array of rows, or a string vector from 447 | a string. If ⍵ is a matrix, each ⍵[i] is treated as a vector and padded to 448 | the maximum length using zeros or empty strings. If ⍵ is a string, ⍵ is 449 | split into substrings, depending on _split. 450 | help(Rerank) 451 | Rerank: Rerank(_w,_a): array of one rank higher (_a>0) or lower (_a<0), 452 | stacking rows (±1) or columns (±2). See Disclose/Enclose. 453 | 454 | The behaviour of `Enclose` and `Disclose` on strings is discussed under 455 | [Strings]. 456 | 457 | ### Axis-dependent functions and operators 458 | 459 | Several APL vector functions generalize to matrices by operating either 460 | on the rows or on the columns. In APL notation, the distinction is 461 | made by using different symbols (`⊖ ⌿ ⍀ ⍪` are the row-wise counterparts 462 | to `⌽ / \ ,`). There are typically three different functions at issue here. 463 | 464 | help(Attach) 465 | Attach(⍵,⍺): ⍺,⍵ → elements of ⍺ followed by elements of ⍵ 466 | help(Attach1) 467 | Attach1(⍵,⍺): ⍺⍪⍵ → rows of ⍺ followed by rows of ⍵ 468 | help(Attach2) 469 | Attach2(⍵,⍺): ⍺,⍵ → columns of ⍺ followed by columns of ⍵ 470 | print(Attach({1,2},{3})) 471 | (3,1,2) 472 | print(Attach1({1,2},{3})) 473 | [3,0;1,2] 474 | print(Attach2({1,2},{3})) 475 | [3,1,2] 476 | 477 | As you can see, `Attach1` pads short rows to the length of the longest list. 478 | `Attach2` pads short columns, but vectors count as one-row matrices. 479 | 480 | In Lua⋆APL, the principle is "act on rows when the axis is 1, act 481 | on colums when the axis is 2", and the implementation is to convert 482 | the matrix to a nested array, do something to that nested array, 483 | and convert back. 484 | 485 | In the case of `Rotate`, there is a possibility of confusion here. 486 | `Rotate1(A,1)` must rotate the rows of `A` up by 1, no question. 487 | What does `Rotate1(A,{2,1,3,4})` do, rotate each row of `A` 488 | individually by the specified amount? If so, `Rotate1(A,{1,1,1,1})` 489 | would rotate every row left by 1, i.e. it would rotate the columns 490 | left by 1. That would violate the APL principle that a singleton 491 | does the same as a constant vector. So we must violate the Lua⋆APL 492 | principle for the sake of the greater cause: `Rotate1(A,{2,1,3,4})` 493 | rotates each _column_ of `A` individually by the specified amount. 494 | 495 | A=Copy{1,2,3,4;5,6,7,8;9,10,11,12;rows=3,cols=4}; print(A) 496 | 1 2 3 4 497 | 5 6 7 8 498 | 9 10 11 12 499 | print(Rotate1(A,{2,1,3,4})) 500 | 9 6 3 8 501 | 1 10 7 12 502 | 5 2 11 4 503 | 504 | ### High-level matrix functions 505 | 506 | In the case where `A` is a non-singular square matrix, `MatDiv(A,b)` 507 | returns the unique solution of a system of linear equations and 508 | `MatInv(A)` to the matrix inverse. 509 | 510 | For a singular or non-square matrix, the numerical rank is determined. 511 | `MatDiv(A,b)` returns the minimum-norm least-squares solution, and 512 | `MatInv(A)` the pseudo-inverse, for that rank. 513 | 514 | The determination of rank depends on non-negative numbers known as 515 | the singular values of `A` (see any text on matrix computations) and 516 | on the comparison tolerances (see [Global control variables]). 517 | Specifically, if `_act` is nonzero, singular values less than `_act` 518 | count as zero; if `_rct` is nonzero, singular values less than 519 | `_rct*s[1]` count as zero. The rank equals the number of nonzero 520 | singular values. 521 | 522 | In library mode, the function `SVD` has been provided for the 523 | convenience of those who know the theory. Its return value is a Lua 524 | table containing a vector `S` of singular values and nested arrays 525 | `U` and `V` of left and right singular vectors respectively. At this 526 | point library mode becomes just too inconvenient, so we resort to 527 | Lua mode. 528 | 529 | A=Reshape(Range(12),{3,4}); print(A) 530 | 1 2 3 4 531 | 5 6 7 8 532 | 9 10 11 12 533 | U=USV.U; V=USV.V; S=USV.S; print(S) 534 | 25.43684 1.722612 2.681529e¯16 535 | B=apl"+/SרU(∘.×)¨V"(); print(B) 536 | 1 2 3 4 537 | 5 6 7 8 538 | 9 10 11 12 539 | 540 | Strings 541 | ------- 542 | 543 | Lua⋆APL does not have character arrays, any more than Lua itself has 544 | them. A string is a scalar. If you need to operate on individual bytes 545 | of a string, the Lua function `string.byte` combines well with the 546 | library. 547 | 548 | tobytes = function(s) return {s:byte(1,-1)} end 549 | frombytes = function(s) return string.char(unpack(s)) end 550 | M=Each(tobytes){"The","quick","brown","fox"}; print(M) 551 | ({84,104,101},{113,117,105,99,107},{98,114,111,119,110},{102,111,120}) 552 | print(Each(frombytes)(M)) 553 | ('The','quick','brown','fox') 554 | 555 | Lua⋆APL does have string arrays, and support for them is provided by the 556 | functions `Disclose` and `Enclose`. These functions normally act 557 | respectively on nested arrays and matrices, but `Disclose(str)` splits 558 | a string into UTF-8 codepoints and `Enclose(vec)` concatenates a vector. 559 | More precisely, `Disclose` returns the result of `apl.util.utfchar` 560 | and `Enclose` the result of `table.concat`. 561 | 562 | This support can be customized: see [Splitting and joining]. 563 | 564 | 565 | Global control variables 566 | ------------------------ 567 | 568 | APL traditionally makes use of some control variables that affect its 569 | overall behaviour. Which these are, differs from one APL implementation 570 | to the next, so they are sometimes called "system variables". In Lua⋆APL, 571 | their names start with an underscore, and they are stored in the module 572 | table. 573 | 574 | --------------- -- ------------------------------------------------- 575 | `_act` Absolute comparison tolerance. 576 | `_rct` Relative comparision tolerance. 577 | `_format` Default format for monadic `Format`. 578 | `_split` String splitting function. 579 | `_join` Table concatenation function. 580 | --------------- -- -------------------------------------------------- 581 | 582 | ###Comparison tolerance 583 | 584 | Lua⋆APL does a straightforward comparison only in the case of functions 585 | that don't test for equality (`TestNE`, `TestGT`, `TestLT`). Functions 586 | that give 1 in the equality case (`TestEq`, `TestGE`, `TestLE`) actually 587 | test for approximate equality. Either of `abs(_a-_w)0 then step(x,p) end 778 | end 779 | x[1]=0 780 | return apl"(⍵>0)/⍵"(x) 781 | end 782 | print(primes(100)) 783 | 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 784 | 785 | Valence 786 | ------- 787 | 788 | In Lua, the number of arguments to a function is no big deal. You 789 | supply more arguments than the function was defined with? The others 790 | are ignored. You supply fewer arguments than the function was defined 791 | with? The missing ones are given the value `nil`. This behaviour lies 792 | at the very heart of the Lua way of thinking, 793 | 794 | In APL, the number of arguments to a function is absolutely central. 795 | Funtions are _niladic_ (no arguments), _monadic_ (one argument) or 796 | _dyadic_ (two arguments). At present, niladic functions can only be 797 | executed from Lua; Lua⋆APL syntax does not make provision for them. 798 | Functions may be _ambivalent_, i.e. take either one or two arguments, 799 | deducible from the way the expression is formed, and in such a case 800 | may call different Lua functions. 801 | 802 | The APL syntax allows the valence of a function to be determined from 803 | its context in the expression. Just like Lua knows the difference 804 | between unary minus and subtraction, APL knows the difference between 805 | monadic and dyadic use of a function symbol — in fact, about 20 APL 806 | symbols can be used for either a monadic or a dyadic function, and 807 | in almost all cases, those functions are implemented by different 808 | Lua functions. 809 | 810 | Operators are even more complicated. In principle there are numerous 811 | possibilities for the valence of an operator, its arguments and its 812 | result. The original APL operators solve the difficulty by taking only 813 | the valence of the operator into account, in effect treating the 814 | arguments and result as ambivalent. 815 | 816 | Monadic operators 817 | : A monadic operator acts on a function (which is the left argument 818 | of the operator) to form another function. 819 | 820 | Dyadic operators 821 | : A dyadic operator acts on a pair of functions to form a single 822 | function. 823 | 824 | Other implementations are not always as catholic as the above suggests; 825 | some APLs do not allow operators to act on user-defined functions. 826 | 827 | Lua*APL, on the other hand, is very permissive. A monadic operator is 828 | simply one that acts on one function, a dyadic operator is one that 829 | acts on two functions. The resulting function could be either case be 830 | monadic or dyadic; we'll know when it is used. In cases where the same 831 | name could denote either a monadic or a dyadic function, the dyadic 832 | function takes precedence. 833 | 834 | Lua⋆APL goes even further: it accepts functions created by operators 835 | as arguments to another operator. In such cases, parentheses must be 836 | used. 837 | 838 | It is occasionally useful to have a dummy left argument to a dyadic 839 | operator. The jot `∘` is traditionally used for that purpose. It is 840 | treated as a function `Pass` by Lua⋆APL. `Pass` is ambivalent and 841 | returns nothing. It can be used to suppress output. 842 | 843 | a←1+!10 844 | 3628801 845 | ∘a←1+!10 -- nothing is printed 846 | 847 | Namespaces 848 | ---------- 849 | 850 | Functions compiled by the APL have `_ENV` set to the to the APL runtime 851 | environment. This environment is not visible to the Lua user. 852 | 853 | You have three main mechanisms for getting values back from compiled 854 | APL code. 855 | 856 | - Return values of APL statements. 857 | - Global assignment. 858 | - Printing to the console (see [Functions added at compiler level]). 859 | 860 | Three namespaces are visible from an APL function. 861 | 862 | - The local namespace of a function. This contains only the names `_a` 863 | and `_w`, which start out as the values of the left and right 864 | arguments, but can be assigned to. They are referred to in the APL 865 | code as `⍺` and `⍵` respectively. 866 | 867 | - The APL global namespace. This contains the functions currently 868 | known to APL under their Lua names. The underscore, though 869 | alphabetic in Lua, has a special meaning during APL assigment 870 | and is therefore best avoided. The APL namespace is a field `_V` 871 | in the APL runtime environment. 872 | 873 | - The Lua global namespace, which is set as the `__index` field of the 874 | metatable of `_V`. The APL global namespace takes precedence when 875 | retrieving a value, but if it does not contain that name, the Lua 876 | namespace is tried next. Assignment into the Lua namespace from APL 877 | is achieved by putting an underscore in front of the name. That 878 | underscore is not part of the real name; it merely indicates which 879 | namesapce to use. 880 | 881 | Several other namespaces are used internally by the APL compiler. These 882 | are collectively referred to as the APL registry, and contain the same 883 | functions as the APL runtime environment, but keyed by their APL names. 884 | 885 | The APL compiler assumes that a name refers to a variable unless it 886 | has been added to the APL registry. The easiest way is to assign a 887 | newly defined function to an APL name. The newly defined function will 888 | be defined as an ambivalent function (use Lua's runtime distinction if 889 | necessary) and can be used wherever a built-in APL function can. 890 | 891 | run=function(str) print(apl(str)()) end 892 | cf=function(_w,_a) return _w+1/_a end 893 | run"2 cf 3" 894 | ./apl-compiler.lua:113: APL syntax error 895 | 2 cf 3 896 | ↑ 897 | run"cf←cf" 898 | cf 899 | run"2 cf 3" 900 | 3.5 901 | run"cf/⌽2 1 2 1 1 4 1 1 6 1 1 8" 902 | 2.718281835206 903 | 904 | For direct assignment to work, the name must be a valid Lua name too. 905 | 906 | More flexibility is offered by the `apl.register` function. You can 907 | define new operators, use a different APL name (which need not be 908 | a valid Lua name) from the Lua name), and define help. 909 | See `help(apl.register)`. 910 | 911 | Some functions that interact with the compiler 912 | ---------------------------------------------- 913 | 914 | Note that `←` and `∇` are used in a non-standard way. 915 | 916 | `←` (`Assign`) 917 | ~ Returns its right argument after assigning it to the left argument. 918 | This can be used inside expressions, except when the left argument 919 | is a parameter (`⍺` or `⍵`). In that case, it must occur at the 920 | very start of the expression. When used monadically (also only 921 | allowed at the start of an expression), `←` forces a `return` 922 | instruction to be generated. 923 | 924 | If the right argument is a Lua function, `Assign` registers it for 925 | use as a function in APL expressions under the name specified by the 926 | left argument. 927 | 928 | `∇` (`Define`) 929 | ~ This is the only Lua⋆APL function that 930 | returns a Lua function as a first-class value. It can take either 931 | a string (which is then compiled) or a function (which is simply 932 | returned). 933 | 934 | =Deal(100,10) 935 | Your APL system is now stoned 936 | print(lua(Define"∇?")) -- see comment below 937 | return Deal 938 | Deal=Execute"∇?" -- recovery! 939 | =Deal(100,10) 940 | (85,41,79,81,92,24,38,3,34,60) 941 | 942 | Note that `apl"∇?"` does not return `Deal`, it returns a niladic 943 | function whose body is `return Deal`; that's why we nead `Execute`. 944 | 945 | `⍎` (`Execute) 946 | ~ Does `Define` and returns the value of the function thus constructed 947 | after calling it with no arguments. 948 | 949 | `⍞`,`⎕` (`Read`,`Do`,`Print`) 950 | ~ Although implemented as Lua functions, these are not really functions 951 | from the APL point of view, they are variables with special semantics. 952 | `⍞←x` or `⎕←x` causes the value `x` to be printed. When used as 953 | a value, not being assigned to, `⍞` stands for the string you will 954 | be prompted to type in and `⎕` for the result of executing that string. 955 | 956 | `lua` 957 | ~ Returns the Lua code to which a function has been compiled. 958 | 959 | `register` 960 | ~ Registers a Lua function for use by the compiler. See 961 | [Extending Lua⋆APL]. The interface may change more quickly than 962 | this manual. `help(register)` should give up-to-date instructions. 963 | 964 | APL functions not documented here 965 | --------------------------------- 966 | 967 | The Wikipedia article does an 968 | excellent job, and Lua⋆APL is designed to be downwards compatible with 969 | the sections "Monadic Functions", "Dyadic Functions" and "Operators" 970 | from a snapshot of that page made on 12 April 2013. 971 | 972 | There are all sorts of little incompatibilities between one APL system 973 | and the next, and Lua⋆APL is no exception. These mainly deal with extra 974 | features that are not quite standard. An APL reference manual for 975 | whatever implementation would nevertheless not be totally useless. 976 | 977 | The interactive help is the only documentation that is intended to be 978 | kept in sync with the code. You can print out a complete quick reference 979 | as follows: 980 | 981 | apl:import() 982 | for k in help(apl,1):match":(.*)":gmatch"%S+" do 983 | if k:match"^_" or k=="NaN" then h=help(k,1) 984 | else h=help(apl[k],1) 985 | end 986 | if (not h or h:match"^That's nil") then 987 | print ("No help on'"..k.."', report this please") 988 | else print(h) 989 | end 990 | end 991 | 992 | Complete syntax of Lua⋆APL 993 | -------------------------- 994 | 995 | The actual grammar is spcified in LPeg in `apl-compiler.c` and may by 996 | now be a little different from what you read below. It allows neutral 997 | whitespace and specifies the Lua code to which a pattern match is 998 | translated. Both of these have here been suppressed in the service 999 | of legibility. 1000 | 1001 | The capitalized names are terminals which, for the same reason, I prefer 1002 | to describe in English. 1003 | 1004 | ---------- -- --------------------------------------------------- 1005 | `Param` `⍺` or `⍵` 1006 | `String` A Lua string delimited by single-quotes 1007 | `Var` A Lua name or a single UTF-8 character 1008 | `Vector` APL-formatted numbers separated by blanks 1009 | ---------- -- --------------------------------------------------- 1010 | 1011 | `String` and `Var` are subject to exceptions. 1012 | 1013 | - As currently implemented, a String may not contain a single-quote 1014 | or a statement separator `⋄`. 1015 | - `Var` is any name that has not been listed as a function, an 1016 | operator, or a parameter. Monadic and dyadic functions and 1017 | operators have names just like the names allowed for variables, 1018 | which are only special because they appear in lists kept by the 1019 | compiler. Those lists are not static. The user can add new items 1020 | to them at run time (see [Extending Lua⋆APL]); once a name has been 1021 | added to a list, it cannot be removed. 1022 | 1023 | Users familiar with BNF should have no difficulty reading this grammar, 1024 | with the following hints: 1025 | 1026 | - `*` means that the two elements are adjacent 1027 | - `+` means that the two elements are alternatives, tried in 1028 | left-to-right order 1029 | 1030 | For example, an expression can consist of just a single variable name 1031 | (since a variable is a value, and a value is a left argument), but this 1032 | possibility is only tried when all the other possibilities for an 1033 | expression have failed. 1034 | 1035 | A simplified version of that grammar is given below. The full grammar, 1036 | complete with compilation captures and special cases, is contained in 1037 | the table `apl_expr` in the module file `lua.apl`. 1038 | 1039 | statement = '←'*expr 1040 | + Param*'←'*expr 1041 | + Param*"["*indices*"]"*'←'*expr 1042 | + expr; 1043 | expr = '∇'*func_expr 1044 | + Var*'←'*expr 1045 | + Var*"["*indices*"]"*'←'*expr 1046 | + leftarg*dyadic_func*expr 1047 | + monadic_func*expr 1048 | + leftarg; 1049 | dyadic_func = ambivalent + Dyadic_function; 1050 | monadic_func = ambivalent + Monadic_function; 1051 | func_expr = '('*(dyadic_func+Monadic_function)*')' 1052 | + Dyadic_function + Monadic_function; 1053 | ambivalent = func_expr*Monadic_operator 1054 | + func_expr*Dyadic_operator*func_expr; 1055 | leftarg = value + '('*expr*')'; 1056 | value = Vector + String + 1057 | Var*'['*indices*']' + Var + 1058 | Param*'['*indices*']' + Param; 1059 | index = expr+""; 1060 | indices = index*';'*index + expr; 1061 | 1062 | Extending Lua⋆APL 1063 | ----------------- 1064 | 1065 | One can extend APL for particular applications. Here is an example. 1066 | 1067 | In Matlab one finds a very convenient function named `diag`, which 1068 | converts between a diagonal matrix and a vector. Let's code a simplified 1069 | version of it in Lua and teach APL to use the function. There's a very 1070 | convenient unused UTF-8 character available for the name. 1071 | 1072 | do 1073 | local rho = apl.util.rho 1074 | local argcheck = apl.util.argcheck 1075 | local shape = apl.util.shape 1076 | local min = math.min 1077 | 1078 | local function diag(A) 1079 | local m,n = shape(A) 1080 | if not m then return A end 1081 | if n then 1082 | n=min(m,n) 1083 | res=rho(0,n) 1084 | for k=1,m do res[k]=A[{k,k}] end 1085 | return res 1086 | end 1087 | res=rho(0,m,m) 1088 | for k=1,m do res[{k,k}]=A[k] end 1089 | return res 1090 | end 1091 | 1092 | apl.register(1,diag,'⍂','Diag') 1093 | end -- do 1094 | 1095 | print(apl"⍂1 2 3 4 5"()) 1096 | 1 0 0 0 0 1097 | 0 2 0 0 0 1098 | 0 0 3 0 0 1099 | 0 0 0 4 0 1100 | 0 0 0 0 5 1101 | print(apl"⍂(⍳6)∘.×⍳6"()) 1102 | 1 4 9 16 25 36 1103 | 1104 | Notes: 1105 | 1106 | The functions in `apl.util` are listed in the [List of Lua⋆APL functions]. 1107 | 1108 | APL mode 1109 | ======== 1110 | 1111 | The APL standalone interpreter gives an approximation to APL's 1112 | look-and-feel. Here is a sample session. 1113 | 1114 | $ lua-apl 1115 | Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio 1116 | Lua⋆APL 0.3.0 © Dirk Laurie 2013 1117 | apl-lib 0.3.0 © Dirk Laurie 2013 1118 | Bug reports are welcome. You'll find me on Lua-L. 1119 | If you can't remember the README, do this: 1120 | apl:import(); help'start' 1121 | -- 1122 | apl:import() 1123 | help'APL' 1124 | Contents: ! + , . / < = > ? \ ¨ × ÷ ← ↑ ↓ ∇ ∊ − ∘ ∣ 1125 | ∧ ∨ ∼ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊖ ⊤ ⊥ ⋆ ⌈ ⌊ ⌹ ⌽ ⌿ ⍀ ⍉ ⍋ ⍎ ⍒ ⍕ ⍟ ⍪ ⍱ ⍲ ⍳ ⍴ ⎕ ○ 1126 | help'⌽' 1127 | Reverse2(⍵): ⌽⍵ → columns of ⍵ in reverse order 1128 | Rotate2(⍵,⍺): ⍺⌽⍵ → elements in rows of ⍵ rotated left by ⍺ or right by -⍺ 1129 | ⍺←⍳5 ⋄ ←A←⍺∘.×⍺ 1130 | 1 2 3 4 5 1131 | 2 4 6 8 10 1132 | 3 6 9 12 15 1133 | 4 8 12 16 20 1134 | 5 10 15 20 25 1135 | 5 ¯5↑A[2 3;3 2] 1136 | 0 0 0 6 4 1137 | 0 0 0 9 6 1138 | 0 0 0 0 0 1139 | 0 0 0 0 0 1140 | 0 0 0 0 0 1141 | 1 3 2 4 5⌽A 1142 | 2 3 4 5 1 1143 | 8 10 2 4 6 1144 | 9 12 15 3 6 1145 | 20 4 8 12 16 1146 | 5 10 15 20 25 1147 | 1148 | This is pretty standard APL, but some points deserve mention: 1149 | 1150 | - The diamond separator `⋄` is supported. It is necessary in this 1151 | case because `⍺∘.×(⍺←⍳5)` is invalid in Lua⋆APL. 1152 | - The parameter names (`⍺` and `⍵`) are available for temporary 1153 | values. Other assignments go into the APL global namespace, or 1154 | even the Lua global namespace (see [Namespaces]). 1155 | - Every input line translates to a single APL function. If that line 1156 | contains a separator, you must explicitly put in a unary assignment 1157 | `←` if you want the function to return something. If there is no 1158 | separator, the value of the sole APL expression is returned. 1159 | - The jot (`∘`) is mapped to the Lua function `Pass`, which throws 1160 | away all its arguments and returns nothing. This can be used to 1161 | suppress printout of one-expression inputs. Implementing jot as 1162 | a function also removes the need for the compiler to have 1163 | a separate syntax for the outer product. 1164 | 1165 | Some other important points have not been illustrated in the short sample. 1166 | 1167 | - The APL-enabled Lua interpreter pre-empts all the command-line 1168 | parameters of the standard Lua interpreter. You cannot redirect 1169 | to its input, and script files cannot be processed. For that, you 1170 | must use the compiler explicitly from Lua. 1171 | 1172 | - Heuristics are used to guess whether an input chunk is APL. These 1173 | change so often that I don't document them outside `lua-apl.c` any 1174 | more. Ideally, it should not bother anybody: it is very hard to 1175 | invent a character string that could parse to either correct APL or 1176 | correct Lua, so it ought to be very easy to see at a glance, even a 1177 | computer's glance, which it is. 1178 | 1179 | The comments in `lua-apl.c` (search for "guess") include tips on how 1180 | to override a wrong guess. 1181 | 1182 | - If you replay input history (assuming your Lua has that feature) you 1183 | will notice that your input has been changed to what you would have 1184 | typed in Lua mode, e.g. `2 3⌽A` turns into `return apl"2 3⌽A"()`. 1185 | 1186 | 1187 | -------------------------------------------------------------------------------- /lua-apl.xmodmap: -------------------------------------------------------------------------------- 1 | ! Keyboard for Lua*APL using the APL385 Unicode font. 2 | 3 | ! APL characters in top row 4 | ! ⍬ ⌶ ⍫ ⍒ ⍋ ⌽ ⍉ ⊖ ⍟ ⍱ ⍲ − ⌹ 5 | ! ⋄ ¨ ¯ < ≤ = ≥ > ≠ ∨ ∧ × ÷ 6 | ! ` 1 2 3 4 5 6 7 8 9 0 - = 7 | keycode 49 = grave asciitilde grave asciitilde U22C4 U236C 8 | keycode 10 = 1 exclam 1 exclam U00A8 U2336 9 | keycode 11 = 2 at 2 at U00AF U236B 10 | keycode 12 = 3 numbersign 3 numbersign less U2352 11 | keycode 13 = 4 dollar 4 dollar U2264 U234B 12 | keycode 14 = 5 percent 5 percent equal U233D 13 | keycode 15 = 6 asciicircum 6 asciicircum U2265 U2349 14 | keycode 16 = 7 ampersand 7 ampersand greater U2296 15 | keycode 17 = 8 asterisk 8 asterisk notequal U235F 16 | keycode 18 = 9 parenleft 9 parenleft U2228 U2371 17 | keycode 19 = 0 parenright 0 parenright U2227 U2372 18 | keycode 20 = minus underscore minus underscore multiply U2212 19 | keycode 21 = equal plus equal plus division U2339 20 | ! APL characters in second row 21 | ! ⍷ ⍸ ⍥ ⊣ ⊢ ⍙ 22 | ! ⍵ ∊ ⍴ ∼ ↑ ↓ ⍳ ○ ⋆ ← → ⍀ 23 | ! Q W E R T Y U I O P [ ] \ 24 | keycode 24 = q Q q Q 25 | keycode 25 = w W w W U2375 26 | keycode 26 = e E e E U220A U2377 27 | keycode 27 = r R r R U2374 28 | keycode 28 = t T t T U223C 29 | keycode 29 = y Y y Y U2191 30 | keycode 30 = u U u U U2193 31 | keycode 31 = i I i I U2373 U2378 32 | keycode 32 = o O o O U25CB U2365 33 | keycode 33 = p P p P U22C6 34 | keycode 34 = bracketleft braceleft bracketleft braceleft U2190 U22A3 35 | keycode 35 = bracketright braceright bracketright braceright U2192 U22A2 36 | keycode 51 = backslash bar backslash bar U2340 U2359 37 | ! APL characters in third row 38 | ! ⌷ ≡ ⍒ ⍋ ⍤ ⍞ ⍂ ⌻ 39 | ! ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ⎕ ⊢ ⊣ 40 | ! A S D F G H J K L ; ' 41 | keycode 38 = a A a A U237A 42 | keycode 39 = s S s S U2308 U2337 43 | keycode 40 = d D d D U230A 44 | keycode 41 = f F f F underscore U2261 45 | keycode 42 = g G g G U2207 U2352 46 | keycode 43 = h H h H U2206 U234B 47 | keycode 44 = j J j J U2218 U2364 48 | keycode 45 = k K k K 49 | keycode 46 = l L l L U2395 U235E 50 | keycode 47 = semicolon colon semicolon colon U22A2 U2342 51 | keycode 48 = apostrophe quotedbl apostrophe quotedbl U22A3 U233B 52 | ! APL characters in bottom row 53 | ! ⍝ ⍎ ⍕ ∵ 54 | ! ⊂ ⊃ ∩ ∪ ⊥ ⊤ ∣ ⍪ ⍀ ⌿ 55 | ! Z X C V B N M , . / 56 | keycode 52 = z Z z Z U2282 57 | keycode 53 = x X x X U2283 58 | keycode 54 = c C c C U2229 U235D 59 | keycode 55 = v V v V U222A 60 | keycode 56 = b B b B U22A5 U234E 61 | keycode 57 = n N n N U22A4 U2355 62 | keycode 58 = m M m M U2223 63 | keycode 59 = comma less comma less U236A 64 | keycode 60 = period greater period greater U2340 U2235 65 | keycode 61 = slash question slash question U233F 66 | -------------------------------------------------------------------------------- /prog-guide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Lua⋆APL Programmer's Guide

12 |

© Dirk Laurie 2013 Lua-style MIT licence

13 |

This part of the documentation is aimed at those who wish understand apl.lua.

14 |

Registry

15 |

APL syntax depends on knowing whether a name refers to a function, a monadic operator, a dyadic operator or a value. For the built-in functions, including functions assigned to APL names, this information is predefined, but for user-defined functions, the function or operator must be registered. The name will then shadow an object of the same name in the APL global namespace.

16 |

An unregistered name in an APL expression is always assumed to refer to a value.

17 |

The registry appears in the source code just before the function register . Each of the five entries is an APL-to-Lua dictionary mapping an APL name to a Lua name. The dictionary Reserved contains items that will be exported in the module table under their Lua names but do not have genuine APL names: the supplied APL name is a mere tag for use by the help facility.

18 |

The unclassified dictionary apl_dict keeps track all the Lua names to which an APL name could be mapped and is used only by the help facility.

19 |

APL levels

20 |

You can set global _APL_LEVEL before requiring apl. This is mainly intended as a debugging aid. Subsets of APL (each including the previous level) will be loaded as follows:

21 |
22 |
_APL_LEVEL=0:
23 |
All primitive scalar functions only act on scalars. Range and Reshape are available and create APL arrays, which have Lua indexing with range checks. ToString is fully-featured. 24 |
25 |
_APL_LEVEL=1:
26 |
All functions act on arrays, mostly treating them as vectors. Disclose, Enclose and Transpose are explicitly matrix-aware. but most of the other functions respect shape only when creating the result. 27 |
28 |
_APL_LEVEL=2: (Default.)
29 |
Lua⋆APL as documented in the User's Manual. This is the only level that prettyprints by default. 30 |
31 |
_APL_LEVEL>2:
32 |
Additional fields exposing the working of the system are available in the module table: f1 f2 op1 op2 rank0 rank1 lib APL_ENV. The first four of these contain the functions used by APL, classified as required by the compiler and indexed by their Lua names. The next two contain similarly classified Level 0 and Level 1 versions of functions that have been redefined. lib contains functions that are accessible in library mode but not from APL code. APL_ENV is the global environment for functions compiled by the APL compiler. 33 |
34 |
35 |

The program is divided into five main sections. The first section defines semi-globals, i.e. local variables whose scope is the entire file, plus the APL compiler. Among the semi-globals are twenty-odd short functions that are later exported in apl.util.

36 |

The next three sections define levels 0, 1 and 2. Each section is almost a module on its own in the sense that dependencies (except semi-globals) are explicitly assigned to local variables.

37 |

The final section populates the module table from the components f1, f2, op1, op2, lib and does some final finishing-off.

38 |

Utilities

39 |
    40 |
  1. All functions that will be defined are declared forward as locals. This allows the routines to refer to each other. The programmer is responsible for avoiding circular references.
  2. 41 |
  3. The functions themselves follow in alphabetic order.
  4. 42 |
  5. They are collected into a table and assigned to apl.util.
  6. 43 |
44 |

Compiler

45 |

The entire compiler consists of a single LPEG grammar, preceded by the definition of its terminals. Considerable thought has gone into the order of alternatives and changes should not be undertaken lightly.

46 |

Several functions depend on the compiler, and the opportunity is also taken to define a couple of UTF-8 utilities before the necessary patterns go out of scope.

47 |

The redefinition of help is made inside the compiler scope for no very good reason; earlier versions required access to some of the compiler tables.

48 |

Level 0

49 |

This level is fairly straightforward. The code appears in this order:

50 |
    51 |
  1. Local aliases for functions in global tables are defined. This fixes the function being called to be the one current at the stage that the module is loaded.
  2. 52 |
  3. Local utility functions, not needed at the other levels and not useful enough to export as utilities, are defined.
  4. 53 |
  5. The forward-declared functions appear in alphabetic order. Local auxiliary functions needed only by a particular library function are declared immediately before the library function itself.
  6. 54 |
  7. The functions are collected into tables lib for functions that will not be revealed to the Lua compiler, f1 for monadic and f2 for dyadic functions that will later be extended to apply to arrays, gen1 and gen2 for dyadic functions that will not be so extended.
  8. 55 |
  9. The routines are inserted into the corresponding tables (apl.f1 etc.) by the replace utility.
  10. 56 |
  11. Help is defined (the use of function keys gives a way to check that the corresponding function has been defined).
  12. 57 |
58 |

Level 1

59 |

This level has two parts. First, the functions that need to be extended are redefined with the aid of the each and both utilities. For these functions, it is necessary to copy over the help information, since help uses the function itself to index its tables.

60 |

Then follows much the same code layout as at Level 1 for the new functions. The main difference is that replace is called with three arguments: if the value associated with a name is being changed, the old value is saved in the table specified by the third argument.

61 |

A subtlety here is that some functions appear in more than one list. For example Attach is stored as lib.Attach, f2.Attach1 and f2.Attach2. The reason is that the compiler knows two symbols, , and , that at Level 1 are both mapped to Attach, but at Level 2 will be mapped to different functions. The other two names are given to remain compatible with the compiler.

62 |

Level 2

63 |

At this level, almost all the functions being defined are matrix versions of Level 1 functions of the same name. Most of them invoke the Level 1 version in some way.

64 |

The functions that will be called but are to be superseded are given local names, mostly simply by converting the name to lower case but occasionally (rawformat,vecget etc) modified to avoid confusion. Level 1 functions that are called but not superseded retain their CamelCase names.

65 |

For these functions, the help information is copied over in a separate loop, since replace does not do so.

66 |

The functions that at Level 1 appeared in more than one list here form a separate group. They are extended at Level 2 to offer two possibilities, one for row-wise and one for column-wise application. The extended versions have new help information. The Level 1 versions with their help information are still available under the name that contains no axis digit.

67 |

Building the compiler tables

68 |

At the end of Level 2, the compiler knows almost no APL yet. The whole language is contained in four tables defining the Lua-APL dictionary, which are used together with the information in apl.f1 etc to build the registry tables.

69 |

The units for functions that allow scanning and reducing over empty vectors are also defined here.

70 |

Finishing off

71 |

Miscellaneous small tasks are done here, such as initializing the comparison tolerances, issuing the start-up message, etc.

72 |

Comparison with APL2

73 |

Though not an official standard, IBM's APL2 was a landmark and is well documented (google for APL2LanguageRef.pdf). Lua⋆APL is not and will not be an implementation of the same APL dialect as APL2, but in cases where Lua⋆APL extends older APL usage in the same way as APL2 does, I have tried to use the same symbol for the extension.

74 |

Differences of which I am aware between features provided by APL2 and Lua⋆APL are listed below. Unless marked "TODO", there are no plans to resolve those differences. Even the "TODO"'s are not all features that I am sure about.

75 |

I would like, though, to be told of any differences not listed or implied below.

76 |

Downward incompatibilities

77 |
    78 |
  1. Complex numbers are not supported. Implications include that monadic + means Copy, not Conjugate, monadic × means Sign, not Direction, etc.
  2. 79 |
  3. Arrays of rank higher than 2 are not supported. Implications include that is no dyadic , etc, and that separate functions and operators are provided for working along the first axis.
  4. 80 |
  5. Nested arrays are provided via Lua tables, respecting Lua's natural row-wise order. Implications include that monadic or respectively enclose or disclose rows (and if ever extended, will do one rank at a time), and there is no dyadic or , etc.
  6. 81 |
  7. System variables have different names and do different things.
  8. 82 |
  9. Index origin 1, not selectable.
  10. 83 |
  11. , <, > do not depend on comparison tolerance.
  12. 84 |
  13. ASCII names for functions are often different even when the function is essentially the same, as motivated above under [Library Mode].
  14. 85 |
  15. Monadic is Lua length; dyadic is Lua identity.
  16. 86 |
  17. No , monadic or .
  18. 87 |
  19. TODO: Dyadic ~.
  20. 88 |
  21. TODO: Monadic .
  22. 89 |
  23. TODO: Dyadic and (will work differently, though).
  24. 90 |
91 |

Upward incompatibilities

92 |
    93 |
  1. Lua⋆APL is a Lua package. All of Lua is available. The implications are not individually listed; they include that scalars can be any Lua value except a table, Lua functions can be used to index an array, arrays can be modified at the Lua level, some string-valued keys are used, etc.
  2. 94 |
  3. Lua⋆APL can be extended by Lua functions registered under APL names. This could be used to provided some non-implemented functionality.
  4. 95 |
  5. Namespaces.
  6. 96 |
  7. is a function acting on a string or function and returning a function.
  8. 97 |
  9. Lua⋆APL supports strings, not character arrays.
  10. 98 |
99 |
100 |

The core C routines

101 |

Some of these have been adapted from other packages: for the block functions and the style of documentation below I acknowledge useful ideas of John Hind. They are delivered in a table returned by require apl_core.

102 |

The table also returns six functions needed for the Circ function (APL ) which are not described here.

103 |

Block functions

104 |

Block functions all have a table and two integers as their first three arguments. The notation tbl[a:b] is used for a block of values with increasing keys if a<b and decreasing keys if a>b. Thus tbl[b:a] is the reverse of tbl[a:b].

105 |

get(tbl,a,b)

106 |

Returns tbl[a:b]. This function may cause stack overflow if too many items are requested.

107 |

move(tbl,a,b,c[,d])

108 |

Moves tbl[a,b] to tbl[c,d], overwriting whatever was there. If omitted, d is calculated so that b-a == d-c. Returns tbl.

109 |

pick(tbl,a,b,fct[,count])

110 |

If count==1, returns the first index i in a:b such that fct(tbl[i]) is true. If count>1, ignores the first count-1 hits. If fct is not a function or if count<1, returns nil.

111 |

set(tbl,a,b,...)

112 |

Sets tbl[a:b] to the given values, overwriting existing values. Returns tbl.

113 |

If the vararg list is empty (not even containing nil), stores nothing. The list is treated cyclically: if it is exhausted before b is reached, the supply of values is resumed from its beginning. If b is nil, values are stored in tbl[a],tbl[a+1],... until the list is exhausted.

114 |

transpose(tbl,a,b,target)

115 |

Stores the transpose of tbl[1:a*b] in target[1:a*b]. tbl is assumed to contain a blocks of b elements, and target will contain b blocks of a elements. Returns target.

116 |

tbl and target are not allowed to be the same array.

117 |

APL functions

118 |

These functions operate on or return tables that conform to the specifications for APL arrays. See main documentation.

119 |

both(f,x1,x2,e1,e2)

120 |
Applies binary `f` term-by-term to every pair of corresponding 
121 | elements of `x1` and `x2`, whose sizes must be compatible as
122 | specified by `e1` and `e2`.
123 | 
124 | `e1=0; e2=0`
125 | :   `x1` and `x2` must have the same shape, which is also the
126 |      shape of the result.
127 | `e1=1`
128 | :   `x1` may be a singleton, treated as equivalent to a constant
129 |     array of the same shape as `x2`.
130 | `e2=1`
131 | :   `x2` may be a singleton, treared as equivalent to a constant
132 |     array of the same shape as `x1`.
133 | `e1=2`
134 | :   `x1` is treated as a constant first argument even if it is
135 |     an array.
136 | `e2=2`
137 | :   `x2` is treated as a constant second argument even if it is
138 |     an array. 
139 |

compat(a,b)

140 |

Tests whether a and b are compatible for term-by-term dyadic functions. That means one of the following conditions holds:

141 |
    142 |
  1. a or b is a scalar or a one-element array.
  2. 143 |
  3. a and b have the same shape.
  4. 144 |
  5. a or b is a vector, and the other is a one-row or a one-column matrix of the same length.
  6. 145 |
146 |

each(f,x)

147 |

Applies unary f term-by-term to every element of x, producing a result of the same shape as x.

148 |

iota(n[,start=1])

149 |

Returns an APL vector containing the first n integers from the given start.

150 |

rho(v,m[,n])

151 |

Returns an APL vector of length m, or an APL matrix of shape m×n, filled with copies of v.

152 |

svd(A)

153 |

Calls the LAPACK routine dgesvd and organizes its output into a Lua table containing APL arrays U, S, V. See User's Manual.

154 |

Other functions

155 |

is_int(x)

156 |

Tests whether x equals tointeger(x).

157 |

keep(count,...)

158 |

Returns count arguments, starting at the first extra argument. As in the case of select, a negative number indexes from the end (-1 is the last argument).

159 |

map(ft,...)

160 |

Each return value is the result of ft applied to the corresponding value in the tuple.

161 |

"Applying" means indexing if ft is a table and calling if ft is a function, which is assumed to be unary with one return value.

162 |

tointeger(x)

163 |

The result of the API function lua_tointeger.

164 |

where(level)

165 |

Returns a string identifying the point in the source code from which where is being called if level=1, the point from which that routine was called if level=2, etc.

166 | 167 | 168 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | _APL_LEVEL=arg[1] and tonumber(arg[1]) or 2 2 | apl=require"apl" 3 | print ("APL Level ".._APL_LEVEL) 4 | apl:import() 5 | idiom = require "finnaplidiom" 6 | 7 | local char = string.char 8 | local function Str(x,y) 9 | if type(x)=='table' then return char(unpack(x)) else return char(x) end 10 | end 11 | apl.register(1,Str,'§','Str') 12 | 13 | local tests = {} 14 | 15 | tests[0] = [[ 16 | j←5 3 1 17 | _A←⍳10 18 | A[2] 19 | A[5]←-5 20 | !10 21 | '%24.16f'⍕1○○÷6 22 | (∘1)⍕∘1 23 | §65 24 | ⍟¯1 25 | ⍕⍟¯1 26 | ]] 27 | 28 | tests[1] = [[ 29 | j←⍒A←?10⍴100 30 | A[j] 31 | A,0↑A[5]←-5 32 | n←(0,⍳10)!10 33 | f←2 3⍴¨5 4 34 | ⊖f 35 | ⊃f 36 | 1 2 3○○÷6 1 4 37 | ○÷¯1 ¯2 ¯3○⎕←1 2 3○○÷6 1 4 38 | (∘1)⍕∘1 39 | §32+⍳94 40 | ⍟¯1,0○1.4 41 | 0 1∘.∨0 1 42 | 0 1∘.∧0 1 43 | 0 1∘.⍱0 1 44 | 0 1∘.⍲0 1 45 | 10|(⍳9)∘.+⍳9 46 | 'raw'⍕10|(⍳9)∘.+⍳9 47 | 10⊥⍳9 48 | (10⍴10)⊤!10 49 | 10|(⍳9)∘.×⍳9 50 | 10|(⍳9)∘.-⍳9 51 | 10|(⍳9)∘.<⍳9 52 | 10|(⍳9)∘.≤⍳9 53 | 10|(⍳9)∘.=⍳9 54 | 10|(⍳9)∘.>⍳9 55 | 10|(⍳9)∘.≠⍳9 56 | 10|(⍳9)∘.⌈⍳9 57 | 10|(⍳9)∘.⌊⍳9 58 | 10|(⍳9)∘.|⍳9 59 | ⌈(⍳9)∘.÷⍳9 60 | ⌊(⍳9)∘.÷⍳9 61 | 1 2 3=1 3⍴1 2 3 62 | 1 2 3≡1 3⍴1 2 3 63 | ⍺←⍳2 ⋄ y←⍺+2 ⋄ x←⍺-y×⍺⌹y 64 | x+.×y 65 | ⌹3 4 66 | +/(⍳10)⋆2 67 | ⌽+\(⍳10)⋆2 68 | ⌈/0⍴0 69 | ]] 70 | 71 | tests[2] = [[ 72 | 2 3⍴¨5 4 73 | j←⍒A←?10⍴100 74 | A[j] 75 | A,0↑A[5]←-5 76 | n←(0,⍳10)!10 77 | f←n⍪?n 78 | ⊂f 79 | ⊖f 80 | ⊖⊂f 81 | 1 2 3○○÷6 1 4 82 | ○÷¯1 ¯2 ¯3○⎕←1 2 3○○÷6 1 4 83 | (∘1)⍕∘1 84 | §32+⍳94 85 | ⍟¯1,0○1.4 86 | 0 1∘.∨0 1 87 | 0 1∘.∧0 1 88 | 0 1∘.⍱0 1 89 | 0 1∘.⍲0 1 90 | 10|(⍳9)∘.+⍳9 91 | 'raw'⍕10|(⍳9)∘.+⍳9 92 | '%d'⍕10⊥10|(⍳9)∘.+⍳9 93 | 10 10⊤10|(⍳9)∘.+⍳9 94 | 10|(⍳9)∘.×⍳9 95 | 10|(⍳9)∘.-⍳9 96 | 10|(⍳9)∘.<⍳9 97 | 10|(⍳9)∘.≤⍳9 98 | 10|(⍳9)∘.=⍳9 99 | 10|(⍳9)∘.>⍳9 100 | 10|(⍳9)∘.≠⍳9 101 | 10|(⍳9)∘.⌈⍳9 102 | 10|(⍳9)∘.⌊⍳9 103 | 10|(⍳9)∘.|⍳9 104 | ⌈(⍳9)∘.÷⍳9 105 | ⌊(⍳9)∘.÷⍳9 106 | 1 2 3=1 3⍴1 2 3 107 | 1 2 3≡1 3⍴1 2 3 108 | +/(⍳10)⋆2 109 | ⌽+\(⍳10)⋆2 110 | (⍳3)⌹3 4⍴⍳12 111 | (⍳4)⌹⍉3 4⍴⍳12 112 | ]] 113 | 114 | aplchars = [[! + , . / < = > ? \ § ¨ × ÷ ↑ ↓ ∇ ∊ − ∘ ∣ ∧ ∨ ∼ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊖ ⊤ ⊥ ⋆ ⌈ ⌊ ⌹ ⌽ ⌿ ⍀ ⍉ ⍋ ⍎ ⍒ ⍕ ⍟ ⍪ ⍱ ⍲ ⍳ ⍴ ⎕ ○]] 115 | apl_tally = {} 116 | lua_tally = {} 117 | apl_omitted, lua_omitted = {},{} 118 | for k in aplchars:gmatch"%S+" do apl_omitted[k]=true end 119 | for k in pairs(apl) do lua_omitted[k]=true end 120 | 121 | function occurs(S,k) 122 | local c,j,i=-1,0 123 | repeat i,j = S:find(k,j+1,true); c=c+1 until not j 124 | return c 125 | end 126 | 127 | print"" 128 | for S in tests[_APL_LEVEL]:gmatch"[^\n]+" do 129 | if S:match"%S" then 130 | f=apl(S) 131 | print(' '..S..' → '..lua(f)) 132 | local res=f() 133 | if res then print(tostring(res)) end 134 | for k in aplchars:gmatch"%S+" do 135 | local c=occurs(S,k) 136 | if c>0 then 137 | apl_tally[k] = (apl_tally[k] or 0) + c 138 | apl_omitted[k] = nil 139 | end 140 | end 141 | for k in pairs(apl) do 142 | c=occurs(lua(f),k) 143 | if c>0 and not (',.'):find(k) then 144 | lua_tally[k] = (lua_tally[k] or 0) + c 145 | lua_omitted[k] = nil 146 | end 147 | end 148 | end 149 | end 150 | 151 | t={} 152 | for k,v in pairs(apl) do if lua_tally[k] then 153 | t[k..":"..(lua_tally[k] or 0)]=true 154 | end end 155 | print "\nTally of Lua functions tested" 156 | help(t) 157 | 158 | print "\nThe following Lua functions are not represented" 159 | help(lua_omitted) 160 | 161 | t={} 162 | for k in aplchars:gmatch"%S+" do if apl_tally[k] then 163 | t[k..":"..(apl_tally[k] or 0)]=true 164 | end end 165 | print "\nTally of APL functions tested" 166 | help(t) 167 | 168 | print "\nThe following APL functions are not represented" 169 | help(apl_omitted) 170 | 171 | --------------------------------------------------------------------------------