9
\$\begingroup\$

Let's parse and process Key-Language! Given the input of a sequence of keyboard keypresses and/or special keys, write a program, function, etc. that outputs the product when all the actions are processed based on the following keyboard:

+-------------------------------------------------------+
| ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | - | + |   |
| ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | _ | = |Del|
+-------------------------------------------------------+
|TAB| q | w | e | r | t | y | u | i | o | p | [ | ] | \ |
|   | Q | W | E | R | T | Y | U | I | O | P | { | } | | |
+-------------------------------------------------------+
|CAPS | a | s | d | f | g | h | j | k | l | ; | ' | RET |
|     | A | S | D | F | G | H | J | K | L | : | " |     |
+-------------------------------------------------------+
| SHIFT | z | x | c | v | b | n | m | , | . | / | SHIFT |
|       | Z | X | C | V | B | N | M | < | > | ? |       |
+-------------------------------------------------------+
|                                                       |
|                      SPACEBAR                         |
+-------------------------------------------------------+                         

The keys that output actual characters not consisting of whitespace and are able to be modified by other keys will be known as "character keys", and those that modify the output of other keys or output whitespace will be known as "special keys". The alphabet character keys, which will be shown in the input with uppercase letters, can be modified with either Shift or Caps Lock to produce uppercase letters, and the rest of the character keys can only be modified with Shift to produce their alternate characters. Therefore A in the input corresponds to the a A character key, whose normal output is a and whose modified output, obtainable with either the Shift or Caps Lock key, is A. On the other hand, /, which corresponds to the / ? character key, has a normal output of / and a modified output of ? obtainable with only with Shift this time.

Rules

  • The input will always be a string consisting of a sequence of the character keys and special keys. The full special key to string mapping for the input (i.e. the format they are guaranteed to be in the input) and their corresponding actions/outputs are as follows:

    • <DEL> -> Delete the previous character (including whitespace). If called when string is empty, nothing happens. If called 2 or more times in a row, 2 consecutive deletes happen. For instance, "RE<DEL><DEL>" should return an empty string ("") and also "R<RET><DEL><DEL>E" should return just "E".
    • <CAPS> -> Enable Caps Lock until <CAPS> appears again, upon which it is disabled, although it is not guaranteed to be disabled by the end of the input. Enabling this only modifies the upcoming alphabet keys resulting in them outputting only uppercase letters. For instance, "<CAPS>RE<CAPS>" results in the output "RE", but <CAPS>.<CAPS> would still result in a ".".
    • <RET> -> Add a new line.
    • <SPC> -> Add a single blank space.
    • <TAB> -> Add 4 spaces.
    • <SHFT> -> Shift is held down resulting in the alternate character of the upcoming keypress to be output, after which the key is released. For instance, "<SHFT>A" results in the output "A", "<SHFT>1" results in the output "!", and "<SHFT>1234" results in the output "!234" as only the first upcoming keypress is modified and nothing else. It is guaranteed that a character key will succeed a <SHFT>. Therefore, <SHFT><SPC> is not a possible input.
  • An empty string is also possible as input, for which the output should be nothing.

  • The use of any built-in that solves this problem directly is disallowed.
  • The use of standard loopholes is disallowed.

Test Cases

