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/pair.go

196 lines
4.1 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 (
"bytes"
"fmt"
"io"
)
// Pair is a type with two values. In other lisps it is often called "cons",
// "cons-cell", or "cell".
type Pair struct {
first, second Value
}
// NewPair creates a new pair from two values.
func NewPair(first, second Value) *Pair {
return &Pair{first, second}
}
// NewPairFromValues creates a new pair list from the given values.
func NewPairFromValues(vals ...Value) *Pair { return NewPairFromSlice(vals) }
// NewPairFromSlice creates a new pair list from the given Value slice.
func NewPairFromSlice(values []Value) *Pair {
if len(values) == 0 {
return Nil()
}
var p *Pair
for i := len(values); i > 0; {
i--
p = NewPair(values[i], p)
}
return p
}
// GetFirst returns the first value of a pair
func (p *Pair) GetFirst() Value {
if p != nil {
return p.first
}
return nil
}
// GetSecond returns the second value of a pair
func (p *Pair) GetSecond() Value {
if p != nil {
return p.second
}
return nil
}
// GetTail returns the tail list of the given pair.
func (p *Pair) GetTail() *Pair {
if p != nil {
if tail, ok := p.second.(*Pair); ok {
return tail
}
}
return nil
}
// Length returns the number of pairs starting with the given.
func (p *Pair) Length() int {
elem, count := p, 0
for {
if IsNil(elem) {
return count
}
elem = elem.GetTail()
count++
}
}
// IsEmpty returns true, if the length of the pair list is zero.
func (p *Pair) IsEmpty() bool { return p == nil || (IsNil(p.first) && IsNil(p.second)) }
// Nil() returns the empty pair.
func Nil() *Pair { return (*Pair)(nil) }
func (p *Pair) IsNil() bool { return p == nil }
func (p *Pair) Equal(other Value) bool {
if p == nil || IsNil(other) {
return p == other
}
if o, ok := other.(*Pair); ok {
return p.first.Equal(o.first) && p.second.Equal(o.second)
}
return false
}
var (
bNil = []byte{'(', ')'}
bLparen = []byte{'('}
bRparen = []byte{')'}
bSpace = []byte{' '}
bPairSep = []byte{' ', '.', ' '}
)
func (p *Pair) Print(w io.Writer) error {
if p == nil {
_, err := w.Write(bNil)
return err
}
_, err := w.Write(bLparen)
if err != nil {
return err
}
for cp := p; ; {
if cp != p {
_, err = w.Write(bSpace)
if err != nil {
return err
}
}
if first := cp.first; !IsNil(first) {
err = first.Print(w)
} else {
_, err = w.Write(bNil)
}
if err != nil {
return err
}
sval := cp.second
if IsNil(sval) {
break
}
if np, ok := sval.(*Pair); ok {
cp = np
continue
}
_, err = w.Write(bPairSep)
if err != nil {
return err
}
err = sval.Print(w)
if err != nil {
return err
}
break
}
_, err = w.Write(bRparen)
return err
}
func (p *Pair) String() string {
var buf bytes.Buffer
p.Print(&buf)
return buf.String()
}
// GetSymbol returns the first value of the pair as a Symbol.
func (p *Pair) GetSymbol() (*Symbol, error) {
if val, ok := p.GetFirst().(*Symbol); ok {
return val, nil
}
return nil, fmt.Errorf("%v is not a symbol", p.GetFirst())
}
// GetString returns first value of the pair as a string.
func (p *Pair) GetString() (string, error) {
first := p.GetFirst()
if val, ok := first.(*String); ok {
return val.GetValue(), nil
} else if val, ok := first.(*Symbol); ok {
return val.GetValue(), nil
}
return "", fmt.Errorf("%v is not a string", first)
}
// GetPair returns the first value of the pair as a Pair.
func (p *Pair) GetPair() (*Pair, error) {
if val, ok := p.GetFirst().(*Pair); ok {
return val, nil
}
return nil, fmt.Errorf("%v is not a pair", p.GetFirst())
}
// GetInteger returns the first value of the pair as an integer.
func (p *Pair) GetInteger() (int64, error) {
if iVal, ok := p.GetFirst().(*Integer); ok {
return iVal.GetValue(), nil
}
return 0, fmt.Errorf("%v is not an integer", p.GetFirst())
}