feat: wogihrsoiuvjsroirgj

This commit is contained in:
2025-12-24 14:55:33 -05:00
parent 1d8ecba118
commit 2c3ce9baf7
8 changed files with 204 additions and 26 deletions

View File

@@ -2,28 +2,37 @@ package main
import ( import (
"errors" "errors"
"log/slog" "fmt"
"os" "os"
"git.maximhutz.com/max/lambda/pkg/cli" "git.maximhutz.com/max/lambda/internal/cli"
"git.maximhutz.com/max/lambda/pkg/lambda"
"git.maximhutz.com/max/lambda/pkg/parser" "git.maximhutz.com/max/lambda/pkg/parser"
"git.maximhutz.com/max/lambda/pkg/tokenizer" "git.maximhutz.com/max/lambda/pkg/tokenizer"
) )
func main() { func main() {
slog.Info("Using program arguments.", "args", os.Args)
options, err := cli.ParseOptions(os.Args[1:]) options, err := cli.ParseOptions(os.Args[1:])
cli.HandleError(err) cli.HandleError(err)
slog.Info("Parsed CLI options.", "options", options)
logger := cli.GetLogger(*options)
logger.Info("Using program arguments.", "args", os.Args)
logger.Info("Parsed CLI options.", "options", options)
tokens, fails := tokenizer.GetTokens([]rune(options.Input)) tokens, fails := tokenizer.GetTokens([]rune(options.Input))
if len(fails) > 0 { if len(fails) > 0 {
cli.HandleError(errors.Join(fails...)) cli.HandleError(errors.Join(fails...))
} }
slog.Info("Parsed tokens.", "tokens", tokens) logger.Info("Parsed tokens.", "tokens", tokens)
ast, err := parser.GetTree(tokens) expression, err := parser.GetTree(tokens)
cli.HandleError(err) cli.HandleError(err)
slog.Info("Parsed syntax tree.", "tree", ast) logger.Info("Parsed syntax tree.", "tree", expression)
evaluated := lambda.Evaluate(expression)
cli.HandleError(err)
logger.Info("Evaluated expression.", "tree", evaluated)
fmt.Println(lambda.ToString(evaluated))
} }

View File

@@ -3,26 +3,26 @@ package cli
import ( import (
"flag" "flag"
"fmt" "fmt"
"log/slog"
"os"
) )
type CLIOptions struct { type CLIOptions struct {
Input string Input string
Verbose bool
} }
func ParseOptions(args []string) (*CLIOptions, error) { func ParseOptions(args []string) (*CLIOptions, error) {
slog.Info("Parsing CLI arguments.", "args", os.Args)
// Parse flags and arguments. // Parse flags and arguments.
verbose := flag.Bool("v", false, "Verbosity. If set, the program will print logs.")
flag.Parse() flag.Parse()
switch flag.NArg() { if flag.NArg() == 0 {
case 0:
return nil, fmt.Errorf("No input given.") return nil, fmt.Errorf("No input given.")
case 1: } else if flag.NArg() > 1 {
return &CLIOptions{Input: flag.Arg(0)}, nil
default:
return nil, fmt.Errorf("More than 1 command-line argument.") return nil, fmt.Errorf("More than 1 command-line argument.")
} }
}
return &CLIOptions{
Input: flag.Arg(0),
Verbose: *verbose,
}, nil
}

21
internal/cli/logger.go Normal file
View File

@@ -0,0 +1,21 @@
package cli
import (
"log/slog"
"os"
)
func GetLogger(o CLIOptions) *slog.Logger {
var level slog.Level
if o.Verbose {
level = slog.LevelInfo
} else {
level = slog.LevelError
}
return slog.New(
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
}),
)
}

70
pkg/lambda/evaluate.go Normal file
View File

@@ -0,0 +1,70 @@
package lambda
// func replaceUnbound(name string, replacement Expression, e Expression) Expression {
// switch e := e.(type) {
// case Atom:
// if e.Value == name {
// return replacement
// } else {
// return e
// }
// case Function:
// if e.Parameter == name {
// return e
// } else {
// return Function{
// Parameter: e.Parameter,
// Body: replaceUnbound(name, replacement, e.Body),
// }
// }
// case Call:
// return Call{
// Function: replaceUnbound(name, replacement, e.Function),
// Argument: replaceUnbound(name, replacement, e.Argument),
// }
// }
// }
// func evaluateAtom(a Atom) Expression {
// return a
// }
// func evaluateFunction(f Function) Expression {
// return Function{
// Parameter: f.Parameter,
// Body: Evaluate(f.Body),
// }
// }
// func evaluateCall(c Call) Expression {
// fn := c.Function
// as_fn, as_fn_ok := fn.(Function)
// if !as_fn_ok {
// fn = Evaluate(fn)
// if as_fn, as_fn_ok = fn.(Function); !as_fn_ok {
// return Call{
// Function: fn,
// Argument: Evaluate(c.Argument),
// }
// }
// }
// return replaceUnboundAtoms(as_fn.Parameter, c.Argument, as_fn.Body)
// }
func Evaluate(e Expression) Expression {
// switch e := e.(type) {
// case Atom:
// return e
// case Call:
// case Function:
// return Function{
// Parameter: e.Parameter,
// Body: Evaluate(e.Body),
// }
// }
return e
}

