Skip to content

Commit

Permalink
libcore|Scripting: Core functionality for object-oriented classes
Browse files Browse the repository at this point in the history
This commit implements the core plumbing of an object-oriented class
system for Doomsday Script.

Added Value::memberScope(): when the value is used with the member
operator (left value), returns the Record where the members should be
looked up from.

NameExpression now checks for the existence of the __isa__ member
that specifies all the superclasses of an instance. Identifiers can
be located from superclasses if they are not actual instance members.

Built-in types like DictionaryValue can return their built-in class
directly via memberScope(), so that any operations done on them
are looked up from the class.

Evaluator now keeps track of the scope in which evaluation occurs
in terms of a Value, so that the appropriate instance can be determined
when doing a member function call. It can then call memberScope()
as appropriate to determine the namespace of the members. It can
also then give out the scope when a result is popped, so that a
function call can take this value and give it to "self" or specify
it via Context::instanceScope() for native calls.

ScriptSystem defines built-in classes in the native "Script" module,
e.g., Script.Dictionary with keys() and values() member functions.

Refactored dictkeys() and dictvalues() to ask DictionaryValue to
compose the returned arrays. Applied pimpl in Evaluator and Context.
  • Loading branch information
skyjake committed Jun 11, 2014
1 parent b9c9dbd commit bbc2dbf
Show file tree
Hide file tree
Showing 30 changed files with 610 additions and 327 deletions.
14 changes: 14 additions & 0 deletions doomsday/libcore/include/de/data/dictionaryvalue.h
Expand Up @@ -26,6 +26,8 @@

