package saccharine import ( "errors" "fmt" "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 %v, got %v'", token.Name(expected), tok.Value) } else { i.Sync(i2) return &tok, nil } } func parseExpression(i *TokenIterator) (ast.Expression, error) { if abs, absErr := parseAbstraction(i); absErr == nil { return abs, nil } else if atm, atmErr := parseApplication(i); atmErr == nil { return atm, nil } else if app, appErr := parseAtom(i); appErr == nil { return app, nil } else { return nil, errors.Join(absErr, appErr, atmErr) } } func parseParameters(i *TokenIterator) ([]string, error) { i2 := i.Copy() variables := []string{} for { if tok, err := parseToken(i2, token.Atom); err != nil { break } else { variables = append(variables, tok.Value) } } i.Sync(i2) return variables, nil } func parseAbstraction(i *TokenIterator) (*ast.Abstraction, error) { i2 := i.Copy() if _, err := parseToken(i2, token.Slash); err != nil { return nil, fmt.Errorf("no function slash (col %d): %w", i2.MustGet().Index, err) } else if parameters, err := parseParameters(i2); err != nil { return nil, err } else if _, err = parseToken(i2, token.Dot); err != nil { return nil, fmt.Errorf("no function dot (col %d): %w", i2.MustGet().Index, err) } else if body, err := parseExpression(i2); err != nil { return nil, err } else { i.Sync(i2) return ast.NewAbstraction(parameters, body), nil } } func parseApplication(i *TokenIterator) (*ast.Application, error) { i2 := i.Copy() expressions := []ast.Expression{} if _, err := parseToken(i2, token.OpenParen); err != nil { return nil, fmt.Errorf("no openning brackets (col %d): %w", i2.MustGet().Index, err) } for { if exp, err := parseExpression(i2); err != nil { break } else { expressions = append(expressions, exp) } } if _, err := parseToken(i2, token.CloseParen); err != nil { return nil, fmt.Errorf("no closing brackets (col %d): %w", i2.MustGet().Index, err) } if len(expressions) == 0 { return nil, fmt.Errorf("application has no arguments") } i.Sync(i2) return ast.NewApplication(expressions[0], expressions[1:]), nil } func parseAtom(i *TokenIterator) (*ast.Atom, error) { if tok, err := parseToken(i, token.Atom); err != nil { return nil, fmt.Errorf("no variable (col %d): %w", i.Index(), err) } else { return ast.NewAtom(tok.Value), nil } } func Parse(tokens []token.Token) (ast.Expression, error) { i := iterator.Of(tokens) exp, err := parseExpression(i) if err != nil { return nil, err } if !i.Done() { return nil, fmt.Errorf("expected EOF, found more code (col %d)", i.MustGet().Index) } return exp, nil }