refactor: simplify iterator.Try and remove unnecessary backtracking #47

Merged
mvhutz merged 1 commits from refactor/simplify-iterator-try into main 2026-02-12 01:04:27 +00:00
5 changed files with 114 additions and 136 deletions
Showing only changes of commit b1fef85d60 - Show all commits

View File

@@ -19,18 +19,6 @@ func (i Iterator[T]) Index() int {
return i.index return i.index
} }
// Copy returns a identical clone of the iterator. The underlying data structure
// is not cloned.
func (i Iterator[T]) Copy() *Iterator[T] {
return &Iterator[T]{items: i.items, index: i.index}
}
// Sync returns the iterator to the position of another. It is assumed that the
// iterators both operate on the same set of data.
func (i *Iterator[T]) Sync(o *Iterator[T]) {
i.index = o.index
}
// Get returns the datum at the current position of the iterator. // Get returns the datum at the current position of the iterator.
func (i Iterator[T]) Get() (T, error) { func (i Iterator[T]) Get() (T, error) {
var null T var null T
@@ -93,14 +81,14 @@ func (i *Iterator[T]) While(fn func(T) bool) {
} }
// Try attempts to perform an operation using the iterator. If the operation // Try attempts to perform an operation using the iterator. If the operation
// succeeds, the iterator is updated. If the operation fails, the iterator is // succeeds, the iterator keeps its new position. If the operation fails, the
// rolled back, and an error is returned. // iterator is rolled back, and an error is returned.
func Try[T any, U any](i *Iterator[T], fn func(i *Iterator[T]) (U, error)) (U, error) { func Try[T any, U any](i *Iterator[T], fn func(i *Iterator[T]) (U, error)) (U, error) {
i2 := i.Copy() saved := i.index
out, err := fn(i2) out, err := fn(i)
if err == nil { if err != nil {
i.Sync(i2) i.index = saved
} }
return out, err return out, err

View File

@@ -18,54 +18,49 @@ func parseVariable(i *tokenIterator) (Expression, error) {
} }
func parseAbstraction(i *tokenIterator) (Expression, error) { func parseAbstraction(i *tokenIterator) (Expression, error) {
return iterator.Try(i, func(i *tokenIterator) (Expression, error) { if _, err := token.ParseRawToken(i, tokenSlash); err != nil {
if _, err := token.ParseRawToken(i, tokenSlash); err != nil { return nil, fmt.Errorf("no backslash (col %d): %w", i.Index(), err)
return nil, fmt.Errorf("no backslash (col %d): %w", i.Index(), err) } else if param, err := token.ParseRawToken(i, tokenAtom); err != nil {
} else if param, err := token.ParseRawToken(i, tokenAtom); err != nil { return nil, fmt.Errorf("no param (col %d): %w", i.Index(), err)
return nil, fmt.Errorf("no param (col %d): %w", i.Index(), err) } else if _, err := token.ParseRawToken(i, tokenDot); err != nil {
} else if _, err := token.ParseRawToken(i, tokenDot); err != nil { return nil, fmt.Errorf("no dot (col %d): %w", i.Index(), err)
return nil, fmt.Errorf("no dot (col %d): %w", i.Index(), err) } else if body, err := parseExpression(i); err != nil {
} else if body, err := parseExpression(i); err != nil { return nil, err
return nil, err } else {
} else { return Abstraction{Parameter: param.Value, Body: body}, nil
return Abstraction{Parameter: param.Value, Body: body}, nil }
}
})
} }
func parseApplication(i *tokenIterator) (Expression, error) { func parseApplication(i *tokenIterator) (Expression, error) {
return iterator.Try(i, func(i *tokenIterator) (Expression, error) { if _, err := token.ParseRawToken(i, tokenOpenParen); err != nil {
if _, err := token.ParseRawToken(i, tokenOpenParen); err != nil { return nil, fmt.Errorf("no opening paren (col %d): %w", i.Index(), err)
return nil, fmt.Errorf("no opening paren (col %d): %w", i.Index(), err) } else if abstraction, err := parseExpression(i); err != nil {
} else if abstraction, err := parseExpression(i); err != nil { return nil, fmt.Errorf("expected function expression: %w", err)
return nil, fmt.Errorf("expected function expression: %w", err) } else if argument, err := parseExpression(i); err != nil {
} else if argument, err := parseExpression(i); err != nil { return nil, fmt.Errorf("expected argument expression: %w", err)
return nil, fmt.Errorf("expected argument expression: %w", err) } else if _, err := token.ParseRawToken(i, tokenCloseParen); err != nil {
} else if _, err := token.ParseRawToken(i, tokenCloseParen); err != nil { return nil, fmt.Errorf("no closing paren (col %d): %w", i.Index(), err)
return nil, fmt.Errorf("no closing paren (col %d): %w", i.Index(), err) } else {
} else { return Application{Abstraction: abstraction, Argument: argument}, nil
return Application{Abstraction: abstraction, Argument: argument}, nil }
}
})
} }
func parseExpression(i *tokenIterator) (Expression, error) { func parseExpression(i *tokenIterator) (Expression, error) {
return iterator.Try(i, func(i *tokenIterator) (Expression, error) { peek, err := i.Get()
if i.Done() { if err != nil {
return nil, fmt.Errorf("unexpected end of input") return nil, err
} }
switch peek := i.MustGet(); peek.Type { switch peek.Type {
case tokenOpenParen: case tokenOpenParen:
return parseApplication(i) return parseApplication(i)
case tokenSlash: case tokenSlash:
return parseAbstraction(i) return parseAbstraction(i)
case tokenAtom: case tokenAtom:
return parseVariable(i) return parseVariable(i)
default: default:
return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Column) return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Column)
} }
})
} }
// parse converts a token slice into a lambda calculus expression. // parse converts a token slice into a lambda calculus expression.

