feat: wogihrsoiuvjsroirgj
This commit is contained in:
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
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
|
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
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 {
|
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:
|
||||||
|
|||||||
Reference in New Issue
Block a user