package saccharine import ( "errors" "fmt" "log/slog" "git.maximhutz.com/max/lambda/pkg/iterator" "git.maximhutz.com/max/lambda/pkg/saccharine/ast" "git.maximhutz.com/max/lambda/pkg/saccharine/token" ) type TokenIterator = iterator.Iterator[token.Token] func parseToken(i *TokenIterator, expected token.Type) (*token.Token, error) { i2 := i.Copy() if tok, err := i2.Next(); err != nil { return nil, err } else if tok.Type != expected { return nil, fmt.Errorf("expected token, got %v'", tok.Value) } else { i.Sync(i2) return &tok, nil } } func parseExpression(i *TokenIterator) (ast.Expression, error) { slog.Info("attempt exp", "index", i.Index()) if abs, absErr := parseAbstraction(i); absErr == nil { slog.Info("got exp") return abs, nil } else if atm, atmErr := parseApplication(i); atmErr == nil { slog.Info("got exp") return atm, nil } else if app, appErr := parseAtom(i); appErr == nil { slog.Info("got exp") return app, nil } else { slog.Info("fail exp") return nil, errors.Join(absErr, appErr, atmErr) } } func parseParameters(i *TokenIterator) ([]string, error) { slog.Info("parse param") i2 := i.Copy() variables := []string{} for { if tok, err := parseToken(i2, token.Atom); err != nil { break } else { variables = append(variables, tok.Value) } } slog.Info("got exp") i.Sync(i2) return variables, nil } func parseAbstraction(i *TokenIterator) (*ast.Abstraction, error) { slog.Info("attempt abs") i2 := i.Copy() if _, err := parseToken(i2, token.Slash); err != nil { slog.Info("fail abs") return nil, err } else if parameters, err := parseParameters(i2); err != nil { slog.Info("fail abs") return nil, err } else if _, err = parseToken(i2, token.Dot); err != nil { slog.Info("fail abs") return nil, err } else if body, err := parseExpression(i2); err != nil { slog.Info("fail abs") return nil, err } else { slog.Info("got abs") i.Sync(i2) return ast.NewAbstraction(parameters, body), nil } } func parseApplication(i *TokenIterator) (*ast.Application, error) { slog.Info("attempt app") i2 := i.Copy() expressions := []ast.Expression{} if _, err := parseToken(i2, token.OpenParen); err != nil { slog.Info("fail app") return nil, err } for { if exp, err := parseExpression(i2); err != nil { break } else { expressions = append(expressions, exp) } } if _, err := parseToken(i2, token.CloseParen); err != nil { slog.Info("fail app") return nil, err } if len(expressions) == 0 { slog.Info("fail app") return nil, fmt.Errorf("application has no arguments") } slog.Info("got app") i.Sync(i2) return ast.NewApplication(expressions[0], expressions[1:]), nil } func parseAtom(i *TokenIterator) (*ast.Atom, error) { slog.Info("attempt atm") if tok, err := parseToken(i, token.Atom); err != nil { slog.Info("fail atm") return nil, err } else { slog.Info("got atm") return ast.NewAtom(tok.Value), nil } } func Parse(i *TokenIterator) (ast.Expression, error) { return parseExpression(i) }