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 (
"errors"
"log/slog"
"fmt"
"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/tokenizer"
)
func main() {
slog.Info("Using program arguments.", "args", os.Args)
options, err := cli.ParseOptions(os.Args[1:])
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))
if len(fails) > 0 {
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)
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 (
"flag"
"fmt"
"log/slog"
"os"
)
type CLIOptions struct {
Input string
Input string
Verbose bool
}
func ParseOptions(args []string) (*CLIOptions, error) {
slog.Info("Parsing CLI arguments.", "args", os.Args)
// Parse flags and arguments.
verbose := flag.Bool("v", false, "Verbosity. If set, the program will print logs.")
flag.Parse()
switch flag.NArg() {
case 0:
if flag.NArg() == 0 {
return nil, fmt.Errorf("No input given.")
case 1:
return &CLIOptions{Input: flag.Arg(0)}, nil
default:
} else if flag.NArg() > 1 {
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
type Expression interface {
isExpression()
Accept(ExpressionVisitor)
}
/** ------------------------------------------------------------------------- */
type Function struct {
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 {
Function 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 {
Value string
}
func (_ Function) isExpression() {}
func (_ Call) isExpression() {}
func (_ Atom) isExpression() {}
func NewAtom(name string) *Atom {
return &Atom{
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 {
case tokenizer.TokenAtom:
return lambda.Atom{Value: token.Value}, nil
return lambda.NewAtom(token.Value), nil
case tokenizer.TokenDot:
return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index)
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 lambda.Function{Parameter: atom.Value, Body: body}, nil
return lambda.NewFunction(atom.Value, body), nil
case tokenizer.TokenOpenParen:
fn, fn_err := ParseExpression(i)
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 lambda.Call{Function: fn, Argument: arg}, nil
return lambda.NewCall(fn, arg), nil
case tokenizer.TokenCloseParen:
return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index)
default: