Comma quibbling: Difference between revisions

Content deleted Content added
No edit summary
→‎{{header|Forth}}: - the previous implementation was nice but mostly unrelated to the task
Line 1,406: Line 1,406:


=={{header|Forth}}==
=={{header|Forth}}==
The efficient and beautiful way to solve the task is to keep a sliding triplet of consequent words. First we unconditionally read the first two words; if there are none, "read" gives us an empty word. Then we read the input stream, typing third to last word together with a comma. When the loop ends, we have two (possible empty) words on the stack, and the only thing left to do is to output it accordingly.
Forth is a set of very low level routines (WORDs) that are concatenated to make higher level WORDs.
<lang>
Programming Forth is like making a custom language for the problem.
: read bl parse ;
Arguments are passed explicitly on the hardware stack.
: not-empty? ( c-addr u -- c-addr u true | false ) ?dup-if true else drop false then ;
As the program is written the language level goes higher.
: third-to-last 2rot ;
This demonstration uses the Forth parser to break the input stream into separate strings and a string stack to collect the input strings. The string stack can also be read as an indexed array.
: second-to-last 2swap ;


: quibble
Stack comments show in/out arguments after a word executes.
." {"
Example: ( input -- output)
read read begin read not-empty? while third-to-last type ." , " repeat

second-to-last not-empty? if type then
<lang>\ string primitives operate on addresses passed on the stack
not-empty? if ." and " type then
: C+! ( n addr -- ) dup >R C@ + R> C! ; \ increment a byte at addr by n
." }" cr ;
: APPEND ( addr1 n addr2 -- ) 2DUP 2>R COUNT + SWAP MOVE 2R> C+! ; \ append u bytes at addr1 to addr2
: PLACE ( addr1 n addr2 -- ) 2DUP 2>R 1+ SWAP MOVE 2R> C! ; \ copy n bytes at addr to addr2
quibble
: ,' ( -- ) [CHAR] ' WORD c@ 1+ ALLOT ALIGN ; \ Parse input stream until ' and write into next
quibble ABC
\ available memory
quibble ABC DEF

quibble ABC DEF G H
\ use ,' to create some counted string literals with mnemonic names
create '"{}"' ( -- addr) ,' "{}"' \ counted strings return the address of the 1st byte
create '"{' ( -- addr) ,' "{'
create '}"' ( -- addr) ,' }"'
create ',' ( -- addr) ,' , '
create 'and' ( -- addr) ,' and '
create "] ( -- addr) ,' "]'

create null$ ( -- addr) 0 ,

HEX
\ build a string stack/array to hold input strings
100 constant ss-width \ string stack width
variable $DEPTH \ the string stack pointer
create $stack ( -- addr) 20 ss-width * allot

DECIMAL
: new: ( -- ) 1 $DEPTH +! ; \ incr. string stack pointer
: ]stk$ ( ndx -- addr) ss-width * $stack + ; \ calc string stack element address from ndx
: TOP$ ( -- addr) $DEPTH @ ]stk$ ; \ returns address of the top string on string stack
: collapse ( -- ) $DEPTH off ; \ reset string stack pointer

\ used primitives to build counted string functions
: move$ ( $1 $2 -- ) >r COUNT R> PLACE ; \ copy $1 to $2
: push$ ( $ -- ) new: top$ move$ ; \ push $ onto string stack
: +$ ( $1 $2 -- top$ ) swap push$ count TOP$ APPEND top$ ; \ concatentate $2 to $1, Return result in TOP$
: LEN ( $1 -- length) c@ ; \ char fetch the first byte returns the string length
: compare$ ( $1 $2 -- -n:0:n ) count rot count compare ; \ compare is an ANS Forth word. returns 0 if $1=$2
: =$ ( $1 $2 -- flag ) compare$ 0= ;
: [""] ( -- ) null$ push$ ; \ put a null string on the string stack

: [" \ collects input strings onto string stack
COLLAPSE
begin
bl word dup "] =$ not \ parse input stream and terminate at "]
while
push$
repeat
drop
$DEPTH @ 0= if [""] then ; \ minimally leave a null string on the string stack


: ]stk$+ ( dest$ n -- top$) ]stk$ +$ ; \ concatenate n ]stk$ to DEST$

: writeln ( $ -- ) cr count type collapse ; \ print string on new line and collapse string stack

\ write the solution with the new words
: 1-input ( -- )
1 ]stk$ LEN 0= \ check for empty string length
if
'"{}"' writeln \ return the null string output
else
'"{' push$ \ create a new string beginning with '{'
TOP$ 1 ]stk$+ '}"' +$ writeln \ concatenate the pieces for 1 input

then ;

: 2-inputs ( -- )
'"{' push$
TOP$ 1 ]stk$+ 'and' +$ 2 ]stk$+ '}"' +$ writeln ;

: 3+inputs ( -- )
$DEPTH @ dup >R \ save copy of the number of inputs on the return stack
'"{' push$
( n) 1- 1 \ loop indices for 1 to 2nd last string
DO TOP$ I ]stk$+ ',' +$ LOOP \ create all but the last 2 strings in a loop with comma
( -- top$) R@ 1- ]stk$+ 'and' +$ \ concatenate the 2nd last string to Top$ + 'and'
R> ]stk$+ '}"' +$ writeln \ use the copy of $DEPTH to get the final string index
2drop ; \ clean the parameter stack

: quibble ( -- )
$DEPTH @
case
1 of 1-input endof
2 of 2-inputs endof
3+inputs \ default case
endcase ;


\ interpret this test code after including the above code
[""] QUIBBLE
[" "] QUIBBLE
[" ABC "] QUIBBLE
[" ABC DEF "] QUIBBLE
[" ABC DEF GHI BROWN FOX "] QUIBBLE
</lang>
{{out}}
<pre>"{}"
"{}"
"{ABC}"
"{ABC and DEF}"
"{ABC, DEF, GHI, BROWN and FOX}" ok</pre>

Works with any ANS Forth

Needs the FMS-SI (single inheritance) library code located here:
http://soton.mpeforth.com/flag/fms/index.html
<lang forth> include FMS-SI.f
include FMS-SILib.f

: foo { l | s -- }
cr ." {"
l size: dup 1- to s
0 ?do
i l at: p:
s i - 1 >
if ." , "
else s i <> if ." and " then
then
loop
." }" l <free ;

${ } foo
\ {}
${ ABC } foo
\ {ABC}
${ ABC DEF } foo
\ {ABC and DEF}
${ ABC DEF G } foo
\ {ABC, DEF and G}
${ ABC DEF G H } foo
\ {ABC, DEF, G and H}
${ ABC DEF G H I } foo
\ {ABC, DEF, G, H and I}
</lang>
</lang>