feat: tokenizer accepts braces, line terminator, and equal sign

This commit is contained in:
2025-12-27 19:52:18 -05:00
parent 0e185fbf41
commit c37e96770f
11 changed files with 107 additions and 61 deletions

View File

@@ -20,9 +20,15 @@ type Atom struct {
Name string
}
type Clause struct {
Statements []Statement
Returns Expression
}
func (Abstraction) IsExpression() {}
func (Application) IsExpression() {}
func (Atom) IsExpression() {}
func (Clause) IsExpression() {}
/** ------------------------------------------------------------------------- */
@@ -37,3 +43,7 @@ func NewApplication(abstraction Expression, arguments []Expression) *Application
func NewAtom(name string) *Atom {
return &Atom{Name: name}
}
func NewClause(statements []Statement, returns Expression) *Clause {
return &Clause{Statements: statements, Returns: returns}
}

View File

@@ -1,5 +0,0 @@
package ast
type Program struct {
Statements []Statement
}

View File

@@ -7,11 +7,6 @@ type Statement interface {
/** ------------------------------------------------------------------------- */
type LetStatement struct {
Variable string
Value Expression
}
type MethodStatement struct {
Name string
Parameters []string
Body Expression
@@ -22,7 +17,6 @@ type DeclareStatement struct {
}
func (LetStatement) IsStatement() {}
func (MethodStatement) IsStatement() {}
func (DeclareStatement) IsStatement() {}
/** ------------------------------------------------------------------------- */

View File

@@ -1,22 +0,0 @@
package ast
import "fmt"
type Visitor[T any] interface {
VisitAtom(*Atom) T
VisitAbstraction(*Abstraction) T
VisitApplication(*Application) T
}
func Visit[T any](visitor Visitor[T], node Expression) T {
switch node := node.(type) {
case *Atom:
return visitor.VisitAtom(node)
case *Abstraction:
return visitor.VisitAbstraction(node)
case *Application:
return visitor.VisitApplication(node)
default:
panic(fmt.Sprintf("unknown node %t", node))
}
}

View File

@@ -1,31 +1,39 @@
package saccharine
import (
"fmt"
"strings"
"git.maximhutz.com/max/lambda/pkg/saccharine/ast"
)
type stringifyVisitor struct{}
func (v stringifyVisitor) VisitAtom(n *ast.Atom) string {
func stringifyAtom(n *ast.Atom) string {
return n.Name
}
func (v stringifyVisitor) VisitAbstraction(n *ast.Abstraction) string {
return "\\" + strings.Join(n.Parameters, " ") + "." + ast.Visit(v, n.Body)
func stringifyAbstraction(n *ast.Abstraction) string {
return "\\" + strings.Join(n.Parameters, " ") + "." + Stringify(n.Body)
}
func (v stringifyVisitor) VisitApplication(n *ast.Application) string {
arguments := []string{ast.Visit(v, n.Abstraction)}
func stringifyApplication(n *ast.Application) string {
arguments := []string{Stringify(n.Abstraction)}
for _, argument := range n.Arguments {
arguments = append(arguments, ast.Visit(v, argument))
arguments = append(arguments, Stringify(argument))
}
return "(" + strings.Join(arguments, " ") + ")"
}
func Stringify(n ast.Expression) string {
return ast.Visit(&stringifyVisitor{}, n)
switch n := n.(type) {
case *ast.Atom:
return stringifyAtom(n)
case *ast.Abstraction:
return stringifyAbstraction(n)
case *ast.Application:
return stringifyApplication(n)
default:
panic(fmt.Errorf("unknown expression type: %v", n))
}
}

View File

@@ -6,6 +6,10 @@ type Type int
const (
OpenParen Type = iota // Denotes the '(' token.
CloseParen // Denotes the ')' token.
OpenBrace // Denotes the '{' token.
CloseBrace // Denotes the '}' token.
End // Denotes the ';' token.
Assign // Denotes the ':=' token.
Atom // Denotes an alpha-numeric variable.
Slash // Denotes the '/' token.
Dot // Denotes the '.' token.
@@ -27,10 +31,26 @@ func NewCloseParen(column int) *Token {
return &Token{Type: CloseParen, Column: column, Value: ")"}
}
func NewOpenBrace(column int) *Token {
return &Token{Type: OpenBrace, Column: column, Value: "{"}
}
func NewCloseBrace(column int) *Token {
return &Token{Type: CloseBrace, Column: column, Value: "}"}
}
func NewDot(column int) *Token {
return &Token{Type: Dot, Column: column, Value: "."}
}
func NewEnd(column int) *Token {
return &Token{Type: End, Column: column, Value: ";"}
}
func NewAssign(column int) *Token {
return &Token{Type: Assign, Column: column, Value: ":="}
}
func NewSlash(column int) *Token {
return &Token{Type: Slash, Column: column, Value: "\\"}
}

View File

@@ -28,6 +28,19 @@ func parseRune(i *iterator.Iterator[rune], expected func(rune) bool) (rune, erro
}
}
func parseCharacter(i *iterator.Iterator[rune], expected rune) (rune, error) {
i2 := i.Copy()
if r, err := i2.Next(); err != nil {
return r, err
} else if r != expected {
return r, fmt.Errorf("got unexpected rune %v'", r)
} else {
i.Sync(i2)
return r, nil
}
}
// Pulls the next token from an iterator over runes. If it cannot, it will
// return nil. If an error occurs, it will return that.
func getToken(i *iterator.Iterator[rune]) (*token.Token, error) {
@@ -53,6 +66,18 @@ func getToken(i *iterator.Iterator[rune]) (*token.Token, error) {
return token.NewSlash(index), nil
case letter == '\n':
return token.NewNewline(index), nil
case letter == '{':
return token.NewNewline(index), nil
case letter == '}':
return token.NewNewline(index), nil
case letter == ':':
if _, err := parseCharacter(i, '='); err != nil {
return nil, err
} else {
return token.NewAssign(index), nil
}
case letter == ';':
return token.NewEnd(index), nil
case unicode.IsSpace(letter):
return nil, nil
case isVariable(letter):