gm_lpeg - Epic pattern shit

Standard Lua pattern matching just wasn’t cutting it for me.
Then one day, I stumbled upon an awesome module for Lua… LPEG!
This module solved all my needs for task at the time, which was to remake my chatbot.
Unfortunately, though, it didn’t work with GMod.

A month or so later, I decided to get off my ass and compile it.
It was fucking easy as hell, compared to how hard I thought it would be.

Anyway, here it is:

Simply put the gm_lpegcore.dll and lpeg.lua in /lua/includes/modules/ and go “require(‘lpeg’)”.
How to actually use LPeg is available [url=]here.


  • PUC-Rio - Making LPeg
  • Me - Spending about 5 minutes making this module
  • Anders - His “Ugly hack” that he used in LuaSocket to get standard Lua modules to work with GMod.


#include "GMLuaModule.h"
extern "C" {
	int luaopen_lpeg (lua_State *L);
GMOD_MODULE(Init, Shutdown);
int Init(void) {
	lua_State *L = (lua_State*)g_Lua->GetLuaState();

	/* Ugly hack stolen from gm_luasocket by Anders: */
	lua_pushcfunction(L, luaopen_lpeg);
	lua_setfield(L, -2, "luaopen_lpeg");
	lua_setglobal(L, "lpeg_stuff"); /* ugly hack end */
	return 0;
int Shutdown(void) {
	return 0;

Compiled along side with lpeg.c, which is available at the official LPEG website.

Have fun!

What does this do that the normal pattern matching can’t?

That’s one awesome thing that the normal Lua pattern matching can’t do :smiley:

And try doing this with the normal pattern matching stuff:
local patt = lpeg.P"The fox jumped over the " * (lpeg.P"sheep" + lpeg.P"cow")
print(“The fox jumped over the sheep”, lpeg.match(patt, “The fox jumped over the sheep”))
print(“The fox jumped over the cow”, lpeg.match(patt, “The fox jumped over the cow”))
print(“The fox jumped over the llama”, lpeg.match(patt, “The fox jumped over the llama”))

Good example:
– Convert an argument string to a table
bot.patterns.argument = ‘"’ * lpeg.Cs(((lpeg.P(1) - ‘"’) + lpeg.P’""’ / ‘"’)^0) * ‘"’ + lpeg.C((1 - lpeg.S’" ')^0)
bot.patterns.argument_list = lpeg.Ct(argument*(^1*argument)^0)
function bot:ParseArguments(str)
return lpeg.match(self.patterns.argument_list, str) – So much friggen shorter than my original. I love LPeg!

Someone deleted the file and I can’t download it!

Friggen auto-report.

Re-uploaded to SolidFiles.

nice thanks for spending 5 mins of your time doing this

The power of LPEG is insane.
I made a JSON parser using it. Compare the below code to the decoding part in this.

local _ =^0
local escapes = {t=’ ‘, n=’
‘, f=’\f’, r=’\r’, b=’\b’, v=’\v’}
local null = newproxy()
local function char_escape(chars) return string.char(tonumber(chars, 16)) end
local function null_loop(t, done)
for k,v in pairs(t) do
if type(v) == “table” and not done[t] then
done[t] = true
null_loop(t, done)
elseif v == null then
t[k] = nil
return t
local function process_array(t)
for i, v in ipairs(t) do
if v == null then array* = nil end
return t
local json_grammar = lpeg.P{
pair = lpeg.Cg(lpeg.V’string’_’:‘lpeg.V’value’),
object = '{'
(’,'lpeg.V’pair’)^0)+0), rawset)lpeg.P’,’^-1’}’,
array = '['
value = lpeg.V’string’+lpeg.V’object’+lpeg.V’number’+lpeg.V’array’+lpeg.V’constant’,
escape = lpeg.P"\"/""
string = ‘"‘lpeg.Cs((lpeg.V’escape’+1-’"’)^0)’"’,
number = lpeg.C(lpeg.P’-’^-1lpeg.R’09’^1(’.‘lpeg.R’09’^1)^-1(lpeg.S’Ee’lpeg.S’±’^-1lpeg.R’09’^1)^-1)/tonumber,
constant = “true”*lpeg.Cc(true)+“false”*lpeg.Cc(false)+“null”*lpeg.Cc(null),
function decode(str)
local index, res = json_grammar:match(str)
null_loop(res, {})
return res

Example usage:
local function foreach(tab, func)
for k, v in pairs(tab) do
function printValue(tab, name)
local parsed = {}
local function doPrint(key, value, space)
space = space or ‘’
if type(value) == ‘table’ then
if parsed[value] then
print(space…key…’ = <’…parsed[value]…’>’)
parsed[value] = key
print(space…key…’ = {’)
local nspace = space…’ ’
foreach(value, function(key, value) doPrint(key, value, nspace) end)
if type(value) == ‘string’ then
value = ‘[[’…tostring(value)…’]]’
print(space…key…’ = '…tostring(value))
doPrint(name, tab)
{“accounting” : [
{ “firstName” : “John” ,
“lastName” : “Doe” ,
“age” : 23 ,
{ “firstName” : “Mary” ,
“lastName” : “Smith” ,
“age” : 32 ,
“sales” : [
{ “firstName” : “Sally” ,
“lastName” : “Green” ,
“age” : 27 ,
{ “firstName” : “Jim” ,
“lastName” : “Galley”,
“age” : 41 ,
“lol” : null,
}]], “result”)

This honestly looks like you just gave a monkey a typewriter with banana smeared over the punctuation keys. Do you not consider readability to be a particularly important when writing examples to try and get laymen to understand just how useful your product is?


He has a lot of simple examples above that.

local json_grammar = lpeg.P{
lpeg.V’value’, – Match space, followed by a value, followed by space. This is the initial thing to match.
pair = lpeg.Cg(lpeg.V’string’_’:‘lpeg.V’value’), – Match “string”: value in a capture group. The string and value, as they are captured apropriately in this patterns, get passed to the lpeg.Cf below.
object = '{'
lpeg.Cf( – Match “{”, followed by a ‘folding pattern’. It calls ‘rawset’ with any captures it gets. BUT, because there is a capture group above, those two will be passed together.
lpeg.Ct’’ – doing this will create a blank table. This is the table to push our data to. The ‘’ is so that it matches, well, ‘nothing’. And just always captures a blank table.
lpeg.V’pair’)^0)+0) – This matches (a pair followed by any number of (a comma then a pair)) or nothing
, rawset – The blank table is the first value to pass to rawset. The folding capture goes: rawset(rawset(rawset(t, 1, ‘a’), 2, ‘b’), 3, ‘c’). And because rawset returns the table, it all works out!
lpeg.P’,’^-1’}’, – Allow for up to 1 trailing comma, then match }
array = '['
lpeg.Ct( – Match [, followed by a new ‘table capture’. It basicaly inserts every capture into the table, numbered by order captured.
(’,'lpeg.V’value’)^0 – match a value followed by any number of (a comma and then a value)
)/process_array – Pass this table to the process_array function which will make it purty.
lpeg.P’,’^-1’]’, – Allow for up to 1 trailing comma, then match ]
value = lpeg.V’string’+lpeg.V’object’+lpeg.V’number’+lpeg.V’array’+lpeg.V’constant’, – Match any value
escape = lpeg.P"\"/""
( – Match , and replace it with a blank string in the end result.
lpeg.S’"\/bfnrtv’/escapes – Then match a basic escape, and replace it with the appropriate value in the end result.
+‘u’lpeg.C(lpeg.xdigitlpeg.xdigitlpeg.xdigitlpeg.xdigit)/char_escape), – OR, match u, and capture the 4 hex digits, then pass the captured digits to char_escape, and replace the end result with the return value.
string = ‘"‘lpeg.Cs((lpeg.V’escape’+1-’"’)^0)’"’, – Match ", followed by any number of (escape sequence OR one character) then ". Use lpeg.Cs because we want the replaces in escape to work (the / ones)
number = lpeg.C(lpeg.P’-’^-1lpeg.R’09’^1(’.‘lpeg.R’09’^1)^-1 – Capture (a potential minus sign, then at least one digit, then potentially (a dot, and then at least one digit)…
(lpeg.S’Ee’lpeg.S’±’^-1lpeg.R’09’^1)^-1 – and then potentially (an E or an e, then potentially (a + or a -), then at least one digit)
)/tonumber, – Take this capture number string, and tonumber it!
constant = “true”*lpeg.Cc(true)+“false”*lpeg.Cc(false)+“null”*lpeg.Cc(null), – (Match ‘true’, and capture true) OR (Match ‘false’ and capture false) OR (Match ‘null’ and capture a special null value, which is later turned to nil)

It’d be nice if you made some wrapper stuff that allowed us to input full strings and not have to have sixty million calls to lpeg.* functions ( since that’s mainly what makes it such a mess ), but then again, I’m not familiar with lpeg, so that’s probably like asking you to cure aids and end world hunger while curing all diseases at the same time.

Every lpeg.X thing is needed, except the lpeg.P ones, sometimes.
This is the proper way of doing it:
But you can go:
but not
Because you can’t add strings.
Whenever a lpeg pattern gets a string, it takes it as if it was lpeg.P’d

5 minutes is all it took him to make this, and it’s fine, because it works.
I think it’s sweet

my browser somehow sent it twice, -ship-

Hi, I am Andres Kramack from

It’s a ‘pleasure’ serving the public if you know what I mean and this message is

Here at Cathy Barry Adult Store, we offer a wide variety of dildos that come in all sizes from goblin-sized dicks to black-man draconic dicks. I know at Facepunch everyone loves good old dicks which is why I personally offer anyone with an association with Facepunch Forums a 50% discount code “DIL4FACEPUNCH”. Don’t forget, this offer lasts until the next Garry’s Mod update which we so much love.

Here are pictures of our products just for you!

Sponsored Images:

Got any questions? Visit my Steam Profile!