From bc3eae186c4d242da056481da53397ea3f3db8e6 Mon Sep 17 00:00:00 2001 From: crc Date: Thu, 30 May 2019 20:47:29 +0000 Subject: [PATCH] update the alternate listener... FossilOrigin-Name: b788daf416bf99b8abed9c46f57c0b6e782b7065e4541656216361358a042a7b --- example/alternate-listener.forth | 165 ++++++++++++++++++++++++------- 1 file changed, 129 insertions(+), 36 deletions(-) diff --git a/example/alternate-listener.forth b/example/alternate-listener.forth index 95cac02..44cdaa8 100644 --- a/example/alternate-listener.forth +++ b/example/alternate-listener.forth @@ -1,9 +1,23 @@ # A New Listener -The basic listener is very minimalistic. This is the start of -something a little nicer. +The basic listener is very minimalistic. There are a few reasons +for this. -# Features +- I don't use it often (I generally code in files or blocks, + using the listener only for brief tests) +- A complex listener adds surface for bugs and incompatibilites + due to terminal and host configurations +- It's harder to pipe data in and out of a more complex listener + +This can be worked around in a number of ways. Tools like +`rlwrap` can be used to provide a line editor and history, and +one can always write their own custom listener... + +But sometimes it's nice to experiment a bit. This is a modestly +expanded version of the listener, adding some new functionality +and providing a more flexible base to build on. + +# Current Features - character breaking input - suggestions on hitting TAB @@ -16,42 +30,121 @@ something a little nicer. # The Code +The RETRO image has a 512 cell buffer between the kernel and the +high level code. This is used for the Text Input Buffer, or TIB. + + +------------------+ + | kernel | 0-1023 + | input buffer | 1024-1536 + | standard library | 1537+ + | ... | + | system buffers | EOM - ??? + +------------------+ + +~~~ +#1024 'TIB const +~~~ + +The listener needs to determine what to treat as the end of the +token. I define `end-of-token?` for this. + +~~~ +:end-of-token? (c-f) hook + { ASCII:CR ASCII:LF ASCII:SPACE } a:contains? ; +~~~ + +It's possible to get an empty string as an input. This isn't +any good, so I define `s:blank?` for this. + +~~~ +:s:blank? (s-sf) dup s:length n:zero? ; +~~~ + +There are certain keys I want to handle differently from others. +The initial ones are backspace, tab, escape, and CTRL+K. I am +defining handlers for these. + +First is backspace. I trap ASCII:BS and ASCII:DEL for this. + +~~~ +:handle:backspace (c-c) hook + dup { ASCII:BS ASCII:DEL } a:contains? + [ buffer:get buffer:get drop-pair ] if ; +~~~ + +Tab is used to display suggestions, based on the token input so +far. + ~~~ {{ - #1025 'TIB const - - :eol? (c-f) - { ASCII:CR ASCII:LF ASCII:SPACE } a:contains? ; - - :valid? (s-sf) - dup s:length n:-zero? ; - - :bs (c-c) - dup { #8 #127 } a:contains? [ buffer:get buffer:get drop-pair ] if ; - + :prefix? + TIB fetch 'prefix:_ [ #7 + store ] sip d:lookup n:-zero? ; :hint - nl TIB d:words-beginning-with nl TIB s:put ; - - :hints (c-c) - dup ASCII:HT eq? [ buffer:get drop hint ] if ; - - :stack (c-c) - dup ASCII:ESC eq? [ buffer:get drop nl &dump-stack dip nl TIB s:put ] if ; - - :check (a-) - [ call ] a:for-each ; - - :c:get (-c) as{ 'liii.... i #1 d }as ; - - :s:get (-s) [ TIB buffer:set - [ c:get dup buffer:add { &bs &hints &stack } check eol? ] until - buffer:start s:chop ] buffer:preserve ; - - :forever (q-) - repeat &call sip again ; + nl TIB prefix? [ n:inc ] if d:words-beginning-with nl + TIB s:put ; ---reveal--- - :new-listener (-) - 'stty_cbreak unix:system - [ s:get valid? &interpret &drop choose ] forever ; + :handle:tab (c-c) hook + dup ASCII:HT eq? [ buffer:get drop hint ] if ; }} ~~~ + +Control + k (`ASCII:VT`) will display help for the word being typed. +This assumes that `retro-describe` is in your $PATH and that the +typed text is a complete word name (without a prefix). + +~~~ +:handle:CTRL+K (c-c) hook + dup ASCII:VT eq? [ buffer:get drop + TIB 'retro-describe_"%s" s:format nl + unix:system TIB s:put ] if ; +~~~ + +The escape key will be used to display the stack. + +~~~ +:handle:escape (c-c) hook + dup ASCII:ESC eq? + [ buffer:get drop nl &dump-stack dip nl TIB s:put ] if ; +~~~ + +To control the checks, I define two words. The first returns an +array of handlers, the second processes them. + +~~~ +:special-keys (-a) hook + { &handle:backspace &handle:tab &handle:escape &handle:CTRL+K } ; + +:check (q-) hook + &call a:for-each ; +~~~ + +And with these, I can quickly implement the `new-listener`. + +~~~ +{{ + :guard (-) buffer:end TIB lt? [ TIB buffer:set ] if ; + :c:get (-c) as{ 'liii.... i #1 d }as dup buffer:add ; + :s:get (-s) [ TIB buffer:set + [ guard c:get special-keys check end-of-token? ] until + buffer:start s:chop ] buffer:preserve ; + :forever (q-) repeat &call sip again ; +---reveal--- + :new-listener (-) hook + 'stty_cbreak unix:system + [ s:get s:blank? &drop &interpret choose ] forever ; + + [ 'stty_cbreak unix:system + #0 unix:exit ] &bye set-hook +}} +~~~ + +All of the exposed words are hooks: your code can patch in and +replace them as you see fit, making this much more mallable at +runtime. + +Future things to (maybe) explore: + +- tab completion +- use of `retro-describe` on the current token to get help +- line editing +- input history