feat: wogihrsoiuvjsroirgj
This commit is contained in:
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
21
internal/cli/logger.go
Normal 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
70
pkg/lambda/evaluate.go
Normal 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
|
||||
}
|
||||
@@ -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
36
pkg/lambda/print.go
Normal 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()
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user