Hack g-code.js parser
112 bytes

G1 $:(ƒ=(Τ,$=+[])=&gt;Τ[$]?Τ[$]+ƒ(Τ,++$+'5'[+[]]*(Τ[$]==':')):'')[ƒ('constructor')](ƒ('alert("pwned")'))()




function gcode(str){
  //Removes comment, parses command name and args
  const [,f,args] = (str.match(/^([GM]\d+) ([^;]+)/) || [,"",""]);
  //A1.2 B43 C -&gt; {A: 1.2, B: 42, C: 0}
  eval(f+"({"+args.replace(/(\w)([\d\.]*)/g,"$1: $2+0,")+"})");
}
const G1 = console.log;

payload = `G1 $:(ƒ=(Τ,$=+[])=&gt;Τ[$]?Τ[$]+ƒ(Τ,++$+'5'[+[]]*(Τ[$]==':')):'')[ƒ('constructor')](ƒ('alert("pwned")'))()`;
gcode(payload);





Each ƒ (U+0192), Τ (U+03A4) cost 2 bytes, they are not as same as ASCII f, T.




The gcode function will keep any /\W/ characters as is. So, we just need to use these characters to write some logic here. The idea is basically based on how JSF**k works. We construct the codes like this:

(()=&gt;{})["constructor"]('alert("pwned")')


We need string literal "constructor" and alert("pwned"). But gcode convert "constructor" to "c: +0,o: +0,n: +0,....r: +0". So we need to convert the string back. We use this function to do the convert:

ƒ=(Τ,$=+[])=&gt;Τ[$]?Τ[$]+ƒ(Τ,++$+'5'[+[]]*(Τ[$]==':')):''


Τ is the text inputed, $ is index. We first initialize $ to 0 using +[]. Then we output the character at the index position, and check if next character is ":", if so we skip the following 5 characters.



Thanks to l4m2, saves few bytes.