Parse a raw IRC message

In the IRC protocol, raw messages look similar to this:

command arg1 arg2 :arg3 with spaces :arg4 with spaces :arg5


In a shell environment (e.g. bash), that would be equivalent to:

command arg1 arg2 "arg3 with spaces" "arg4 with spaces" arg5


The format specification is as follows:

• Raw messages will only contain printable ASCII characters (ordinals 32-126).
• Raw messages will start with a command, which will not contain any spaces.
• Any number of arguments may follow a command. Arguments are delimited by one or more spaces followed by a colon (<space>: and <space><space>: are both valid delimiters).
• Any arguments that do not contain spaces may omit the colon from the delimiter so long as all previous arguments (if any) have omitted the colon.
• Colons will not appear within arguments or commands.
• Arguments will not begin with a space.

For example, these are all valid raw messages:

join #foo #bar #baz
msg person hi
msg #channel :hello world
help :how do I use IRC
foo bar :baz bip :abc def :ghi


These are all invalid raw messages:

error :n:o colons within arguments
error ::need spaces between colons
:error no colons in commands
error non-ASCII character Ω


Given a valid raw IRC message as input, output a list containing the command and the arguments, properly parsed.

Test Cases

"join #foo #bar #baz" -> ["join", "#foo", "#bar", "#baz"]
"msg person hi" -> ["msg", "person", "hi"]
"msg #channel :hello world" -> ["msg", "#channel", "hello world"]
"help :how do I use IRC" -> ["help", "how do I use IRC"]
foo bar :baz bip :abc def :ghi :xyz -> ["foo", "bar", "baz bip", "abc def", "ghi", "xyz"]

• Could the final argument end with spaces? Dec 3 '16 at 0:16
• @JonathanAllan Yes
– user45941
Dec 3 '16 at 1:43
• Can we assume that input will be valid? Dec 4 '16 at 12:48
• @Titus Yes, that was the goal of defining what constitutes valid input.
– user45941
Dec 4 '16 at 23:32
• Does the output have to be a flat list? Dec 8 '16 at 16:36

PHP, 87 bytes

$a=($s=preg_split)("# +:#",$argv[1]);array_splice($a,0,1,$s("# +#",$a[0]));print_r($a);  The first regex splits the argument by one or more spaces followed by a colon. The second regex splits the first element by one or more spaces with no colon. array_splice replaces the first element with the result of the second preg_split. print_r prints the resulting array. function version for testing $cases=[
"join #foo #bar #baz" => ["join", "#foo", "#bar", "#baz"],
"msg person hi" => ["msg", "person", "hi"],
"msg #channel :hello world" => ["msg", "#channel", "hello world"],
"help :how do I use IRC" => ["help", "how do I use IRC"],
"foo bar :baz bip :abc def :ghi :xyz" => ["foo", "bar", "baz bip", "abc def", "ghi", "xyz"],
];
function i($argv_1_){$a=($s=preg_split)("# +:#",$argv_1_);array_splice($a,0,1,$s("# +#",$a[0]));return$a;}
foreach($cases as$x=>$e) {$y=i($x); echo "case '$x': ['",join("','",$y),'"]: ',($y<>$e?"FAIL. Expected: ['". join("','",$e)."']":OK),"\n";
}

• what should i learn from that downvote? Dec 3 '16 at 12:53
• I'm not sure why whoever downvoted did as they did, but maybe it didn't work for all test cases? Dec 3 '16 at 16:46
• @Downgoat Or maybe someone just doesn´t like my nose. It doesn´t check the input for correctness; but I don´t see that required anywhere. Dec 4 '16 at 12:58

Retina, 24 bytes

M!:.+?(?=\s+:|$)|\S+ :  (note there is a trailing newline) Try it online! • Added that new line. Could you pop an explanation in at some point? Dec 6 '16 at 15:25 PowerShell v2+, 43 34 bytes $a,$b=$args-split' +:';-split$a;$b


This -splits the input string $args on the regex +: (i.e., one or more spaces and a colon), stores the left-most (i.e., first result) into $a and any remaining results into $b. We then need to -split $a on spaces (here done by leveraging that whitespace is the default for unary -split), and that's left on the pipeline. We then simply put $b on the pipeline. Output via implicit Write-Output inserts a newline between elements. Test cases PS C:\Tools\Scripts\golfing> .\parse-raw-irc.ps1 'join #foo #bar #baz' join #foo #bar #baz PS C:\Tools\Scripts\golfing> .\parse-raw-irc.ps1 'msg person hi' msg person hi PS C:\Tools\Scripts\golfing> .\parse-raw-irc.ps1 'msg channel :hello world' msg channel hello world PS C:\Tools\Scripts\golfing> .\parse-raw-irc.ps1 'help :how do I use IRC' help how do I use IRC PS C:\Tools\Scripts\golfing> .\parse-raw-irc.ps1 'foo bar :baz bip :abc def :ghi :xyz' foo bar baz bip abc def ghi xyz  Mathematica, 44 bytes Flatten@MapAt[s=StringSplit,#,1]&@s[#," :"]&  Jelly, 19 bytes ;⁷œṣ⁾ :µḢḲ;œr€⁶ỴF$€


