Skip to content

Commit

Permalink
Merge pull request #69 from skx/regexp-literal-capture
Browse files Browse the repository at this point in the history
Regexp literal capture
  • Loading branch information
skx authored Jul 28, 2020
2 parents 7dae5a5 + 1925ca2 commit 9fab472
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 16 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,11 +500,16 @@ capture groups in the match.

This is demonstrated in the [examples/regexp.mon](examples/regexp.mon) example.

You can also perform matching, but not capturing, with a literal regular expression object:
You can also perform matching (complete with captures), with a literal regular expression object:

if ( Name ~= /steve/i ) { puts( "Hello Steve\n" ); }
if ( Name !~ /[aeiou]/i ) { puts( "You have no vowels.\n" ); }

// captures become $1, $2, $N, etc.
ip = "192.168.1.1";
if ( ip ~= /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ ) {
printf("Matched! %s.%s.%s.%s\n", $1, $2, $3, $4 );
}

## 2.12 File I/O

Expand Down
43 changes: 31 additions & 12 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
if isError(right) {
return right
}
res := evalInfixExpression(node.Operator, left, right)
res := evalInfixExpression(node.Operator, left, right, env)
if isError(res) {
fmt.Printf("Error: %s\n", res.Inspect())
if PRAGMAS["strict"] == 1 {
Expand Down Expand Up @@ -265,7 +265,7 @@ func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
}
}