View File

@@ -1,23 +1,65 @@
package lambda package lambda
type Expression interface { type Expression interface {
isExpression() Accept(ExpressionVisitor)
} }
/** ------------------------------------------------------------------------- */
type Function struct { type Function struct {
Parameter string Parameter string
Body Expression Body Expression
} }
func NewFunction(parameter string, body Expression) *Function {
return &Function{
Parameter: parameter,
Body: body,
}
}
func (f *Function) Accept(v ExpressionVisitor) {
v.VisitFunction(f)
}
/** ------------------------------------------------------------------------- */
type Call struct { type Call struct {
Function Expression Function Expression
Argument Expression Argument Expression
} }
func NewCall(function Expression, argument Expression) *Call {
return &Call{
Function: function,
Argument: argument,
}
}
func (c *Call) Accept(v ExpressionVisitor) {
v.VisitCall(c)
}
/** ------------------------------------------------------------------------- */
type Atom struct { type Atom struct {
Value string Value string
} }
func (_ Function) isExpression() {} func NewAtom(name string) *Atom {
func (_ Call) isExpression() {} return &Atom{
func (_ Atom) isExpression() {} Value: name,
}
}
func (a *Atom) Accept(v ExpressionVisitor) {
v.VisitAtom(a)
}
/** ------------------------------------------------------------------------- */
type ExpressionVisitor interface {
VisitFunction(*Function)
VisitCall(*Call)
VisitAtom(*Atom)
}

36
pkg/lambda/print.go Normal file
View File

@@ -0,0 +1,36 @@
package lambda
import "strings"
type StringifyVisitor struct {
builder strings.Builder
}
func (v *StringifyVisitor) VisitAtom(a *Atom) {
v.builder.WriteString(a.Value)
}
func (v *StringifyVisitor) VisitFunction(f *Function) {
v.builder.WriteRune('\\')
v.builder.WriteString(f.Parameter)
v.builder.WriteRune('.')
f.Body.Accept(v)
}
func (v *StringifyVisitor) VisitCall(c *Call) {
v.builder.WriteRune('(')
c.Function.Accept(v)
v.builder.WriteRune(' ')
c.Argument.Accept(v)
v.builder.WriteRune(')')
}
func ToString(e Expression) string {
b := StringifyVisitor{
builder: strings.Builder{},
}
e.Accept(&b)
return b.builder.String()
}

View File

@@ -16,7 +16,7 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
switch token.Type { switch token.Type {
case tokenizer.TokenAtom: case tokenizer.TokenAtom:
return lambda.Atom{Value: token.Value}, nil return lambda.NewAtom(token.Value), nil
case tokenizer.TokenDot: case tokenizer.TokenDot:
return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index) return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index)
case tokenizer.TokenSlash: case tokenizer.TokenSlash:
@@ -39,7 +39,7 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
return nil, fmt.Errorf("Could not parse function body: %w", body_err) return nil, fmt.Errorf("Could not parse function body: %w", body_err)
} }
return lambda.Function{Parameter: atom.Value, Body: body}, nil return lambda.NewFunction(atom.Value, body), nil
case tokenizer.TokenOpenParen: case tokenizer.TokenOpenParen:
fn, fn_err := ParseExpression(i) fn, fn_err := ParseExpression(i)
if fn_err != nil { if fn_err != nil {
@@ -58,7 +58,7 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
return nil, fmt.Errorf("Expected call terminating parenthesis, got '%v' (column %v).", close.Value, close.Index) return nil, fmt.Errorf("Expected call terminating parenthesis, got '%v' (column %v).", close.Value, close.Index)
} }
return lambda.Call{Function: fn, Argument: arg}, nil return lambda.NewCall(fn, arg), nil
case tokenizer.TokenCloseParen: case tokenizer.TokenCloseParen:
return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index) return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index)
default: default: