├── README.md ├── Shell.h └── examples ├── ShellAnalogPins └── ShellAnalogPins.ino ├── ShellBenchmarks └── ShellBenchmarks.ino ├── ShellBlink └── ShellBlink.ino ├── ShellBlinkFrame └── ShellBlinkFrame.ino ├── ShellDemo └── ShellDemo.ino ├── ShellMultiBlink └── ShellMultiBlink.ino ├── ShellScript └── ShellScript.ino └── ShellTrap └── ShellTrap.ino /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-Shell 2 | 3 | This library provides a Forth/PostScript style shell for Arduino 4 | sketches. Shell is a virtual stack machine with a byte token threaded 5 | instruction set. The tokens, characters, are chosen so that it is 6 | possible to write small scripts directly without a token compiler. As 7 | Forth scripts are written in Reverse Polish Notation (RPN), and 8 | executed in the given order. 9 | 10 | ![screenshot](https://dl.dropboxusercontent.com/u/993383/Cosa/screenshots/Screenshot%20from%202016-02-26%2015%3A15%3A48.png) 11 | 12 | Shell has built-in instruction level trace to aid script debugging and 13 | performance tuning. It prints the instruction cycle count, script 14 | address, opcode (or full operation name), stack depth and 15 | contents. Typical output in the Serial Monitor above. 16 | 17 | Shell can be configured to trace full operation names or tokens. 18 | 19 | The classical Arduino Blink sketch in the shell script language is 20 | ``` 21 | 13O{13H1000D13L1000DT}w 22 | ``` 23 | And with some extra spacing to make the operations easier to read. 24 | ``` 25 | 13 O { 13 H 1000 D 13 L 1000 D T } w 26 | ``` 27 | And with full instruction names and some formatting: 28 | ``` 29 | 13 output 30 | { 31 | 13 high 1000 delay 32 | 13 low 1000 delay 33 | true 34 | } while 35 | ``` 36 | A further compressed version (shorter): 37 | ``` 38 | 13 output 39 | { 40 | 1000 13 over over high delay 41 | low delay 42 | true 43 | } while 44 | 45 | 13O{1000,13ooHDLDT}w 46 | ``` 47 | And a slightly faster version: 48 | ``` 49 | 1000 13 dup output 50 | { 51 | over over high delay 52 | over over low delay 53 | true 54 | } while 55 | 56 | 1000,13uO{ooHDooLDT}w 57 | ``` 58 | And an even shorter version: 59 | ``` 60 | 13 output 61 | { 62 | 13 digitalToggle 1000 delay 63 | true 64 | } while 65 | 66 | 13O{13X1000DT}w 67 | ``` 68 | Scripts can be defined and executed from both Random Access Memory 69 | (SRAM), Electrically Erasable Programmable Read-Only Memory (EEPROM), 70 | and Program Memory (PROGMEM). The supporting data type `script_t` and 71 | macro `SCRIPT` can be used to define an application dictionary with 72 | program memory based scripts. All named scripts are persistent. 73 | Variables may be written to EEPROM with the operation `z`. They are 74 | automatically restored at startup together with defined scripts. 75 | 76 | ## Install 77 | 78 | Download and unzip the Arduino-Shell library into your sketchbook 79 | libraries directory. Rename from Arduino-Shell-master to Arduino-Shell. 80 | 81 | The Shell library and examples should be found in the Arduino IDE 82 | File>Examples menu. 83 | 84 | ## Instruction Set 85 | 86 | Opcode | Parameters | Description | Forth 87 | -------|:-----------|:------------|:----- 88 | , | -- | no operation | 89 | + | x y -- x+y | addition | + 90 | - | x y -- x-y | subtraction | - 91 | * | x y -- x*y | multiplication | * 92 | / | x y -- x/y | division | / 93 | % | x y -- x%y | modulo | MOD 94 | # | x y -- x!=y | not equal | 95 | = | x y -- x==y | equal | = 96 | < | x y -- xy | greater than | > 98 | ~ | x -- ~x | bitwise not | NOT 99 | & | x y -- x&y | bitwise and | AND 100 | | | x y -- x|y | bitwise or | OR 101 | ^ | x y -- x^y | bitwise xor | XOR 102 | @ | addr -- val | read variable | @ 103 | ! | val addr -- | write variable | ! 104 | . | x -- | print number followed by one space | . 105 | ? | addr -- | print variable | ? 106 | ; | addr block -- | copy block and assign variable (function) | 107 | \ | x1..xn n -- x1..xn | n > 0: mark stack frame with n-elements | 108 | \ | x1..xn y1..ym n -- y1..ym | n < 0: remove stack frame with n-elements | 109 | $ | n -- addr | address of n-element in frame | 110 | a | -- bytes entries | allocated eeprom | 111 | b | base -- | number print base | BASE 112 | c | xn..x1 n -- | drop n stack elements | 113 | d | x -- | drop | DROP 114 | e | flag if-block else-block -- | execute block on flag | IF ELSE THEN 115 | f | addr -- | forget variable | 116 | g | xn..x1 n -- xn-1..x1 xn | rotate n-elements | ROLL 117 | h | x y z -- (x*y)/z | scale | */ 118 | i | flag block -- | execute block if flag is true | IF THEN 119 | j | xn..x1 -- xn..x1 n | stack depth | DEPTH 120 | k | -- char | read character from input stream | KEY 121 | l | low high block( index -- ) -- | execute block from low to high | DO LOOP 122 | m | -- | write new line to output stream | CR 123 | n | x -- -x | negate | NEGATE 124 | o | x y -- x y x | over | OVER 125 | p | xn..x1 n -- xn..x1 xn | pick | PICK 126 | q | x -- [x x] or 0 | duplicate if not zero | ?DUP 127 | r | x y z --- y z x | rotate | ROT 128 | s | x y -- y x | swap | SWAP 129 | t | addr -- bool | write variable name to output stream | .NAME 130 | u | x -- x x | duplicate | DUP 131 | v | char -- | write character to output stream | EMIT 132 | w | block( -- flag) -- | execute block while flag is true | BEGIN UNTIL 133 | x | block -- | execute block | EXECUTE 134 | y | -- | yield for multi-tasking scheduler | 135 | z | addr -- | write variable to eeprom memory | 136 | A | pin -- sample | analogRead(pin) | 137 | C | xn..x1 -- | clear | ABORT 138 | D | ms -- | delay | 139 | E | period addr -- bool | check if timer variable has expired | 140 | F | -- false | false | FALSE 141 | H | pin -- | digitalWrite(pin, HIGH) | 142 | I | pin -- | pinMode(pin, INPUT) | 143 | K | -- [char true] or false | non-blocking read character from input stream | ?KEY 144 | L | pin -- | digitalWrite(pin, LOW) | 145 | M | -- ms | millis() | 146 | N | -- | no operation | 147 | O | pin -- | pinMode(pin, OUTPUT) | 148 | P | value pin -- | analogWrite(pin, value) | 149 | R | pin -- bool | digitalRead(pin) | 150 | S | -- | print stack contents | .S 151 | T | -- true | true | TRUE 152 | U | pin -- | pinMode(pin, INPUT_PULLUP) | 153 | W | value pin -- | digitalWrite(pin, value) | 154 | X | pin -- | digitalToggle(pin) | 155 | Y | -- | list dictionaries | WORDS 156 | Z | -- | toggle trace mode | 157 | 158 | ## Special forms 159 | 160 | The shell script language allows several special forms such as literal 161 | values, blocks, control structures and argument frames. 162 | 163 | ### Boolean 164 | 165 | Boolean values are true(-1) and false(0). Mapping to boolean may be 166 | done with testing non-zero (0#). Defining true(-1) allows Boolean 167 | values and relation operations to be directly combined with bitwise 168 | operations. 169 | ``` 170 | 10 0 # . m 171 | T F ~ = . m 172 | ``` 173 | The instructions to push the boolean values are _T_ and _F_. 174 | 175 | ### Literal Numbers 176 | 177 | Integer numbers (decimal, binary and hexadecimal) may be used directly 178 | in scripts. When the script is executed the value of the number is 179 | pushed on the parameter stack. The statement: 180 | ``` 181 | println((3 + (-5)) * 6); 182 | ``` 183 | may be written as the following script expression: 184 | ``` 185 | 3 -5 + 6 * . m 186 | ``` 187 | and compressed to: 188 | ``` 189 | 3,-5+6*.m 190 | ``` 191 | Binary literal numbers are prefixed with `0b`, and hexadecimal with 192 | `0x` as in C. 193 | ``` 194 | 10 . 0b10 . 0x10 . 195 | ``` 196 | 197 | ### Literal Characters 198 | 199 | Quote (apostrophe) a character to push it on the parameter stack. 200 | ``` 201 | 'A . 202 | ``` 203 | 204 | ### Variables 205 | 206 | Variables are defined with `:name`. The operator will push the 207 | address of the variable on the parameter stack. It may be accessed 208 | using the operators fetch `@` and store `!`. 209 | ``` 210 | 42:x! 211 | :x@ 212 | ``` 213 | The operator `?` can be used to print the value of a variable. 214 | ``` 215 | :x? 216 | ``` 217 | It is short hand for: 218 | ``` 219 | :x@. 220 | ``` 221 | 222 | ### Blocks 223 | 224 | Code blocks have the following form `{ code-block }`. When the script 225 | is executed the address of the block is pushed on the parameter 226 | stack. The block can be executed with the instruction _x_. 227 | ``` 228 | { code-block } x 229 | ``` 230 | The operation `;` will copy a block to the eeprom and assign a 231 | variable. Used in the form: 232 | ``` 233 | :fun { code-block }; 234 | :fun@x 235 | ``` 236 | The short hand for executing a function is `` `name ``. 237 | ``` 238 | `fun 239 | ``` 240 | The instruction _f_ may be used to forget variable(s). The eeprom 241 | allocation point is reset accordingly. 242 | ``` 243 | :fun,f 244 | ``` 245 | 246 | ### Control Structures 247 | 248 | Control structures follow the same format at PostScript. They are also 249 | in Reverse Polish Notation (RPN). The blocks are pushed on the 250 | parameter stack before the control structure instruction. Below are 251 | the control structures with full instruction names. 252 | ``` 253 | bool { if-block } if 254 | bool { if-block } { else-block } ifelse 255 | 256 | low high { index loop-block } loop 257 | 258 | { while-block bool } while 259 | ``` 260 | The `loop-block` will recieve the current index on top of parameter 261 | stack. The `while-block` should push a non-zero value to continue the 262 | while loop otherwise zero (false) to terminate the loop. 263 | 264 | The instructions are _i_ for `if`, _e_ for `ifelse`, _l_ for `loop` 265 | and _w_ for `while`. 266 | 267 | ### Output Strings 268 | 269 | Output strings have the following form `( output-string )`. When executed the 270 | string within the parenthesis is written to the output stream. The 271 | instruction _m_ will print a new-line (corresponds to forth cr). 272 | 273 | ### Stack Marker 274 | 275 | A stack marker has the following form `[ code-block ]`. When executed the 276 | number of stack elements generated by the code block is pushed on the 277 | parameter stack. 278 | 279 | ### Frame Marker 280 | 281 | A frame marker has the following form `n\ ... -n\` where _n_ is the 282 | number of elements (parameters and locals) in the frame. Positive _n_ 283 | marks the frame and negative _n_ removes the frame stack elements 284 | leaving any return values. Elements within the frame can be accessed 285 | with `m$` where _m_ is the element index (1..n). The element address 286 | is pushed on the parameter stack and the value may be accessed with 287 | fetch `@` and store `!`. 288 | 289 | Swap could be defined as: 290 | ``` 291 | :swap {2\2$@1$@-2\}; 292 | ``` 293 | which will mark a frame with two arguments, copy the second and then 294 | the first argument, and last remove the frame, leaving the two return 295 | values. 296 | 297 | ### Extended Instructions 298 | 299 | Shell allows application extension with a virtual member function, 300 | `trap()`. The function is called when the current instruction could not 301 | be handled. The `trap()` function may parse any number of 302 | instructions. Underscore `_` is used as the escape operation code. 303 | 304 | ## Example Scripts 305 | 306 | ### Blink 307 | 308 | Turn board LED, pin 13, on/off with 1000 ms period. 309 | ``` 310 | 13 output 311 | { 312 | 13 high 1000 delay 313 | 13 low 1000 delay 314 | true 315 | } while 316 | ``` 317 | Script: 318 | ``` 319 | 13O{13H1000D13L1000DT}w 320 | ``` 321 | 322 | ### Blink with digitalToggle 323 | 324 | Toggle board LED, pin 13, on/off with 1000 ms period. 325 | ``` 326 | 13 output 327 | { 328 | 13 digitalToggle 1000 delay 329 | true 330 | } while 331 | ``` 332 | Script: 333 | ``` 334 | 13O{13X1000DT}w 335 | ``` 336 | 337 | ### Blink without delay 338 | 339 | Turn board LED, pin 13, on/off without using delay. Use timer expired 340 | instruction. 341 | ``` 342 | 13 output 343 | { 344 | 1000 `timer ?expired 345 | { 13 digitalToggle } if 346 | true 347 | } while 348 | ``` 349 | Script: 350 | ``` 351 | 13O{1000:timer,E{13X}iT}w 352 | ``` 353 | 354 | ### Blink controlled by on/off button 355 | 356 | Turn board LED, pin 13, on/off with 1000 ms period if pin 2 is low. 357 | ``` 358 | 2 inputPullup 359 | 13 output 360 | { 361 | 2 digitalRead not 362 | { 363 | 13 high 1000 delay 364 | 13 low 1000 delay 365 | } if 366 | true 367 | } while 368 | ``` 369 | Script: 370 | ``` 371 | 2U13O{2R~{13H1000D13L1000D}iT}w 372 | ``` 373 | 374 | ### Read Analog Pins 375 | 376 | Read analog pins and print value in format "An = value". 377 | ``` 378 | 0 4 { ." A" dup . ." =" analogRead . cr } loop 379 | ``` 380 | Script: 381 | ``` 382 | 0,4{(A)u.(= )A.m}l 383 | ``` 384 | ### Continously Read Analog Pins 385 | 386 | Read analog pins (0..4) and print value continuously with 100 ms delay. 387 | ``` 388 | { 389 | 0 4 { analogRead . } loop 390 | cr 100 delay true 391 | } while 392 | ``` 393 | Script: 394 | ``` 395 | {0,4{A.}lm100DT}w 396 | ``` 397 | 398 | ### Termostat 399 | 400 | Read analog pin 0, turn board LED on if value is within 100..200 else off. 401 | ``` 402 | 13 output 403 | { 404 | 0 analogRead 405 | dup 100 < swap 200 > or not 406 | 13 digitalWrite 407 | true 408 | } while 409 | ``` 410 | Script: 411 | ``` 412 | 13O{0Au100|~13WT}w 413 | ``` 414 | 415 | ### Range check function 416 | 417 | Check that a given parameter is within a range low to high. 418 | ``` 419 | : within ( x low high -- bool ) 420 | rot swap over swap > swap rot < or not ; 421 | 422 | 10 5 100 within . 423 | -10 5 100 within . 424 | 110 5 100 within . 425 | ``` 426 | Script: 427 | ``` 428 | :within{rsos>sr<|~}; 429 | 10,5,100`within. 430 | -10,5,100`within. 431 | 110,5,100`within. 432 | ``` 433 | 434 | ### Range check function with stack frame 435 | 436 | Check that a given parameter is within a range low to high. Use a 437 | stack frame for the three parameters. 438 | ``` 439 | : within { x low high -- bool } 440 | x @ high @ > x @ low @ < or not ; 441 | 442 | 10 5 100 within . 443 | -10 5 100 within . 444 | 110 5 100 within . 445 | ``` 446 | Script: 447 | ``` 448 | :within{3\1$@3$@>1$@2$@<|~-3\}; 449 | 10,5,100`within. 450 | -10,5,100`within. 451 | 110,5,100`within. 452 | ``` 453 | 454 | ### Iterative Factorial (while) 455 | 456 | Calculate factorial number of given parameter. 457 | ``` 458 | : fac ( n -- n! ) 459 | 1 swap 460 | { 461 | dup 0> 462 | { swap over * swap 1- true } 463 | { drop false } 464 | ifelse 465 | } while ; 466 | 467 | 5 fac . 468 | ``` 469 | Script: 470 | ``` 471 | :fac{1s{u0>{so*s1-T}{dF}e}w}; 472 | 5`fac. 473 | ``` 474 | 475 | ### Iterative Factorial (loop) 476 | 477 | Calculate factorial number of given parameter. 478 | ``` 479 | : fac ( n -- n! | n > 0 ) 480 | 1 2 rot { * } loop ; 481 | 482 | 5 fac . 483 | ``` 484 | Script: 485 | ``` 486 | :fac{1,2r{*}l}; 487 | 5`fac. 488 | ``` 489 | 490 | ### Recursive Factorial 491 | 492 | Calculate factorial number of given parameter. 493 | ``` 494 | : fac ( n -- n! ) 495 | dup 0> 496 | { dup 1- fac * } 497 | { drop 1 } 498 | ifelse ; 499 | 500 | 5 fac . 501 | ``` 502 | Script: 503 | ``` 504 | :fac{u0>{u1-`fac*}{d1}e}; 505 | 5`fac. 506 | ``` 507 | 508 | ### Stack vector sum 509 | 510 | Sum a vector of integers on stack. Use that stack marker to get number 511 | of elements in vector. 512 | ``` 513 | [ 1 2 3 4 5 6 7 8 ] 0 1 rot { drop + } loop 514 | ``` 515 | Script: 516 | ``` 517 | [1,2,3,4,5,6,7,8]0,1r{d+}l 518 | ``` 519 | 520 | -------------------------------------------------------------------------------- /Shell.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Shell.h 3 | * @version 2.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | */ 18 | 19 | #ifndef SHELL_H 20 | #define SHELL_H 21 | 22 | /** 23 | * Application script dictionary entry (in program memory). 24 | */ 25 | struct script_t { 26 | const char* name; //!< Script name (in program memory). 27 | const char* code; //!< Script code (in program memory). 28 | }; 29 | 30 | /** 31 | * Create script name and script strings in program memory. 32 | * @param[in] name identifier. 33 | * @param[in] script string. 34 | */ 35 | #define SCRIPT(name,code) \ 36 | const char name ## _name[] PROGMEM = #name; \ 37 | const char name ## _code[] PROGMEM = code \ 38 | 39 | /** 40 | * Create script entry in script table in program memory. 41 | * @param[in] name identifier. 42 | */ 43 | #define SCRIPT_ENTRY(name) \ 44 | { name ## _name, name ## _code } 45 | 46 | /** 47 | * Create last entry in script table in program memory. 48 | */ 49 | #define SCRIPT_NULL() { NULL, NULL } 50 | 51 | /** 52 | * Script Shell with stack machine instruction set. Instructions are 53 | * printable characters so that command lines and scripts can be 54 | * written directly. 55 | * @param[in] STACK_MAX max stack depth (default 16). 56 | * @param[in] VAR_MAX max number of variables (default 32). 57 | * @param[in] FULL_OP_NAMES trace with operation name (default true). 58 | */ 59 | template 60 | class Shell { 61 | public: 62 | /** 63 | * Construct a shell with given stream. 64 | * @param[in] ios input output stream. 65 | * @param[in] scripts null terminated vector of application scripts. 66 | */ 67 | Shell(Stream& ios, const script_t* scripts = NULL) : 68 | m_scripts(scripts), 69 | m_dp((char*) sizeof(m_dp) + sizeof(m_entries) + (sizeof(dict_t) * VAR_MAX)), 70 | m_dict((dict_t*) sizeof(m_dp) + sizeof(m_entries)), 71 | m_entries(0), 72 | m_fp(m_stack + STACK_MAX), 73 | m_sp(m_stack + STACK_MAX), 74 | m_tos(0), 75 | m_marker(-1), 76 | m_trace(false), 77 | m_cycle(0), 78 | m_base(10), 79 | m_ios(ios) 80 | { 81 | // Restore state from eeprom 82 | uint8_t entries = eeprom_read_byte((const uint8_t*) sizeof(char*)); 83 | char* dp = (char*) eeprom_read_word(0); 84 | 85 | // Check valid state before restore 86 | if (dp != (char*) 0xffff && entries < VAR_MAX) { 87 | m_entries = entries; 88 | m_dp = dp; 89 | for (uint8_t i = 0; i < entries; i++) 90 | m_var[i] = eeprom_read_word((uint16_t*) &m_dict[i].value); 91 | } 92 | } 93 | 94 | /** 95 | * Set trace to given flag. 96 | * @param[in] flag trace true/false. 97 | */ 98 | void trace(bool flag) 99 | { 100 | m_trace = flag; 101 | } 102 | 103 | /** 104 | * Get trace mode. 105 | * @return trace mode. 106 | */ 107 | bool trace() const 108 | { 109 | return (m_trace); 110 | } 111 | 112 | /** 113 | * Get depth of parameter stack. 114 | * @return number of elements on stack. 115 | */ 116 | int depth() const 117 | { 118 | return (m_stack + STACK_MAX - m_sp); 119 | } 120 | 121 | /** 122 | * Get top of stack. 123 | * @return top of stack. 124 | */ 125 | int tos() const 126 | { 127 | return (m_tos); 128 | } 129 | 130 | /** 131 | * Set top of stack to given value. 132 | * @param[in] value to set. 133 | */ 134 | void tos(int value) 135 | { 136 | m_tos = value; 137 | } 138 | 139 | /** 140 | * Set top of stack to given value. 141 | * @param[in] s to set. 142 | */ 143 | void tos(const char* s) 144 | { 145 | m_tos = (int) s; 146 | } 147 | 148 | /** 149 | * Drop top element from parameter stack. 150 | */ 151 | void drop() 152 | { 153 | m_tos = (depth() > 0) ? *m_sp++ : 0; 154 | } 155 | 156 | /** 157 | * Pop top element from parameter stack. 158 | * @return top of stack. 159 | */ 160 | int pop() 161 | { 162 | int res = m_tos; 163 | m_tos = (depth() > 0) ? *m_sp++ : 0; 164 | return (res); 165 | } 166 | 167 | /** 168 | * Push given value onto the parameter stack. 169 | * @param[in] value to push. 170 | */ 171 | void push(int value) 172 | { 173 | if (depth() != STACK_MAX) *--m_sp = m_tos; 174 | m_tos = value; 175 | } 176 | 177 | /** 178 | * Push given value onto the parameter stack. 179 | * @param[in] s string to push. 180 | */ 181 | void push(const char* s) 182 | { 183 | push((int) s); 184 | } 185 | 186 | /** 187 | * Clear parameter stack. 188 | */ 189 | void clear() 190 | { 191 | m_sp = m_stack + STACK_MAX; 192 | } 193 | 194 | /** 195 | * Print parameter stack contents in format: n: xn..x1 196 | */ 197 | void print() const 198 | { 199 | int n = depth(); 200 | m_ios.print(n); 201 | m_ios.print(':'); 202 | if (n > 0) { 203 | const int* tp = m_stack + STACK_MAX - 1; 204 | while (--n) { 205 | m_ios.print(' '); 206 | m_ios.print(*--tp); 207 | } 208 | m_ios.print(' '); 209 | m_ios.print(m_tos); 210 | } 211 | m_ios.println(); 212 | } 213 | 214 | /** 215 | * Read variable. 216 | * @param[in] addr address. 217 | * @return value. 218 | */ 219 | int read(int addr) const 220 | { 221 | if (addr >= 0 && addr < (VAR_MAX + STACK_MAX)) return (m_var[addr]); 222 | return (-pgm_read_word(&m_scripts[addr - 0x4000].code)); 223 | } 224 | 225 | /** 226 | * Write variable. 227 | * @param[in] addr address. 228 | * @param[in] value to assign. 229 | */ 230 | void write(int addr, int value) 231 | { 232 | if (addr < 0 || addr >= (VAR_MAX + STACK_MAX)) return; 233 | m_var[addr] = value; 234 | } 235 | 236 | /** 237 | * Write variable. 238 | * @param[in] addr address. 239 | * @param[in] s script. 240 | */ 241 | void write(int addr, const char* s) 242 | { 243 | write(addr, (int) s); 244 | } 245 | 246 | /** 247 | * Define a dictionary entry with given name and value. 248 | * @param[in] var name string (in program memory). 249 | * @param[in] value. 250 | * @return index or negative error code. 251 | */ 252 | int set(const __FlashStringHelper* var, int value = 0) 253 | { 254 | char name[NAME_MAX]; 255 | strcpy_P(name, (const char*) var); 256 | int i = lookup(name, strlen(name), true); 257 | if (i < 0) return (i); 258 | m_var[i] = value; 259 | return (i); 260 | } 261 | 262 | /** 263 | * Define a function with given name and script in data memory. 264 | * @param[in] var name string. 265 | * @param[in] script string in data memory. 266 | * @return index or negative error code. 267 | */ 268 | int set(const __FlashStringHelper* var, const char* script) 269 | { 270 | return (set(var, (int) script)); 271 | } 272 | 273 | /** 274 | * Define a function with given name and script in program memory. 275 | * @param[in] var name string. 276 | * @param[in] script in program memory. 277 | * @return index or negative error code. 278 | */ 279 | int set(const __FlashStringHelper* var, const __FlashStringHelper* script) 280 | { 281 | return (set(var, (int) m_progmem.as_addr((const char*) script))); 282 | } 283 | 284 | /** 285 | * List dictionaries; eeprom and progmem. 286 | */ 287 | void words() 288 | { 289 | const char* np; 290 | char c; 291 | uint8_t i = 0; 292 | 293 | // List scripts in eeprom dicionary (dynamic) 294 | m_ios.print(m_eeprom.prefix()); 295 | m_ios.print(F(": ")); 296 | for (; i < m_entries; i++) { 297 | np = (const char*) eeprom_read_word((const uint16_t*) &m_dict[i].name); 298 | while ((c = (char) eeprom_read_byte((const uint8_t*) np++)) != 0) 299 | m_ios.print(c); 300 | m_ios.print(' '); 301 | } 302 | m_ios.println(); 303 | 304 | // List scripts in program memory dicionary (static) 305 | m_ios.print(m_progmem.prefix()); 306 | m_ios.print(F(": ")); 307 | i = 0; 308 | while ((np = (const char*) pgm_read_word(&m_scripts[i].name)) != NULL) { 309 | m_ios.print((const __FlashStringHelper*) np); 310 | m_ios.print(' '); 311 | i += 1; 312 | } 313 | m_ios.println(); 314 | } 315 | 316 | /** 317 | * Non-blocking read next character from shell stream. If available 318 | * add to buffer. If newline was read the buffer is null-terminated 319 | * and true is returned, otherwise false. 320 | * @param[in,out] bp buffer pointer. 321 | * @return true if line was read otherwise false. 322 | */ 323 | bool read(char* &bp) 324 | { 325 | int c = m_ios.read(); 326 | if (c < 0) return (false); 327 | *bp++ = c; 328 | if (c != '\n') return (false); 329 | *bp = 0; 330 | m_cycle = 0; 331 | return (true); 332 | } 333 | 334 | /** 335 | * Execute given script (null terminated sequence of operation 336 | * codes). Return NULL if successful otherwise script reference that 337 | * failed. Prints error position in trace mode. 338 | * @param[in] script. 339 | * @return script reference or NULL. 340 | */ 341 | const char* execute(const char* script) 342 | { 343 | Memory* mem = access(script); 344 | next_fn next = mem->get_next_fn(); 345 | const char* ip = mem->as_local(script); 346 | bool neg = false; 347 | int base = 10; 348 | int* fp = m_fp; 349 | size_t len = 0; 350 | int w, n, addr, pin; 351 | const char* sp; 352 | char op; 353 | 354 | // Execute operation code in script 355 | while ((op = next(ip++)) != 0) { 356 | 357 | // Check for negative numbers 358 | if (op == '-') { 359 | op = next(ip); 360 | if (op < '0' || op > '9') { 361 | op = '-'; 362 | } 363 | else { 364 | neg = true; 365 | ip += 1; 366 | } 367 | } 368 | 369 | // Check for base prefix 370 | else if (op == '0') { 371 | op = next(ip++); 372 | if (op == 'x') base = 16; 373 | else if (op == 'b') base = 2; 374 | else ip -= 2; 375 | op = next(ip++); 376 | } 377 | 378 | // Check for literal numbers 379 | if (is_digit(op, base)) { 380 | int w = 0; 381 | do { 382 | if (base == 16 && op >= 'a') 383 | w = (w * base) + (op - 'a') + 10; 384 | else 385 | w = (w * base) + (op - '0'); 386 | op = next(ip++); 387 | } while (is_digit(op, base)); 388 | if (neg) { 389 | w = -w; 390 | neg = false; 391 | } 392 | push(w); 393 | base = 10; 394 | if (op == 0) break; 395 | } 396 | 397 | // Translate newline (for trace mode) 398 | if (op == '\n') op = 'N'; 399 | 400 | // Check for trace mode 401 | if (m_trace) { 402 | m_ios.print(++m_cycle); 403 | m_ios.print(':'); 404 | m_ios.print(mem->prefix()); 405 | m_ios.print('/'); 406 | m_ios.print((int) ip - 1); 407 | m_ios.print(':'); 408 | const class __FlashStringHelper* str = as_fstr(op); 409 | if (str == NULL) 410 | m_ios.print(op); 411 | else 412 | m_ios.print(str); 413 | if (op != '`' && op != ':') { 414 | m_ios.print(':'); 415 | print(); 416 | } 417 | } 418 | 419 | // Execute operation or parse special form 420 | char left = 0, right; 421 | switch (op) { 422 | /* 423 | * No operations. 424 | */ 425 | case ' ': // -- | no operation 426 | case ',': 427 | continue; 428 | /* 429 | * Arithmetic operators. 430 | */ 431 | case 'n': // x -- -x | negate 432 | tos(-tos()); 433 | continue; 434 | case '+': // x y -- x+y | addition 435 | w = pop(); 436 | tos(tos() + w); 437 | continue; 438 | case '-': // x y -- x-y | subtraction 439 | w = pop(); 440 | tos(tos() - w); 441 | continue; 442 | case '*': // x y -- x*y | multiplication 443 | w = pop(); 444 | tos(tos() * w); 445 | continue; 446 | case '/': // x y -- x/y | division 447 | w = pop(); 448 | tos(tos() / w); 449 | break; 450 | case '%': // x y -- x%y | modulo 451 | w = pop(); 452 | tos(tos() % w); 453 | break; 454 | case 'h': // x y z -- x*y/z | scale 455 | w = pop(); 456 | n = pop(); 457 | tos(tos() * ((long) n) / w); 458 | break; 459 | /* 460 | * Comparison/relational operators. 461 | */ 462 | case 'F': // -- false | false 463 | push(0); 464 | continue; 465 | case 'T': // -- true | true 466 | push(-1); 467 | continue; 468 | case '=': // x y -- x==y | equal 469 | w = pop(); 470 | tos(as_bool(tos() == w)); 471 | continue; 472 | case '#': // x y -- x!=y | not equal 473 | w = pop(); 474 | tos(as_bool(tos() != w)); 475 | continue; 476 | case '<': // x y -- x': // x y -- x>y | greater than 481 | w = pop(); 482 | tos(as_bool(tos() > w)); 483 | continue; 484 | /* 485 | * Bitwise/logical operators. 486 | */ 487 | case '~': // x -- ~x | bitwise not 488 | tos(~tos()); 489 | continue; 490 | case '&': // x y -- x&y | bitwise and 491 | w = pop(); 492 | tos(tos() & w); 493 | continue; 494 | case '|': // x y -- x|y | bitwise or 495 | w = pop(); 496 | tos(tos() | w); 497 | continue; 498 | case '^': // x y -- x^y | bitwise xor 499 | w = pop(); 500 | tos(tos() ^ w); 501 | continue; 502 | /* 503 | * Stack operations. 504 | */ 505 | case 'c': // xn..x1 n -- | drop n stack elements 506 | n = tos(); 507 | if (n > 0 && n < depth()) { 508 | m_sp += n; 509 | } 510 | case 'd': // x -- | drop 511 | drop(); 512 | continue; 513 | case 'g': // xn..x1 n -- xn-1..x1 xn | rotate n-elements 514 | n = tos(); 515 | if (n > 0 && n < depth()) { 516 | tos(m_sp[--n]); 517 | for (; n > 0; n--) 518 | m_sp[n] = m_sp[n - 1]; 519 | m_sp += 1; 520 | } 521 | else drop(); 522 | continue; 523 | case 'j': // xn..x1 -- xn..x1 n | stack depth 524 | push(depth()); 525 | continue; 526 | case 'o': // x y -- x y x | over 527 | push(*m_sp); 528 | continue; 529 | case 'p': // xn..x1 n -- xn..x1 xn | pick 530 | tos(*(m_sp + tos() - 1)); 531 | continue; 532 | case 'r': // x y z --- y z x | rotate 533 | w = tos(); 534 | tos(*(m_sp + 1)); 535 | *(m_sp + 1) = *m_sp; 536 | *m_sp = w; 537 | continue; 538 | case 's': // x y -- y x | swap 539 | w = tos(); 540 | tos(*m_sp); 541 | *m_sp = w; 542 | continue; 543 | case 'q': // x -- [x x] or 0 | duplicate if not zero 544 | if (tos() == 0) continue; 545 | case 'u': // x -- x x | duplicate 546 | push(tos()); 547 | continue; 548 | /* 549 | * Memory access operations. 550 | */ 551 | case '@': // addr -- value | read variable 552 | tos(read(tos())); 553 | continue; 554 | case '!': // value addr -- | write variable 555 | addr = pop(); 556 | w = pop(); 557 | write(addr, w); 558 | continue; 559 | case 'z': // addr -- | write variable to eeprom 560 | w = pop(); 561 | if (w >= 0 && w < m_entries) 562 | eeprom_write_word((uint16_t*) &m_dict[w].value, (uint16_t) m_var[w]); 563 | continue; 564 | case 'a': // -- bytes entries | allocated eeprom 565 | push((int) m_dp); 566 | push(m_entries); 567 | continue; 568 | /* 569 | * Stack frame operations. 570 | */ 571 | case '\\': 572 | n = pop(); 573 | // x1..xn n -- x1..xn | mark n-element stack frame 574 | if (n > 0) { 575 | m_fp = m_sp + n - 1; 576 | } 577 | // x1..xn y1..ym n -- y1..ym | resolve n-element stack frame 578 | else { 579 | n = m_fp - m_sp + n; 580 | if (n >= 0) { 581 | while (n--) *--m_fp = m_sp[n]; 582 | m_sp = m_fp; 583 | } 584 | else { 585 | m_sp = m_fp; 586 | drop(); 587 | } 588 | } 589 | continue; 590 | case '$': // n -- addr | address of n-element in frame 591 | n = tos(); 592 | tos((m_fp - n) - m_var); 593 | continue; 594 | /* 595 | * Input/output operations. 596 | */ 597 | case 'k': // -- char | blocking read from input stream 598 | while ((w = m_ios.read()) < 0) yield(); 599 | push(w); 600 | continue; 601 | case 'b': // base -- | number print base 602 | m_base = pop(); 603 | continue; 604 | case '?': // addr -- | print variable 605 | tos(read(tos())); 606 | case '.': // x -- | print number followed by one space 607 | w = pop(); 608 | if (m_base == 2) m_ios.print(F("0b")); 609 | else if (m_base == 8) m_ios.print(F("0")); 610 | else if (m_base == 16) m_ios.print(F("0x")); 611 | m_ios.print(w, m_base > 0 ? m_base : -m_base); 612 | m_ios.print(' '); 613 | continue; 614 | case 'm': // -- | write new line to output stream 615 | m_ios.println(); 616 | continue; 617 | case 't': // addr -- | write variable name output stream 618 | addr = tos(); 619 | if (addr >= 0 && addr < m_entries) { 620 | const uint8_t* np = 621 | (const uint8_t*) eeprom_read_word((const uint16_t*) &m_dict[addr].name); 622 | char c; 623 | while ((c = eeprom_read_byte(np++)) != 0) 624 | m_ios.print(c); 625 | m_ios.print(' '); 626 | tos(-1); 627 | } 628 | else tos(0); 629 | continue; 630 | case 'v': // char -- | write character to output stream 631 | w = pop(); 632 | m_ios.write(w); 633 | continue; 634 | /* 635 | * Control structure operations. 636 | */ 637 | case 'e': // flag if-block else-block -- | execute block on flag 638 | w = *(m_sp + 1); 639 | if (w != 0) { 640 | drop(); 641 | sp = (const char*) pop(); 642 | } 643 | else { 644 | sp = (const char*) pop(); 645 | drop(); 646 | } 647 | drop(); 648 | if (execute(sp) != NULL) goto error; 649 | continue; 650 | case 'f': // addr -- | forget variable 651 | w = pop(); 652 | if (w >= 0 && w < m_entries) { 653 | m_entries = w; 654 | m_dp = (char*) eeprom_read_word((const uint16_t*) &m_dict[w].name); 655 | eeprom_update_block(&m_dp, 0, sizeof(m_dp)); 656 | eeprom_update_byte((uint8_t*) sizeof(m_dp), m_entries); 657 | } 658 | continue; 659 | case 'i': // flag block -- | execute block if flag is true 660 | sp = (const char*) pop(); 661 | if (pop() && execute(sp) != NULL) goto error; 662 | continue; 663 | case 'l': // low high block( i -- ) -- | execute block from low to high 664 | { 665 | sp = (const char*) pop(); 666 | int high = pop(); 667 | int low = pop(); 668 | for (int i = low; i <= high; i++) { 669 | push(i); 670 | if (execute(sp) != NULL) goto error; 671 | } 672 | } 673 | continue; 674 | case 'w': // block( -- flag) -- | execute block while 675 | sp = (const char*) pop(); 676 | do { 677 | if (execute(sp) != NULL) goto error; 678 | } while (pop()); 679 | continue; 680 | case 'x': // script -- | execute script 681 | sp = (const char*) pop(); 682 | if (execute(sp) != NULL) goto error; 683 | continue; 684 | case 'y': // -- | yield 685 | yield(); 686 | continue; 687 | /* 688 | * Script operations. 689 | */ 690 | case ':': // -- addr | lookup and append variable/function 691 | case '`': // -- | lookup and execute function 692 | { 693 | char name[NAME_MAX]; 694 | size_t len = 0; 695 | bool flag = (op == ':'); 696 | 697 | // Scan variable/function name 698 | op = next(ip); 699 | if (!isalpha(op)) goto error; 700 | name[len++] = op; 701 | while (((op = next(++ip)) != 0) && isalnum(op)) 702 | name[len++] = op; 703 | name[len] = 0; 704 | if (m_trace) { 705 | m_ios.print(name); 706 | m_ios.print(':'); 707 | print(); 708 | } 709 | if (len == 0) goto error; 710 | 711 | // Lookup in dictionaries with append flag 712 | int i = lookup(name, len, flag); 713 | if (i < 0) goto error; 714 | 715 | // Push address on colon and execute on quote 716 | if (flag) { 717 | if (i < VAR_MAX) 718 | push(i); 719 | else push((i - VAR_MAX) + 0x4000); 720 | } 721 | else if (i < VAR_MAX) { 722 | sp = (const char*) read(i); 723 | if (sp == NULL) goto error; 724 | if (execute(sp) != NULL) goto error; 725 | } 726 | else { 727 | i = i - VAR_MAX; 728 | sp = (const char*) pgm_read_word(&m_scripts[i].code); 729 | if (execute(m_progmem.as_addr(sp)) != NULL) goto error; 730 | } 731 | } 732 | continue; 733 | case ';': // addr block -- | copy block to variable 734 | { 735 | const char* dest = m_eeprom.as_addr(m_dp); 736 | const char* src = (const char*) pop(); 737 | int addr = pop(); 738 | if (addr >= 0 && addr < m_entries) { 739 | eeprom_update_block(src, m_dp, len); 740 | m_dp += len; 741 | eeprom_update_byte((uint8_t*) m_dp, 0); 742 | m_dp += 1; 743 | eeprom_update_block(&m_dp, 0, sizeof(m_dp)); 744 | write(addr, dest); 745 | eeprom_write_word((uint16_t*) &m_dict[addr].value, (uint16_t) dest); 746 | } 747 | } 748 | continue; 749 | case '{': // -- block | start code block 750 | left = '{'; 751 | right = '}'; 752 | push(mem->as_addr(ip)); 753 | break; 754 | case '}': // -- | end of block 755 | return (NULL); 756 | case '(': // -- | start output string 757 | left = '('; 758 | right = ')'; 759 | break; 760 | case '[': // -- | start stack marker 761 | if (m_marker == -1) { 762 | m_marker = depth(); 763 | continue; 764 | } 765 | break; 766 | case ']': // xn..x1 -- n | end stack marker 767 | if (m_marker != -1) { 768 | push(depth() - m_marker); 769 | m_marker = -1; 770 | continue; 771 | } 772 | case '\'': // -- char | push character 773 | op = next(ip); 774 | if (op != 0) { 775 | push(op); 776 | ip += 1; 777 | } 778 | continue; 779 | /* 780 | * Arduino operations. 781 | */ 782 | case 'A': // pin -- sample | analogRead(pin) 783 | pin = tos(); 784 | tos(analogRead(pin)); 785 | continue; 786 | case 'C': // xn..x1 -- | clear 787 | clear(); 788 | continue; 789 | case 'D': // ms -- | delay() 790 | w = pop(); 791 | delay((unsigned) w); 792 | continue; 793 | case 'E': // period addr -- bool | time-out 794 | addr = pop(); 795 | w = read(addr); 796 | n = tos(); 797 | if ((((unsigned) millis() & 0xffff) - ((unsigned) w)) >= ((unsigned) n)) { 798 | tos(-1); 799 | write(addr, millis()); 800 | } 801 | else tos(0); 802 | continue; 803 | case 'H': // pin -- | digitalWrite(pin, HIGH) 804 | pin = pop(); 805 | digitalWrite(pin, HIGH); 806 | continue; 807 | case 'I': // pin -- | pinMode(pin, INPUT) 808 | pin = pop(); 809 | pinMode(pin, INPUT); 810 | continue; 811 | case 'K': // -- [char true] or false | non-blocking read from input stream 812 | w = m_ios.read(); 813 | if (w < 0) { 814 | push(0); 815 | } else { 816 | push(w); 817 | push(-1); 818 | } 819 | continue; 820 | case 'L': // pin -- | digitalWrite(pin, LOW) 821 | pin = pop(); 822 | digitalWrite(pin, LOW); 823 | continue; 824 | case 'M': // -- ms | millis() 825 | push(millis()); 826 | continue; 827 | case 'N': // -- | no operation 828 | continue; 829 | case 'O': // pin -- | pinMode(pin, OUTPUT) 830 | pin = pop(); 831 | pinMode(pin, OUTPUT); 832 | continue; 833 | case 'P': // value pin -- | analogWrite(pin, value) 834 | pin = pop(); 835 | w = pop(); 836 | analogWrite(pin, w); 837 | continue; 838 | case 'R': // pin -- bool | digitalRead(pin) 839 | pin = tos(); 840 | tos(as_bool(digitalRead(pin))); 841 | continue; 842 | case 'S': // -- | print stack contents 843 | print(); 844 | continue; 845 | case 'U': // pin -- | pinMode(pin, INPUT_PULLUP) 846 | pin = pop(); 847 | pinMode(pin, INPUT_PULLUP); 848 | continue; 849 | case 'W': // value pin -- | digitalWrite(pin, value) 850 | pin = pop(); 851 | w = pop(); 852 | digitalWrite(pin, w); 853 | continue; 854 | case 'X': // pin -- | digitalToggle(pin) 855 | pin = pop(); 856 | digitalWrite(pin, !digitalRead(pin)); 857 | continue; 858 | case 'Y': // -- | words 859 | words(); 860 | continue; 861 | case 'Z': // -- | toggle trace mode 862 | m_trace = !m_trace; 863 | continue; 864 | case TRAP_OP_CODE: 865 | sp = trap(ip); 866 | if (sp == NULL) goto error; 867 | ip = sp; 868 | continue; 869 | default: 870 | goto error; 871 | } 872 | 873 | // Parse special forms (allow nesting) 874 | if (left) { 875 | int n = 1; 876 | while ((n != 0) && ((op = next(ip++)) != 0)) { 877 | if (op == left) n++; 878 | else if (op == right) n--; 879 | if (left == '(' && n > 0) m_ios.print(op); 880 | } 881 | if (op == '}') { 882 | const char* src = (const char*) tos(); 883 | len = ip - src - 1; 884 | } 885 | if (op == 0) { 886 | ip = ip - 1; 887 | op = left; 888 | break; 889 | } 890 | } 891 | } 892 | 893 | // Restore frame pointer 894 | m_fp = fp; 895 | 896 | // Check for no errors 897 | if (op == 0 || op == '}') return (NULL); 898 | 899 | // Check for trace mode and error print 900 | error: 901 | ip = ip - 1; 902 | if (m_trace && mem == &m_memory) { 903 | m_ios.print(script); 904 | if (script[strlen(script) - 1] != '\n') m_ios.println(); 905 | for (int i = 0, n = ip - script; i < n; i++) 906 | m_ios.print(' '); 907 | m_ios.println(F("^--?")); 908 | } 909 | 910 | // Return error position 911 | return (ip); 912 | } 913 | 914 | /** 915 | * Execute given script in program memory (null terminated sequence 916 | * of operation codes). Return NULL if successful otherwise script 917 | * reference that failed. Prints error position in trace mode. 918 | * @param[in] script program memory based script. 919 | * @return script reference or NULL. 920 | */ 921 | const char* execute(const __FlashStringHelper* script) 922 | { 923 | return (execute(m_progmem.as_addr((const char*) script))); 924 | } 925 | 926 | /** 927 | * Execute script with extended operation code (character). Return 928 | * next script reference if successful otherwise NULL. 929 | * @param[in] ip script. 930 | * @return script reference or NULL. 931 | */ 932 | virtual const char* trap(const char*) 933 | { 934 | return (NULL); 935 | } 936 | 937 | protected: 938 | /** Max length of name. */ 939 | static const size_t NAME_MAX = 16; 940 | 941 | /** Trap operation code prefix. */ 942 | static const char TRAP_OP_CODE = '_'; 943 | 944 | /** 945 | * Return next token from given source. 946 | * @param[in] src source pointer. 947 | * @return next token. 948 | */ 949 | typedef char (*next_fn)(const char* src); 950 | 951 | /** Dictionary entry (in eeprom). */ 952 | struct dict_t { 953 | const char* name; //!< Name string (in eeprom). 954 | int value; //!< Value persistent. 955 | }; 956 | 957 | const script_t* m_scripts; //!< Application scripts (in progmem). 958 | char* m_dp; //!< Dictionary pointer (in eeprom). 959 | dict_t* m_dict; //!< Dictionary (in eeprom). 960 | uint8_t m_entries; //!< Dictionary entries. 961 | int* m_fp; //!< Frame pointer. 962 | int* m_sp; //!< Stack pointer. 963 | int m_tos; //!< Top of stack register. 964 | int m_marker; //!< Stack marker. 965 | bool m_trace; //!< Trace mode. 966 | unsigned m_cycle; //!< Cycle counter. 967 | int m_base; //!< Number print base. 968 | Stream& m_ios; //!< Input/output Stream. 969 | int m_var[VAR_MAX]; //!< Variable table. 970 | int m_stack[STACK_MAX]; //!< Parameter stack. 971 | 972 | /** 973 | * Map given integer value to boolean (true(-1) and false(0)). 974 | * @param[in] val value. 975 | * @return bool. 976 | */ 977 | int as_bool(int value) 978 | { 979 | return (value ? -1 : 0); 980 | } 981 | 982 | /** 983 | * Check that the given character is a digit in the given base. 984 | * @param[in] c character. 985 | * @param[in] base must be bin, dec or hex. 986 | * @return bool. 987 | */ 988 | bool is_digit(char c, int base) 989 | { 990 | if (base == 2) 991 | return (c >= '0' && c <= '1'); 992 | if (base == 16 && c >= 'a') 993 | return (c >= 'a' && c <= 'f'); 994 | return (c >= '0' && c <= '9'); 995 | } 996 | 997 | /** 998 | * Return full operation code name. 999 | * @param[in] op operation code (character). 1000 | * @return program memory string or NULL. 1001 | */ 1002 | const class __FlashStringHelper* as_fstr(char op) 1003 | { 1004 | if (!FULL_OP_NAMES) return (NULL); 1005 | switch (op) { 1006 | case 'a': return (F("allocated")); 1007 | case 'b': return (F("base")); 1008 | case 'c': return (F("ndrop")); 1009 | case 'd': return (F("drop")); 1010 | case 'e': return (F("ifelse")); 1011 | case 'f': return (F("forget")); 1012 | case 'g': return (F("roll")); 1013 | case 'h': return (F("*/")); 1014 | case 'i': return (F("if")); 1015 | case 'j': return (F("depth")); 1016 | case 'k': return (F("key")); 1017 | case 'l': return (F("loop")); 1018 | case 'm': return (F("cr")); 1019 | case 'n': return (F("negate")); 1020 | case 'o': return (F("over")); 1021 | case 'p': return (F("pick")); 1022 | case 'q': return (F("?dup")); 1023 | case 'r': return (F("rot")); 1024 | case 's': return (F("swap")); 1025 | case 't': return (F(".name")); 1026 | case 'u': return (F("dup")); 1027 | case 'v': return (F("emit")); 1028 | case 'w': return (F("while")); 1029 | case 'x': return (F("execute")); 1030 | case 'y': return (F("yield")); 1031 | case 'z': return (F("zap")); 1032 | case 'A': return (F("analogRead")); 1033 | case 'C': return (F("clear")); 1034 | case 'D': return (F("delay")); 1035 | case 'E': return (F("?expired")); 1036 | case 'F': return (F("false")); 1037 | case 'H': return (F("high")); 1038 | case 'I': return (F("input")); 1039 | case 'K': return (F("?key")); 1040 | case 'L': return (F("low")); 1041 | case 'M': return (F("millis")); 1042 | case 'N': return (F(" ")); 1043 | case 'O': return (F("output")); 1044 | case 'P': return (F("analogWrite")); 1045 | case 'R': return (F("digitalRead")); 1046 | case 'S': return (F(".s")); 1047 | case 'T': return (F("true")); 1048 | case 'U': return (F("inputPullup")); 1049 | case 'W': return (F("digitalWrite")); 1050 | case 'X': return (F("digitalToggle")); 1051 | case 'Y': return (F("words")); 1052 | case 'Z': return (F("toggleTraceMode")); 1053 | default: 1054 | return (NULL); 1055 | } 1056 | } 1057 | 1058 | /** 1059 | * Memory access class to allow scripts in various types of memory. 1060 | * Maps to a linear address space; SRAM, EEPROM(16K), PROGMEM(32K). 1061 | */ 1062 | class Memory { 1063 | public: 1064 | /** 1065 | * Return prefix for address space. 1066 | * @return string. 1067 | */ 1068 | virtual const __FlashStringHelper* prefix() 1069 | { 1070 | if (FULL_OP_NAMES) return (F("SRAM")); 1071 | return (F("D")); 1072 | } 1073 | 1074 | /** 1075 | * Return local script address mapped to linear address space. 1076 | * @param src local address. 1077 | * @return linear address. 1078 | */ 1079 | virtual const char* as_addr(const char* src) 1080 | { 1081 | return (src); 1082 | } 1083 | 1084 | /** 1085 | * Return script address mapped to local address space. 1086 | * @param src linear address. 1087 | * @return local address. 1088 | */ 1089 | virtual const char* as_local(const char* src) 1090 | { 1091 | return (src); 1092 | } 1093 | 1094 | /** 1095 | * Return read function for memory. 1096 | * @return read function. 1097 | */ 1098 | virtual next_fn get_next_fn() 1099 | { 1100 | return (next); 1101 | } 1102 | 1103 | /** 1104 | * Read byte from given local address. 1105 | * @param src local address. 1106 | * @return byte. 1107 | */ 1108 | static char next(const char* src) 1109 | { 1110 | return (*src); 1111 | }; 1112 | } m_memory; 1113 | 1114 | class ProgramMemory : public Memory { 1115 | public: 1116 | /** 1117 | * Return prefix for address space. 1118 | * @return string. 1119 | */ 1120 | virtual const __FlashStringHelper* prefix() 1121 | { 1122 | if (FULL_OP_NAMES) return (F("PROGMEM")); 1123 | return (F("P")); 1124 | } 1125 | 1126 | /** 1127 | * Return local script address mapped to linear address space. 1128 | * @param src local address. 1129 | * @return linear address. 1130 | */ 1131 | virtual const char* as_addr(const char* src) 1132 | { 1133 | return ((const char*) -((int) src)); 1134 | } 1135 | 1136 | /** 1137 | * Return script address mapped to local address space. 1138 | * @param src linear address. 1139 | * @return local address. 1140 | */ 1141 | virtual const char* as_local(const char* src) 1142 | { 1143 | return ((const char*) -((int) src)); 1144 | } 1145 | 1146 | /** 1147 | * Return read function for program memory. 1148 | * @return read function. 1149 | */ 1150 | virtual next_fn get_next_fn() 1151 | { 1152 | return (next); 1153 | } 1154 | 1155 | /** 1156 | * Read byte from given local address. 1157 | * @param src local address. 1158 | * @return byte. 1159 | */ 1160 | static char next(const char* src) 1161 | { 1162 | return (pgm_read_byte(src)); 1163 | }; 1164 | } m_progmem; 1165 | 1166 | class EEPROM : public Memory { 1167 | public: 1168 | /** 1169 | * Return prefix for address space. 1170 | * @return string. 1171 | */ 1172 | virtual const __FlashStringHelper* prefix() 1173 | { 1174 | if (FULL_OP_NAMES) return (F("EEPROM")); 1175 | return (F("E")); 1176 | } 1177 | 1178 | /** 1179 | * Return local script address mapped to linear address space. 1180 | * @param src local address. 1181 | * @return linear address. 1182 | */ 1183 | virtual const char* as_addr(const char* src) 1184 | { 1185 | return (src + 0x4000); 1186 | } 1187 | 1188 | /** 1189 | * Return script address mapped to local address space. 1190 | * @param src linear address. 1191 | * @return local address. 1192 | */ 1193 | virtual const char* as_local(const char* src) 1194 | { 1195 | return (src - 0x4000); 1196 | } 1197 | 1198 | /** 1199 | * Return read function for eeprom. 1200 | * @return read function. 1201 | */ 1202 | virtual next_fn get_next_fn() 1203 | { 1204 | return (next); 1205 | } 1206 | 1207 | /** 1208 | * Read byte from given local address. 1209 | * @param src local address. 1210 | * @return byte. 1211 | */ 1212 | static char next(const char* src) 1213 | { 1214 | return (eeprom_read_byte((const uint8_t*) src)); 1215 | }; 1216 | } m_eeprom; 1217 | 1218 | /** 1219 | * Return memory access handler for given script pointer (access factory). 1220 | * @param[in] ip script pointer. 1221 | * @return memory access. 1222 | */ 1223 | Memory* access(const char* ip) 1224 | { 1225 | Memory* mem = &m_memory; 1226 | if (((int) ip) < 0) 1227 | mem = &m_progmem; 1228 | else if (ip > (const char*) 0x4000) 1229 | mem = &m_eeprom; 1230 | return (mem); 1231 | } 1232 | 1233 | /** 1234 | * Lookup given name in dictionary. Return entry index or negative 1235 | * error code. 1236 | * @param[in] name string. 1237 | * @param[in] len length of string. 1238 | * @param[in] flag append. 1239 | * @return entry index or negative error code. 1240 | */ 1241 | int lookup(const char* name, size_t len, bool flag) 1242 | { 1243 | int i = 0; 1244 | 1245 | // Lookup entry in dictionary 1246 | for (; i < m_entries; i++) { 1247 | const uint8_t* np = 1248 | (const uint8_t*) eeprom_read_word((const uint16_t*) &m_dict[i].name); 1249 | size_t j = 0; 1250 | for (; j < len; j++) 1251 | if (name[j] != (char) eeprom_read_byte(np++)) 1252 | break; 1253 | if (j == len && (eeprom_read_byte(np) == 0)) 1254 | return (i); 1255 | } 1256 | 1257 | // Lookup entry in application dictionary 1258 | if (m_scripts != NULL && !flag) { 1259 | const char* np; 1260 | i = 0; 1261 | while ((np = (const char*) pgm_read_word(&m_scripts[i].name)) != NULL) { 1262 | if (!strcmp_P(name, np)) return (VAR_MAX + i); 1263 | i += 1; 1264 | } 1265 | } 1266 | 1267 | // Check if dictionary is full 1268 | if (m_entries == VAR_MAX || !flag) return (-1); 1269 | i = m_entries; 1270 | 1271 | // Add entry to dictionary 1272 | eeprom_update_block(&m_dp, &m_dict[i].name, sizeof(m_dp)); 1273 | eeprom_update_block(name, m_dp, len); 1274 | m_dp += len; 1275 | eeprom_update_byte((uint8_t*) m_dp, 0); 1276 | m_dp += 1; 1277 | eeprom_update_block(&m_dp, 0, sizeof(m_dp)); 1278 | m_entries += 1; 1279 | eeprom_update_byte((uint8_t*) sizeof(m_dp), m_entries); 1280 | m_var[i] = 0; 1281 | 1282 | // Return entry index 1283 | return (i); 1284 | } 1285 | }; 1286 | 1287 | #endif 1288 | -------------------------------------------------------------------------------- /examples/ShellAnalogPins/ShellAnalogPins.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellAnalogPins.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library to 20 | * execute scripts; generates output for Serial Plotter. 21 | */ 22 | 23 | #include 24 | 25 | Shell<> shell(Serial); 26 | 27 | void setup() 28 | { 29 | Serial.begin(57600); 30 | while (!Serial); 31 | 32 | // Periodically (100 ms) read analog pins (0..4) and write value. 33 | // { 34 | // 0 4 { analogRead . } loop cr 35 | // 100 delay true 36 | // } while 37 | shell.execute(F("{0,4{A.}lm100DT}w")); 38 | } 39 | 40 | void loop() 41 | { 42 | } 43 | -------------------------------------------------------------------------------- /examples/ShellBenchmarks/ShellBenchmarks.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellBenchmarks.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch measures the Shell library performance. 20 | */ 21 | 22 | #include 23 | 24 | // Shell 16 depth stack and 16 variables 25 | Shell<16,16> shell(Serial); 26 | 27 | // Define to benchmark scripts in data memory 28 | #ifdef USE_SRAM_SCRIPTS 29 | #undef SCRIPT 30 | #define SCRIPT(x) x 31 | #endif 32 | 33 | #define BENCHMARK(script) \ 34 | do { \ 35 | uint32_t start; \ 36 | uint32_t start0 = micros(); \ 37 | while ((start = micros()) == start0); \ 38 | shell.execute(F(script)); \ 39 | uint32_t stop = micros(); \ 40 | us = stop - start - baseline; \ 41 | Serial.print(F(script)); \ 42 | Serial.print(':'); \ 43 | Serial.print(us); \ 44 | Serial.print(':'); \ 45 | shell.print(); \ 46 | Serial.flush(); \ 47 | } while (0) 48 | 49 | void setup() 50 | { 51 | Serial.begin(57600); 52 | while (!Serial); 53 | Serial.println(F("ShellBenchmarks: started")); 54 | Serial.flush(); 55 | 56 | uint32_t us, baseline = 0; 57 | BENCHMARK(""); 58 | baseline = us; 59 | 60 | BENCHMARK(" "); 61 | BENCHMARK(","); 62 | BENCHMARK("T"); 63 | BENCHMARK("F"); 64 | BENCHMARK("0"); 65 | BENCHMARK("1"); 66 | BENCHMARK("-1"); 67 | BENCHMARK("10"); 68 | BENCHMARK("-10"); 69 | BENCHMARK("100"); 70 | BENCHMARK("-100"); 71 | BENCHMARK("s"); 72 | BENCHMARK("ss"); 73 | BENCHMARK("r"); 74 | BENCHMARK("u"); 75 | BENCHMARK("o"); 76 | BENCHMARK("oo"); 77 | BENCHMARK("d"); 78 | BENCHMARK("j"); 79 | BENCHMARK("c"); 80 | 81 | BENCHMARK("10000,1000,100,10,1"); 82 | BENCHMARK("~"); 83 | BENCHMARK("n"); 84 | BENCHMARK("+"); 85 | BENCHMARK("-"); 86 | BENCHMARK("*"); 87 | BENCHMARK("/"); 88 | BENCHMARK("1+"); 89 | BENCHMARK("1-"); 90 | BENCHMARK("0<"); 91 | BENCHMARK("0>"); 92 | BENCHMARK("C"); 93 | 94 | BENCHMARK("0@"); 95 | BENCHMARK("0!"); 96 | 97 | BENCHMARK("{}x"); 98 | 99 | BENCHMARK("F{}i"); 100 | BENCHMARK("T{}i"); 101 | 102 | BENCHMARK("F{}{}e"); 103 | BENCHMARK("T{}{}e"); 104 | 105 | BENCHMARK("{F}w"); 106 | BENCHMARK("1{1-q}w"); 107 | BENCHMARK("10{1-q}w"); 108 | BENCHMARK("100{1-q}w"); 109 | BENCHMARK("1000{1-q}w"); 110 | BENCHMARK("10000{1-q}w"); 111 | 112 | BENCHMARK("1,0{}l"); 113 | BENCHMARK("1,1{}l"); 114 | BENCHMARK("1,1{d}l"); 115 | BENCHMARK("1,10{d}l"); 116 | BENCHMARK("1,100{d}l"); 117 | BENCHMARK("1,1000{d}l"); 118 | BENCHMARK("1,10000{d}l"); 119 | 120 | BENCHMARK("1D"); 121 | BENCHMARK("10D"); 122 | BENCHMARK("M"); 123 | BENCHMARK("13O"); 124 | BENCHMARK("1,13W"); 125 | BENCHMARK("13H"); 126 | BENCHMARK("0,13W"); 127 | BENCHMARK("13L"); 128 | BENCHMARK("13R"); 129 | BENCHMARK("13X"); 130 | BENCHMARK("0A"); 131 | BENCHMARK("100,3P"); 132 | BENCHMARK("C"); 133 | 134 | BENCHMARK("1,1000{d1,13W}l"); 135 | BENCHMARK("1,1000{d13H}l"); 136 | BENCHMARK("1,1000{d0,13W}l"); 137 | BENCHMARK("1,1000{d13L}l"); 138 | BENCHMARK("1,1000{d13Rd}l"); 139 | BENCHMARK("1,1000{d13R~13W}l"); 140 | BENCHMARK("1,1000{d13X}l"); 141 | BENCHMARK("1,1000{dA0d}l"); 142 | 143 | BENCHMARK("0f"); 144 | 145 | shell.set(F("abs"), F("u0<{n}i")); 146 | shell.set(F("min"), F("oo>{s}id")); 147 | shell.set(F("max"), F("oo<{s}id")); 148 | shell.set(F("fac"), F("1,2r{*}l")); 149 | shell.set(F("fac5"), F("5`fac")); 150 | shell.set(F("x"), 0); 151 | shell.set(F("y"), 0); 152 | eeprom_busy_wait(); 153 | 154 | BENCHMARK(":abs"); 155 | BENCHMARK(":min"); 156 | BENCHMARK(":max"); 157 | BENCHMARK(":fac"); 158 | BENCHMARK(":fac5"); 159 | BENCHMARK(":x"); 160 | BENCHMARK(":y"); 161 | BENCHMARK("C"); 162 | 163 | BENCHMARK("-10:abs@x"); 164 | BENCHMARK("-10`abs"); 165 | BENCHMARK("-10,10`min"); 166 | BENCHMARK("-10,10`max"); 167 | BENCHMARK("5`fac"); 168 | BENCHMARK("`fac5"); 169 | BENCHMARK("C"); 170 | 171 | BENCHMARK(":x@"); 172 | BENCHMARK(":y!"); 173 | BENCHMARK(":x,u@1+s!"); 174 | BENCHMARK(":x@:y!"); 175 | BENCHMARK("C"); 176 | 177 | BENCHMARK("0f"); 178 | shell.set(F("fib"), F("u1>{u1-`fib,s2-`fib+}i")); 179 | eeprom_busy_wait(); 180 | 181 | BENCHMARK(":fib"); 182 | BENCHMARK("10`fib"); 183 | } 184 | 185 | void loop() 186 | { 187 | } 188 | -------------------------------------------------------------------------------- /examples/ShellBlink/ShellBlink.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellBlink.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library to 20 | * implement the classical blink sketch as a script. 21 | */ 22 | 23 | #include 24 | 25 | Shell<16,16> shell(Serial); 26 | 27 | void setup() 28 | { 29 | Serial.begin(57600); 30 | while (!Serial); 31 | Serial.println(F("ShellBlink: started")); 32 | 33 | // Use shell trace mode 34 | shell.trace(true); 35 | 36 | // : blink ( ms pin -- ) 37 | // dup output 38 | // { 39 | // dup high over delay 40 | // dup low over delay 41 | // true 42 | // } while ; 43 | shell.set(F("blink"), F("uO{uHoDuLoDT}w")); 44 | 45 | // 1000 13 blink 46 | shell.execute(F("1000,13`blink")); 47 | } 48 | 49 | void loop() 50 | { 51 | } 52 | -------------------------------------------------------------------------------- /examples/ShellBlinkFrame/ShellBlinkFrame.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellBlinkFrame.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library to 20 | * implement the classical blink sketch as a script using parameter 21 | * frame and element access. 22 | */ 23 | 24 | #include 25 | 26 | Shell<16,16> shell(Serial); 27 | 28 | void setup() 29 | { 30 | Serial.begin(57600); 31 | while (!Serial); 32 | Serial.println(F("ShellBlinkFrame: started")); 33 | shell.trace(true); 34 | 35 | // : blink ( ms pin -- ) 36 | // pin output 37 | // { 38 | // pin high ms delay 39 | // pin low ms delay 40 | // true 41 | // } while ; 42 | shell.set(F("blink"), F("2\\2$@O{2$@H1$@D2$@L1$@DT}w")); 43 | 44 | // 1000 13 blink 45 | shell.execute(F("1000,13`blink")); 46 | } 47 | 48 | void loop() 49 | { 50 | } 51 | -------------------------------------------------------------------------------- /examples/ShellDemo/ShellDemo.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellDemo.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library as 20 | * as an interactive shell. 21 | */ 22 | 23 | #include 24 | 25 | const int BUF_MAX = 64; 26 | char buf[BUF_MAX]; 27 | char* bp = buf; 28 | 29 | // Iterative factorial function 30 | // : fac ( n -- n! ) 1 2 rot { * } loop ; 31 | SCRIPT(fac, "1,2r{*}l"); 32 | 33 | // Fibonacci function 34 | // : fib ( n -- fib(n) ) 35 | // dup 1 > { dup 1- fib swap 2- fib + } if ; 36 | SCRIPT(fib, "u1>{u1-`fib,s2-`fib+}i"); 37 | 38 | // Blink given pin, given number of times 39 | // : blinks ( n ms pin -- ) 40 | // dup output 41 | // rot 1 swap { drop dup high over delay dup low over delay } loop 42 | // drop drop ; 43 | SCRIPT(blinks, "uOr1s{duHoDuLoD}ldd"); 44 | 45 | const script_t scripts[] PROGMEM = { 46 | SCRIPT_ENTRY(fac), 47 | SCRIPT_ENTRY(fib), 48 | SCRIPT_ENTRY(blinks), 49 | SCRIPT_NULL() 50 | }; 51 | 52 | // #define USE_SHORT_OP_NAMES 53 | 54 | #if defined(USE_SHORT_OP_NAMES) 55 | Shell<32,32,false> shell(Serial, scripts); 56 | #else 57 | Shell<32,32> shell(Serial, scripts); 58 | #endif 59 | 60 | void setup() 61 | { 62 | Serial.begin(57600); 63 | while (!Serial); 64 | Serial.println(F("ShellDemo: started, use [Newline] mode")); 65 | 66 | // Define a script in EEPROM 67 | // : demo ( -- ) 10 1000 13 blinks ; 68 | shell.execute(":demo{10,1000,13`blinks};"); 69 | 70 | // Execute program memory script: list words 71 | shell.execute(F("Y")); 72 | shell.trace(true); 73 | } 74 | 75 | void loop() 76 | { 77 | // Read command line to execute 78 | if (shell.read(bp)) { 79 | shell.execute(buf); 80 | bp = buf; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /examples/ShellMultiBlink/ShellMultiBlink.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellMultiBlink.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library to 20 | * implement the classical blink without delay sketch as a script. 21 | * Toggle pin 13, 12 and 11 with different periods without delay. 22 | */ 23 | 24 | #include 25 | 26 | Shell<> shell(Serial); 27 | 28 | void setup() 29 | { 30 | Serial.begin(57600); 31 | while (!Serial); 32 | Serial.println(F("ShellMultiBlink: started")); 33 | 34 | // : multiblink ( -- ) 35 | // 13 output 12 output 11 output 36 | // { 37 | // 500 :timer1 ?expired { 13 toggle } if 38 | // 300 :timer2 ?expired { 12 toggle } if 39 | // 10 :timer3 ?expired { 11 toggle } if 40 | // true 41 | // } while; 42 | 43 | shell.set(F("multiblink"), 44 | F("13O,12O,11O" 45 | "{" 46 | " 500:timer1,E{13X}i" 47 | " 300:timer2,E{12X}i" 48 | " 10:timer3,E{11X}i" 49 | " T" 50 | "}w")); 51 | shell.execute(F("`multiblink")); 52 | } 53 | 54 | void loop() 55 | { 56 | } 57 | -------------------------------------------------------------------------------- /examples/ShellScript/ShellScript.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellScript.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library to 20 | * execute scripts. 21 | */ 22 | 23 | #include 24 | 25 | // : blinks ( n ms pin -- ) 26 | // dup output 27 | // rot 1 swap { drop dup high over delay dup low over delay } loop 28 | // drop drop ; 29 | SCRIPT(blinks, "uOr1s{duHoDuLoD}ldd"); 30 | 31 | // : monitor ( buttonPin ledPin -- ) 32 | // over inputPullup 33 | // dup output 34 | // { 35 | // over digitalRead 36 | // { 1000 } { 200 } ifElse 37 | // over high dup delay over low delay 38 | // true 39 | // } while ; 40 | SCRIPT(monitor, "oUuO{oR{1000}{200}eoHuDoLDT}w"); 41 | 42 | // Script table 43 | const script_t scripts[] PROGMEM = { 44 | SCRIPT_ENTRY(blinks), 45 | SCRIPT_ENTRY(monitor), 46 | SCRIPT_NULL() 47 | }; 48 | 49 | // Shell 16 depth stack and 16 variables, and application script table 50 | Shell<16,16> shell(Serial, scripts); 51 | 52 | void setup() 53 | { 54 | Serial.begin(57600); 55 | while (!Serial); 56 | Serial.println(F("ShellScript: started")); 57 | shell.trace(true); 58 | shell.execute(F("5,1000,13`blinks")); 59 | shell.execute(F("2,13`monitor")); 60 | } 61 | 62 | void loop() 63 | { 64 | } 65 | -------------------------------------------------------------------------------- /examples/ShellTrap/ShellTrap.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ShellTrap.ino 3 | * @version 1.0 4 | * 5 | * @section License 6 | * Copyright (C) 2016, Mikael Patel 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * @section Description 19 | * This Arduino sketch shows how to use the Shell library 20 | * as an interactive shell and trap extended instructions. 21 | */ 22 | 23 | #include 24 | 25 | typedef Shell<16,16> BaseShell; 26 | class ExtendedShell : public BaseShell { 27 | public: 28 | ExtendedShell(Stream& ios) : BaseShell(ios) 29 | {} 30 | virtual const char* trap(const char* ip) 31 | { 32 | char op = *ip++; 33 | m_ios.print(F("trap::op=")); 34 | m_ios.println(op); 35 | return (ip); 36 | } 37 | }; 38 | 39 | ExtendedShell shell(Serial); 40 | 41 | const int BUF_MAX = 64; 42 | char buf[BUF_MAX]; 43 | char* bp = buf; 44 | 45 | void setup() 46 | { 47 | Serial.begin(57600); 48 | while (!Serial); 49 | Serial.println(F("ShellTrap: started, use [Newline] mode")); 50 | shell.trace(true); 51 | } 52 | 53 | void loop() 54 | { 55 | if (shell.read(bp)) { 56 | shell.execute(buf); 57 | bp = buf; 58 | } 59 | } 60 | --------------------------------------------------------------------------------