func evalInfixExpression(operator string, left, right object.Object) object.Object {
func evalInfixExpression(operator string, left, right object.Object, env *object.Environment) object.Object {
switch {
case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
return evalIntegerInfixExpression(operator, left, right)
Expand All @@ -284,7 +284,7 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
case operator == "!~":
return notMatches(left, right)
case operator == "~=":
return matches(left, right)
return matches(left, right, env)

case operator == "==":
return nativeBoolToBooleanObject(left == right)
Expand All @@ -302,7 +302,7 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
}
}

func matches(left, right object.Object) object.Object {
func matches(left, right object.Object, env *object.Environment) object.Object {

str := left.Inspect()

Expand All @@ -323,8 +323,17 @@ func matches(left, right object.Object) object.Object {
return newError("error compiling regexp '%s': %s", right.Inspect(), err)
}

res := r.FindStringSubmatch(str)

// Do we have any captures?
if len(res) > 1 {
for i := 1; i < len(res); i++ {
env.Set(fmt.Sprintf("$%d", i), &object.String{Value: res[i]})
}
}

// Test if it matched
if r.MatchString(str) {
if len(res) > 0 {
return TRUE
}

Expand Down Expand Up @@ -582,14 +591,24 @@ func evalStringInfixExpression(operator string, left, right object.Object) objec
// if the condition matches, and running any optional else block
// otherwise.
func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object {
condition := Eval(ie.Condition, env)
//
// Create an environment for handling regexps
//
var permit []string
i := 1
for i < 32 {
permit = append(permit, fmt.Sprintf("$%d", i))
i++
}
nEnv := object.NewTemporaryScope(env, permit)
condition := Eval(ie.Condition, nEnv)
if isError(condition) {
return condition
}
if isTruthy(condition) {
return Eval(ie.Consequence, env)
return Eval(ie.Consequence, nEnv)
} else if ie.Alternative != nil {
return Eval(ie.Alternative, env)
return Eval(ie.Alternative, nEnv)
} else {
return NULL
}
Expand Down Expand Up @@ -637,7 +656,7 @@ func evalAssignStatement(a *ast.AssignStatement, env *object.Environment) (val o
return newError("%s is unknown", a.Name.String())
}

res := evalInfixExpression("+=", current, evaluated)
res := evalInfixExpression("+=", current, evaluated, env)
if isError(res) {
fmt.Printf("Error handling += %s\n", res.Inspect())
return res
Expand All @@ -654,7 +673,7 @@ func evalAssignStatement(a *ast.AssignStatement, env *object.Environment) (val o
return newError("%s is unknown", a.Name.String())
}

res := evalInfixExpression("-=", current, evaluated)
res := evalInfixExpression("-=", current, evaluated, env)
if isError(res) {
fmt.Printf("Error handling -= %s\n", res.Inspect())
return res
Expand All @@ -670,7 +689,7 @@ func evalAssignStatement(a *ast.AssignStatement, env *object.Environment) (val o
return newError("%s is unknown", a.Name.String())
}

res := evalInfixExpression("*=", current, evaluated)
res := evalInfixExpression("*=", current, evaluated, env)
if isError(res) {
fmt.Printf("Error handling *= %s\n", res.Inspect())
return res
Expand All @@ -687,7 +706,7 @@ func evalAssignStatement(a *ast.AssignStatement, env *object.Environment) (val o
return newError("%s is unknown", a.Name.String())
}

res := evalInfixExpression("/=", current, evaluated)
res := evalInfixExpression("/=", current, evaluated, env)
if isError(res) {
fmt.Printf("Error handling /= %s\n", res.Inspect())
return res
Expand Down
14 changes: 14 additions & 0 deletions examples/regexp.mon
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ if (out) {
} else {
puts( "Not true!\n");
}


//
// The same thing using literal a regular expression
//

ip = "192.168.1.1";

if ( ip ~= /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ ) {
printf( "We matched an IP address succesfully.\n");
printf( "Captures: %s.%s.%s.%s\n", $1, $2, $3, $4 );
}


2 changes: 1 addition & 1 deletion lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ func (l *Lexer) peekChar() rune {
// determinate ch is identifier or not
func isIdentifier(ch rune) bool {

if unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '.' || ch == '?' || ch == '_' {
if unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '.' || ch == '?' || ch == '$' || ch == '_' {
return true
}

Expand Down
4 changes: 2 additions & 2 deletions static.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ var RESOURCES = map[string]EmbeddedResource{

"data/stdlib.mon": {
Filename: "data/stdlib.mon",
Contents: "H4sIAAAAAAAC/+w87ZbbtpX/5ymueboxlaE0omSncSaKT9pMd72bJjnjSbtnR2oNkZCEDgVwAWg+6jMvkP7Jg+S98hp7cAGQAEWNJ3Z2T39s2qYWeHGJ+4n7RZ+cHJ2cwMWGKVixikIhuCaMK1jteKGZ4ArINWEVWVYUlndQ0hXZVToDtq0ruqVc0xIYh3w8/heDqd5JClvBr+jd6MgiZyM6sm9gCmoiNYgViJ0EpQkviSyHFVtKIpsNr1ZwJ3Zww9QGtIAtuaJQbAhfU2V+6w3d2xoct6QrxmlpEG2opAbV06oCTmlpdku63LGqhDdKE82K0Vq8AbLSVAItmWZ8DdozIwOiDBq9IbpljHk93S5pWdISClHfGXJwTyFKCjcbVmwMpYwX1a5E7lgkFNaUU0kMxyyHhoxrKmtJNZWe+Iv+I+4URRRvDN8J129AC1F1OFYK/lQDqSQl5R1syHUIz7jSpKpoCSuqzRE1VOyKghKfOSzmn7WANdUw3AGsmd7slqNCbE/U1e2JQ3TigL8RNyCppyjmyjWVigneMAY1i/CyoUxvKGpGSz7cML0xREgv6uBU7tUwZLzeaSiJJicwFDttfjVMcsBrAfYlyJwjh+WPRG/o1kCSysjSKJA20j3CH/DdK5jBdJQ/y5+/mHzyfPr80xe/fTE9dU/PAGYAk9Fv808nn5r/PXv+Yvzs+anH/gdW0aFY/o0WWsFKSHh98dWrbzLzf99+f5Eh7a8vvjo7P/cSOzfnkUa1OIiacqt4VKGRcbKlJzXRG7ihVoxbsmbFUGnJ+FpZHTNYiKQgaSHWnP2dlmarYWBiECZQS7Zlml3TUUsmngtm+MoUkif4+0kCg9MW4NvvL2KIb7+/6ICcnZ/HIGfn5xak4UhjjyhYwiGx/BmZg9EVu8XDt75FCyBVBfqupsoz6c9mK2oQGjUzCIHxYV2RAg0bCmIWrsWV1UD7iuGW6o0oQw2CJBkZ1Ong1K9MuwuXi+4K1YX3YVxpSkqj0RtyjcISIHccz5vCsiIbwH1H3hG5sziU8PYIACTVO8lTXFK0Wg0Gp0f3/8wsswoXMW1v6XKxv/YIxrk9D7HOo42Y5xZD9nkGXhj/SJSiUr8BS5BhkLixThvdM+O9V4851RJ9kqbKOGh6Tc0q21LjKJmCSpCSlqPonPZd6TWpMtiqNcwgOadqV2m4IQq40PBUyx19akzDEsFWkFqduSbVAGYzSCxBiQNAQhHFDOg1qVK4JpXhj32G+584iHYLQL3TKoXEnWieJJABniuZJwNYEWYc/xASPKhZ5UmLFYDeMp3mzcL9kf8XrRRtXmPfbk+09+7H4r03MnPnhMTwB3f4FbMQ/k6ewIpUip5GUE9wLYKzlmgdHHJ2nph7x7CiF8y4ucfAGV+3D9d6fqk0UBsKGS0nHIiU5C5SlJWBSu0DxzrkZUV5iosD+GIGecNVp+t2w+V4YVl6f3QEcHLingJnFcYZ27piBdMY7Ywi5rrXXo6zfGFpGMdEWoDLeaI0vabzJMsXjlS3EIM/cfCLgeNBYHiSqoh+GNrAz3goxx4FZ//5+7PvLtAaEVXEJIMh5lFFdWsNl4vToz6+BWwz8AxmkFt9WwmZAoPPQ/AeM6t3apPaX5njOFu0Fnd8bP/Y2oTjv91yGquz0Rok5HIxcKxElF1WmiNZgi/H2cRIB6HzB6DyAG7yMFw2zZ5lzxvo5zG0c6H2mJHs3Ykv80Wj5k7EX5NHqHlFDmp5s/oFjLtqbnneCmmYLyJ9f+MVfldVb96h8ngEZGkfn57Yx63+evJe2Stiza6p11+6rfXdy9jfmwcj+yANPbo5O95IaGORkuHJjVM77eqPdWvR6S8XDfbOwS+N9A8+bIU47QAFNP4eg4WGyva25UBJsQHKtbzLXJyOJ2xsGQN5G7JrurUhrk10DL5a0pIVJguw+0yiJXd01MO7Fas0lWmwxfHKm+741P+qYNbytVntuoPAyAO2G0gkB2Zg9l+yRXiDNm9PEWgQXmbxW9A3gHcOFmV8SwYuAvW1xzu4DMFkOQ1HDZevKK1B8OrO3Kg76uJ6DPKSSTIKzVsDMTRDnsEkgylkMMyz1k1n8OwZLE5jIswO0nB8xVM+gLfN5YHWAadwH17XVptJG3lZdzBZzJPoxu/VbceUOGSIQgQSvQv2TKFF0uiuy5bcq0y8ulopqjNzUsLvMqeaoGpasBUz6bamWxPmmVWrdL16yMvUQDqqVkKiDTBe0tvMysMgMcrT0oLcsc9mM/uiDlccPYimoee+VY6TEwwLV2LHy1GgLcO84wjQiS/sOX+Lchh27oYIJO+73SOIaZ8/tBC9MA3vvxI0dI6WfHrLlDbBPStpy+k+b+nKIOplasJ6iIN55K99t3v6xBAKg35utLgmXRcIPTBT5wJ9pMZL2DLOtrttK+D9S8wee8s4eviObzrsnOw9AJ8H4Ugj3fFppAFfKrXbugjIn0T5o/k3bBn3zmvsXJ3JTu5qCsWGFleMr0dxRrFlHPmXMK7pmsoEPvqo82RVCaJDU/Zpg6fZuqMbIa+UuRf4brukUsEQtdbkFA0+G+s39hwG+g2hr1ZwY+jkJRBQW1JVVILgNANlWKpHPQ7c4uslNSTWefUDBEdP94h+X7I91g7p3Szn/ig4rNsDn6NAgzME8m0uJ3eh9N4nW8Zjo0gblbe6OsMEIjaLFPLRKB8/CHE5zLPhJBuGiIbTHrjg+RgGSQaJtyYbD2Lc4a43puDvVIpOqmQMkNw+0gDJ7f+1AdqjNQZIbnsMkHK1kxRu6FNJoaSkYnxt6zVYhulYJLk9ZJH+yTssktw+0iLJ7S+3yLWkRP9TWuRjyf4Qi/wCJRxaZCvwx1kkue1apLG25051jbU97zEjb7QeaPqwSXqwYd5vkv65N0lnX48zyX8XLlBqMvQwg1/eBfe+DQn7Yqm/CcZTKDZEhsG8hBkkyemjYnunnH8QMshEHlTFVzbqc3E6U8AFHzpqy9JGhLQmkmghQ03FRDlOQo1Aj2d4/o66nJwgMmFZxOltkwE7lBKOZxAUJI0qPqAwsj+qsfybJyMMsjHizkeT0bRbMujCB+CTaZuvB0HzNZWKNvLsk520MGlQb3G51SFBoUSqmIN+n8uWMmtE1TBfNAF/NRzalP4QNxwbPZHNwXwOMsXMJyxMNIQqITUtXzrEypYQWTcvaKzAgvdxwyHy987JCZxZd49aiTDDLTWeCDbEBsWKbCn6opHjmN7WMIO3956D7b2FIimEtOrpqvadKpUtIjSMtcgU1ane1pn3Ta6nkDW10lDlfNHkApufBeGwpNaZCk6fRCWZK3qnUnzHAOJCmnfHX2ptjMqV7dH1t+4Bb70tu6XlEG8Gm8Q+CSvAFk1Ysu+UiAM/jQePKnj9Cmjub1+UMA7tRPDGfxnxkuqG3LVSDgKDSafw1KSunmevVkCgwjvRZpGqiVqxUac31AJSIivmoZr0x/ohHby6rzjRCQvtn4b5IvRH+1nx/b6QIyIixzLMs3GWL1p97jqSw4+6TyJDe31DaiTXxkwm9b4RnnaT8rZ9KN/v36m2L2kwG1XyVjcC+P1OSsp1dWe7LPyphlooxZaVbcYI2O400a0T8/bnW1ojgNfCxDQFBjNAgNObJqiEQkhJC4Nf3ZC6dl1WX28hOrjgkIDo+DdiV5XGfChRKO2V7ceWkNBKUbZKQO3qWkidgbm6sHYIBVF4dmXOjZcFQPrVjvrRBW4bTGwFO0XW1NYwB33e6IbUKZAMlkE5nPyJVD5KWTZuehmsEhus7vvy5gp+wLH36SozHphE0Vrk8GWGp2qCrW4BqEWy7JZLOniQjCCK66sl9d81bBHte5fNdO6epr6Wj7PJeHHqSmcogHGWD05dXexyvMACCV591vCaM1prhVNXu/rluHPEnT8Cd2SSQupu6bprg2splDImxulqxQpGuVHY5TUTO1XdHboI//8W7L8FDW/+dy/BkxP484ZVdK8lYR1k53p5YotobewSBco4PEWgJkqBuMaLrCmLQk/P7EByfSASt+9gbswnuIRvpOBr9Lk+qXzw7uv4Bay6zhxd6AXZMM+A7Zt42KBz3WXLwNcb77xLEx9wcTNqzd8gPuABwMWZ0Foq2sKp+1PPnfmYvc5z5O+x0/qFyXvsnODOaVzOPe+0dtxcm78ZXUZl0sC6ru78chNGGxSNs9DC5Wte8N2qe+CExM5ireoNWVI7hyVkSSUsaUF2imKIYAkcLokJGzRVxujsGMm1uKI2qsvgbzul8eXWLxWCK6a0OYE9vhr1FpTqFFa8gLCq1NdOso0A10WIGgAPtIZWvHD9hMHDvaB9EU6zF9nwWdi9sYI0B97r2nwM/BTuB3vATuphJ6enkfMig/yTDD7Nexo6j+jj9Ldp9imaZMM8G+aTR9J0PPkAkob5OIM8g2e/JkmNtVjV2XH23212uaFgFnYU7C0YFU36NM9A+3ZxcDP6GYXD+hbdfYCXn4GLrzxXrWjnQtBe0VXh3RYNlOhtPcILb+Dv+MGhNNz+Z7Jwx29GAzKYxEm4by8Lfk3N1QhY3sPpNA6uEOi9wdfsisK/YsBeMlUQFwbYfMKO4WJHmUomygyUgGejF3DDqsq4CbGl8CzmML5rpMVfWWdOzLzZ3jWwR6Sd7ZmOcr/REecOuz+rYfV6Osp9tOY32vpdq8yduC1M5LDXuhe/RYMHG6I2fXMHZv2hsQMn0g+dPnh7f2jAAN7OE062dJ581vSc73sGDfZ0oVEAVAcnrYg2ryBa/HWV9nQH4RjGo/EBAbpNfjzLYO8KrwEyohqN94YixI5rEEWxk5QXVPl+shVIsSGSFFgPcHbVUwV1a4XBlJodbbZWNGGvt/NiI7tmjqI067NZVEXFfwo4bmOz+04aU8RcaUTjzjJP6Dzpa/vuA545wHEX8Ocffvr5Hz8GkPirb1bJwnb+G2/94Se39ZOuGM7pVlxTqCgpsaGyYZqqmhQUVlJs38X6Sku2TcPiM8VhzL+k87k6HqSjjwe/aUvRJhKZwZboYpNKurYJhatCoywMQNeIcPUyXwzAWlJ8lURxJXRmHFNIAH7+x48///ATQNIcdjaDpFk1KUFIjJvYjDiMeFBwXTR+8SEsndJwTYlxahYavsFJV9XLXImwKQoxKBPb6r7PpPBp3Hvr8OTe5ysWNKof2zq6Zx5YkHcUjFPPvcSfcBryNPx3yBcLe4C9Hze47DxF8vF7bB2//9bh417bW+R34sr6RbhX5ffNmccX+RshYWH/kWX9SEr+ECglFE5Mp6Wkn0nTyaSLYjKZvmN/j4fRkrDqPVyMPOhiRh+/HBg/82v5mPGv4GNk4Bz8esSrXu+ARUt8rnZLpWUU8rouID5wSRnyzWRt+FnOCHAOuKJ8rTfNWFYDjJ9e2eEoBaI2vCWVnTbE8M59WAZa+K+lHKbme4dR8+WRpp/BH8W1SRFNdkk27guDG6z22Hl9LDyz0IthubhPtpaoVGkidebeOgNjjG99a9J2kyS1JX5/NL2hPDi4T6B1TP0ojNcQt+3mtt0z98a29DUEPAzEzXu7tjXJ75JCLRR+3dNgt48/D23WLs268w+uN9YnKkOgFgKWbG3Ju6FPqwoKLOSYJ6PO+449mi9svcYQMXgP4gpjK7AUO24igCGVUkjVfdkXs776oFcs126+D7P7uAtdiJrRsp0kQT+XesY1qAfw0Ud4qSH0556IQXi5qcYd4nbvEPFHU5GyGJpecGDpBkEnqP03WlUCboSsysTrJOTZMxveJuZpAq0NOwj/cUcSxmK9qD5xiOzyIzF57+2RjCG8ZR+NxXr7lqoWy/seZOJQvDch2eT9SOngyVs8H4IkH48/HM0wwvMYsoImuoXB0aDQ79t52zgtim4BIY03Y6u2g+emW3s8rZ305JSWFYXAva6phpJqwioFZImlwp00xvVLxkdil4YNR0pksQndsNn9tdttj9HuX2Oa39AGRNvqpm9IOo8r+KGx8wbQl66dWFgGX4N7D76ptCO1OHOmi83LIA9sccxCPnXGi7tzTX3TJm6kuC/rQzFgdggPp4cO8NoDTt8B+N2rMw863E8PUSFbYP8b+jNPpX/+x4/0mv78w09UBbvaLZ90psSdnO1XxaKyHyTgta/gN2+XFdncu1G9sIWdmAcJBjM+8miHRrDy0qfJwUtS2KgN7IeG6Us1sOHhfP6b+fxtevmX+fx+cTyYz+9NSvq4aBEVzUeLQR26lkbPbMB4GqxrceXW82i9VtqtTxanISLXbDH4jn2XaqM2l1pcLQZwbDaGeA6dtXvB2cqb+0Azko+qK6YbH9M0InAZm2yCA+F3bfGl/ZbetgLi8a4waj85gd/dtREZirKy8aDgQcj/GSSQZJBcfPm7JIOPIDk/u/j+/JukPzw0KFJFayNUmMu5xuZdk0s91FeP+/BRc9Slz4cm3Iw3wq9WPReCfl0Rzik2O5iKN4DgtPGEhoZhy9GX0SxK7Rxz0Q7gd1p83/4H9jtQLrWkrnU8JEWx2+4q/CsNtLjCKQotbCcn6DGG9Uq9rffm7zC7DFr62Izt6/VFnAtTpKP2qEoLSTucaE/lD9yMBVqkxzMoDk/a4FcQzbf4BErC1yaJdB/kvwR4tQIlgNQ15aXreD5AcTDyYB6+czqBp/MkhwlM54nTxkGfO8aqaBdynkzmSd+niEdxSSjOk0BiuqyAOEeJuV5TZHB+FfMFbXbbL9Zx9gbhD1WSDO4URFVmCNsakYK2AoRGxUuK47j+65CqhHBOBR9/4cvde1Zjv5c/6nhL5a/kcWYxhJey36p2q2ir2q3Cre7VmPakoioH7c2uGjdqaDs2O/2jA+TEclfdVgy9pknLtYQal3XmLr9E6bPrsySqEyHcoK+Cso/sS0QWYDPP34EtUpgLybZNwfajhwsrh0eI3Vq3uuKnmXyZ8zS8WqqmvtHJoACSUVD5CMg5VFNNII/35A9s6utwdKkDLaASN1QOC6L6zUALBEh7hqXDDjS2ArodQdcrINLofvIlztfjz89nkPzXXgMUH9muwkjIMo0/v8OnxzOYTk4PbSo2Mt373hE3NfPSD404v7Za11Lcp2rt0z4B+RwnxuFW34HkUIOyFRVxXxY4kPQ7G37fuc5V5uPxO9+8GvRL9K8WTU+BcD4ffXBZEC3B97oO1wZbQNYCRvKYjvJpEh0Xu2L5NGRk86xHHHjF9KCxUrHfdTwWVzLtO8wvPkv/UfwHKO9CdvRoq97V9cNWjQC/glWT2Kr//j5WPfwlVv2LzVp5s/YkG46/vjj701nEb//0YbMOcfSYdQ+SIzeX9/riq69f/c79zSz4/c//BAAA///W4P3lzkwAAA==",
Length: 19662,
Contents: "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",
Length: 19636,
},
}

Expand Down

0 comments on commit 9fab472

Please sign in to comment.