Note: As a full program the implicit print that Jelly performs may be a little confusing in this case, since a list containing strings (really lists of characters in Jelly) is printed as if it were a single string. So, for completeness here are test harnesses employing the quick Ç which calls the previous link monadically:

1. Ṿ shows the Jelly representation of a list of character lists;
2. Y joins the list with line feeds;
3. ŒṘ shows the underlying Python representation (showing the actual lists of characters).

How?

Handling the edge case of possible trailing spaces for the ultimate argument (the only one which may have trailing spaces) is quite an expense at 6 bytes; maybe there is a shorter way?

;⁷œṣ⁾ :µḢḲ;œr€⁶ỴF$€ - Link expecting a single list of characters as input ; - concatenate ⁷ - a line feed (to avoid right trimming spaces from ultimate argument) œṣ - split on sublists equal to ⁾ : - two character list [' ',':'] µ - monadic chain separation (call the split result z) Ḣ - head - pop first element (modifies z) Ḳ - split at spaces ; - concatenate with z ⁶ - space œr€ - right-strip from €ach$€ - last two links as a monad for €ach
Ỵ    -     split on line feeds
F   -     flatten (removes the line feed from the beginning)

• How does this handle <space><space>:? Dec 2 '16 at 23:52
• @isaacg - thanks for pointing that out, I have fixed it up, although it opened up an edge case I had to address too. Dec 3 '16 at 11:48
• I think you can add the Ṙ×0 test harness too. Dec 4 '16 at 13:54

C, 140 bytes (excluding sample usage)

void p(char*m){char*c=strchr(m,':');if(c) {*c=0;c++;}
char*n=strtok(m," ");while(n) {puts(n);n=strtok(NULL," ");}
if(c){puts(c);}}


Sample usage:

#include <string.h>
#include <stdio.h>
void parse(char*msg){char*c=strchr(msg,':');if(c) {*c=0;c++;}
char*n=strtok(msg," ");while(n){puts(n);n=strtok(NULL," ");}
if(c){puts(c);}}
main(){char msg[]="sample : abcnsjd hajs";parse(msg);}


• Welcome to PPCG! :) First of all, you should format your golfed code the same way as your 'sample usage' code. You could turn every name that is over 1 characters to 1, ex. parse to p and msg to m. Useless whitespaces could be removed from after if(c) (both cases) and while(n). Dec 4 '16 at 13:37
• I count 131 bytes, and there's still extraneous whitespace.
– user45941
Dec 19 '16 at 5:08

Python 2, 64 60 bytes

import re;a=re.split(' +:',input());print a[0].split()+a[1:]


Edit:
Requires quoted input to use input.
Preserves trailing spaces on last argument.

• Since we allow input to be wrapped in quotes, you can use input() instead of raw_input()
– user45941
Dec 5 '16 at 21:59
• Needs to handle the edge case of the final argument having trailing spaces Dec 6 '16 at 15:21

JavaScript (ES6), 595756 49 bytes

This is my first entry on PPCG, any input appreciated!

(s,[a,...b]=s.split(/ +:/))=>a.split .concat(b)


EDIT History:

• Thanks @Mego for saving 2 bytes by making it an anonymous function.

• Replaced \s with   to save 1 byte.

• Thanks @BassdropCumberwubwubwub for showing me the split  syntax and the [a,...b]=array_expression_here syntax. I didn't know these were possible, thank you very much! Very clever!

Test snippet

let f = (s,[a,...b]=s.split(/ +:/))=>a.split .concat(b)
<input id=I type="text" size=70 value="command arg1 arg2 :arg3 with spaces :arg4 with spaces :arg5"><button onclick="console.log(f(I.value))">Run</button>

• Welcome to PPCG! The only improvement I see is that you can drop the f= to make it an anonymous function, which are allowed on the site by default.
– user45941
Dec 5 '16 at 0:07
• You can write split(" ") as split  to save 2 bytes Dec 5 '16 at 16:11
• Actually, even shorter like this (s,[a,...b]=s.split(/ +:/))=>a.split .concat(b) Dec 6 '16 at 16:32

Perl 6,  49  47 bytes

{m/(<-[\ :]>+)+%%" "+":"?(.+?)*%[" "+":"]$/.flat}  Perl 6 version of the Python answer {$/=.split(/" "+":"/);|$0.split(" "),|$/[1..*]}


The to |s are there to flatten the list.

the first entry expanded:

{    # bare block lambda with implicit parameter ｢$_｣ m/ # regex match implicitly against ｢$_｣

(               # capture
<-[\ :]>+     # at least one non-space non-colon character
)+              # at least once

%%                # separated (with optional trailing separator)

" "+            # by at least one space

":"?              # optionally match a colon ( needed for rest of regex )

(               # capture
.+?           # at least one character as short as possible
)*              # any number of times

%                 # separated (no optional trailing separator)

[ " "+ ":" ]    # by at least one space, and a colon

\$                 # match end of string (needed because of ｢.+?｣)
/

.flat         # flatten the captures
}
`