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 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) { var err error var exp ast.Expression peek := i.MustGet() switch peek.Type { case token.OpenParen: exp, err = parseApplication(i) case token.Slash: exp, err = parseAbstraction(i) case token.Atom: exp, err = parseAtom(i) default: return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Index) } return exp, err } 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, trace.WrapError(fmt.Errorf("no function slash (col %d)", 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, trace.WrapError(fmt.Errorf("no function dot (col %d)", 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, trace.WrapError(fmt.Errorf("no openning brackets (col %d)", i2.MustGet().Index), err) } for { if exp, err := parseExpression(i2); err != nil { if len(expressions) == 0 { return nil, trace.WrapError(fmt.Errorf("application has no arguments"), err) } break } else { expressions = append(expressions, exp) } } if _, err := parseToken(i2, token.CloseParen); err != nil { return nil, trace.WrapError(fmt.Errorf("no closing brackets (col %d)", i2.MustGet().Index), err) } 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, trace.WrapError(fmt.Errorf("no variable (col %d)", 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 }