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

@@ -25,7 +25,7 @@ formatters:
# replace `interface{}` with `any` in the code on format # replace `interface{}` with `any` in the code on format
- pattern: 'interface{}' - pattern: 'interface{}'
replacement: 'any' replacement: 'any'
# make sure imports are always in a deterministic order # make sure imports are always in a deterministic order
# https://github.com/daixiang0/gci/ # https://github.com/daixiang0/gci/
gci: # define the section orders for imports gci: # define the section orders for imports

View File

@@ -1,18 +1,18 @@
package convert package convert
import ( import (
"fmt"
"git.maximhutz.com/max/lambda/pkg/lambda" "git.maximhutz.com/max/lambda/pkg/lambda"
"git.maximhutz.com/max/lambda/pkg/saccharine/ast" "git.maximhutz.com/max/lambda/pkg/saccharine/ast"
) )
type compileVisitor struct{} func convertAtom(n *ast.Atom) lambda.Expression {
func (v compileVisitor) VisitAtom(n *ast.Atom) lambda.Expression {
return lambda.NewVariable(n.Name) return lambda.NewVariable(n.Name)
} }
func (v compileVisitor) VisitAbstraction(n *ast.Abstraction) lambda.Expression { func convertAbstraction(n *ast.Abstraction) lambda.Expression {
result := ast.Visit(v, n.Body) result := SaccharineToLambda(n.Body)
parameters := n.Parameters parameters := n.Parameters
@@ -31,12 +31,12 @@ func (v compileVisitor) VisitAbstraction(n *ast.Abstraction) lambda.Expression {
return result return result
} }
func (v compileVisitor) VisitApplication(n *ast.Application) lambda.Expression { func convertApplication(n *ast.Application) lambda.Expression {
result := ast.Visit(v, n.Abstraction) result := SaccharineToLambda(n.Abstraction)
arguments := []lambda.Expression{} arguments := []lambda.Expression{}
for _, argument := range n.Arguments { for _, argument := range n.Arguments {
convertedArgument := ast.Visit(v, argument) convertedArgument := SaccharineToLambda(argument)
arguments = append(arguments, convertedArgument) arguments = append(arguments, convertedArgument)
} }
@@ -48,5 +48,14 @@ func (v compileVisitor) VisitApplication(n *ast.Application) lambda.Expression {
} }
func SaccharineToLambda(n ast.Expression) lambda.Expression { func SaccharineToLambda(n ast.Expression) lambda.Expression {
return ast.Visit(&compileVisitor{}, n) switch n := n.(type) {
case *ast.Atom:
return convertAtom(n)
case *ast.Abstraction:
return convertAbstraction(n)
case *ast.Application:
return convertApplication(n)
default:
panic(fmt.Errorf("unknown expression type: %v", n))
}
} }

View File

@@ -26,7 +26,7 @@ func (v *stringifyVisitor) VisitApplication(c *Application) {
} }
func Stringify(e Expression) string { func Stringify(e Expression) string {
b := &stringifyVisitor{} b := &stringifyVisitor{builder: strings.Builder{}}
e.Accept(b) e.Accept(b)
return b.builder.String() return b.builder.String()
} }

View File

@@ -20,9 +20,15 @@ type Atom struct {
Name string Name string
} }
type Clause struct {
Statements []Statement
Returns Expression
}
func (Abstraction) IsExpression() {} func (Abstraction) IsExpression() {}
func (Application) IsExpression() {} func (Application) IsExpression() {}
func (Atom) IsExpression() {} func (Atom) IsExpression() {}
func (Clause) IsExpression() {}
/** ------------------------------------------------------------------------- */ /** ------------------------------------------------------------------------- */
@@ -37,3 +43,7 @@ func NewApplication(abstraction Expression, arguments []Expression) *Application
func NewAtom(name string) *Atom { func NewAtom(name string) *Atom {
return &Atom{Name: name} 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 { type LetStatement struct {
Variable string
Value Expression
}
type MethodStatement struct {
Name string Name string
Parameters []string Parameters []string
Body Expression Body Expression
@@ -22,7 +17,6 @@ type DeclareStatement struct {
} }
func (LetStatement) IsStatement() {} func (LetStatement) IsStatement() {}
func (MethodStatement) IsStatement() {}
func (DeclareStatement) 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 package saccharine
import ( import (
"fmt"
"strings" "strings"
"git.maximhutz.com/max/lambda/pkg/saccharine/ast" "git.maximhutz.com/max/lambda/pkg/saccharine/ast"
) )
type stringifyVisitor struct{} func stringifyAtom(n *ast.Atom) string {
func (v stringifyVisitor) VisitAtom(n *ast.Atom) string {
return n.Name return n.Name
} }
func (v stringifyVisitor) VisitAbstraction(n *ast.Abstraction) string { func stringifyAbstraction(n *ast.Abstraction) string {
return "\\" + strings.Join(n.Parameters, " ") + "." + ast.Visit(v, n.Body) return "\\" + strings.Join(n.Parameters, " ") + "." + Stringify(n.Body)
} }
func (v stringifyVisitor) VisitApplication(n *ast.Application) string { func stringifyApplication(n *ast.Application) string {
arguments := []string{ast.Visit(v, n.Abstraction)} arguments := []string{Stringify(n.Abstraction)}
for _, argument := range n.Arguments { for _, argument := range n.Arguments {
arguments = append(arguments, ast.Visit(v, argument)) arguments = append(arguments, Stringify(argument))
} }
return "(" + strings.Join(arguments, " ") + ")" return "(" + strings.Join(arguments, " ") + ")"
} }
func Stringify(n ast.Expression) string { 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 ( const (
OpenParen Type = iota // Denotes the '(' token. OpenParen Type = iota // Denotes the '(' token.
CloseParen // 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. Atom // Denotes an alpha-numeric variable.
Slash // Denotes the '/' token. Slash // Denotes the '/' token.
Dot // Denotes the '.' token. Dot // Denotes the '.' token.
@@ -27,10 +31,26 @@ func NewCloseParen(column int) *Token {
return &Token{Type: CloseParen, Column: column, Value: ")"} 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 { func NewDot(column int) *Token {
return &Token{Type: Dot, Column: column, Value: "."} 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 { func NewSlash(column int) *Token {
return &Token{Type: Slash, Column: column, Value: "\\"} 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 // Pulls the next token from an iterator over runes. If it cannot, it will
// return nil. If an error occurs, it will return that. // return nil. If an error occurs, it will return that.
func getToken(i *iterator.Iterator[rune]) (*token.Token, error) { 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 return token.NewSlash(index), nil
case letter == '\n': case letter == '\n':
return token.NewNewline(index), nil 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): case unicode.IsSpace(letter):
return nil, nil return nil, nil
case isVariable(letter): case isVariable(letter):

View File

@@ -1,9 +1,16 @@
0 := \f x.x (true x y) := x
(inc n) := \f x.(f (n f x)) (false x y) := y
(add n m) := (m inc n)
(mult n m) := (m (n f))
(exp m n) := (m n)
# This is the final output. (pair a b) := \c.(c a b)
5 := (inc (inc (inc (inc (inc 0))))) (left p) := (p true)
(exp 5 5) (false p) := (p false)
zero 0 1 x = x
inc n := \0 1 x.{
initial := (pair true x)
(on_zero p) := (pair false ((left p 1 0) (right p)))
(on_one p) := (pair (left p) (1 (right p)))
(n on_zero on_one initial)
}