Add IHashable and the implementation for all the IExpr types

This commit is contained in:
Sameer Rahmani 2020-12-20 18:09:06 +00:00
parent 1d66c2a56e
commit 5ee7e93647
17 changed files with 190 additions and 14 deletions

View File

@ -4,6 +4,7 @@ go 1.15
require (
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/rjeczalik/pkgconfig v0.0.0-20190903131546-94d388dab445
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1
)

View File

@ -143,6 +143,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rjeczalik/pkgconfig v0.0.0-20190903131546-94d388dab445 h1:HTpnmNOc0MiWqyJqaiKRO3paZOCo+JqGaDN9ZMKy57w=
github.com/rjeczalik/pkgconfig v0.0.0-20190903131546-94d388dab445/go.mod h1:TmjBR6hcDoH9/baYY27h9m2Qk2obtYgLAwgGGvsuwPA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=

View File

@ -23,6 +23,7 @@ import (
"strings"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
// Block struct represents a group of forms. Don't confuse it with
@ -56,6 +57,11 @@ func (b *Block) GetLocation() ast.Location {
return ast.MakeUnknownLocation()
}
func (l *Block) Hash() uint32 {
bytes := []byte("TODO")
return hash.HashOf(append([]byte{byte(ast.Block)}, bytes...))
}
func (b *Block) ToSlice() []IExpr {
return b.body
}

View File

@ -28,6 +28,7 @@ import (
var BUILTINS = map[string]NativeFunction{
"print": MakeNativeFn("print", PrintNativeFn),
"require": MakeNativeFn("print", RequireNativeFn),
"hash": MakeNativeFn("hash", HashNativeFn),
}
func PrintNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
@ -44,9 +45,9 @@ func PrintNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError
func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
switch args.Count() {
case 0:
return nil, MakeErrorFor(rt, args, "'require' special form is missing")
return nil, MakeErrorFor(rt, args, "'require' function is missing")
case 1:
return nil, MakeErrorFor(rt, args.First(), "'require' special form needs at least one argument")
return nil, MakeErrorFor(rt, args.First(), "'require' function needs at least one argument")
default:
}
@ -62,3 +63,19 @@ func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IErr
return result, nil
}
func HashNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
if args.Count() != 2 {
return nil, MakeErrorFor(rt, args.First(), "'hash' function needs exactly one argument")
}
expr := args.Rest().First()
result, err := MakeInteger(expr.Hash())
if err != nil {
return nil, err
}
result.Node = n
return result, nil
}

View File

