Advertisement
Guest User

Untitled

a guest
May 14th, 2021
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
q/kdb+ 17.42 KB | None | 0 0
  1. /
  2.     Q function editor
  3.     Copyright (c) 2018 Kx Systems, Inc.
  4.  
  5.     Licensed under the Apache License, Version 2.0 (the "License");
  6.     you may not use this file except in compliance with the License.
  7.     You may obtain a copy of the License at:
  8.  
  9.     http://www.apache.org/licenses/LICENSE-2.0
  10.  
  11.     Unless required by applicable law or agreed to in writing,
  12.     software distributed under the License is distributed on an
  13.     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  14.     either express or implied.  See the License for the specific
  15.     language governing permissions and limitations under the
  16.     License.
  17.  
  18.     ----------------
  19.  
  20.     Contains two different workspace-centric function editors,
  21.     <ed> and <qed>.  <ed> shells out to the OS to invoke an
  22.     external editor of choice, and <qed> edits entirely within Q
  23.     using a line-mode editor.
  24.  
  25.     Usage information appears at the bottom of this file.
  26.    
  27.     Author:     Leslie Goldsmith, Kx Systems
  28. \
  29.  
  30.  
  31. \d .ed
  32.  
  33. ED:$["w"=first string .z.o;"\"C:\\Program Files\\Notepad++\\notepad++.exe\" -multiInst -nosession -notabbar";"Editor_not_specified"] / External editor invocation
  34. TABS:8
  35. POW:1 10 100 1000 10000
  36.  
  37. enl:enlist
  38.  
  39.  
  40. //
  41. // @desc Edits a new or existing function using an external editor.
  42. //
  43. // @param x {symbol|string} Specifies the name of the function to edit.  If the
  44. //                          function does not exist, a new one is created.  The
  45. //                          name may be preceded by `:` to force definition (or
  46. //                          redefinition) in the specified namespace.
  47. //
  48. ed:{
  49.     if[0~v:ncsv x;:()];nm:first v;c:v 1; / Extract name (with possible namespace) and context
  50.     `:_ed.tmp 0:enl fn0:ssr[;"\n ";"\n\t"]fn:nm,":",last v; / Write temporary file in canonical format
  51.     if[v 2;fn0:0];b:1b; / Kill inceptive defn if new, and set loop terminator
  52.    
  53.     while[b;
  54.         system ED," _ed.tmp"; / Invoke editor
  55.         n:name fn:read0`:_ed.tmp; / Grab new defn and name
  56.         nm:def[n;ctx[c;nm;n];(1+fn?":")_fn:"\n"sv fn]; / Attempt to define function
  57.         if[b:nm~"";-1 "Press any key to re-edit, or \"\\q\" to quit and discard changes";b:not"\\q"~read0 0]];
  58.    
  59.     if[count nm;-1 nm,(" defined";" unchanged")fn0~fn];
  60.     }
  61.  
  62.  
  63. //
  64. // @desc Edits a new or existing function using an internal line-mode editor.
  65. // Editor commands are described at the end of this file.
  66. //
  67. // @param x {symbol|string} Specifies the name of the function to edit.  If the
  68. //                          function does not exist, a new one is created.  The
  69. //                          name may be preceded by `:` to force definition (or
  70. //                          redefinition) in the specified namespace.
  71. //
  72. qed:{
  73.     if[0~v:ncsv x;:()];nm:first v;c:v 1; / Extract name (with possible namespace) and context
  74.     i:0,1+where"\n"=fn0:nm,":",last v; / Find line breaks
  75.     Lns::10000*til count Fn::i _fn0,"\n"; / Scaled line numbers and corresponding lines
  76.     Cur::0|-2+count Lns; / Current line (= insert point), before closing }
  77.     Mode::0b; / Set insert (vs. edit) mode
  78.     Ln::-1; / User-specified line number, if any
  79.     if[v 2;fn0:0]; / Kill inceptive defn if new
  80.     d:system"c";system"c 1000 2000"; / Set display size
  81.     p:system"P";system"P 10"; / Set formatting precision
  82.    
  83.     dl(); / Display all lines
  84.  
  85.     while[not$[[2 pr:fmtn seln[];"\\w"~s:read0 0];
  86.             [s:"";count nm:def[n;ctx[c;nm;n:name Fn];(1+fn?":")_fn:-1_(,/)Fn]];[if[i:"\\q"~s;nm:""];i]]; / Attempt to define function
  87.         r:$[0=count s:ltrim s;Cur; / No change if input empty
  88.             [Ln::-1;"["=first s];lcmd s; / Look for edit command
  89.             upd pr,s]; / Otherwise, update current line
  90.         $[r=-1;-2 "Command error";Mode&::r=Cur::r&-1+count Lns]];
  91.  
  92.     if[count nm;-1 nm,(" defined";" unchanged")fn0~fn];
  93.  
  94.     system"c ",.Q.s1 d;system"P ",string p; / Restore settings
  95.     }
  96.  
  97.  
  98. //
  99. // @desc Returns the name, context namespace, status, and value of an object.
  100. //
  101. // @param x {symbol|string} Specifies the name of the function to edit.  The
  102. //                          name may be preceded by `:` to force definition (or
  103. //                          redefinition) in the specified namespace.
  104. //
  105. // @return {list[4]|0}      A 4-element array containing the name, context,
  106. //                          status, and value of the function, or `0` if the
  107. //                          name is illegal.  Status is `1b` if the object
  108. //                          is new or if an explicit namespace override is
  109. //                          specified, or `0b` otherwise.
  110. //
  111. ncsv:{
  112.     nm:$[10h=type x;;-11h=type x;string;0#]x; / Convert name to string (empty if illegal)
  113.     c:`$$[":."~2#nm;$["."in i:2_nm;(i?".")#i;""];1_string system"d"]; / Get context namespace
  114.     v:$[(0=count nm)|" "in nm:sqz nm;0;b:0h=type key x:`$nm:(i:":"=first nm)_nm;(0;0;0;c;"{\n }");100h=type v:value x;value v;0]; / Validate name; hallucinate value if new function
  115.     $[v~0;0*-2 "Unable to edit";(nm;$[i;c;first v 3];b|i;last v)] / Name, context, status, value
  116.     }
  117.  
  118.  
  119. //
  120. // Extracts the function name (with possible namespace) from its definition.
  121. //
  122. // @param x {string[]}      The function definition, split by lines.  The first
  123. //                          line has the form:  `name:{...` .
  124. //
  125. // @return {string}         The function name if plausible, or an empty string
  126. //                          otherwise.  Note that the returned name may be still
  127. //                          be illegal; the caller is expected to accommodate
  128. //                          this.
  129. //
  130. name:{{$[(first[x]in .Q.n)<":"in x;(x?":")#x;""]}ltrim first x}
  131.  
  132.  
  133. //
  134. // @desc Computes the context for a function based on its initial and final
  135. // properties.
  136. //
  137. // @param c {symbol}        Specifies the context of the original definition.
  138. // @param nm0 {string}      Specifies the initial function name (with possible
  139. //                          namespace).
  140. // @param nm {string}       Specifies the final function name (with possible
  141. //                          namespace).
  142. //
  143. // @return {symbol}         The context in which the function should be defined.
  144. //
  145. ctx:{[c;nm0;nm]
  146.     i:`${$["."=first y;(y?".")#y:1_y;x]}[1_string system"d"]@/:(nm0;nm); / Extract namespace from names, defaulting to active namespace
  147.     $[(=/)i;c;last i] / Use context if no change; else use namespace of final name
  148.     }
  149.  
  150.  
  151. //
  152. // @desc Displays lines to the console.
  153. //
  154. // @param x {long[2|0]} The starting line number and the number of lines to
  155. //                      display, or an empty vector to display all lines.
  156. //
  157. // @return {long}       The line index of the last line displayed.
  158. //
  159. dl:{
  160.     i:first[x]+til last x:2#x,0,count Lns; / Indices of selected lines (all if none specified)
  161.     1"",/((1+5|count each j)$j:"[",/:string[0.0001*Lns i],\:"]"),'Fn i; / Prepend line numbers and display
  162.     last count[Lns],i
  163.     }
  164.  
  165.  
  166. //
  167. // @desc Formats a line number for output.
  168. //
  169. // @param x {long}      The scaled line number.
  170. //
  171. // @return {string}     The decorated string representation of the line number.
  172. //
  173. fmtn:{(1+5|count s)$s:"[",string[0.0001*x],"]"}
  174.  
  175.  
  176. //
  177. // @desc Selects the line number to display for the current line.
  178. //
  179. // @return {long}   The line number to display.
  180. //
  181. seln:{[] $[Mode;Lns Cur;Ln>=0;Ln;$[null i:nextn[];[-2 "No room for insertion";Lns Cur];i]]}
  182.  
  183.  
  184. //
  185. // @desc Computes the line number for insertion mode.
  186. //
  187. // @return {long}   The next line number, or `0N` if there is no room for
  188. //                  insertion at the current line index.
  189. //
  190. nextn:{[] {x+POW(last where i=(_)i:x%POW)&POW bin -1+y-x}. 2#Cur _Lns,0W}
  191.    
  192.  
  193. //
  194. // @desc Processes a line command (beginning with `[`).  Valid command formats are
  195. // described at the end of this file.
  196. //
  197. // @param s {string}    The input line command, starting with the leading bracket.
  198. //
  199. // @return {long}       The new line index if the operation is successful, or `-1`
  200. //                      if an error occurred.
  201. //
  202. lcmd:{[s]
  203.     if[not"]"in s;:-1]; / Must be matched
  204.     (del;edt;upd)[("~$"in(s?"]")#s)?1b]s / Invoke appropriate routine
  205.     }
  206.  
  207.  
  208. //
  209. // @desc Deletes one or more lines.
  210. //
  211. // @param s {string}    The input line command, starting with the leading bracket.
  212. //                      Valid formats are `[~n1 n2 n3]` and `[n~m]`.
  213. //
  214. // @return {long}       The new line index if the operation is successful, or `-1`
  215. //                      if an error occurred.
  216. //
  217. del:{[s]
  218.     if[1b in" "<>(1+count i:(s?"]")#s)_s:ltrim 1_s;:-1]; / Must be no line residual
  219.    
  220.     i:$["~"=first i; / Distinguish case
  221.         [if[-1~n:getnv 1_i;:-1];Lns in n]; / Monadic (vector) form
  222.         [if[-1=n:getn(j:i?"~")#i;:-1];if[n>m:getn(j+1)_i;:-1];Lns within n:n,m]]; / Dyadic form
  223.    
  224.     if[1b in i;Lns@:i:where not i;Fn@:i]; / Remove affected lines
  225.     Mode::count[Lns]>p:Lns binr last n; / Compute new line index and adjust line mode
  226.     p
  227.     }
  228.  
  229.  
  230. //
  231. // @desc Displays one or more lines, or edits a line.
  232. //
  233. // @param s {string}    The input line command, starting with the leading bracket.
  234. //                      Valid formats are `[$]`, `[$n]`, `[n$]`, and `[n$m]`.
  235. //
  236. // @return {long}       The new line index if the operation is successful, or `-1`
  237. //                      if an error occurred.
  238. //
  239. edt:{[s]
  240.     if[1b in" "<>(1+count i:(s?"]")#s)_s:ltrim 1_s;:-1]; / Must be no line residual
  241.    
  242.     if["$"=first i;$[0=count ltrim 1_i;[Mode::0b;:dl()]; / Display entire function
  243.         [if[-1=n:getn 1_i;:-1];Mode::0b;:dl p,count[Lns]-p:Lns binr n]]]; / Display from specified line to end
  244.        
  245.     if[-1=n:getn(j:i?"$")#i;:-1]; / Extract line number
  246.     p:Lns binr n; / Where this line is (or goes)
  247.     if[0=count ltrim(j+1)_i;:$[Mode::n=Lns p;dl p,1;[Ln::n;p]]]; / Display specified line
  248.    
  249.     if[0>m:"J"$(j+1)_i;:-1]; / Reject illegal or negative position values
  250.     $[Mode::n=Lns p;upd edln[fmtn[n],-1_Fn p;m];[Ln::n;p]] / Edit specified line (note line number may be modified as well)
  251.     }
  252.  
  253.  
  254. //
  255. // @desc Sets or updates a line.
  256. //
  257. // @param s {string}    The input line command, starting with the leading bracket.
  258. //                      Format is `[n]` optionally followed by line text.
  259. //
  260. // @return {long}       The new line index if the operation is successful, or `-1`
  261. //                      if an error occurred.
  262. //
  263. upd:{[s]
  264.     if[-1=n:getn(j:s?"]")#s:ltrim 1_s;:-1]; / Extract line number
  265.     s:ltrim(j+1)_s; / Text to insert
  266.     Mode::n=Lns p:Lns binr n; / Where this line is (or goes)
  267.    
  268.     if[i:count s;$[Mode;[Fn[p]:s,"\n";p+:1]; / Update existing line
  269.         [Lns::(p#Lns),n,p _Lns;Fn::(p#Fn),enl[s,"\n"],p _Fn]]]; / Insert new line
  270.        
  271.     if[not Mode+i;Ln::n]; / Retain user line number if insert mode with no change
  272.     p
  273.     }
  274.  
  275.    
  276. //
  277. // @desc Gets a line number from an input string.
  278. //
  279. // @param s {string}    The character line number, which may be nonintegral.
  280. //
  281. // @return {long}       The line number (scaled to integer) if the operation is
  282. //                      successful, or `-1` if an error occurred.
  283. //
  284. getn:{[s]
  285.     if[0n=n:10000*"F"$s;:-1]; / Interpret and scale number
  286.     if[(n<0)|0.0001<n-(_)0.1+n;:-1]; / Disallow negatives and excessive fractions
  287.     "j"$n
  288.     }
  289.  
  290.    
  291. //
  292. // @desc Gets one or more line numbers from an input string.
  293. //
  294. // @param s {string}    The character line numbers, which may be nonintegral.
  295. //
  296. // @return {long}       The line numbers (scaled to integer) if the operation is
  297. //                      successful, or `-1` if an error occurred.
  298. //
  299. getnv:{[s]
  300.     if[0n in n:10000*"F"$" "vs sqz s;:-1]; / Interpret and scale numbers
  301.     if[1b in(n<=0)|0.0001<n-(_)0.1+n;:-1]; / Disallow negatives and line 0, and excessive fractions
  302.     "j"$n
  303.     }
  304.  
  305.  
  306. //
  307. // @desc Emulates APL-style superedit line editing, subject to limitations of q console.
  308. //
  309. // A displayed line can be modified using the following control characters:
  310. //
  311. //      /       Characters above slashes are deleted
  312. //      .       First dot to the right of possible slashes inserts subsequent text
  313. //      ,       As per dot, but process repeats until dot is used or input is empty
  314. //
  315. // @param x {string}    Specifies the line to edit.
  316. // @param n {long}      Specifies the horizontal position of the cursor after initial
  317. //                      line display (origin 1).  If n is `0`, the cursor is positioned
  318. //                      at the end of the displayed line and typed input can be directly
  319. //                      appended.
  320. //
  321. // @return {string}     The modified line.
  322. //
  323. edln:{[s;n]
  324.     while[1b;
  325.         if[n;-2@[s;where s="\t";:;" "];2 pr:(count[s]&n-1)#" ";n:count a:read0 0]; / If nonzero position, display line, space over, and acquire input
  326.         if[not n;2@[s;where s="\t";:;" "];:s,read0 0]; / If final display, return input appended to line -- too bad user can't backspace over prompt
  327.         j:count[s]&i:(&/)(a:exptb[pr,a;TABS])?".,"; / Pre-insertion control chars
  328.         s:((j#s)where not k:"/"=j#a),((i+1)_a),j _s; / Delete selected chars and insert new
  329.         if["."=a i;:s]; / Quit if dot
  330.         n:$[","<>a i;0;count[a]-(+/)k]]; / Position to right of insertion, or at end of line
  331.     }
  332.  
  333.  
  334. //
  335. // @desc Expands tabs in a character string.
  336. //
  337. // @param s {string}    Specifies the string to process.
  338. // @param t {int}       Specifies the tab spacing.
  339. //
  340. // @return {string}     The input string with tabs replaced by spaces.
  341. //
  342. exptb:{[s;t]
  343.     if[not 1b in i:s="\t";:s]; / Quick exit if no tabs
  344.     j:-1+-1-':k:where i; / Lengths of segments between tabs
  345.     p:0|t-j mod t; / Number of blanks to replace tabs
  346.     @[s;k;:;" "]where 1+@["j"$i;k;:;p-1] / Replace tabs with blanks and replicate to fill
  347.     }
  348.  
  349.  
  350. //
  351. // @desc Defines a function within its associated namespace.
  352. //
  353. // @param nm {string}   Specifies the fully-qualified name of the function to define.
  354. // @param c {symbol}    Specifies the context in which to define the function.
  355. // @param f {string}    Specifies the definition of the function.
  356. //
  357. // @return {string}     The name of the object defined if the operation is successful,
  358. //                      or an empty string if an error occurred.
  359. //
  360. def:{[nm;c;f]
  361.     d:system "d";system "d .",string c; / Set namespace
  362.     if[b:(nm~"")|" "in nm:sqz nm;f:"name"]; / Prepare to reject illegal names
  363.     r:@[$[b;{'x};string(`$nm)set value@];f;{-2 "Unable to define: ",x," error";""}]; / Define object (reification performed by <value> must be under proper ns)
  364.     system "d ",string d; / Restore previous namespace
  365.     r
  366.     }
  367.  
  368.  
  369. //
  370. // @desc Removes leading, trailing, and multiple internal blanks from a string.
  371. //
  372. // @param x {string}    Specifies the string to be trimmed.
  373. //
  374. // @return {string}     A character string with extraneous blanks removed.
  375. //
  376. sqz:{-1_x where i|-1_0b,i:" "<>x," "}
  377.  
  378.  
  379. \d .
  380.  
  381. ed:.ed.ed
  382. qed:.ed.qed
  383.  
  384. \
  385.  
  386. <ed> and <qed> edit new or existing functions in the Q workspace.  The function
  387. name can be specified either as a symbol or a string, and may have an optional
  388. namespace prefix.
  389.  
  390. By default, an existing function is saved back to its namespace with the same
  391. prevailing context in which it was previously defined.  A new function is saved
  392. to the specified namespace (or the active one if the name is unqualified) using
  393. the context in effect when the editor was invoked.  This behavio[u]r can be
  394. overridden by prefixing the function name with colon (:) in the editor argument.
  395. This causes the namespace specified in the argument to be used to define both
  396. the function name and the context.
  397.  
  398. If the name of the function is changed during the editing session, the new
  399. function is saved (possibly replacing an earlier definition of the same name)
  400. and the original version is untouched.  If the namespace is changed, the new
  401. namespace is also taken to be the new context.
  402.  
  403. <ed> usage:
  404.  
  405. Define the variable <.ed.ED> to refer to the external editor of choice.
  406.  
  407. To keep changes made in the external editor, save the function in the editor and
  408. exit back to Q.  If <ed> cannot define the function in Q, enter any key when
  409. prompted to return to the editor (with the changes intact), or "\q" to discard
  410. the changes.
  411.  
  412. <qed> usage:
  413.  
  414. <qed> starts by displaying all lines of the function and prompting in insert
  415. mode at a line before the closing "}".  If the function is defined on a single
  416. line, <qed> prompts after that line.
  417.  
  418. Valid <qed> commands are as follows:
  419.  
  420.     [n]             Move to specified line; prepare to insert line if new
  421.     [n] text        Define (or redefine) specified line
  422.  
  423.     [~n1 n2 n3]     Delete specified line or lines
  424.     [n~m]           Delete from line <n> to <m> inclusive
  425.  
  426.     [$]             Display all lines
  427.     [$n]            Display from specified line to end
  428.     [n$]            Display specified line
  429.  
  430.     [n$m]           Edit line <n>, setting cursor to position <m> (see below)
  431.  
  432.     \w              Save function and exit; stay in editor on failure
  433.     \q              Quit (discard changes)
  434.  
  435. Lines in a <qed> session are initially numbered consecutively starting at [0].
  436. Line numbers can be fractional, and <qed> attempts to make it difficult to
  437. overwrite an existing line when in insert mode by choosing line numbers that
  438. do not already exist.
  439.  
  440. When editing a line, the following commands are supported:
  441.  
  442.     ,xxx            Insert text (including any trailing spaces) before character
  443.                     above ","; redisplay line and remain in edit mode
  444.     .xxx            Insert text (including any trailing spaces) before character
  445.                     above "."; move to next line
  446.     / //            Delete characters above slashes; can be combined with
  447.                     trailing ",xxx" or ".xxx"
  448.  
  449. In edit mode, invalid characters are ignored.  Because backspacing over a prompt
  450. is not permitted in Q, the cursor position <m> specified by [n$m] should be at
  451. or before the position of interest; space or tab over to get to the desired
  452. character position.  As a special case, if <m> is 0 the cursor is positioned at
  453. the end of the line and the edit phase is bypassed.
  454.  
  455. Any part of a line can be edited, including the line number itself provided that
  456. the cursor position is suitably to the left.  If the line number is changed, the
  457. old line remains and the new line either replaces an existing line or is
  458. inserted into the appropriate position in the function.  This provides a simple
  459. way to duplicate a line.
  460.  
  461. The function name can also be changed.  If it is, the new function replaces any
  462. existing function of the same name and the original version is left unchanged.
  463.  
  464.  
  465. Globals (ed):
  466.  
  467. .ed.ED   - External editor command line prefix (name to edit is appended); assign to change
  468.  
  469. Globals (qed):
  470.  
  471. .ed.Fn   - Function lines
  472. .ed.Lns  - Line numbers, scaled by 10000
  473. .ed.Cur  - Index in <Lns> of current line
  474. .ed.Ln   - User-specified explicit line number, or -1
  475. .ed.Mode - Mode flag (0b = input, 1b = edit)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement