Browse Source

Simplify object model to a flat nominal typing but still with methods.

Beoran 2 years ago
parent
commit
790a2cf074
2 changed files with 185 additions and 187 deletions
  1. 45 39
      README
  2. 140 148
      selsl/object.go

+ 45 - 39
README

@@ -31,7 +31,7 @@ i
 
 
 Rethinking the OOP part: SELSL is focused for use in MUDs and similar.
-Thinking about it pure OOP is a bad fit for that. We can see this from this
+Thinking about, it pure OOP is a bad fit for that. We can see this from this
 example command: "Move apple from tree to box." On which object this
 command is acting? On the apple as it would seem? But the apple itself is
 basically unchanged, only it's membership changed from being part of the tree
@@ -41,7 +41,7 @@ either.
 
 Furthermore, there's another object in play and that is the player/actor
 themselves who actually moves the object. In a MUD this may e.g.
-add fattigue to the player, like it does in real life.
+add fatigue to the player, like it does in real life.
 
 And then there is the environment in which these objects may be present,
 or even absent. In a MUD this will be a room in a zone in a land. That room,
@@ -53,7 +53,6 @@ All in all the OOP model is too simple because it put primacy on a single
 object, and defines the operation to one of the involved objects while
 treating the others as second class parameters.
 
-
 What I think would be better for SELSL is ACtor Oriented Programming (ACOP)
 where the actor defines all the operations, but where the environment is always
 an implicit parameter (may be expicit in the Go side). Like this actor
@@ -81,26 +80,21 @@ These might be defined on some kind of "universe" level.
 
 Another aspect is that of pass by name versus pass by reference. High level
 actions which the actor performed are based on the name of the objects.
+Lower level functions on the other hand have to operate on references to
+the objects or copies of the objects themselves. The named objects therefore
+need to be looked up by name, and then, if found, handled by reference
+or by copy.
 
-
-
-
-
-SELSL is object oriented but not class based. In stead it is prototype
-based much like the SELF language. In SELSL, everything is an object.
-Objects have slots that can contain other values. There are basic objects
-which evaluate to themselves, and excutable objects which perform
-some computation to produce their result.
+SELSL does have simplified objects, but without inheritance, only with
+message passing. Every object can also evaluate itself in a context.
+There are basic objects which evaluate to themselves, and excutable objects
+which perform some computation to produce their result.
 
 An object also has an effect on computation. Normal objects have no effect,
-but a Return causes the block to end execution early. A Throw
-will keep on unless if it is stopped with a Catch.
-
-Variables, and also arguments are simply slots of the current activation
-record of an executable object.  There are no stand alone variables.
-Eveything exists in something else, except for the top level "world".
-There is also a "me" object which represents the the current actor
-that is operating on the state of the world.
+but a Break causes the block to end execution early, a Return causes the
+wrapping function of mehod toreturn a value, and a Throw will keep on
+breaking out of scopes and bublle back to the top unless if it is stopped with
+a Catch.
 
 A SELSL script consists of commands. Commands operate on the state of the
 world and may cause output to be produced as well.
@@ -112,16 +106,23 @@ the English prepositions or conjunctions that SELSL accepts. See below for
 the lists. Commands are also separated by the particles "a", "an", "the",
 or the demonstratives "this", "that", "those".
 
+Text in the SELSL input that is not a quoted string or a number is normally
+a name. However, if it is peceed by a or an, it is a type, and if preceded by
+this, that, those, or such, it is a variable. If it is preceded by "the",
+it is explicitly a name.
+
+Selsl is case insensitive, by folding every character to lower case.
+This also goes for names, so internally Bob should be referred to as bob.
+
 A command is ended by a period. A column in a command announces the start of a
 block. The command end ends a block.
 
-
 Teach an actor to open a door do
-	Check if door's open then
-		Set door's open to false.
-		Tell an actor to: Format "You close the %s" with door's name;
+	Check is that door's open then
+		Set that door's open to false.
+		Order that actor to: Format "You close the %s" with name of that door;
 	else do
-		Tell an actor to: Format "The %s is already closed" with door's name;
+		Order that actor to: Format "The %s is already closed" with name of that door;
 	end of check.
 End of teach.
 
@@ -130,10 +131,12 @@ Set hp of me as add 1 with divide hp of me by 2 end end
 
 Create a red door as a door.
 
-open red door with green ke
+open red door with green key
 
 cast cure light at smart bob
 
+SELSL sytax is configurable in that the key words can be configured before
+parsing. This could allow key words from non-English languages to be used.
 
 
 (This is (nestable)
@@ -141,40 +144,43 @@ multi line comment)
 # This is single line comment for compatibility with shell scripts.
 
 end of command with `.` or newline
-start of block with `:`, do.
+start of block with `:`, do, then, else .
 end of block with 'end', ';', ''
 Syntactic sugar: `as` and `with` are translated to `to do`
 
 
 
 
-
 SCRIPT -> COMMAND eoc EOC SCRIPT | .
 EOC -> eoc EOC | .
 COMMAND -> word EXPRESSIONS .
 EXPRESSIONS -> EXPRESSION separator EXPRESSIONS | .
-EXPRESSION -> OBJECT | number | BLOCK.
+EXPRESSION -> OBJECT | number | BLOCK | QUERY .
+QUERY ->  ISQUERY | QWQUERY.
+ISQUERY -> is EXPRESSION qm .
+QWQUERY -> qw EXPRESSION qm .
 OBJECT-> word ATTRIBUTES .
 ATTRIBUTES -> OFS EXPRESSION | .
 OFS -> of | s .
 BLOCK -> ob SCRIPT END .
-END -> end COMMAND .
+END -> end COMMANDOFS .
+COMMANDOFS -> COMMAND | OFS .
 
-Including queries:
 
 SCRIPT -> COMMAND eoc EOC SCRIPT | .
 EOC -> eoc EOC | .
 COMMAND -> word EXPRESSIONS .
 EXPRESSIONS -> EXPRESSION separator EXPRESSIONS | .
-EXPRESSION -> OBJECT | number | BLOCK | QUERY .
-QUERY ->  ISQUERY | QWQUERY.
-ISQUERY -> is EXPRESSION qm .
-QWQUERY -> qw EXPRESSION qm .
-OBJECT-> word ATTRIBUTES .
-ATTRIBUTES -> OFS EXPRESSION | .
-OFS -> of | s .
+EXPRESSION -> OBJECT | string | number | BLOCK | QUERY .
+QUERY ->  ISQUERY | QWQUERY.
ISQUERY -> is EXPRESSION qm .
+QWQUERY -> qw EXPRESSION qm .
OBJECT-> WORD ATTRIBUTES .
+WORD -> name | variable .
+ATTRIBUTES -> OFS EXPRESSION | .
OFS -> of | s .
 BLOCK -> ob SCRIPT END .
-END -> end COMMAND .
+END -> end COMMANDOFS .
+COMMANDOFS -> COMMAND | OFS .
+
+
 
 Conjunction List
 

+ 140 - 148
selsl/object.go

@@ -17,6 +17,7 @@ func (s Slots) Get(name string) Object {
 	return res
 }
 
