package saccharine import ( "fmt" "git.maximhutz.com/max/lambda/pkg/iterator" "git.maximhutz.com/max/lambda/pkg/saccharine/ast" "git.maximhutz.com/max/lambda/pkg/saccharine/token" "git.maximhutz.com/max/lambda/pkg/trace" ) type TokenIterator = iterator.Iterator[token.Token] func parseRawToken(i *TokenIterator, expected token.Type) (*token.Token, error) { return iterator.Do(i, func(i *TokenIterator) (*token.Token, error) { if tok, err := i.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 { return &tok, nil } }) } func passSoftBreaks(i *TokenIterator) { for { if _, err := parseRawToken(i, token.SoftBreak); err != nil { return } } } func parseToken(i *TokenIterator, expected token.Type, ignoreSoftBreaks bool) (*token.Token, error) { return iterator.Do(i, func(i *TokenIterator) (*token.Token, error) { if ignoreSoftBreaks { passSoftBreaks(i) } return parseRawToken(i, expected) }) } func parseExpression(i *TokenIterator) (ast.Expression, error) { return iterator.Do(i, func(i *TokenIterator) (ast.Expression, error) { passSoftBreaks(i) peek := i.MustGet() switch peek.Type { case token.OpenParen: return parseApplication(i) case token.Slash: return parseAbstraction(i) case token.Atom: return parseAtom(i) default: return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Column) } }) } func parseParameters(i *TokenIterator) ([]string, error) { return iterator.Do(i, func(i *TokenIterator) ([]string, error) { variables := []string{} for { if tok, err := parseToken(i, token.Atom, true); err != nil { return variables, nil } else { variables = append(variables, tok.Value) } } }) } func parseAbstraction(i *TokenIterator) (*ast.Abstraction, error) { return iterator.Do(i, func(i *TokenIterator) (*ast.Abstraction, error) { if _, err := parseToken(i, token.Slash, true); err != nil { return nil, trace.Wrap(err, "no function slash (col %d)", i.MustGet().Column) } else if parameters, err := parseParameters(i); err != nil { return nil, err } else if _, err = parseToken(i, token.Dot, true); err != nil { return nil, trace.Wrap(err, "no function dot (col %d)", i.MustGet().Column) } else if body, err := parseExpression(i); err != nil { return nil, err } else { return ast.NewAbstraction(parameters, body), nil } }) } func parseApplication(i *TokenIterator) (*ast.Application, error) { return iterator.Do(i, func(i *TokenIterator) (*ast.Application, error) { expressions := []ast.Expression{} if _, err := parseToken(i, token.OpenParen, true); err != nil { return nil, trace.Wrap(err, "no openning brackets (col %d)", i.MustGet().Column) } for { if exp, err := parseExpression(i); err != nil { if len(expressions) == 0 { return nil, trace.Wrap(err, "application has no arguments") } break } else { expressions = append(expressions, exp) } } if _, err := parseToken(i, token.CloseParen, true); err != nil { return nil, trace.Wrap(err, "no closing brackets (col %d)", i.MustGet().Column) } return ast.NewApplication(expressions[0], expressions[1:]), nil }) } func parseAtom(i *TokenIterator) (*ast.Atom, error) { if tok, err := parseToken(i, token.Atom, true); err != nil { return nil, trace.Wrap(err, "no variable (col %d)", i.Index()) } 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().Column) } return exp, nil }