Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: "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",
Length: 19662,
Contents: "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",
Length: 19636,
},
}

Expand Down