|
40 | 40 | :workspace-defaults '(:index-origin 1 :print-precision 10 :division-method 0
|
41 | 41 | :comparison-tolerance double-float-epsilon
|
42 | 42 | :rngs (list :generators :rng (aref *rng-names* 1)))
|
43 |
| - :variables *system-variables* :closure-wrapping "()" :function-wrapping "{}" |
44 |
| - :axis-wrapping "[]") |
| 43 | + :variables *system-variables* :string-delimiters "'\"" :comment-delimiters "⍝" |
| 44 | + :closure-wrapping "()" :function-wrapping "{}" :axis-wrapping "[]" |
| 45 | + :negative-signs "¯" :number-spacers "_" |
| 46 | + :axis-separators ";;" :path-separators "..") |
45 | 47 |
|
46 | 48 | ;; parameters for describing and documenting the idiom in different ways; currently, these options give
|
47 | 49 | ;; the order in which output from the blocks of tests is printed out for the (test) and (demo) options
|
|
64 | 66 | :match-newline-character (lambda (char) (member char '(#\⋄ #\◊ #\Newline #\Return) :test #'char=))
|
65 | 67 | ;; set the language's valid blank, newline characters and token characters
|
66 | 68 | :match-numeric-character
|
67 |
| - (lambda (char) (or (digit-char-p char) (position char ".._¯eEjJrR"))) |
| 69 | + (lambda (char) (or (digit-char-p char) (position char ".._¯eEjJrR" :test #'char=))) |
68 | 70 | :match-token-character
|
69 | 71 | (lambda (char) (or (is-alphanumeric char)
|
70 |
| - (position char ".._⎕∆⍙¯"))) |
| 72 | + (position char ".._⎕∆⍙¯" :test #'char=))) |
71 | 73 | ;; match characters that can only appear in homogenous symbols, this is needed so that
|
72 | 74 | ;; things like ⍺⍺.⍵⍵, ⍺∇⍵ or ⎕NS⍬ can work without spaces between the symbols
|
73 |
| - :match-uniform-token-character (lambda (char) (position char "⍺⍵⍶⍹∇⍬")) |
| 75 | + :match-uniform-token-character (lambda (char) (position char "⍺⍵⍶⍹∇⍬" :test #'char=)) |
74 | 76 | ;; match characters specifically representing function/operator arguments, this is needed
|
75 | 77 | ;; so ⍵.path.to will work
|
76 |
| - :match-arg-token-character (lambda (char) (position char "⍺⍵⍶⍹")) |
| 78 | + :match-arg-token-character (lambda (char) (position char "⍺⍵⍶⍹" :test #'char=)) |
77 | 79 | ;; match characters used to link parts of paths together like namespace.path.to,
|
78 | 80 | ;; this is needed so that ⍵.path.to will work
|
79 |
| - :match-path-joining-character (lambda (char) (position char "..")) |
| 81 | + :match-path-joining-character (lambda (idiom) |
| 82 | + (let ((chars (of-system idiom :path-separators))) |
| 83 | + (lambda (char) (position char chars :test #'char=)))) |
80 | 84 | ;; overloaded numeric characters may be functions or operators or may be part of a numeric token
|
81 | 85 | ;; depending on their context
|
82 |
| - :match-overloaded-numeric-character (lambda (char) (position char "..")) |
| 86 | + :match-overloaded-numeric-character (lambda (char) (position char ".." :test #'char=)) |
83 | 87 | ;; match character(s) used to separate axes
|
84 |
| - :match-axis-separating-character (lambda (char) (position char ";;")) |
| 88 | + :match-axis-separating-character (lambda (idiom) |
| 89 | + (let ((chars (of-system idiom :axis-separators))) |
| 90 | + (lambda (char) (position char chars :test #'char=)))) |
| 91 | + |
| 92 | + ;; generate the string of matched closing and opening characters that wrap code sections; |
| 93 | + ;; used to identify stray closing characters such as ) without a corresponding ( |
| 94 | + :collect-delimiters |
| 95 | + (lambda (idiom) |
| 96 | + (let ((output) (cw (of-system idiom :closure-wrapping)) |
| 97 | + (fw (of-system idiom :function-wrapping)) (aw (of-system idiom :axis-wrapping))) |
| 98 | + (loop :for i :from (/ (length cw) 2) :to (1- (length cw)) :do (push (aref cw i) output)) |
| 99 | + (loop :for i :from (/ (length fw) 2) :to (1- (length fw)) :do (push (aref fw i) output)) |
| 100 | + (loop :for i :from (/ (length aw) 2) :to (1- (length aw)) :do (push (aref aw i) output)) |
| 101 | + (loop :for i :below (/ (length cw) 2) :do (push (aref cw i) output)) |
| 102 | + (loop :for i :below (/ (length fw) 2) :do (push (aref fw i) output)) |
| 103 | + (loop :for i :below (/ (length aw) 2) :do (push (aref aw i) output)) |
| 104 | + (reverse (coerce output 'string)))) |
85 | 105 | ;; this code preprocessor removes comments, starting with each ⍝ and ending before the next newline
|
86 | 106 | :prep-code-string
|
87 |
| - (lambda (string) |
88 |
| - (let ((commented) (osindex 0) (comment-char #\⍝) |
89 |
| - (out-string (make-string (length string) :initial-element #\ ))) |
90 |
| - (loop :for char :across string |
91 |
| - :do (if commented (when (member char '(#\Newline #\Return) :test #'char=) |
92 |
| - (setf commented nil |
93 |
| - (row-major-aref out-string osindex) char |
94 |
| - osindex (1+ osindex))) |
95 |
| - (if (char= char comment-char) (setf commented t) |
96 |
| - (setf (row-major-aref out-string osindex) char |
97 |
| - osindex (1+ osindex))))) |
98 |
| - ;; return displaced string to save time processing blanks |
99 |
| - (make-array osindex :element-type 'character :displaced-to out-string))) |
| 107 | + (lambda (idiom) |
| 108 | + (let ((comment-delimiters (of-system idiom :comment-delimiters))) |
| 109 | + (lambda (string) |
| 110 | + (let ((commented) (osindex 0) |
| 111 | + (out-string (make-string (length string) :initial-element #\ ))) |
| 112 | + (loop :for char :across string |
| 113 | + :do (if commented (when (member char '(#\Newline #\Return) :test #'char=) |
| 114 | + (setf commented nil |
| 115 | + (row-major-aref out-string osindex) char |
| 116 | + osindex (1+ osindex))) |
| 117 | + (if (position char comment-delimiters :test #'char=) |
| 118 | + (setf commented t) |
| 119 | + (setf (row-major-aref out-string osindex) char |
| 120 | + osindex (1+ osindex))))) |
| 121 | + ;; return displaced string to save time processing blanks |
| 122 | + (make-array osindex :element-type 'character :displaced-to out-string))))) |
100 | 123 | ;; handles axis strings like "'2;3;;' from 'array[2;3;;]'"
|
101 | 124 | :process-axis-string
|
102 |
| - (lambda (string) |
103 |
| - (let ((indices) (last-index) (quoted) |
104 |
| - (nesting (vector 0 0 0)) |
105 |
| - (delimiters "[({])}") |
106 |
| - (dllen-plus 7) ;; 1 plus the number of delimiters |
107 |
| - (quote-delimiter #\')) |
108 |
| - (loop :for char :across string :counting char :into charix |
109 |
| - :do (let ((mx (or (loop :for d :across delimiters :counting d :into dx |
110 |
| - :when (char= d char) :do (return (- dllen-plus dx))) |
111 |
| - 0))) |
112 |
| - (if (char= char quote-delimiter) |
113 |
| - (setf quoted (not quoted)) |
114 |
| - (unless quoted |
115 |
| - (if (< 3 mx) (incf (aref nesting (- 6 mx))) |
116 |
| - (if (< 0 mx 4) |
117 |
| - (if (< 0 (aref nesting (- 3 mx))) |
118 |
| - (decf (aref nesting (- 3 mx))) |
119 |
| - (error "Each closing ~a must match with an opening ~a." |
120 |
| - (aref delimiters mx) (aref delimiters (- 3 mx)))) |
121 |
| - (when (and (char= char #\;) |
122 |
| - (zerop (loop :for ncount :across nesting |
123 |
| - :summing ncount))) |
124 |
| - (setq indices (cons (1- charix) indices))))))))) |
125 |
| - (loop :for index :in (reverse (cons (length string) indices)) |
126 |
| - :counting index :into iix |
127 |
| - :collect (make-array (- index (if last-index 1 0) |
128 |
| - (or last-index 0)) |
129 |
| - :element-type 'character :displaced-to string |
130 |
| - :displaced-index-offset (if last-index (1+ last-index) 0)) |
131 |
| - :do (setq last-index index)))) |
| 125 | + (let ((delimiters) (axis-separators) (full-len) (half-len) (nesting (vector 0 0 0))) |
| 126 | + (lambda (idiom) |
| 127 | + (unless delimiters |
| 128 | + (setf delimiters (reverse (funcall (of-utilities idiom :collect-delimiters) idiom)) |
| 129 | + full-len (length delimiters) |
| 130 | + half-len (/ full-len 2) |
| 131 | + axis-separators (of-system idiom :axis-separators))) |
| 132 | + (lambda (string) |
| 133 | + (let ((indices) (last-index) (quoted)) |
| 134 | + (loop :for i :below (length nesting) :do (setf (aref nesting i) 0)) |
| 135 | + (loop :for char :across string :counting char :into charix |
| 136 | + :do (let ((mx (or (loop :for d :across delimiters :counting d :into dx |
| 137 | + :when (char= d char) :do (return (- full-len -1 dx))) |
| 138 | + 0))) |
| 139 | + (if (position char (of-system idiom :string-delimiters) :test #'char=) |
| 140 | + (setf quoted (not quoted)) |
| 141 | + (unless quoted |
| 142 | + (if (< half-len mx) (incf (aref nesting (- full-len mx))) |
| 143 | + (if (<= 1 mx half-len) |
| 144 | + (if (< 0 (aref nesting (- half-len mx))) |
| 145 | + (decf (aref nesting (- half-len mx))) |
| 146 | + (error "Each closing ~a must match with an opening ~a." |
| 147 | + (aref delimiters mx) |
| 148 | + (aref delimiters (- half-len mx)))) |
| 149 | + (when (and (position char axis-separators :test #'char=) |
| 150 | + (zerop (loop :for ncount :across nesting |
| 151 | + :summing ncount))) |
| 152 | + (setq indices (cons (1- charix) indices))))))))) |
| 153 | + (loop :for index :in (reverse (cons (length string) indices)) |
| 154 | + :counting index :into iix |
| 155 | + :collect (make-array (- index (if last-index 1 0) |
| 156 | + (or last-index 0)) |
| 157 | + :element-type 'character :displaced-to string |
| 158 | + :displaced-index-offset (if last-index (1+ last-index) 0)) |
| 159 | + :do (setq last-index index)))))) |
132 | 160 | ;; macro to process lexical specs of functions and operators
|
133 | 161 | :process-fn-op-specs #'process-fnspecs
|
134 | 162 | :test-parameters '((:space unit-test-staging))
|
135 |
| - :format-number #'parse-apl-number-string |
| 163 | + :build-number-formatter #'generate-apl-number-string-parser |
136 | 164 | :format-value #'format-value
|
137 | 165 | ;; process system state input passed as with (april (with (:state ...)) "...")
|
138 | 166 | :preprocess-state-input
|
|
273 | 301 | (is "÷2 4 8" #(1/2 1/4 1/8))
|
274 | 302 | (is "{⎕div←0 ⋄ ÷⍨⍵} 0" 1)
|
275 | 303 | (is "{⎕div←1 ⋄ ÷⍨⍵} 0" 0)
|
276 |
| - (is "{⎕div←1 ⋄ ÷⍵} 0" 0))) |
| 304 | + (is "{⎕div←1 ⋄ ÷ ⍵} 0" 0))) |
277 | 305 | (⋆ (has :titles ("Exponential" "Power") :aliases (*))
|
278 | 306 | (ambivalent (scalar-function apl-exp)
|
279 | 307 | (scalar-function (reverse-op :dyadic apl-expt)))
|
|
0 commit comments