Create your own text control codes: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
(→‎{{header|Raku}}: Add a Raku example)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(12 intermediate revisions by 5 users not shown)
Line 1:
{{draft task|Text processing}}
{{task|Text processing}}
 
A control code is a text character or sequence of characters with a special meaning that, rather than print a text character to the terminal or screen, instructs the computer to do something text-related. Examples include:
Line 10 ⟶ 9:
 
The [[C]] code below shows the <code>%d</code> control code in action:
<langsyntaxhighlight Clang="c">int foo = 1;
int bar = 2;
int baz = foo + bar;
printf("%d plus %d is: %d",foo,bar,baz); //outputs "1 plus 2 is: 3"</langsyntaxhighlight>
 
 
Line 35 ⟶ 34:
 
The routine below is called repeatedly by a routine named <code>PrintString_Color</code> until the null terminator is read.
<langsyntaxhighlight lang="asm">PrintChar_Color: ;Print AL to screen
push dx
push ax
Line 56 ⟶ 55:
pop ax
pop dx
ret</langsyntaxhighlight>
 
 
=={{header|Julia}}==
The example below extends the Base show function, as used to stringify printed output,
to allow formatting of larger numbers with commas and to print negative integers with parentheses.
<syntaxhighlight lang="julia">using Formatting
import Base.show
 
Base.show(io::IO, x::Float64) = print(io, format(x, commas=true))
Base.show(io::IO, x::Int) = print(io, format(x, commas=true, parens=true))
 
println("15.1 + 31415926.5 = $(15.1 + 31415926.5)")
println("10000000000 / -3 = $(10000000000 / -3)")
println("2345 * 76 = $(2345 * 76), 2345 * -9876 = $(2345 * -9876)")
</syntaxhighlight>{{out}}
<pre>
15.1 + 31415926.5 = 31,415,941.6
10000000000 / -3 = -3,333,333,333.333333
2345 * 76 = 178,220 , 2345 * -9876 = (23,159,220)
</pre>
 
 
=={{header|Phix}}==
The printf() function is implemented in fairly standard Phix hll code in builtins\VM\pprntfN.e<br>
Recent-ish additions along the lines of the task description include:<br>
%q, %Q (print with quotes)<br>
%v, %V (apply sprint internally)<br>
%a, %A (print in bases 2..62)<br>
%[i]<i>specifier</i> (print i'th arg here)<br>
%=<i>specifier</i> (centre, with odd padding as eg 3:4)<br>
%|<i>specifier</i> (centre, with odd padding as eg 4:3)<br>
Just as an example, so we can have some actual code here, but omitting the setup of
"centre" from the format string, the latter two were mainly implemented as follows:
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">elsif</span> <span style="color: #000000;">centre</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">mh</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">minfieldwidth</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">centre</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">-- '=', split 3:4</span>
<span style="color: #000000;">r1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mh</span><span style="color: #0000FF;">)&</span><span style="color: #000000;">r1</span><span style="color: #0000FF;">&</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">minfieldwidth</span><span style="color: #0000FF;">-</span><span style="color: #000000;">mh</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span> <span style="color: #000080;font-style:italic;">-- '|', split 4:3</span>
<span style="color: #000000;">r1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">minfieldwidth</span><span style="color: #0000FF;">-</span><span style="color: #000000;">mh</span><span style="color: #0000FF;">)&</span><span style="color: #000000;">r1</span><span style="color: #0000FF;">&</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mh</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<!--</syntaxhighlight>-->
At the same time, similar changes were made to the version of printf() defined in pwa/p2js.js:
<syntaxhighlight lang="javascript"> let padlen = parseInt(size,10)-res.length;
let half = Math.floor(padlen/2);
case '=': res = pad.repeat(half) + res + pad.repeat(padlen-half); break;
case '|': res = pad.repeat(padlen-half) + res + pad.repeat(half); break;</syntaxhighlight>
 
