1
0
Fork 0
Little S-Expression Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sxpf/eval.go

196 lines
4.7 KiB

//-----------------------------------------------------------------------------
// Copyright (c) 2022 Detlef Stern
//
// This file is part of sxpf.
//
// sxpf is licensed under the latest version of the EUPL // (European Union
// Public License). Please see file LICENSE.txt for your rights and obligations
// under this license.
//-----------------------------------------------------------------------------
package sxpf
import (
"fmt"
"strings"
)
type SymbolMaker interface {
// MakeSymbol creates a new or uses an existing symbol with the given
// string value.
MakeSymbol(string) *Symbol
}
type trivialSymbolMaker struct {
symbols map[string]*Symbol
}
// NewTrivialSymbolMaker creates a new SymbolMaker, that makes unique symbols.
func NewTrivialSymbolMaker() SymbolMaker {
return &trivialSymbolMaker{make(map[string]*Symbol)}
}
func (smk *trivialSymbolMaker) MakeSymbol(s string) *Symbol {
if s == "" {
return nil
}
s = strings.ToUpper(s)
sym, found := smk.symbols[s]
if !found {
sym = &Symbol{s}
smk.symbols[s] = sym
}
return sym
}
// Environment provides methods to evaluate a s-expression.
type Environment interface {
// LookupForm returns the form associated with the given symbol.
LookupForm(*Symbol) (Form, error)
// Evaluate the symbol. In many cases this result in returning a value
// found in some internal lookup tables.
EvalSymbol(*Symbol) (Value, error)
// Evaluate the given pair list. In many cases this means to evaluate the
// first element to a form and then call the form with the remaning
// elements (possibly evaluated) as parameters.
EvalPair(*Pair) (Value, error)
// Evaluate a value other than symbol or pair list. In most cases, such a
// value evaluates to itself.
EvalOther(Value) (Value, error)
}
// Eval the given s-expression value in the given environment.
func Eval(env Environment, value Value) (Value, error) {
switch val := value.(type) {
case *Symbol:
return env.EvalSymbol(val)
case *Pair:
return env.EvalPair(val)
default:
return env.EvalOther(value)
}
}
// EvalCall is trying to evaluate the first element as a form and will
// then call the form, returning its value, error, and true.
// If the first element is not a form, value and error are nil, and the
// last value will be false.
func EvalCall(env Environment, pl *Pair) (Value, error, bool) {
if pl.IsEmpty() {
return nil, nil, false
}
if sym, ok := pl.GetFirst().(*Symbol); ok {
form, err := env.LookupForm(sym)
if err != nil {
return nil, err, true
}
args := getArgsFromCall(pl)
if !form.IsSpecial() {
args, err = EvalArgs(env, args)
if err != nil {
return nil, err, true
}
}
res, err := form.Call(env, args)
return res, err, true
}
return nil, nil, false
}
func getArgsFromCall(pl *Pair) *Pair {
second := pl.GetSecond()
if IsNil(second) {
return Nil()
}
if rest, ok := second.(*Pair); ok {
return rest
}
return NewPair(second, nil)
}
// EvalSequence will evaluate all elements of the given pair list, returning
// the last result.
func EvalSequence(env Environment, seq *Pair) (Value, error) {
if seq.IsEmpty() {
return Nil(), nil
}
elem := seq
for {
res, err := Eval(env, elem.GetFirst())
if err != nil {
return res, err
}
nVal := elem.GetSecond()
if IsNil(nVal) {
return res, nil
}
next, ok := nVal.(*Pair)
if !ok {
return Eval(env, nVal)
}
elem = next
}
}
// EvalList will evaluate all elements of the given pair list, returning a
// pair list of results.
func EvalArgs(env Environment, args *Pair) (*Pair, error) {
if args.IsEmpty() {
return Nil(), nil
}
var res, cur *Pair
elem := args
for {
val, err := Eval(env, elem.GetFirst())
if err != nil {
return res, err
}
temp := NewPair(val, nil)
if cur != nil {
cur.second = temp
cur = temp
} else {
cur = temp
res = cur
}
nVal := elem.GetSecond()
if IsNil(nVal) {
return res, nil
}
next, ok := nVal.(*Pair)
if !ok {
val, err = Eval(env, nVal)
if err != nil {
return res, err
}
cur.second = val
return res, nil
}
elem = next
}
}
// EvalCallOrSeq calls EvalCall. If this returned true, return its
// results. Otherwise return the results of calling EvalSequence.
func EvalCallOrSeq(env Environment, pl *Pair) (Value, error) {
if res, err, done := EvalCall(env, pl); done {
return res, err
}
return EvalSequence(env, pl)
}
// NotFormBoundError is returned as an error, if a symbol is not bound to a form.
type NotFormBoundError struct {
Sym *Symbol
}
func (e *NotFormBoundError) Error() string {
return fmt.Sprintf("symbol %q not found to form", e.Sym.GetValue())
}
// ErrNotFormBound creates an error.
func ErrNotFormBound(sym *Symbol) error { return &NotFormBoundError{sym} }