Presented in the format Actual String Input -> Actual String Output followed by an explanation for a few.

  1. 1<SHFT>2<TAB><CAPS>R.KAP.<SPC><SHFT>123 -> 1@ R.KAP. !23

    Output 1 as the 1 key is pressed without a toggle, then Shift is held down and the 2 key is pressed resulting in the @ output. Then the Shift key is released and Tab is pressed, resulting in a 4 spaced indentation. Following up, the Caps Lock key is pressed, after which the R,.,K,A,P, and . keys are pressed, resulting in the output R.KAP.. Finally, a single space is output followed by shift resulting in !23 being output when the 1,2, and 3 keys are pressed at the end.

  2. <SHFT>ABCDEFG<SHFT>HIJK<SHFT>1<SHFT>2<SHFT>3<SHFT>4567890 -> AbcdefgHijk!@#$567890

    The Shift key is held down followed by the A key, resulting in the output A followed by the output bcdefg when the B-G keys are pressed. Then, the Shift key is held down again succeeded by the H key, after which the output is H, followed by ijk when the I-K keys are pressed. Finally, the 1-4 keys are all modified as shift is held down before each keypress resulting in the output !@#$ finished off by 567890 when the 5-0 keys re pressed.

  3. <CAPS>THIS<SPC>IS<SPC>IN<SPC>ALL<SPC>CAPS<CAPS><SPC>NOW<SPC>THIS<SPC>IS<SPC>IN<SPC>ALL<SPC>LOWERCASE -> THIS IS IN ALL CAPS now this is in all lowercase

  4. <TAB><SPC><TAB><SHFT>1 -> !
  5. <CAPS>WWW<CAPS>.CODEGOLF.STACKEXCHANGE<SHFT>.COM -> WWW.codegolf.stackexchange>com
  6. PROGRAMMING<CAPS><SPC>IS<SPC><CAPS>AWESOME -> programming IS awesome
  7. <DEL><RET><DEL><RET><DEL> -> "" (Empty String)

    The delete key is pressed in the beginning after which nothing happens. Then, the Return key is pressed resulting in a new line, which is deleted after the backspace key is pressed again. Finally, the same sequence (new line followed by backspace) is repeated. After all this, the output is an empty string.

  8. <SHFT>HI<SPC>HOW<SPC>ARE<SPC>YOU<SHFT>/<RET><SHFT>I<SPC><SHFT>AM<SPC>O<DEL><SHFT>GOOD<SHFT>1 -> Hi how are you?\nI Am Good!

  9. <SHFT>,<CAPS>RET<CAPS><SHFT>. -> <RET>

    The string <RET> should be the actual string output. Thus, this should not output a new line.

  10. <CAPS>67890,.;'[]<CAPS> -> 67890,.;'[]

  11. <CAPS><SHFT>A -> A
  12. RE<DEL><DEL> -> "" (Empty String)
  13. U<RET><DEL><DEL>I -> i
  14. <DEL><DEL><DEL>5<DEL> -> "" (Empty string)
  15. "" (Empty String) -> "" (Empty String)

This is so the shortest code in bytes wins!

\$\endgroup\$
9
  • 5
    \$\begingroup\$ That's a weird Delete key you have there... \$\endgroup\$
    – Dennis
    Jul 2, 2016 at 2:35
  • 1
    \$\begingroup\$ @Dennis Well, I'm describing keys based on my MacBook Pro's keyboard, where the delete key deletes the previous character. I still agree with you though. It's a pretty weird layout. \$\endgroup\$
    – R. Kap
    Jul 2, 2016 at 2:36
  • \$\begingroup\$ Ah, that explains it. It's called Backspace on literally every keyboard I've ever owned. mumbles something about normal keyboard keypresses \$\endgroup\$
    – Dennis
    Jul 2, 2016 at 2:40
  • 1
    \$\begingroup\$ In test #2, should the output be AbcdefgHijk!@#$567890? Also, in test #8, <SHFT> is at the end of the string, but the rules state: "It is guaranteed that a character key will succeed a <SHFT>." \$\endgroup\$ Jul 2, 2016 at 5:13
  • \$\begingroup\$ @atlasologist Yes, you are right, and nice catch! I forgot to update those. \$\endgroup\$
    – R. Kap
    Jul 2, 2016 at 6:25

3 Answers 3

6
\$\begingroup\$

16-bit x86 machine code, 140 139 bytes

Saved 1 byte by replacing DL with DX in the second-to-last opcode. Also corrected jump offsets in disassembly to match hex dump.

Since the nature of the task requires some pre-initialized data, and the answer is not a full program but a function, I assume that there's a data section in the program and the linker promptly updates the address of the data. The address placeholder is denoted by '????'.

This is a hex representation of the code. The parameters are pointer to input string in SI and pointer to the output buffer in DI. Strings are assumed to be NULL-terminated.

