sexp/parse.go
2025-08-23 20:02:18 -03:00

329 lines
6.1 KiB
Go

package main
import (
"errors"
"fmt"
"strings"
)
var (
noMatch = errors.New("no match")
)
// type Progn struct {
// *List
// }
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 List
for _, arg := range v.params {
args.els = append(args.els, arg)
}
var s []string
for _, e := range v.body {
s = append(s, fmt.Sprint(e))
}
return fmt.Sprintf("(lambda %v %v)", &args, strings.Join(s, " "))
}
func (t Symbol) String() string {
return fmt.Sprintf("%v", t.id.value)
}
func parseQuotedObject(in *Input) (Expression, error) {
l, ok := in.Pop()
if !ok {
return nil, noMatch
}
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, ok := in.Pop()
if !ok {
return nil, noMatch
}
switch x := l.(type) {
case *Token:
switch {
case x.Equals(TrueTok):
v := BoolValue(true)
return &v, nil
case x.Equals(NilTok):
v := BoolValue(false)
return &v, nil
default:
return nil, noMatch
}
case *Identifier:
return &Symbol{id: x}, nil
default:
return nil, noMatch
}
}
func parseValue(in *Input) (Expression, error) {
l, ok := in.Pop()
if !ok {
return nil, noMatch
}
switch x := l.(type) {
case *IntToken:
r := IntValue(x.value)
return &r, nil
case *FloatToken:
r := FloatValue(x.value)
return &r, nil
default:
return nil, noMatch
}
}
func parseParamList(in *Input) (Expression, error) {
list, err := in.TakeParens(Lparen, Rparen)
if err != nil {
return nil, err
}
var params []*Symbol
for list.Len() > 0 {
p, err := list.TakeParens(LsquareBracket, RsquareBracket)
if err != nil {
return nil, err
}
if p.Len() != 2 {
return nil, fmt.Errorf("expected parameter in the following form: [identifier type]")
}
id, ok := p.Peek(0).(*Identifier)
if !ok {
return nil, noMatch
}
typ, ok := p.Peek(1).(*Identifier)
if !ok {
return nil, noMatch
}
params = append(params, &Symbol{
id: ,
})
}
return args, nil
}
func parseList(in *Input) (Expression, error) {
openingParens, ok := in.Pop()
if !ok {
return nil, noMatch
}
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)
}
_, ok = in.Pop()
if !ok {
return nil, noMatch
}
return args, nil
}
func parseLet(in *Input) (Expression, error) {
openingParens, ok := in.Pop()
if !ok {
return nil, noMatch
}
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)
_, ok = in.Pop()
if !ok {
return nil, noMatch
}
tok, ok := decl.Pop()
if !ok {
return nil, noMatch
}
if !tok.Equals(Let) {
return nil, noMatch
}
l, err := parseList(decl)
if err != nil {
return nil, err
}
list, ok := l.(*List)
if !ok {
return nil, noMatch
}
var variables []*Symbol
for _, e := range list.els {
l, ok := e.(*List)
if !ok {
return nil, noMatch
}
if len(l.els) != 2 {
return nil, noMatch
}
d, ok := l.els[0].(*Symbol)
if !ok {
return nil, noMatch
}
d.expr = l.els[1]
variables = append(variables, d)
}
body, err := consumeAll(decl)
if err != nil {
return nil, err
}
return &Declaration{variables: variables, body: body}, nil
}
func parseLambda(in *Input) (Expression, error) {
openingParens, ok := in.Pop()
if !ok {
return nil, noMatch
}
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)
if tok, ok := decl.Pop(); !ok || !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 []*Symbol
for _, e := range list.els {
id, ok := e.(*Symbol)
if !ok {
return nil, noMatch
}
args = append(args, id)
}
body, err := consumeAll(decl)
if err != nil {
return nil, err
}
_, ok = in.Pop()
if !ok {
return nil, noMatch
}
return &Lambda{params: args, body: body}, nil
}
func parseConditional(in *Input) (Expression, error) {
openingParens, ok := in.Pop()
if !ok {
return nil, noMatch
}
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)
_, ok = in.Pop()
tok, ok := decl.Pop()
if !ok {
return nil, noMatch
}
if !tok.Equals(If) {
return nil, noMatch
}
predicate, err := consume(decl)
if err != nil {
return nil, err
}
ifTrue, err := consume(decl)
if err != nil {
return nil, err
}
ifFalse, err := consume(decl)
if err != nil {
return nil, err
}
return &Conditional{predicate: predicate, ifTrue: ifTrue, ifFalse: ifFalse}, nil
}
type ParserFunc func(*Input) (Expression, error)
func consume(in *Input) (n Expression, err error) {
var parseFunctions = []ParserFunc{
parseQuotedObject,
parseLambda,
parseConditional,
parseLet,
parseList,
parseSymbol,
parseValue,
}
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)
}
func consumeAll(in *Input) (e []Expression, err error) {
for in.Len() > 0 {
start := in.Index()
n, err := consume(in)
if err != nil {
in.Seek(start)
return nil, err
}
e = append(e, n)
}
return e, err
}