Create your own text control codes: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Raku}}: add some commentary)
(→‎{{header|Wren}}: Added a note to preamble.)
Line 92: Line 92:


=={{header|Wren}}==
=={{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.
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.
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.

Revision as of 23:57, 2 October 2021

Create your own text control codes is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task
Create your own text control codes
You are encouraged to solve this task according to the task description, using any language you may know.

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:

  • NUL (ASCII 0) = The null terminator. Most programming languages silently place this at the end of a string so that the print function knows when to stop.
  • \n (New Line) = This tells the print function to start a new line. Older computers don't have this, rather they use an ASCII 13 (carriage return) followed by an ASCII 10 (line feed).
  • \ (Escape Character) = Any control code placed directly after the escape character loses is special meaning and is printed as-is.
  • %d = Insert a base 10 numeral into the string. The value is loaded from a variable specified after the string, separated by commas.

The C code below shows the %d control code in action: <lang 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"</lang>


Task

Add a new control code to your language's standard print function, which does some sort of text related task that is not already built into the language. Have the standard print function print a string that uses that code, and display the output. What the control code actually does is up to you, but some examples include:

  • Changing the color of the text
  • Starting a new line at a custom location, without padding the string with blank spaces


If the language allows, try to make something entirely new that isn't just a macro of existing control codes combined to produce a trivial result (e.g. \n\n\n\n for four new lines would be trivial)

If your language doesn't allow you to modify the standard print function, note it. (This is a draft task for now because I don't know if anything other than assembly can do this.)

8086 Assembly

In languages where you have to write your own print function, this task is relatively straightforward. MS-DOS has a "PrintString" function built-in, but this example will use a custom one with its own control codes. Currently this print function supports two types of control codes beyond the standard ones: a set of control codes that change the text color, and one that starts a new line with a specified number of spaces.

(Generally speaking, the implementation below isn't the best way to do it for compatibility reasons, since you end up sacrificing the ability to print certain characters. It's usually better to have one escape character and then the character after it becomes the control code. That way you only sacrifice one character instead of dozens. And technically you don't even lose the escape character since it can always escape itself by doubling it up.)

The routine below is called repeatedly by a routine named PrintString_Color until the null terminator is read. <lang asm>PrintChar_Color: ;Print AL to screen push dx push ax mov dx,8F80h call CompareRange8 ;sets carry if 80h <= al <= 8Fh, clears carry otherwise jc isColorCode cmp al,90h jne skipCCR_Color call CustomCarriageReturn ;prints new line, then moves cursor a variable amount of spaces forward.

                                                 ;this variable is set prior to printing a string that needs it.

jmp done_PrintChar_Color skipCCR_Color: mov ah,0Eh int 10h ;prints character AL to screen, with the ink color stored in BL. jmp done_PrintChar_Color isColorCode: and al,01111111b ;clear bit 7 to convert to VGA colors. mov bl,al ;text from this point forward will use this color. done_printChar_Color: pop ax pop dx ret</lang>

Raku

This, in general would be a bad idea. It isn't smart to add a lot of overhead to core functions, especially for so little gain. That being said, something being a bad idea never stopped us before.

printf already has so many directives, most of the good mnemonics are already taken. Add a "commas" directive as %y and an "invert" directive as %z.

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.

<lang perl6>use Lingua::EN::Numbers; use Acme::Text::UpsideDown;

sub printf (Str $format is copy, *@vars is copy) {

   my @directives = $format.comb(/ <?after <-[%]>|^> '%' <[ +0#-]>* <alpha>/);
   for ^@directives {
       if @directives[$_] eq '%y' {
           $format.=subst('%y', '%s');
           @vars[$_].=,
       } elsif @directives[$_] eq '%z' {
           $format.=subst('%z', '%s');
           @vars[$_].=&upsidedown;
       }
   }
   &CORE::printf($format, @vars)

}

printf "Integer %d with commas: %y\nSpelled out: %s\nInverted: %z\n",

      12345, 12345, 12345.&cardinal, 12345.&cardinal;</lang>
Output:
Integer 12345 with commas: 12,345
Spelled out: twelve thousand, three hundred forty-five
Inverted: ǝʌᴉɟ-ʎʇɹoɟ pǝɹpunɥ ǝǝɹɥʇ ‘puɐsnoɥʇ ǝʌꞁǝʍʇ

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. <lang ecmascript>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(", "))</lang>