8D1E????89F931D231C0FCAC84C07419D0EA72173C3C74263C41720A3C5A770684F675020C20AAEBE2AAC33C41720B3C5A76F324170402D7EBEC2C27EBF94646AC46240F74154848741748741948741A4848741A39F973B34FEBB04680F601EBAAB020AAAAAAB020EBBCB00AEBB84642EB99

Content of the mapping table (25 bytes):

"   =<_>?)!@#$%^&*( :{}|`

The byte count accounts for both code and data.

Disassembly:

8d 1e ?? ??        lea    bx,ds:???? ;Load address of mapping table to BX
89 f9              mov    cx,di      ;Save pointer to output buffer in CX
31 d2              xor    dx,dx      ;DX is the status register, bit 0 - shift status
31 c0              xor    ax,ax      ;bit 8 - caps lock status
fc                 cld               ;Clear DF

_loop:
ac                 lodsb             ;Fetch next char
84 c0              test   al,al      ;If end of string found
74 19              je     _end       ;break
d0 ea              shr    dl,1       ;Copy shift flag to CF and clear it
72 17              jc     _shift     ;Branch to input procssing with shift set
3c 3c              cmp    al,0x3c    ;If AL == '<'
74 26              je     _special   ;branch to special character processing
3c 41              cmp    al,0x41    ;At this point anything
72 0a              jb     _out       ;not in 'A'..'Z' range
3c 5a              cmp    al,0x5a    ;should be printed unmodified
77 06              ja     _out
84 f6              test   dh,dh      ;If caps lock status flag is set
75 02              jne    _out       ;go to printing right away
0c 20              or     al,0x20    ;otherwise convert to lower case
_out:
aa                 stosb             ;Store AL into output buffer
eb e2              jmp    _loop      ;Continue
_end:
aa                 stosb             ;NULL-terminate the output string
c3                 ret               ;and return

_shift:
3c 41              cmp    al,0x41    ;AL in the range [0x27..0x3b] with
72 0b              jb     _xlat0     ;a couple of holes in it

3c 5a              cmp    al,0x5a    ;AL in the range 'A'..'Z'
76 f3              jbe    _out       ;Since shift is active, go print it

24 17              and    al,0x17    ;AL is 0x5b, 0x5c, 0x5d or 0x7e,
04 02              add    al,0x2     ;convert to the [0x15..0x18] range
_xlat:
d7                 xlatb             ;Lookup mapping table (AL=[BX+AL])
eb ec              jmp    _out
_xlat0:
2c 27              sub    al,0x27    ;Convert AL to be a zero-based index
eb f9              jmp    _xlat      ;Reuse lookup code

_special:                            ;The next 4 or 5 chars are special character opcode
46                 inc    si         ;Since correct input format is guaranteed
46                 inc    si         ;don't bother reading & checking all of them,
ac                 lodsb             ;just load the third one and skip the rest
46                 inc    si         ;The lower 4 bits of the 3rd char
24 0f              and    al,0xf     ;allow to differentiate opcodes

74 15              jz     _sc_caps   ;0x0
48                 dec    ax
48                 dec    ax
74 17              jz     _sc_tab    ;0x2
48                 dec    ax
74 19              jz     _sc_spc    ;0x3
48                 dec    ax
74 1a              jz     _sc_ret    ;0x4
48                 dec    ax
48                 dec    ax
74 1a              jz     _sc_shft   ;0x6

_sc_del:                             ;0xC, <DEL> opcode
39 f9              cmp    cx,di      ;Check the length of the current output
73 b3              jae    _loop      ;DI <= CX ==> NOOP
4f                 dec    di         ;Remove the last char
eb b0              jmp    _loop
_sc_caps:                            ;<CAPS> opcode
46                 inc    si         ;Consume leftover '>' from the input
80 f6 01           xor    dh,0x1     ;Flip caps lock status bit
eb aa              jmp    _loop
_sc_tab:                             ;<TAB> opcode
b0 20              mov    al,0x20    ;Space char
aa                 stosb             ;Print it three times
aa                 stosb             ;and let the <SPC> handler
aa                 stosb             ;do the last one
_sc_spc:                             ;<SPC> opcode
b0 20              mov    al,0x20    ;Space char
eb bc              jmp    _out       ;Go print it
_sc_ret:                             ;<RET> opcode
b0 0a              mov    al,0xa     ;Newline char
eb b8              jmp    _out       ;Go print it
_sc_shft:                            ;<SHFT> opcode
46                 inc    si         ;Consume leftover '>' from the input
42                 inc    dx         ;Set shift status bit (DL is guaranteed to be zero)
eb 99              jmp    _loop