=={{header|PL/M}}==
PL/M doesn't have a standard printf function or indeed, a standard library.
This sample implements a PRINTF procedure somewhat like the standard C library routine.<br><br>
Although CP/M uses ASCII, Kildall's original 8080 PL/M compiler only supports a limited character set for the program's source. In particular the compiler doesn't like lowercase letters, % or \. PL/M also requires procedures to be called with the same number of parameters they were defined with. The PRINTF defined here has the format string plus seven parameters, if fewer parameters are required, additional dummy parameters must be supplied<br>
As % and lowercase are not available, the format frames are preceeded by / and must be in uppercase.<br>
The following are supported:
<pre>
/S -> print a string
/LS -> print a string in lowercase
/C -> print a single character
/LC -> print a single character in lowercase
/D or /I -> print a signed integer in decimal
/U -> print an unsigned integer in decimal
/H or /X -> print an unsigned integer in hexadecimal
/O -> print an unsigned integer in octal
/N -> print a newline
/anything-else
-> print the anything-else character, e.g. /$ prints a $ without terminating the format
</pre>
The additional options such as field width available in C's printf are not allowed in this PRINTF.
<br><br>
Note that under CP/M, strings are terminated by $, not a nul character, hence the need for /$.<br>
The original 8080 PL/M compiler also only supports unsigned 8 and 16 bit values. If a number must be treated as signed, the values 65535 downto 32768 represent -1 downto -32768, hence the somewhat cryptic handling of the D and I frames.
<syntaxhighlight lang="pli">100H: /* FORMATTED OUTPUT */
 
/* CP/M BDOS SYSTEM CALL */
BDOS: PROCEDURE( FN, ARG ); DECLARE FN BYTE, ARG ADDRESS; GOTO 5;END;
/* CONSOLE OUTPUT ROUTINES */
PR$CHAR: PROCEDURE( C ); DECLARE C BYTE; CALL BDOS( 2, C ); END;
PR$STRING: PROCEDURE( S ); DECLARE S ADDRESS; CALL BDOS( 9, S ); END;
PR$NL: PROCEDURE; CALL PR$STRING( .( 0DH, 0AH, '$' ) ); END;
PR$NUMBER: PROCEDURE( N );
DECLARE N ADDRESS;
DECLARE V ADDRESS, N$STR( 6 ) BYTE INITIAL( '.....$' ), W BYTE;
N$STR( W := LAST( N$STR ) - 1 ) = '0' + ( ( V := N ) MOD 10 );
DO WHILE( ( V := V / 10 ) > 0 );
N$STR( W := W - 1 ) = '0' + ( V MOD 10 );
END;
CALL PR$STRING( .N$STR( W ) );
END PR$NUMBER;
PR$OCTAL: PROCEDURE( N );
DECLARE N ADDRESS;
DECLARE V ADDRESS, N$STR( 7 ) BYTE INITIAL( '......$' ), W BYTE;
DO W = 0 TO LAST( N$STR ) - 2; N$STR( W ) = '0'; END;
N$STR( W := LAST( N$STR ) - 1 ) = '0' + ( ( V := N ) AND 7 );
DO WHILE( ( V := SHR( V, 3 ) ) > 0 );
N$STR( W := W - 1 ) = '0' + ( V AND 7 );
END;
CALL PR$STRING( .N$STR( W ) );
END PR$OCTAL;
 
/* FORMATTED PRINT ROUTINE - VAGUELY LIKE THE C ROUTINE */
PRINTF: PROCEDURE( FMT, A, B, C, D, E, F, G );
DECLARE ( FMT, A, B, C, D, E, F, G ) ADDRESS;
 
PR$HEX: PROCEDURE( B ); /* PRINTS B AS A 2 DIGIT HEX NUMBER */
DECLARE B BYTE;
DECLARE D BYTE;
IF ( D := SHR( B, 4 ) ) > 9 THEN CALL PR$CHAR( ( D - 10 ) + 'A' );
ELSE CALL PR$CHAR( D + '0' );
IF ( D := B AND 0FH ) > 9 THEN CALL PR$CHAR( ( D - 10 ) + 'A' );
ELSE CALL PR$CHAR( D + '0' );
END PR$HEX ;
/* RETURNS A CONVERTED TO LOWERCASE, IF NECESSARY */
TO$LOWER: PROCEDURE( A )BYTE;
DECLARE A BYTE;
IF A >= 'A' AND A <= 'Z' THEN RETURN ( A + 32 ); ELSE RETURN A;
END TO$LOWER;
 
