Skip to content

Commit

Permalink
Scripting|libcore: Parsing and executing a scope statement
Browse files Browse the repository at this point in the history
ScopeStatement executes a compound in a specific namespace. This makes
it possible to define member functions and other members in a named
record.
  • Loading branch information
skyjake committed Jun 13, 2014
1 parent 42a2371 commit f6c9017
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 15 deletions.
3 changes: 2 additions & 1 deletion doomsday/libcore/include/de/scriptsys/context.h
Expand Up @@ -45,7 +45,8 @@ class DENG2_PUBLIC Context
enum Type {
BaseProcess,
GlobalNamespace,
FunctionCall
FunctionCall,
Namespace
};

public:
Expand Down
5 changes: 4 additions & 1 deletion doomsday/libcore/include/de/scriptsys/expression.h
Expand Up @@ -81,7 +81,10 @@ class DENG2_PUBLIC Expression : public ISerializable
ReadOnly = 0x200,

/// Variable will be raised into a higher namespace.
Export = 0x400
Export = 0x400,

/// If missing, create a new subrecord. Otherwise, reuse the existing record.
NewSubrecordIfNotInScope = 0x800
};
Q_DECLARE_FLAGS(Flags, Flag)

Expand Down
3 changes: 2 additions & 1 deletion doomsday/libcore/include/de/scriptsys/parser.h
Expand Up @@ -33,6 +33,7 @@
namespace de {

class Compound;
class Statement;
class ExpressionStatement;
class PrintStatement;
class IfStatement;
Expand Down Expand Up @@ -99,7 +100,7 @@ class Parser : public IParser

ExpressionStatement *parseExportStatement();

ExpressionStatement *parseDeclarationStatement();
Statement *parseDeclarationStatement();

DeleteStatement *parseDeleteStatement();

Expand Down
10 changes: 8 additions & 2 deletions doomsday/libcore/include/de/scriptsys/process.h
Expand Up @@ -199,11 +199,17 @@ class DENG2_PUBLIC Process
void namespaces(Namespaces &spaces) const;

/**
* Returns the global namespace of the process. This is always the
* bottommost context in the stack.
* Returns the global namespace of the process. This is always the bottommost context
* in the stack.
*/
Record &globals();

/**
* Returns the local namespace of the process. This is always the topmost context in
* the stack.
*/
Record &locals();

private:
DENG2_PRIVATE(d)
};
Expand Down
3 changes: 2 additions & 1 deletion doomsday/libcore/include/de/scriptsys/statement.h
Expand Up @@ -72,7 +72,8 @@ class Statement : public ISerializable
PRINT,
TRY,
WHILE,
DELETE
DELETE,
SCOPE
};

private:
Expand Down
3 changes: 2 additions & 1 deletion doomsday/libcore/src/data/recordvalue.cpp
Expand Up @@ -28,6 +28,7 @@
#include "de/Variable"
#include "de/Writer"
#include "de/Reader"
#include "de/ScopeStatement"
#include "de/math.h"

namespace de {
Expand Down Expand Up @@ -247,7 +248,7 @@ void RecordValue::call(Process &process, Value const &arguments, Value *) const

ArrayValue *super = new ArrayValue;
*super << new RecordValue(d->record);
instance->record()->add(new Variable("__super__", super));
instance->record()->add(new Variable(ScopeStatement::SUPER_NAME, super));

// If there is an initializer method, call it now.
if(dereference().hasMember("__init__"))
Expand Down
45 changes: 41 additions & 4 deletions doomsday/libcore/src/scriptsys/parser.cpp
Expand Up @@ -31,6 +31,7 @@
#include "de/AssignStatement"
#include "de/DeleteStatement"
#include "de/FunctionStatement"
#include "de/ScopeStatement"
#include "de/TryStatement"
#include "de/CatchStatement"
#include "de/ArrayExpression"
Expand Down Expand Up @@ -312,17 +313,36 @@ ExpressionStatement *Parser::parseExportStatement()
Expression::Export | Expression::LocalOnly));
}

ExpressionStatement *Parser::parseDeclarationStatement()
Statement *Parser::parseDeclarationStatement()
{
// "record" name-expr ["," name-expr]*
// "record" name-expr "(" [ name-expr ["," name-expr]* ] ")" members-compound

if(_statementRange.size() < 2)
{
throw MissingTokenError("Parser::parseDeclarationStatement",
"Expected identifier to follow " + _statementRange.firstToken().asText());
}
Expression::Flags flags = Expression::LocalOnly | Expression::NewSubrecord;
return new ExpressionStatement(parseList(_statementRange.startingFrom(1), Token::COMMA, flags));

// Is this a class record declaration?
dint pos = _statementRange.find(Token::PARENTHESIS_OPEN);
if(pos >= 0)
{
QScopedPointer<Expression> name(parseExpression(_statementRange.between(1, pos),
Expression::NewSubrecordIfNotInScope));
QScopedPointer<ScopeStatement> names(new ScopeStatement(name.take(),
parseList(_statementRange.between(pos + 1, _statementRange.closingBracket(pos)))));

parseConditionalCompound(names->compound(),
IgnoreExtraBeforeColon | StayAtClosingStatement);
return names.take();
}
else
{
// Regular record declaration.
Expression::Flags flags = Expression::LocalOnly | Expression::NewSubrecord;
return new ExpressionStatement(parseList(_statementRange.startingFrom(1), Token::COMMA, flags));
}
}

