Skip to content

Commit

Permalink
feat: pattern matching case as inline functions
Browse files Browse the repository at this point in the history
  • Loading branch information
pmqueiroz committed Dec 11, 2024
1 parent 6d56e34 commit fa27afb
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 111 deletions.
27 changes: 4 additions & 23 deletions ast/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ func (p *Parser) expression() Expression {
return expr
}

func (p *Parser) inlineFunction() Expression {
func (p *Parser) inlineFunction() FunctionExpression {
var params []Parameter

if !p.check(tokens.PIPE) {
Expand Down Expand Up @@ -710,32 +710,13 @@ func (p *Parser) matchStatement() Statement {
for !p.check(tokens.RIGHT_BRACE) && !p.isAtEOF() {
caseExpr := p.expression()

var params []MatchCaseParameter
p.consume("Expect callback after case matching", tokens.PIPE)

if p.match(tokens.PIPE) && !p.check(tokens.LEFT_BRACE) {
for {
paramName := p.consume("Expect parameter name.", tokens.IDENTIFIER)

params = append(params, MatchCaseParameter{
Name: paramName,
})

if !p.match(tokens.COMMA) {
break
}
}

p.consume("Expect '|' after case parameters", tokens.PIPE)
}

p.consume("Expect '{' before case body", tokens.LEFT_BRACE)

_, body := p.block()
callback := p.inlineFunction()

cases = append(cases, MatchCase{
Expression: caseExpr,
Parameters: params,
Body: body,
Callback: callback,
})
}

Expand Down
3 changes: 1 addition & 2 deletions ast/statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ type MatchCaseParameter struct {

type MatchCase struct {
Expression Expression
Parameters []MatchCaseParameter
Body []Statement
Callback FunctionExpression
}

type MatchStatement struct {
Expand Down
74 changes: 2 additions & 72 deletions interpreter/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,55 +360,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac

switch parsedCallee := callee.(type) {
case FunctionDeclaration:
funcEnv := environment.NewEnvironment(parsedCallee.Environment)

for i, param := range parsedCallee.Itself.Params {
if param.Variadic {
var variadicArgs []interface{}
for j := i; j < len(expr.Arguments); j++ {
argValue, err := Evaluate(expr.Arguments[j], env)
if err != nil {
return nil, err
}

typeErr := types.CheckPrimitiveType(param.Type, argValue, param.Nullable)
if typeErr != nil {
return nil, typeErr
}

variadicArgs = append(variadicArgs, argValue)
}
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type, param.Nullable, false, false)
break
} else {

argValue, err := Evaluate(expr.Arguments[i], env)
if err != nil {
return nil, err
}

typeErr := types.CheckPrimitiveType(param.Type, argValue, param.Nullable)
if typeErr != nil {
return nil, typeErr
}

funcEnv.Create(param.Name.Lexeme, argValue, param.Type, param.Nullable, false, false)
}
}

var result interface{}
for _, stmt := range parsedCallee.Itself.Body {
if err := Interpret(stmt, funcEnv); err != nil {
if returnValue, ok := err.(Return); ok {
result = returnValue.value
break
}

return nil, err
}
}

return result, nil
return processFunctionCall(parsedCallee, expr.Arguments, env)
case native.InternalModuleFn:
var args []interface{}
for _, arg := range expr.Arguments {
Expand Down Expand Up @@ -643,29 +595,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
}
return nil, defaultError
case ast.FunctionExpression:
parsedReturnType, parentEnum, err := parseRuntimeType(expr.ReturnType, env)

if err != nil {
return nil, err
}

fun := FunctionDeclaration{Itself: &expr, Environment: env, ReturnType: struct {
Type types.UmbraType
Parent ast.EnumStatement
}{Type: parsedReturnType, Parent: parentEnum}}

if expr.Name.Lexeme != "" {
env.Create(
expr.Name.Lexeme,
fun,
types.FUN,
false,
false,
false,
)
}

return fun, nil
return processFunction(expr, env)
default:
return nil, exception.NewRuntimeError("RT017", litter.Sdump(expr))
}
Expand Down
101 changes: 101 additions & 0 deletions interpreter/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package interpreter

import (
"github.com/pmqueiroz/umbra/ast"
"github.com/pmqueiroz/umbra/environment"
"github.com/pmqueiroz/umbra/types"
)

func processFunction(funcExpr ast.FunctionExpression, env *environment.Environment) (FunctionDeclaration, error) {
parsedReturnType, parentEnum, err := parseRuntimeType(funcExpr.ReturnType, env)

if err != nil {
return FunctionDeclaration{}, err
}

fun := FunctionDeclaration{Itself: &funcExpr, Environment: env, ReturnType: struct {
Type types.UmbraType
Parent ast.EnumStatement
}{Type: parsedReturnType, Parent: parentEnum}}

if funcExpr.Name.Lexeme != "" {
env.Create(
funcExpr.Name.Lexeme,
fun,
types.FUN,
false,
false,
false,
)
}

return fun, nil
}

func resolveArgs(args interface{}, env *environment.Environment) ([]interface{}, error) {
result := make([]interface{}, 0)

switch args := args.(type) {
case []ast.Expression:
for _, arg := range args {
value, err := Evaluate(arg, env)
if err != nil {
return nil, err
}

result = append(result, value)
}
case []ast.EnumArgument:
for _, arg := range args {
result = append(result, arg.Value)
}
}

return result, nil
}

func processFunctionCall(callee FunctionDeclaration, args interface{}, env *environment.Environment) (interface{}, error) {
funcEnv := environment.NewEnvironment(callee.Environment)
parsedArgs, err := resolveArgs(args, env)

if err != nil {
return nil, err
}

for i, param := range callee.Itself.Params {
if param.Variadic {
var variadicArgs []interface{}
for j := i; j < len(parsedArgs); j++ {
typeErr := types.CheckPrimitiveType(param.Type, parsedArgs[j], param.Nullable)
if typeErr != nil {
return nil, typeErr
}

variadicArgs = append(variadicArgs, parsedArgs[j])
}
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type, param.Nullable, false, false)
break
} else {
typeErr := types.CheckPrimitiveType(param.Type, parsedArgs[i], param.Nullable)
if typeErr != nil {
return nil, typeErr
}

funcEnv.Create(param.Name.Lexeme, parsedArgs[i], param.Type, param.Nullable, false, false)
}
}

var result interface{}
for _, stmt := range callee.Itself.Body {
if err := Interpret(stmt, funcEnv); err != nil {
if returnValue, ok := err.(Return); ok {
result = returnValue.value
break
}

return nil, err
}
}

return result, nil
}
19 changes: 5 additions & 14 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,23 +359,14 @@ func Interpret(statement ast.Statement, env *environment.Environment) error {
}

if checkMatch(expr, value) {
caseEnv := environment.NewEnvironment(env)
callback, err := processFunction(matchCase.Callback, env)

for i, param := range matchCase.Parameters {
arg := value.(ast.EnumMember).Arguments[i]

caseEnv.Create(param.Name.Lexeme, arg.Value, types.ANY, false, false, false)
}

for _, stmt := range matchCase.Body {
err := Interpret(stmt, caseEnv)

if err != nil {
return err
}
if err != nil {
return err
}

return nil
_, err = processFunctionCall(callback, value.(ast.EnumMember).Arguments, env)
return err
}
}

Expand Down

0 comments on commit fa27afb

Please sign in to comment.