namespace de {

class ArrayValue;

/**
* Subclass of Value that contains an array of values, indexed by any value.
*
Expand Down Expand Up @@ -85,9 +87,21 @@ class DENG2_PUBLIC DictionaryValue : public Value

void remove(Elements::iterator const &pos);

enum ContentSelection { Keys, Values };

/**
* Creates an array with the keys or the values of the dictionary.
*
* @param selection Keys or values.
*
* @return Caller gets ownership.
*/
ArrayValue *contentsAsArray(ContentSelection selection) const;

// Implementations of pure virtual methods.
Value *duplicate() const;
Text asText() const;
Record *memberScope() const;
dsize size() const;
Value const &element(Value const &index) const;
Value &element(Value const &index);
Expand Down
1 change: 1 addition & 0 deletions doomsday/libcore/include/de/data/recordvalue.h
Expand Up @@ -106,6 +106,7 @@ class DENG2_PUBLIC RecordValue : public Value, DENG2_OBSERVES(Record, Deletion)
Value *duplicate() const;
Value *duplicateAsReference() const;
Text asText() const;
Record *memberScope() const;
dsize size() const;
void setElement(Value const &index, Value *elementValue);
Value *duplicateElement(Value const &value) const;
Expand Down
3 changes: 2 additions & 1 deletion doomsday/libcore/include/de/data/refvalue.h
Expand Up @@ -62,6 +62,7 @@ class DENG2_PUBLIC RefValue : public Value,
Value *duplicate() const;
Number asNumber() const;
Text asText() const;
Record *memberScope() const;
dsize size() const;
Value const &element(Value const &index) const;
Value &element(Value const &index);
Expand All @@ -79,7 +80,7 @@ class DENG2_PUBLIC RefValue : public Value,
void multiply(Value const &value);
void modulo(Value const &divisor);
void assign(Value *value);
void call(Process &process, Value const &arguments) const;
void call(Process &process, Value const &arguments, Value *instanceScope) const;

// Implements ISerializable.
void operator >> (Writer &to) const;
Expand Down
21 changes: 16 additions & 5 deletions doomsday/libcore/include/de/data/value.h
Expand Up @@ -27,6 +27,7 @@
namespace de {

class Process;
class Record;
class NumberValue;

/**
Expand Down Expand Up @@ -138,6 +139,15 @@ class DENG2_PUBLIC Value : public String::IPatternArg, public ISerializable
return dynamic_cast<ValueType const *>(this);
}

/**
* Returns the scope for any members of this value. When evaluating a member in
* reference to this value, this will return the primary scope using which the
* members can be found.
*
* For built-in types, this may return one of the Script.* records.
*/
virtual Record *memberScope() const;

/**
* Determine the size of the value. The meaning of this
* depends on the type of the value.
Expand Down Expand Up @@ -279,12 +289,13 @@ class DENG2_PUBLIC Value : public String::IPatternArg, public ISerializable
/**
* Applies the call operator on the value.
*
* @param process Process where the call is made.
* @param arguments Arguments of the call.
*
* @return Result of the call operator, which can be anything.
* @param process Process where the call is made.
* @param arguments Arguments of the call.
* @param instanceScope Optional scope that becomes the value of the "self"
* variable in the called function's local namespace.
* Ownership taken.
*/
virtual void call(Process &process, Value const &arguments) const;
virtual void call(Process &process, Value const &arguments, Value *instanceScope = 0) const;

public:
/**
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libcore/include/de/scriptsys/arrayexpression.h
Expand Up @@ -52,7 +52,7 @@ class ArrayExpression : public Expression
*/
void add(Expression *arg);

void push(Evaluator &evaluator, Record *names = 0) const;
void push(Evaluator &evaluator, Value *scope = 0) const;

/**
* Returns one of the expressions in the array.
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libcore/include/de/scriptsys/builtinexpression.h
Expand Up @@ -65,7 +65,7 @@ class DENG2_PUBLIC BuiltInExpression : public Expression

~BuiltInExpression();

void push(Evaluator &evaluator, Record *names = 0) const;
void push(Evaluator &evaluator, Value *scope = 0) const;

Value *evaluate(Evaluator &evaluator) const;

Expand Down
97 changes: 21 additions & 76 deletions doomsday/libcore/include/de/scriptsys/context.h
@@ -1,7 +1,7 @@
/*
* The Doomsday Engine Project -- libcore
*
* Copyright © 2004-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
* Copyright © 2004-2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
Expand Down Expand Up @@ -39,6 +39,9 @@ class DENG2_PUBLIC Context
/// Attempting a jump when there is no suitable target (continue or break). @ingroup errors
DENG2_ERROR(JumpError);

/// There is no instance scope defined for the context. @ingroup errors
DENG2_ERROR(UndefinedScopeError);

enum Type {
BaseProcess,
GlobalNamespace,
Expand All @@ -55,13 +58,11 @@ class DENG2_PUBLIC Context
*/
Context(Type type, Process *owner, Record *globals = 0);

virtual ~Context();

/// Determines the type of the execution context.
Type type() { return _type; }
Type type();

/// Returns the process that owns this context.
Process &process() { return *_owner; }
Process &process();

/// Returns the namespace of the context.
Record &names();
Expand Down Expand Up @@ -135,84 +136,28 @@ class DENG2_PUBLIC Context
void setIterationValue(Value *value);

/**
* Returns the throwaway variable. This can be used for dumping
* values that are not needed. For instance, the weak assignment operator
* will use this when the identifier already exists.
* Sets the instance scope of the context. This is equivalent to the value
* of the "self" variable, however only used for native function calls.
*
* @param scope Value that specifies the instance whose scope the context
* is being evaluated in. Ownership taken.
*/
Variable &throwaway() { return _throwaway; }
void setInstanceScope(Value *scope);

private:
/**
* Information about the control flow is stored within a stack of
* ControlFlow instances.
* Returns the current instance scope. A scope must exist if this is called.
*/
class ControlFlow {
public:
/**
* Constructor.
*
* @param current Current statement being executed.
* @param f Statement where normal flow continues.
* @param c Statement where to jump on "continue".
* @c NULL if continuing is not allowed.
* @param b Statement where to jump to and flow from on "break".
* @c NULL if breaking is not allowed.
*/
ControlFlow(Statement const *current,
Statement const *f = 0,
Statement const *c = 0,
Statement const *b = 0)
: flow(f), jumpContinue(c), jumpBreak(b), iteration(0), _current(current) {}

/// Returns the currently executed statement.
Statement const *current() const { return _current; }

/// Sets the currently executed statement. When the statement
/// changes, the phase is reset back to zero.
void setCurrent(Statement const *s) { _current = s; }

public:
Statement const *flow;
Statement const *jumpContinue;
Statement const *jumpBreak;
Value *iteration;

private:
Statement const *_current;
};

private:
/// Returns the topmost control flow information.
ControlFlow &flow() { return _controlFlow.back(); }

/// Pops the topmost control flow instance off of the stack. The
/// iteration value is deleted, if it has been defined.
void popFlow();
Value &instanceScope() const;

/// Sets the currently executed statement.
void setCurrent(Statement const *statement);
/**
* Returns the throwaway variable. This can be used for dumping
* values that are not needed. For instance, the weak assignment operator
* will use this when the identifier already exists.
*/
Variable &throwaway();

private:
/// Type of the execution context.
Type _type;

/// The process that owns this context.
Process *_owner;

/// Control flow stack.
typedef std::vector<ControlFlow> FlowStack;
FlowStack _controlFlow;

/// Expression evaluator.
Evaluator _evaluator;

/// Determines whether the namespace is owned by the context.
bool _ownsNamespace;

/// The local namespace of this context.
Record *_names;

Variable _throwaway;
DENG2_PRIVATE(d)
};

} // namespace de
Expand Down
Expand Up @@ -48,7 +48,7 @@ class DictionaryExpression : public Expression
*/
void add(Expression *key, Expression *value);