DECLARE FRAME LITERALLY '''/''';
DECLARE P ( 7 )ADDRESS;
DECLARE FPTR ADDRESS;
DECLARE ( PPOS, FCH BASED FPTR, IN$LOWERCASE ) BYTE;
P( 0 ) = A; P( 1 ) = B; P( 2 ) = C; P( 3 ) = D;
P( 4 ) = E; P( 5 ) = F; P( 6 ) = G;
PPOS = 0;
FPTR = FMT;
DO WHILE( FCH <> '$' );
IF FCH <> FRAME THEN DO; /* NOT A FORMAT FRAME */
CALL PR$CHAR( FCH );
END;
ELSE DO; /* IS A FORMAT FRAME */
FPTR = FPTR + 1;
IF ( IN$LOWERCASE := FCH = 'L' ) THEN FPTR = FPTR + 1;
IF FCH = 'C' OR FCH = 'S' THEN DO;
/* CHARACTER OR STRING OPTIONALLY CONVERTED TO LOWER CASE */
IF FCH = 'C' THEN DO; /* CHARACTER */
IF IN$LOWERCASE THEN CALL PR$CHAR( TO$LOWER( P( PPOS ) ) );
ELSE CALL PR$CHAR( P( PPOS ) );
END;
ELSE IF NOT IN$LOWERCASE THEN DO; /* STRING AS-IS */
CALL PR$STRING( P( PPOS ) );
END;
ELSE DO; /* LOWERCASE STRING */
DECLARE SPTR ADDRESS;
DECLARE SCH BASED SPTR BYTE;
SPTR = P( PPOS );
DO WHILE( SCH <> '$' );
CALL PR$CHAR( TO$LOWER( SCH ) );
SPTR = SPTR + 1;
END;
END;
PPOS = PPOS + 1;
END;
ELSE IF FCH = 'I' OR FCH = 'D' OR FCH = 'U' THEN DO;
/* SIGNED OR UNSIGNED DECIMAL INTEGER */
DECLARE V ADDRESS;
V = P( PPOS );
IF FCH <> 'U' AND V > 32767 THEN DO;
/* THE NUMBER IS NEGATIVE AND MUST BE PRINTED AS SIGNED */
CALL PR$CHAR( '-' );
V = - V;
END;
CALL PR$NUMBER( V );
PPOS = PPOS + 1;
END;
ELSE IF FCH = 'H' OR FCH = 'X' THEN DO;
/* UNSIGNED INTEGER IN HEX */
DECLARE V ADDRESS;
V = P( PPOS );
CALL PR$CHAR( '$' );
CALL PR$HEX( HIGH( V ) );
CALL PR$HEX( LOW( V ) );
PPOS = PPOS + 1;
END;
ELSE IF FCH = 'O' THEN DO; /* UNSIGNED OCTAL INTEGER */
CALL PR$OCTAL( P( PPOS ) );
PPOS = PPOS + 1;
END;
ELSE IF FCH = 'N' THEN DO; /* NEWLINE */
CALL PR$NL;
END;
ELSE DO; /* ANYTHING ELSE - JUST PRINT IT */
CALL PR$CHAR( FCH );
END /* IF VARIOUS FRAMES;; */ ;
END /* IF FCH <> FRAME;; */ ;
FPTR = FPTR + 1;
END /* WHILE FCH <> '$' */ ;
END PRINTF ;
 
/* TEST PRINTF */
DECLARE ( P3, P4, P5, P6, P7 ) ADDRESS;
P3 = 301; P4, P5, P6 = 0;
P3 = - P3; P4 = P4 - 1; P5 = P5 - 2; P6 = P6 - 3; P7 = 65535;
CALL PRINTF( .'HELLO, /S/C /I/$ /D /U /X./N(END)/O/N$'
, .'WORLD$', 33, P3, P4, P5, P6, P7
);
CALL PRINTF( .'H/LC/LC/LC/LC, W/LS/C$'
, 'E', 'L', 'L', 'O', .'ORLD$', 33, 0
);
EOF</syntaxhighlight>
{{out}}
<pre>
HELLO, WORLD! -301$ -1 65534 $FFFD.
(END)177777
Hello, World!</pre>
 
