329 lines
6.1 KiB
Go
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
|
|
}
|