void push(Evaluator &evaluator, Record *names = 0) const;
void push(Evaluator &evaluator, Value *scope = 0) const;

/**
* Collects the result keys and values of the arguments and puts them
Expand Down
44 changes: 12 additions & 32 deletions doomsday/libcore/include/de/scriptsys/evaluator.h
@@ -1,7 +1,7 @@
/*
* The Doomsday Engine Project -- libcore
*
* Copyright © 2004-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
* Copyright © 2004-2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
Expand Down Expand Up @@ -49,9 +49,8 @@ class DENG2_PUBLIC Evaluator

public:
Evaluator(Context &owner);
~Evaluator();

Context &context() { return _context; }
Context &context();

/**
* Returns the process that owns this evaluator.
Expand Down Expand Up @@ -114,9 +113,10 @@ class DENG2_PUBLIC Evaluator
* Insert the given expression to the top of the expression stack.
*
* @param expression Expression to push on the stack.
* @param names Namespace scope for this expression only.
* @param scope Scope for this expression only (using memberScope()).
* Evaluator takes ownership of this value.
*/
void push(Expression const *expression, Record *names = 0);
void push(Expression const *expression, Value *scope = 0);

/**
* Push a value onto the result stack.
Expand All @@ -129,10 +129,15 @@ class DENG2_PUBLIC Evaluator
/**
* Pop a value off of the result stack.
*
* @param evaluationScope If not @c NULL, the scope used for evaluating the
* result is passed here to the caller. If there was
* a scope, ownership of the scope value is given to
* the caller.
*
* @return Value resulting from expression evaluation. Caller
* gets ownership of the returned object.
*/
Value *popResult();
Value *popResult(Value **evaluationScope = 0);

/**
* Pop a value off of the result stack, making sure it has a specific type.
Expand Down Expand Up @@ -163,32 +168,7 @@ class DENG2_PUBLIC Evaluator
Value &result();

private:
void clearNames();
void clearResults();
void clearStack();

/// The context that owns this evaluator.
Context &_context;

struct ScopedExpression {
Expression const *expression;
Record *names;
ScopedExpression(Expression const *e = 0, Record *n = 0) : expression(e), names(n) {}
};
typedef std::vector<ScopedExpression> Expressions;
typedef std::vector<Value *> Results;

/// The expression that is currently being evaluated.
Expression const *_current;

/// Namespace for the current expression.
Record *_names;

Expressions _stack;
Results _results;

/// Returned when there is no result to give.
NoneValue _noResult;
DENG2_PRIVATE(d)
};

} // namespace de
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libcore/include/de/scriptsys/expression.h
Expand Up @@ -88,7 +88,7 @@ class DENG2_PUBLIC Expression : public ISerializable
public:
virtual ~Expression();

virtual void push(Evaluator &evaluator, Record *names = 0) const;
virtual void push(Evaluator &evaluator, Value *scope = 0) const;

virtual Value *evaluate(Evaluator &evaluator) const = 0;

Expand Down
2 changes: 1 addition & 1 deletion doomsday/libcore/include/de/scriptsys/functionvalue.h
Expand Up @@ -45,7 +45,7 @@ class FunctionValue : public Value
bool isTrue() const;
bool isFalse() const;
dint compare(Value const &value) const;
void call(Process &process, Value const &arguments) const;
void call(Process &process, Value const &arguments, Value *instanceScope) const;

// Implements ISerializable.
void operator >> (Writer &to) const;
Expand Down
5 changes: 2 additions & 3 deletions doomsday/libcore/include/de/scriptsys/nameexpression.h
Expand Up @@ -48,10 +48,9 @@ class NameExpression : public Expression
public:
NameExpression();
NameExpression(String const &identifier, Flags const &flags = ByValue);
~NameExpression();

/// Returns the identifier in the name expression.
String const &identifier() const { return _identifier; }
String const &identifier() const;

Value *evaluate(Evaluator &evaluator) const;

Expand All @@ -60,7 +59,7 @@ class NameExpression : public Expression
void operator << (Reader &from);

private:
String _identifier;
DENG2_PRIVATE(d)
};

} // namespace de
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libcore/include/de/scriptsys/operatorexpression.h
Expand Up @@ -76,7 +76,7 @@ class OperatorExpression : public Expression

~OperatorExpression();

void push(Evaluator &evaluator, Record *names = 0) const;
void push(Evaluator &evaluator, Value *scope = 0) const;

Value *evaluate(Evaluator &evaluator) const;

Expand Down

0 comments on commit bbc2dbf

Please sign in to comment.