=={{header|Raku}}==
Line 65 ⟶ 268:
Some languages already have a commas directive as that one is actually useful. I doubt if any language has an "invert" directive.
 
This is ''really'' basic and sketchy. It only modifies printf, not sprintf, so probably isn't terribly useful as is... but it satisfies the task requirements. It actually ''does'' add new, non-standard directives to the core printf function, not just implement a separate formatting function to pre-format a string which is then passed to the printing function.
<syntaxhighlight lang="raku" perl6line>use Lingua::EN::Numbers;
use Acme::Text::UpsideDown;
 
Line 85 ⟶ 288:
 
printf "Integer %d with commas: %y\nSpelled out: %s\nInverted: %z\n",
12345, 12345, 12345.&cardinal, 12345.&cardinal;</langsyntaxhighlight>
{{out}}
<pre>Integer 12345 with commas: 12,345
Spelled out: twelve thousand, three hundred forty-five
Inverted: ǝʌᴉɟ-ʎʇɹoɟ pǝɹpunɥ ǝǝɹɥʇ ‘puɐsnoɥʇ ǝʌꞁǝʍʇ</pre>
 
=={{header|Wren}}==
Wren's standard print statement, ''System.print'' (or ''System.write'' without a terminating new line), has no formatting capabilities whatsoever though it does support string interpolation. It cannot be changed without forking Wren itself.
 
When doing RC tasks, I often use methods in my own ''Wren-fmt'' module which does most of what C's ''printf'' statement does and other things besides. Although I could add anything I like to that, it's already more than 800 lines long and so I don't think it would be appropriate to patch it for the purposes of this task.
 
What I've done instead is to create a special class called ''Sgr'' (Select graphic rendition) which adds special effects when printing text to terminals which support ANSI escape sequences. The effects supported are: color, bold, faint, italic, underline, wink, strike and overline each of which is represented by a method consisting of its initial letter.
 
When these methods complete, they restore the terminal attributes to what they were before.
''System.print'' can now interpolate these method calls.
 
Although it would be possible to abbreviate the color arguments passed to ''Sgr.c'', I haven't done so because I didn't think it would be very user friendly.
<syntaxhighlight lang="wren">class Sgr {
// capitalize the initial letter for bright colors
static init_() {
__cm = { "black": 30, "red" : 31, "green": 32, "yellow": 33,
"blue" : 34, "magenta": 35, "cyan" : 36, "white" : 37,
"Black": 90, "Red" : 91, "Green": 92, "Yellow": 93,
"Blue" : 94, "Magenta": 95, "Cyan" : 96, "White" : 97,
"gray" : 90, "Gray" : 90
}
}
 
static c(fore, back, text) { // colorize
if (!__cm) init_()
var fcn = __cm[fore]
if (!fcn) Fiber.abort("Invalid foreground color.")
var bcn = __cm[back]
if (!bcn) Fiber.abort("Invalid background color.")
if (!(text is String)) text = text.toString
var reset = "\e[39;49m"
return "\e[%(fcn);%(bcn+10)m%(text)%(reset)"
}
 
 
static b(text) { "\e[1m%(text)\e[22m" } // bold
 
static f(text) { "\e[2m%(text)\e[22m" } // faint
 
static i(text) { "\e[3m%(text)\e[23m" } // italic
 
static u(text) { "\e[4m%(text)\e[24m" } // underline
 
static w(text) { "\e[5m%(text)\e[25m" } // wink (or blink)
 
static r(text) { "\e[7m%(text)\e[27m" } // reverse video
 
static s(text) { "\e[9m%(text)\e[29m" } // strike out
 
static o(text) { "\e[53m%(text)\e[55m" } // overline
 
}
 
System.print("%(Sgr.c("red", "green", "This")) is a color %(Sgr.c("yellow", "blue", "test")).")
System.print("\nOther effects:")
var effects = [
Sgr.b("Bold"), Sgr.f("Faint"), Sgr.i("Italic"), Sgr.u("Underline"),
Sgr.w("Wink"), Sgr.r("Reverse"), Sgr.s("Strike"), Sgr.o("Overline")
]
System.print(effects.join(", "))</syntaxhighlight>
9,483

edits