@ -18,7 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core
import "serene-lang.org/bootstrap/pkg/ast"
import (
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type FalseType struct{}
@ -40,3 +43,8 @@ func (n FalseType) String() string {
func (n FalseType) ToDebugStr() string {
return "false"
}
func (n FalseType) Hash() uint32 {
bytes := []byte("false")
return hash.HashOf(append([]byte{byte(ast.False)}, bytes...))
}

View File

@ -42,6 +42,7 @@ import (
"fmt"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
@ -86,6 +87,11 @@ func (f *Function) GetType() ast.NodeType {
return ast.Fn
}
func (f *Function) Hash() uint32 {
// TODO: Fix this function to return an appropriate hash for a function
return hash.HashOf([]byte(f.String()))
}
func (f *Function) IsMacro() bool {
return f.isMacro
}
@ -169,7 +175,12 @@ func (f *NativeFunction) GetType() ast.NodeType {
}
func (f *NativeFunction) String() string {
return fmt.Sprintf("<NativeFn: %s>", f.name)
return fmt.Sprintf("<NativeFn: %s at %p>", f.name, f)
}
func (f *NativeFunction) Hash() uint32 {
// TODO: Fix this function to return an appropriate hash for a function
return hash.HashOf([]byte(f.String()))
}
func (f *NativeFunction) ToDebugStr() string {

View File

@ -23,6 +23,7 @@ import (
"strings"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
/** WARNING:
@ -73,6 +74,11 @@ func (l *List) Rest() ISeq {
return MakeList(l.exprs[1:])
}
func (l *List) Hash() uint32 {
bytes := []byte("TODO")
return hash.HashOf(append([]byte{byte(ast.List)}, bytes...))
}
// END: ISeq ---
// Implementing ICountable for List ---

View File

@ -22,6 +22,7 @@ import (
"fmt"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type INamespace interface {
@ -101,6 +102,10 @@ func (n *Namespace) GetName() string {
return n.name
}
func (n *Namespace) Hash() uint32 {
return hash.HashOf([]byte(n.String()))
}
func (n *Namespace) hasExternal(nsName string) bool {
_, ok := n.externals[nsName]
return ok

View File

@ -40,3 +40,7 @@ func (n NilType) String() string {
func (n NilType) ToDebugStr() string {
return "nil"
}
func (n NilType) Hash() uint32 {
return 0
}

View File

@ -18,7 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core
import "serene-lang.org/bootstrap/pkg/ast"
import (
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type NothingType struct{}
@ -28,6 +31,11 @@ func (n NothingType) GetType() ast.NodeType {
return ast.Nothing
}
func (n NothingType) Hash() uint32 {
bytes := []byte("Nothing")
return hash.HashOf(append([]byte{byte(ast.Block)}, bytes...))
}
func (n NothingType) GetLocation() ast.Location {
return ast.MakeUnknownLocation()
}

View File

@ -19,10 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"encoding/binary"
"fmt"
"math"
"strconv"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type INumber interface {
@ -57,14 +60,16 @@ type Integer struct {
value int64
}
func (i Integer) Eval() IExpr {
return &i
}
func (i Integer) GetType() ast.NodeType {
return ast.Number
}
func (i Integer) Hash() uint32 {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(i.value))
return hash.HashOf(b)
}
func (i Integer) String() string {
return fmt.Sprintf("%d", i.value)
}
@ -81,19 +86,38 @@ func (i Integer) F64() float64 {
return float64(i.value)
}
func MakeInteger(x interface{}) (*Integer, IError) {
var value int64
switch x.(type) {
case uint32:
value = int64(x.(uint32))
case int32:
value = int64(x.(int32))
default:
return nil, MakePlainError(fmt.Sprintf("don't know how to make 'integer' out of '%s'", x))
}
return &Integer{
value: value,
}, nil
}
type Double struct {
Node
value float64
}
func (d Double) Eval() IExpr {
return &d
}
func (d Double) GetType() ast.NodeType {
return ast.Number
}
func (d Double) Hash() uint32 {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, math.Float64bits(d.value))
return hash.HashOf(b)
}
func (d Double) String() string {
return fmt.Sprintf("%f", d.value)
}
@ -136,3 +160,21 @@ func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
return ret, nil
}
func MakeDouble(x interface{}) (*Double, IError) {
var value float64
switch x.(type) {
case uint32:
value = float64(x.(uint32))
case int32:
value = float64(x.(int32))
case float32:
value = float64(x.(float32))
default:
return nil, MakePlainError(fmt.Sprintf("don't know how to make 'double' out of '%s'", x))
}
return &Double{
value: value,
}, nil
}

View File

@ -24,6 +24,8 @@ import (
"os"
"path"
"strings"
"serene-lang.org/bootstrap/pkg/dl"
)
/** TODO:
@ -196,6 +198,10 @@ func (r *Runtime) LookupBuiltin(k string) IExpr {
// runtime initialization such as adding default namespaces and vice
// versa has to happen here.
func MakeRuntime(paths []string, debug bool) *Runtime {
_, e := dl.Open("/home/lxsameer/src/serene/serene/bootstrap/examples/ffi/foo/libfoo.so")
if e != nil {
panic(e)
}
rt := Runtime{
namespaces: map[string]Namespace{},
currentNS: "",

View File

@ -22,6 +22,7 @@ import (
"fmt"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type String struct {
@ -41,6 +42,11 @@ func (s *String) ToDebugStr() string {
return fmt.Sprintf("<%s at %p>", s.content, s)
}
func (s *String) Hash() uint32 {
bytes := []byte(s.content)
return hash.HashOf(append([]byte{byte(ast.String)}, bytes...))
}
func MakeString(n Node, s string) *String {
return &String{n, s}
}

View File

@ -23,6 +23,7 @@ import (
"strings"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type Symbol struct {
@ -55,6 +56,11 @@ func (s *Symbol) ToDebugStr() string {
return s.String()
}
func (s *Symbol) Hash() uint32 {
// TODO: Return a combined hash of nsPart and name
return hash.HashOf([]byte(s.nsPart + "/" + s.name))
}
func (s *Symbol) IsRestable() bool {
// Weird name ? I know :D
return strings.HasPrefix(s.name, "&")

View File

@ -18,7 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core
import "serene-lang.org/bootstrap/pkg/ast"
import (
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
type TrueType struct{}
@ -40,3 +43,8 @@ func (n TrueType) String() string {
func (n TrueType) ToDebugStr() string {
return "true"
}
func (n TrueType) Hash() uint32 {
bytes := []byte("true")
return hash.HashOf(append([]byte{byte(ast.True)}, bytes...))
}

View File

@ -22,6 +22,7 @@ import (
"fmt"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/hash"
)
// IPrintable is the interface which any value that wants to have a string
@ -43,6 +44,7 @@ type IDebuggable interface {
type IExpr interface {
ast.ILocatable
ast.ITypable
hash.IHashable
IPrintable
IDebuggable
}

View File

@ -0,0 +1,38 @@
/*
Serene --- Yet an other Lisp
Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Package hash provides the hashing functionality
package hash
import "hash/crc32"
var hashTable *crc32.Table = crc32.MakeTable(crc32.Castagnoli)
//IHashable is the interface types which allows expressions to have a hash
// value that doesn't change through out their life time. The origin
// of each expression can be checked by comparing their hashes. Basically
// two expressions with the same hash consider to be the same.
type IHashable interface {
// Returns a 32 bit hash of the the entity which implements it.
// The hash should be constant to the life time of the implementor.
Hash() uint32
}
func HashOf(in []byte) uint32 {
return crc32.Checksum(in, hashTable)
}