diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index bb6fd95..43389ec 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -25,7 +25,7 @@ func main() { cli.HandleError(err) // Parse tokens. - tokens, err := saccharine.GetTokens([]rune(input)) + tokens, err := saccharine.GetTokens(input) cli.HandleError(err) logger.Info("parsed tokens", "tokens", tokens) diff --git a/pkg/iterator/iterator.go b/pkg/iterator/iterator.go index 2aa5dbb..56aa5bd 100644 --- a/pkg/iterator/iterator.go +++ b/pkg/iterator/iterator.go @@ -39,6 +39,15 @@ func (i Iterator[T]) Get() (T, error) { return i.items[i.index], nil } +func (i Iterator[T]) MustGet() T { + var null T + if i.Done() { + return null + } + + return i.items[i.index] +} + // Create a new iterator, over a set of items. func (i *Iterator[T]) Next() (T, error) { item, err := i.Get() diff --git a/pkg/saccharine/parser.go b/pkg/saccharine/parser.go index 2b99ab3..a9a7507 100644 --- a/pkg/saccharine/parser.go +++ b/pkg/saccharine/parser.go @@ -17,7 +17,7 @@ func parseToken(i *TokenIterator, expected token.Type) (*token.Token, error) { 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) + return nil, fmt.Errorf("expected token %v, got %v'", token.Name(expected), tok.Value) } else { i.Sync(i2) return &tok, nil @@ -56,11 +56,11 @@ func parseAbstraction(i *TokenIterator) (*ast.Abstraction, error) { i2 := i.Copy() if _, err := parseToken(i2, token.Slash); err != nil { - return nil, err + 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, err + 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 { @@ -74,7 +74,7 @@ func parseApplication(i *TokenIterator) (*ast.Application, error) { expressions := []ast.Expression{} if _, err := parseToken(i2, token.OpenParen); err != nil { - return nil, err + return nil, fmt.Errorf("no openning brackets (col %d): %w", i2.MustGet().Index, err) } for { @@ -86,7 +86,7 @@ func parseApplication(i *TokenIterator) (*ast.Application, error) { } if _, err := parseToken(i2, token.CloseParen); err != nil { - return nil, err + return nil, fmt.Errorf("no closing brackets (col %d): %w", i2.MustGet().Index, err) } if len(expressions) == 0 { @@ -99,12 +99,23 @@ func parseApplication(i *TokenIterator) (*ast.Application, error) { func parseAtom(i *TokenIterator) (*ast.Atom, error) { if tok, err := parseToken(i, token.Atom); err != nil { - return nil, err + return nil, fmt.Errorf("no variable (col %d): %w", i.Index(), err) } else { return ast.NewAtom(tok.Value), nil } } -func Parse(i *TokenIterator) (ast.Expression, error) { - return parseExpression(i) +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 } diff --git a/pkg/saccharine/token/token.go b/pkg/saccharine/token/token.go index bc0704b..25983df 100644 --- a/pkg/saccharine/token/token.go +++ b/pkg/saccharine/token/token.go @@ -45,3 +45,24 @@ func NewSlash(index int) *Token { func NewAtom(name string, index int) *Token { return &Token{Type: Atom, Index: index, Value: name} } + +func Name(typ Type) string { + switch typ { + case OpenParen: + return "(" + case CloseParen: + return ")" + case Slash: + return "\\" + case Dot: + return "." + case Atom: + return "ATOM" + default: + return "?" + } +} + +func (t Token) Name() string { + return Name(t.Type) +} diff --git a/pkg/saccharine/tokenizer.go b/pkg/saccharine/tokenizer.go index 610a128..cdf4cbc 100644 --- a/pkg/saccharine/tokenizer.go +++ b/pkg/saccharine/tokenizer.go @@ -70,8 +70,8 @@ func getToken(i *iterator.Iterator[rune]) (*token.Token, error) { } // Parses a list of runes into tokens. All error encountered are returned, as well. -func GetTokens(input []rune) (*iterator.Iterator[token.Token], error) { - i := iterator.Of(input) +func GetTokens(input string) ([]token.Token, error) { + i := iterator.Of([]rune(input)) tokens := []token.Token{} errorList := []error{} @@ -84,5 +84,5 @@ func GetTokens(input []rune) (*iterator.Iterator[token.Token], error) { } } - return iterator.Of(tokens), errors.Join(errorList...) + return tokens, errors.Join(errorList...) } diff --git a/samples/simple.txt b/samples/simple.txt index 0b88432..fe8eaed 100644 --- a/samples/simple.txt +++ b/samples/simple.txt @@ -7,7 +7,7 @@ \n m.(m n) ) \m n f.(m (n f)) - ) + )) \n m.(m inc n) ) \n f x.(f (n f x))