+/*
 func (s Slots) Clone() Slots {
 	clone := Slots{}
 	for k, v := range s {
@@ -24,6 +25,7 @@ func (s Slots) Clone() Slots {
 	}
 	return clone
 }
+*/
 
 // Constants for special object slot names
 const (
@@ -33,32 +35,82 @@ const (
 	ArgsSlot   = "*args"
 )
 
-// Effect is the effect an object has on return from a Run
-type Effect int
+// IsReturner is a marker interface for types that cause
+// a return.
+type IsReturner interface{ IsReturn() }
 
-// Constants for effects
-const (
-	EffectNone   Effect = 0
-	EffectBreak  Effect = 1
-	EffectReturn Effect = 2
-	EffectThrow  Effect = 4
-)
+// Breaker is a marker interface for a type that cases
+// a break in flow.
+type IsBreaker interface{ IsBreak() }
+
+// A Thrower is a marker interface for a type that causes
+// a throw.
+type IsThrower interface{ IsThrow() }
+
+// Type is the type of an object. It is simply a string.
+type Type string
+
+// A message can be send to an object. It is also an object.
+type Message struct {
+	Name string
+	Args []Object
+	Env  Object
+	Actor
+}
+
+func (m Message) String() string {
+	return "message: " + m.Name
+}
+
+func (Message) Type() Type {
+	return Type("message")
+}
+
+func (m Message) Value(env Object) Object {
+	return m
+}
+
+func (m Message) Send(Message) Object {
+	return m
+}
 
 // Object is the interface for all objects in SELSL.
 type Object interface {
-	Name() string
-	Slots() Slots
-	Run(env Object) Object
-	Effect() Effect
-	Clone() Object
+	// String stringifies the value of the object.
+	String() string
+	// Type returns the name of the type in lower case.
+	Type() Type
+	// Value evaluates the object in a context.
+	Value(env Object) Object
+	// Send sends a message to the object.
+	Send(msg Message) Object
 }
 
-var Nil Object = NewInstance("nil")
+type NilObject struct{}
+
+func (NilObject) String() string {
+	return "nil"
+}
+
+func (NilObject) Value(env Object) Object {
+	return nil
+}
+
+func (NilObject) Type() Type {
+	return Type("nil")
+}
+
+func (NilObject) Send(Message) Object {
+	return Nil
+}
+
+var Nil Object = NilObject{}
 
 func ParentOf(val Object) Object {
-	return val.Slots().Get(ParentSlot)
+	return val.Send(Message{Name: ParentSlot})
 }
 
+/*
 type Environment struct {
 	Instance
 }
@@ -86,33 +138,50 @@ func SendInEnv(to Object, message string, env Object) Object {
 		}
 		value = lookup.Slots().Get(message)
 	}
-	return value.Run(env)
+	return value.Value(env)
 }
 
 func Send(to Object, message string, me, here Object, args ...Object) Object {
 	env := NewEnvironment(here, me, to, args...)
 	return SendInEnv(to, message, env)
 }
+*/
 
+// Instance is a concrete implementation of Object
+// which can be embedded into Go structs to help them
+// become Objects themselves.
 type Instance struct {
-	name  string
+	// the type of the instance, used in Type()
+	kind Type
+	// Slots, used to store members
 	slots Slots
 }
 
-func (i Instance) Name() string {
-	return i.name
+// String stringifies the value of the instance.
+func (i Instance) String() string {
+	res := string(i.kind) + " { "
+	for k, v := range i.slots {
+		res += "\n" + k + ":" + v.String()
+	}
+	res += " } "
+	return res
 }
 
-func (i Instance) Slots() Slots {
-	return i.slots
+func (i Instance) Type() Type {
+	return Type(i.kind)
 }
 
-func (i Instance) Run(env Object) Object {
+func (i Instance) Value(env Object) Object {
 	return i
 }
 
-func (i Instance) Effect() Effect {
-	return EffectNone
+func (i Instance) Send(message Message) Object {
+	value := i.slots.Get(message.Name)
+	if value == nil || value == Nil {
+		// XXX should return an error in stead.
+		return Nil
+	}
+	return value.Value(message.Env)
 }
 
 func (i *Instance) Set(name string, inst Object) *Instance {
@@ -120,51 +189,37 @@ func (i *Instance) Set(name string, inst Object) *Instance {
 	return i
 }
 
-func (i Instance) DeepCopy() Instance {
-	return Instance{name: i.name + "", slots: i.slots.Clone()}
-}
-
-func (i Instance) Clone() Object {
-	return i.DeepCopy()
-}
-
-func NewInstance(name string) *Instance {
-	return &Instance{name: name, slots: Slots{}}
+func NewInstance(kind string) *Instance {
+	return &Instance{kind: Type(kind), slots: Slots{}}
 }
 
+// A primitive is a function implemented in Go that
+// on evaluation as an Object calls that function
+// with the given environment
 type Primitive func(env Object) Object
 
-func (p Primitive) Name() string {
-	return "primitive"
+func (p Primitive) Type() Type {
+	return Type("primitive")
 }
 
-func (p Primitive) Slots() Slots {
-	emptySlots := Slots{}
-	return emptySlots
+func (p Primitive) Send(m Message) Object {
+	return p.Value(m)
 }
 
-func (p Primitive) Effect() Effect {
-	return EffectNone
-}
-
-func (p Primitive) Run(env Object) Object {
+func (p Primitive) Value(env Object) Object {
 	return p(env)
 }
 
-func (p Primitive) Clone() Object {
-	return p
-}
-
 func Self(o Object) Object {
-	return o.Slots().Get(SelfSlot)
+	return o.Send(Message{Name: SelfSlot})
 }
 
 func Me(o Object) Object {
-	return o.Slots().Get(MeSlot)
+	return o.Send(Message{Name: MeSlot})
 }
 
 func Args(o Object) List {
-	list := o.Slots().Get(ArgsSlot)
+	list := o.Send(Message{Name: ArgsSlot})
 	if list == Nil {
 		return List{}
 	}
@@ -185,136 +240,68 @@ func Nullary(nullary func() Object) Primitive {
 
 type String string
 
-func (s String) Name() string {
+func (s String) String() string {
 	return string(s)
 }
 
-var StringType Object = NewInstance("String").
-	Set(ParentSlot, RootType)
-
-var StringSlots Slots = Slots{}.Set(ParentSlot, StringType)
-
-func (s String) Slots() Slots {
-	return StringSlots
+func (s String) Type() Type {
+	return Type("string")
 }
 
-func (s String) Effect() Effect {
-	return EffectNone
+func (p String) Send(m Message) Object {
+	// XXX implement string methods here
+	return Nil
 }
 
-func (s String) Run(env Object) Object {
+func (s String) Value(env Object) Object {
 	return s
 }
 
-func (s String) Clone() Object {
-	return String(s + "")
-}
-
-func NameOf(o Object) Object {
-	return String(o.Name())
-}
+type List []Object
 
-func NameString(o Object) func() Object {
-	return func() Object {
-		return NameOf(o)
-	}
+func (l List) Type() Type {
+	return Type("list")
 }
 
-var RootType Object = NewInstance("Type").Set("name", Unary(NameOf))
-
-type List []Object
-
-func (l List) Name() string {
+func (l List) String() string {
 	res := fmt.Sprintf("list: ")
 	sep := ""
 	for _, o := range l {
-		res = fmt.Sprintf("%s %s %s", res, sep, o.Name())
+		res = fmt.Sprintf("%s %s %s", res, sep, o.String())
 		sep = "and"
 	}
 	res += ";"
 	return res
 }
 
-var ListType Object = NewInstance("List").
-	Set(ParentSlot, RootType)
-
-var ListSlots Slots = Slots{}.Set(ParentSlot, ListType)
-
-func (l List) Slots() Slots {
-	return ListSlots
+func (l List) Send(m Message) Object {
+	// XXX implement list methods here
+	return Nil
 }
 
-func (l List) Effect() Effect {
-	return EffectNone
-}
-
-func (l List) Run(env Object) Object {
+func (l List) Value(env Object) Object {
 	return l
 }
 
-func (l List) DeepCopy() List {
-	res := List{}
-	for _, o := range l {
-		res = append(res, o.Clone())
-	}
-	return res
-}
-
-func (l List) Clone() Object {
-	return l.DeepCopy()
-}
-
-/*
-var RootType Object = NewInstance("Type").Slot("string")
-var InstanceType Type = NewType("Instance", &RootType, nil)
-var ErrorType Type = NewType("Error", &InstanceType, nil)
-var StringType Type = NewType("String", &InstanceType, nil)
-var IntType Type = NewType("Int", &InstanceType, nil)
-var ListType Type = NewType("List", &InstanceType, nil)
-var MethodType Type = NewType("Method", &InstanceType, nil)
-*/
-
 type Error struct {
 	*Instance
 	Error error
 }
 
-func (e Error) Name() string {
-	return string(fmt.Sprintf("error `%s`", e.Error))
-}
-
-var ErrorType Object = NewInstance("Error").
-	Set(ParentSlot, RootType)
-
-var ErrorSlots Slots = Slots{}.Set(ParentSlot, ErrorType)
-
-func (e Error) Slots() Slots {
-	return ErrorSlots
-}
-
-func (e Error) Effect() Effect {
-	return EffectThrow
+func (e Error) String() string {
+	return fmt.Sprintf("error `%s`", e.Error)
 }
 
-func (e Error) Run(env Object) Object {
+func (e Error) Value(env Object) Object {
 	return e
 }
 
-func (e Error) Clone() Object {
-	i := e.Instance.Clone().(Instance)
-	return Error{&i, e.Error}
-}
-
-func (s Error) String() string {
-	return fmt.Sprintf("%s", s.Error)
-}
-
-func NewErrorBasic(name string, err error) Error {
-	return Error{Instance: NewInstance(name), Error: err}
+func NewErrorBasic(err error) Error {
+	return Error{Instance: NewInstance("error"), Error: err}
 }
 
 func NewError(err string) Error {
-	return NewErrorBasic("error", fmt.Errorf("error: %s", err))
+	return NewErrorBasic(fmt.Errorf("%s", err))
 }
 
 type Int struct {
@@ -322,14 +309,18 @@ type Int struct {
 	Int int64
 }
 
-func NewInt(name string, Slots Slots, i int64) Int {
-	return Int{Instance: NewInstance(name), Int: i}
+func NewInt(i int64) Int {
+	return Int{Instance: NewInstance("int"), Int: i}
 }
 
 func (i Int) String() string {
 	return fmt.Sprintf("%d", i.Int)
 }
 
+func (i Int) Value(env Object) Object {
+	return i
+}
+
 type Statement struct {
 	*Instance
 	Command Object
@@ -337,14 +328,15 @@ type Statement struct {
 }
 
 func (s Statement) String() string {
-	return fmt.Sprintf("%s %s.", s.Command.Name(), s.Param.Name())
+	return fmt.Sprintf("%s %s.", s.Command.String(), s.Param.String())
 }
 
-func (s Statement) Run(env Object) Object {
-	cenv := env.Clone()
+/*
+func (s Statement) Value(env Object) Object {
+	cenv := env
 	cenv.Slots().Set(ArgsSlot, s.Param)
 	return SendInEnv(env, s.Command.Name(), cenv)
-}
+}*/
 
 var _ Object = Error{}
 var _ Object = String("")