For 32-bit instruction set the code is absolutely the same except for the first instruction which is 2 bytes longer due to 32-bit addressing (8d1d???????? lea ebx,ds:????????)

\$\endgroup\$
7
  • \$\begingroup\$ Nice work! :) If it's not too much trouble, can you please check if your program returns and output of i for the test caseU<RET><DEL><DEL>I and an empty string for the input RE<DEL><DEL>? I clarified the rules a little bit regarding the delete key, so if those 2 test cases don't work, could you please also update your code so that it produces the correct output for those test cases? Thank you! \$\endgroup\$
    – R. Kap
    Jul 5, 2016 at 7:44
  • \$\begingroup\$ All test cases succeeded. Why would <DEL> work incorrectly? It's just a register decrement with boundary check \$\endgroup\$
    – meden
    Jul 5, 2016 at 9:06
  • \$\begingroup\$ All right. I just wanted to make sure that your program worked as it should. Great answer. \$\endgroup\$
    – R. Kap
    Jul 5, 2016 at 9:09
  • \$\begingroup\$ We need more special cases. It'd be more interesting if <DEL> could not delete the <RET>. I can implement it in just 3 bytes. \$\endgroup\$
    – meden
    Jul 5, 2016 at 16:25
  • 1
    \$\begingroup\$ When typing in a shell's command line that perfectly makes sense. But, mind you, I'm not asking for the rule change. Thanks for the challenge. \$\endgroup\$
    – meden
    Jul 6, 2016 at 13:55
4
\$\begingroup\$

Retina, 136 bytes

Probably can be golfed further.

<SHFT>
§
<SPC>

<TAB>

<CAPS>
¶
<RET>
þ
<DEL>
÷
T`L`l`(?<=^(.*¶.*¶)*).+
T`-=;'[]/\\,.w`\_+:"{}?|<>_)!@#$%^&*(lL`§.
§|¶

