207 lines
3.7 KiB
Go
207 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var noMatch = errors.New("no match")
|
|
|
|
type Function func([]Expression) (Expression, error)
|
|
|
|
type Expression interface {
|
|
Step() (Expression, error)
|
|
Replace(Id, Expression)
|
|
// Visit(func(Expression) error)
|
|
}
|
|
|
|
type List struct {
|
|
els []Expression
|
|
}
|
|
|
|
type Value[T interface{ *Int | int | Function }] struct {
|
|
value T
|
|
}
|
|
|
|
type Quoted struct {
|
|
expr Expression
|
|
}
|
|
|
|
type Declaration struct {
|
|
id *Identifier
|
|
expr Expression
|
|
}
|
|
|
|
type Lambda struct {
|
|
args []*Identifier
|
|
body []Expression
|
|
}
|
|
|
|
func (v Value[T]) String() string {
|
|
return fmt.Sprint(v.value)
|
|
}
|
|
|
|
func (v Quoted) String() string {
|
|
return fmt.Sprintf("'%s", v.expr)
|
|
}
|
|
|
|
func (v List) String() string {
|
|
var s []string
|
|
for _, e := range v.els {
|
|
s = append(s, fmt.Sprint(e))
|
|
}
|
|
return fmt.Sprintf("(%s)", strings.Join(s, " "))
|
|
}
|
|
|
|
func (v *Lambda) String() string {
|
|
// var args = new(List)
|
|
// args.els = append(args.els, v.args...)
|
|
//
|
|
// var s []string
|
|
// for _, e := range v.args {
|
|
// s = append(s, fmt.Sprint(e))
|
|
// }
|
|
return "fuck you"
|
|
}
|
|
|
|
func parseQuotedObject(in *Input) (Expression, error) {
|
|
l := in.Pop()
|
|
switch {
|
|
case l.Equals(SingleQuote):
|
|
n, err := consume(in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Quoted{n}, nil
|
|
default:
|
|
return nil, noMatch
|
|
}
|
|
}
|
|
|
|
func parseSymbol(in *Input) (Expression, error) {
|
|
l := in.Pop()
|
|
switch x := l.(type) {
|
|
case *Identifier:
|
|
return x, nil
|
|
default:
|
|
return nil, noMatch
|
|
}
|
|
}
|
|
|
|
func parseValue(in *Input) (Expression, error) {
|
|
l := in.Pop()
|
|
switch x := l.(type) {
|
|
case *Int:
|
|
return &Value[*Int]{x}, nil
|
|
default:
|
|
return nil, noMatch
|
|
}
|
|
}
|
|
|
|
func parseList(in *Input) (Expression, error) {
|
|
openingParens := in.Pop()
|
|
if !openingParens.Equals(Lparen) {
|
|
return nil, noMatch
|
|
}
|
|
idx, ok := in.Find(Lparen, Rparen)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unmatched parenthesis starting at %s", openingParens.Position())
|
|
}
|
|
list := in.Take(idx)
|
|
var args = new(List)
|
|
for list.Len() > 0 {
|
|
arg, err := consume(list)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args.els = append(args.els, arg)
|
|
}
|
|
_ = in.Pop()
|
|
return args, nil
|
|
}
|
|
|
|
func parseLet(in *Input) (Expression, error) {
|
|
token := in.Pop()
|
|
if !token.Equals(Let) {
|
|
return nil, noMatch
|
|
}
|
|
id, ok := in.Pop().(*Identifier)
|
|
if !ok {
|
|
return nil, noMatch
|
|
}
|
|
expr, err := consume(in)
|
|
if err != nil {
|
|
return nil, noMatch
|
|
}
|
|
return &Declaration{id: id, expr: expr}, nil
|
|
}
|
|
|
|
func parseLambda(in *Input) (Expression, error) {
|
|
openingParens := in.Pop()
|
|
if !openingParens.Equals(Lparen) {
|
|
return nil, noMatch
|
|
}
|
|
idx, ok := in.Find(Lparen, Rparen)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unmatched parenthesis starting at %s", openingParens.Position())
|
|
}
|
|
decl := in.Take(idx)
|
|
tok := decl.Pop()
|
|
if !tok.Equals(LambdaTok) {
|
|
return nil, noMatch
|
|
}
|
|
l, err := parseList(decl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
list, ok := l.(*List)
|
|
if !ok {
|
|
return nil, noMatch
|
|
}
|
|
var args []*Identifier
|
|
for _, e := range list.els {
|
|
id, ok := e.(*Identifier)
|
|
if !ok {
|
|
return nil, noMatch
|
|
}
|
|
args = append(args, id)
|
|
}
|
|
var body []Expression
|
|
for decl.Len() > 0 {
|
|
n, err := consume(decl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body = append(body, n)
|
|
}
|
|
_ = in.Pop()
|
|
return &Lambda{args: args, body: body}, nil
|
|
}
|
|
|
|
type ParserFunc func(*Input) (Expression, error)
|
|
|
|
func consume(in *Input) (n Expression, err error) {
|
|
var parseFunctions = []ParserFunc{
|
|
parseQuotedObject,
|
|
parseLambda,
|
|
parseList,
|
|
parseSymbol,
|
|
parseValue,
|
|
parseLet,
|
|
}
|
|
for _, f := range parseFunctions {
|
|
start := in.Index()
|
|
n, err := f(in)
|
|
if err != nil {
|
|
in.Seek(start)
|
|
if errors.Is(err, noMatch) {
|
|
continue
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
return nil, fmt.Errorf("unrecognized construction: %s", in)
|
|
}
|