DeleteStatement *Parser::parseDeleteStatement()
Expand Down Expand Up @@ -861,6 +881,14 @@ Expression *Parser::parseTokenExpression(TokenRange const &range, Expression::Fl
{
return ConstantExpression::Pi();
}
else if(token.equals(ScriptLex::SCOPE) &&
range.size() == 2 &&
range.token(1).type() == Token::IDENTIFIER)
{
// Explicit local scope.
return new NameExpression(range.token(1).str(), flags,
NameExpression::LOCAL_SCOPE);
}
}

switch(token.type())
Expand All @@ -870,12 +898,21 @@ Expression *Parser::parseTokenExpression(TokenRange const &range, Expression::Fl
{
return new NameExpression(range.token(0).str(), flags);
}
else if(range.size() == 3 &&
range.token(1).equals(ScriptLex::SCOPE) &&
range.token(2).type() == Token::IDENTIFIER)
{
// Scoped name. This is intended for allowing access to shadowed
// identifiers from super records.
return new NameExpression(range.token(2).str(), flags,
range.token(0).str());
}
else
{
throw UnexpectedTokenError("Parser::parseTokenExpression",
"Unexpected token " + range.token(1).asText());
}

case Token::LITERAL_STRING_APOSTROPHE:
case Token::LITERAL_STRING_QUOTED:
case Token::LITERAL_STRING_LONG:
Expand Down
29 changes: 25 additions & 4 deletions doomsday/libcore/src/scriptsys/process.cpp
Expand Up @@ -236,23 +236,26 @@ void Process::execute()
}

// We will execute until this depth is complete.
duint startDepth = depth();
duint startDepth = d->depth();
if(startDepth == 1)
{
// Mark the start time.
d->startedAt = Time();
}

// Execute the next command(s).
while(d->state == Running && depth() >= startDepth)
while(d->state == Running && d->depth() >= startDepth)
{
try
{
if(!context().execute())
dsize execDepth = d->depth();
if(!context().execute() && d->depth() == execDepth)
{
// There was no statement left to execute, and no new contexts were
// added to the stack.
finish();
}
if(d->startedAt.since() > MAX_EXECUTION_TIME)
else if(d->startedAt.since() > MAX_EXECUTION_TIME)
{
/// @throw HangError Execution takes too long.
throw HangError("Process::execute",
Expand Down Expand Up @@ -368,6 +371,10 @@ void Process::call(Function const &function, ArrayValue const &arguments, Value
{
pushContext(new Context(Context::GlobalNamespace, this, function.globals()));
}

// If the function is not in the global namespace, add its own parent
// namespace to the stack, too.


// Create a new context.
pushContext(new Context(Context::FunctionCall, this));
Expand Down Expand Up @@ -412,10 +419,19 @@ void Process::call(Function const &function, ArrayValue const &arguments, Value
void Process::namespaces(Namespaces &spaces) const
{
spaces.clear();

bool gotFunction = false;

DENG2_FOR_EACH_CONST_REVERSE(Instance::ContextStack, i, d->stack)
{
Context &context = **i;
if(context.type() == Context::FunctionCall)
{
// Only the topmost function call namespace is available: one cannot
// access the local variables of the callers.
if(gotFunction) continue;
gotFunction = true;
}
spaces.push_back(&context.names());
if(context.type() == Context::GlobalNamespace)
{
Expand All @@ -430,4 +446,9 @@ Record &Process::globals()
return d->stack[0]->names();
}

Record &Process::locals()
{
return d->stack.back()->names();
}

} // namespace de
5 changes: 5 additions & 0 deletions doomsday/libcore/src/scriptsys/statement.cpp
Expand Up @@ -27,6 +27,7 @@
#include "de/IfStatement"
#include "de/FlowStatement"
#include "de/PrintStatement"
#include "de/ScopeStatement"
#include "de/TryStatement"
#include "de/WhileStatement"
#include "de/Reader"
Expand Down Expand Up @@ -86,6 +87,10 @@ Statement *Statement::constructFrom(Reader &reader)
case WHILE:
result.reset(new WhileStatement);
break;

case SCOPE:
result.reset(new ScopeStatement);
break;

default:
/// @throw DeserializationError The identifier that species the type of the
Expand Down

0 comments on commit f6c9017

Please sign in to comment.