i(`þ
¶
[^§]?÷

Verify all testcases. (Slightly modified to run all testcases at once.)

\$\endgroup\$
21
  • \$\begingroup\$ Caps + Shift + A = a on my keyboard. \$\endgroup\$
    – Neil
    Jul 2, 2016 at 18:40
  • \$\begingroup\$ @Neil Well, for the purposes of this challenge (and according to my Macbook Pro's keyboard) Caps+Shift+A = A. Man my keyboard is weird... \$\endgroup\$
    – R. Kap
    Jul 2, 2016 at 18:47
  • \$\begingroup\$ CAPS + SHIFT + A = A. Why on earth would caps invert shift?? \$\endgroup\$
    – cat
    Jul 2, 2016 at 21:30
  • 1
    \$\begingroup\$ @cat in millions on Windows system CAPS invert shift, no matter how many question marks you write. Because it's convenient and users are used to it \$\endgroup\$
    – edc65
    Jul 2, 2016 at 22:00
  • 1
    \$\begingroup\$ Aaaand, two 110 byte solutions: retina.tryitonline.net/…, retina.tryitonline.net/… ... I think I'm done for now. ;) \$\endgroup\$ Jul 5, 2016 at 7:27
4
\$\begingroup\$

JavaScript (ES6), 207

Updated to fix the bug with repeated deletes, even some bytes shorter.

s=>s.replace(/<\w+>|./g,x=>(k=x[3])=='L'?o=o.slice(0,-1):k=='P'?l=!l:k=='F'?s=0:o+=k?k<'C'?'    ':k<'D'?' ':`
`:s?l?x.toLowerCase():x:s=")!@#$%^&*("[x]||'_={}|:"<>?'["-+[]\\;',./".indexOf(x)]||x,l=s,o='')&&o

less golfed

s=>s.replace( /<\w+>|./g, x =>
  (k=x[3]) == 'L' ? o = o.slice(0,-1)
  : k == 'P' ? l = !l
  : k == 'F' ? s = 0
  : o+= k ? k < 'C' ? '    ' : k < 'D' ? ' ' : '\n'
  : s ? l ? x.toLowerCase() : x
  : s = ")!@#$%^&*("[x] || '_={}|:"<>?' ["-+[]\\;',./".indexOf(x)] || x,
  l = s, o = ''
) && o

Test

F=
s=>s.replace(/<\w+>|./g,x=>(k=x[3])=='L'?o=o.slice(0,-1):k=='P'?l=!l:k=='F'?s=0:o+=k?k<'C'?'    ':k<'D'?' ':`
`:s?l?x.toLowerCase():x:s=")!@#$%^&*("[x]||'_={}|:"<>?'["-+[]\\;',./".indexOf(x)]||x,l=s,o='')&&o

console.log=(...x)=>O.textContent+=x.join` `+'\n'

;[["1<SHFT>2<TAB><CAPS>R.KAP.<SPC><SHFT>123", "1@    R.KAP. !23"]
,["<SHFT>ABCDEFG<SHFT>HIJK<SHFT>1<SHFT>2<SHFT>3<SHFT>4567890", "AbcdefgHijk!@#$567890"]
,["<CAPS>THIS<SPC>IS<SPC>IN<SPC>ALL<SPC>CAPS<CAPS><SPC>NOW<SPC>THIS<SPC>IS<SPC>IN<SPC>ALL<SPC>LOWERCASE", "THIS IS IN ALL CAPS now this is in all lowercase"]
,["<TAB><SPC><TAB><SHFT>1", "         !"]
,["<CAPS>WWW<CAPS>.CODEGOLF.STACKEXCHANGE<SHFT>.COM", "WWW.codegolf.stackexchange>com"]
,["PROGRAMMING<CAPS><SPC>IS<SPC><CAPS>AWESOME", "programming IS awesome"]
,["<DEL><RET><DEL><RET><DEL>", ""]
,["<SHFT>HI<SPC>HOW<SPC>ARE<SPC>YOU<SHFT>/<RET><SHFT>I<SPC><SHFT>AM<SPC>O<DEL><SHFT>GOOD<SHFT>1", "Hi how are you?\nI Am Good!"]
,["<SHFT>,<CAPS>RET<CAPS><SHFT>.", "<RET>"]
,["<CAPS>67890,.;'[]<CAPS>", "67890,.;'[]"]
,["<CAPS><SHFT>A", "A"]
,["U<RET><DEL><DEL>I", "i"]
,["RE<DEL><DEL>", ""]
,["", ""]].forEach(t=>{
  var i=t[0],k=t[1],r=F(i)
  console.log(
    k==r?'OK':'KO',i,'\n->',r,k==r?'\n':'(should be ->'+k+')\n'
  )
})
<pre id=O></pre>

\$\endgroup\$
3
  • \$\begingroup\$ Nice job! :) If it's not too much trouble, can you please check if your program returns and output of I for the test case U<RET><DEL><DEL>I and an empty string for the input RE<DEL><DEL>? I clarified the rules a little bit regarding the delete key, so if those 2 test cases don't work, could you please also update your code so that it produces the correct output for those test cases? Thank you! \$\endgroup\$
    – R. Kap
    Jul 5, 2016 at 7:09
  • \$\begingroup\$ Wrong for these test cases. I have to take another approach. Meanwhile, i presume U<RET><DEL>I should give i not I \$\endgroup\$
    – edc65
    Jul 5, 2016 at 7:34
  • \$\begingroup\$ Yes, you are correct about that. Updated. \$\endgroup\$
    – R. Kap
    Jul 5, 2016 at 7:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.