View File

@@ -19,13 +19,11 @@ func passSoftBreaks(i *tokenIterator) {
} }
func parseToken(i *tokenIterator, expected TokenType, ignoreSoftBreaks bool) (*Token, error) { func parseToken(i *tokenIterator, expected TokenType, ignoreSoftBreaks bool) (*Token, error) {
return iterator.Try(i, func(i *tokenIterator) (*Token, error) { if ignoreSoftBreaks {
if ignoreSoftBreaks { passSoftBreaks(i)
passSoftBreaks(i) }
}
return token.ParseRawToken(i, expected) return token.ParseRawToken(i, expected)
})
} }
func parseString(i *tokenIterator) (string, error) { func parseString(i *tokenIterator) (string, error) {
@@ -47,33 +45,29 @@ func parseBreak(i *tokenIterator) (*Token, error) {
} }
func parseAbstraction(i *tokenIterator) (*Abstraction, error) { func parseAbstraction(i *tokenIterator) (*Abstraction, error) {
return iterator.Try(i, func(i *tokenIterator) (*Abstraction, error) { if _, err := parseToken(i, TokenSlash, true); err != nil {
if _, err := parseToken(i, TokenSlash, true); err != nil { return nil, fmt.Errorf("no function slash (col %d): %w", i.MustGet().Column, err)
return nil, fmt.Errorf("no function slash (col %d): %w", i.MustGet().Column, err) } else if parameters, err := token.ParseList(i, parseString, 0); err != nil {
} else if parameters, err := token.ParseList(i, parseString, 0); err != nil { return nil, err
return nil, err } else if _, err = parseToken(i, TokenDot, true); err != nil {
} else if _, err = parseToken(i, TokenDot, true); err != nil { return nil, fmt.Errorf("no function dot (col %d): %w", i.MustGet().Column, err)
return nil, fmt.Errorf("no function dot (col %d): %w", i.MustGet().Column, err) } else if body, err := parseExpression(i); err != nil {
} else if body, err := parseExpression(i); err != nil { return nil, err
return nil, err } else {
} else { return &Abstraction{Parameters: parameters, Body: body}, nil
return &Abstraction{Parameters: parameters, Body: body}, nil }
}
})
} }
func parseApplication(i *tokenIterator) (*Application, error) { func parseApplication(i *tokenIterator) (*Application, error) {
return iterator.Try(i, func(i *tokenIterator) (*Application, error) { if _, err := parseToken(i, TokenOpenParen, true); err != nil {
if _, err := parseToken(i, TokenOpenParen, true); err != nil { return nil, fmt.Errorf("no openning brackets (col %d): %w", i.MustGet().Column, err)
return nil, fmt.Errorf("no openning brackets (col %d): %w", i.MustGet().Column, err) } else if expressions, err := token.ParseList(i, parseExpression, 1); err != nil {
} else if expressions, err := token.ParseList(i, parseExpression, 1); err != nil { return nil, err
return nil, err } else if _, err := parseToken(i, TokenCloseParen, true); err != nil {
} else if _, err := parseToken(i, TokenCloseParen, true); err != nil { return nil, fmt.Errorf("no closing brackets (col %d): %w", i.MustGet().Column, err)
return nil, fmt.Errorf("no closing brackets (col %d): %w", i.MustGet().Column, err) } else {
} else { return &Application{Abstraction: expressions[0], Arguments: expressions[1:]}, nil
return &Application{Abstraction: expressions[0], Arguments: expressions[1:]}, nil }
}
})
} }
func parseAtom(i *tokenIterator) (*Atom, error) { func parseAtom(i *tokenIterator) (*Atom, error) {
@@ -133,36 +127,36 @@ func parseClause(i *tokenIterator, braces bool) (*Clause, error) {
} }
func parseExpression(i *tokenIterator) (Expression, error) { func parseExpression(i *tokenIterator) (Expression, error) {
return iterator.Try(i, func(i *tokenIterator) (Expression, error) { passSoftBreaks(i)
passSoftBreaks(i)
switch peek := i.MustGet(); peek.Type { if i.Done() {
case TokenOpenParen: return nil, fmt.Errorf("unexpected end of input")
return parseApplication(i) }
case TokenSlash:
return parseAbstraction(i) switch peek := i.MustGet(); peek.Type {
case TokenAtom: case TokenOpenParen:
return parseAtom(i) return parseApplication(i)
case TokenOpenBrace: case TokenSlash:
return parseClause(i, true) return parseAbstraction(i)
default: case TokenAtom:
return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Column) return parseAtom(i)
} case TokenOpenBrace:
}) return parseClause(i, true)
default:
return nil, fmt.Errorf("expected expression, got '%v' (col %d)", peek.Value, peek.Column)
}
} }
func parseLet(i *tokenIterator) (*LetStatement, error) { func parseLet(i *tokenIterator) (*LetStatement, error) {
return iterator.Try(i, func(i *tokenIterator) (*LetStatement, error) { if parameters, err := token.ParseList(i, parseString, 1); err != nil {
if parameters, err := token.ParseList(i, parseString, 1); err != nil { return nil, err
return nil, err } else if _, err := parseToken(i, TokenAssign, true); err != nil {
} else if _, err := parseToken(i, TokenAssign, true); err != nil { return nil, err
return nil, err } else if body, err := parseExpression(i); err != nil {
} else if body, err := parseExpression(i); err != nil { return nil, err
return nil, err } else {
} else { return &LetStatement{Name: parameters[0], Parameters: parameters[1:], Body: body}, nil
return &LetStatement{Name: parameters[0], Parameters: parameters[1:], Body: body}, nil }
}
})
} }
func parseDeclare(i *tokenIterator) (*DeclareStatement, error) { func parseDeclare(i *tokenIterator) (*DeclareStatement, error) {
@@ -174,9 +168,9 @@ func parseDeclare(i *tokenIterator) (*DeclareStatement, error) {
} }
func parseStatement(i *tokenIterator) (Statement, error) { func parseStatement(i *tokenIterator) (Statement, error) {
if let, letErr := parseLet(i); letErr == nil { if let, letErr := iterator.Try(i, parseLet); letErr == nil {
return let, nil return let, nil
} else if declare, declErr := parseDeclare(i); declErr == nil { } else if declare, declErr := iterator.Try(i, parseDeclare); declErr == nil {
return declare, nil return declare, nil
} else { } else {
return nil, errors.Join(letErr, declErr) return nil, errors.Join(letErr, declErr)

View File

@@ -8,17 +8,18 @@ import (
// ParseRawToken consumes the next token from the iterator if its type matches // ParseRawToken consumes the next token from the iterator if its type matches
// the expected type. // the expected type.
// Uses [iterator.Try] for automatic backtracking on failure. // Returns an error if the iterator is exhausted or the token type does not
// match.
func ParseRawToken[T Type](i *iterator.Iterator[Token[T]], expected T) (*Token[T], error) { func ParseRawToken[T Type](i *iterator.Iterator[Token[T]], expected T) (*Token[T], error) {
return iterator.Try(i, func(i *iterator.Iterator[Token[T]]) (*Token[T], error) { tok, err := i.Get()
if tok, err := i.Next(); err != nil { if err != nil {
return nil, err return nil, err
} else if tok.Type != expected { }
return nil, fmt.Errorf("expected token %v, got %v'", expected.Name(), tok.Value) if tok.Type != expected {
} else { return nil, fmt.Errorf("expected token %v, got %v'", expected.Name(), tok.Value)
return &tok, nil }
} i.Forward()
}) return &tok, nil
} }
// ParseList repeatedly applies a parse function, collecting results into a // ParseList repeatedly applies a parse function, collecting results into a

View File

@@ -17,15 +17,15 @@ func IsVariable(r rune) bool {
// predicate. // predicate.
// Returns an error if the iterator is exhausted or the rune does not match. // Returns an error if the iterator is exhausted or the rune does not match.
func ScanRune(i *iterator.Iterator[rune], expected func(rune) bool) (rune, error) { func ScanRune(i *iterator.Iterator[rune], expected func(rune) bool) (rune, error) {
return iterator.Try(i, func(i *iterator.Iterator[rune]) (rune, error) { r, err := i.Get()
if r, err := i.Next(); err != nil { if err != nil {
return r, err return r, err
} else if !expected(r) { }
return r, fmt.Errorf("got unexpected rune %v'", r) if !expected(r) {
} else { return r, fmt.Errorf("got unexpected rune %v'", r)
return r, nil }
} i.Forward()
}) return r, nil
} }
// ScanCharacter consumes the next rune from the iterator if it matches the // ScanCharacter consumes the next rune from the iterator if it matches the