Skip to content

Commit

Permalink
libdeng2|Script: Added "export" statement and keyword
Browse files Browse the repository at this point in the history
"export" can be used to lift variables to the next higher namespace.
It can be used on existing variables and with assignment.
  • Loading branch information
skyjake committed Nov 29, 2012
1 parent c92a82f commit b43e176
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 48 deletions.
5 changes: 4 additions & 1 deletion doomsday/libdeng2/include/de/scriptsys/expression.h
Expand Up @@ -78,7 +78,10 @@ namespace de
NotInScope = 0x100,

/// Variable will be set to read-only mode.
ReadOnly = 0x200
ReadOnly = 0x200,

/// Variable will be raised into a higher namespace.
Export = 0x400
};
Q_DECLARE_FLAGS(Flags, Flag)

Expand Down
2 changes: 2 additions & 0 deletions doomsday/libdeng2/include/de/scriptsys/parser.h
Expand Up @@ -96,6 +96,8 @@ namespace de
ForStatement *parseForStatement();

ExpressionStatement *parseImportStatement();

ExpressionStatement *parseExportStatement();

ExpressionStatement *parseDeclarationStatement();

Expand Down
1 change: 1 addition & 0 deletions doomsday/libdeng2/include/de/scriptsys/scriptlex.h
Expand Up @@ -63,6 +63,7 @@ namespace de
static String const DEF;
static String const TRY;
static String const IMPORT;
static String const EXPORT;
static String const RECORD;
static String const DEL;
static String const PASS;
Expand Down
81 changes: 35 additions & 46 deletions doomsday/libdeng2/src/scriptsys/nameexpression.cpp
Expand Up @@ -53,6 +53,7 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const
evaluator.namespaces(spaces);

Record *foundInNamespace = 0;
Record *higherNamespace = 0;
Variable *variable = 0;

DENG2_FOR_EACH(Evaluator::Namespaces, i, spaces)
Expand All @@ -63,16 +64,12 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const
// The name exists in this namespace.
variable = &ns[_identifier];
foundInNamespace = &ns;

// Also note the higher namespace (for export).
Evaluator::Namespaces::iterator next = i;
if(++next != spaces.end()) higherNamespace = *next;
break;
}
/*
if(ns.hasSubrecord(_identifier))
{
// The name exists in this namespace (as a record).
record = &ns.subrecord(_identifier);
foundInNamespace = &ns;
}
*/
if(flags().testFlag(LocalOnly))
{
break;
Expand All @@ -95,22 +92,15 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const
// Should we delete the identifier?
if(flags().testFlag(Delete))
{
if(!variable) // && !record)
if(!variable)
{
throw NotFoundError("NameExpression::evaluate",
"Cannot delete nonexistent identifier '" + _identifier + "'");
}
DENG2_ASSERT(foundInNamespace != 0);
//if(variable)
//{

delete foundInNamespace->remove(*variable);

/*}
else if(record)
{
delete foundInNamespace->remove(_identifier);
}*/
return new NoneValue();
}

Expand All @@ -125,12 +115,40 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const

// If nothing is found and we are permitted to create new variables, do so.
// Occurs when assigning into new variables.
if(!variable /*&& !record*/ && flags().testFlag(NewVariable))
if(!variable && flags().testFlag(NewVariable))
{
variable = new Variable(_identifier);

// Add it to the local namespace.
spaces.front()->add(variable);

// Take note of the namespaces.
foundInNamespace = spaces.front();
if(!higherNamespace && spaces.size() > 1)
{
Evaluator::Namespaces::iterator i = spaces.begin();
higherNamespace = *(++i);
}
}

// Export variable into a higher namespace?
if(flags().testFlag(Export))
{
if(!variable)
{
throw NotFoundError("NameExpression::evaluate",
"Cannot export nonexistent identifier '" + _identifier + "'");
}
if(!higherNamespace)
{
throw NotFoundError("NameExpression::evaluate",
"No higher namespace for exporting '" + _identifier + "' into");
}
if(higherNamespace != foundInNamespace)
{
foundInNamespace->remove(*variable);
higherNamespace->add(variable);
}
}

// Should we import a namespace?
Expand All @@ -154,13 +172,6 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const
}

return new RecordValue(record);

/*
// Take a copy if requested ("import record").
if(flags().testFlag(NewRecord))
{
record = &spaces.front()->add(_identifier, new Record(*record));
}*/
}

if(variable)
Expand All @@ -177,28 +188,6 @@ Value *NameExpression::evaluate(Evaluator &evaluator) const
return variable->value().duplicate();
}
}

/*
// We may be permitted to create a new record.
if(flags() & NewRecord)
{
if(!record)
{
// Add it to the local namespace.
record = &spaces.front()->addRecord(_identifier);
}
else
{
// Create a variable referencing the record.
spaces.front()->add(new Variable(_identifier, new RecordValue(record)));
}
}
if(record)
{
// Records can only be referenced.
return new RecordValue(record);
}
*/

throw NotFoundError("NameExpression::evaluate", "Identifier '" + _identifier +
"' does not exist");
Expand Down
27 changes: 26 additions & 1 deletion doomsday/libdeng2/src/scriptsys/parser.cpp
Expand Up @@ -187,7 +187,11 @@ void Parser::parseStatement(Compound &compound)
{
compound.add(parseAssignStatement());
}
else
else if(firstToken.equals(ScriptLex::EXPORT))
{
compound.add(parseExportStatement());
}
else
{
compound.add(parseExpressionStatement());
}
Expand Down Expand Up @@ -293,6 +297,20 @@ ExpressionStatement *Parser::parseImportStatement()
return new ExpressionStatement(parseList(_statementRange.startingFrom(startAt), Token::COMMA, flags));
}

ExpressionStatement *Parser::parseExportStatement()
{
// "export" name-expr ["," name-expr]*

if(_statementRange.size() < 2)
{
throw MissingTokenError("Parser::parseExportStatement",
"Expected identifier to follow " + _statementRange.firstToken().asText());
}

return new ExpressionStatement(parseList(_statementRange.startingFrom(1), Token::COMMA,
Expression::Export | Expression::LocalOnly));
}

ExpressionStatement *Parser::parseDeclarationStatement()
{
// "record" name-expr ["," name-expr]*
Expand Down Expand Up @@ -449,6 +467,13 @@ AssignStatement *Parser::parseAssignStatement()
{
Expression::Flags flags = Expression::NewVariable | Expression::ByReference | Expression::LocalOnly;

/// "export" will export the newly assigned variable.
if(_statementRange.firstToken().equals(ScriptLex::EXPORT))
{
flags |= Expression::Export;
_statementRange = _statementRange.startingFrom(1);
}

/// "const" makes read-only variables.
if(_statementRange.firstToken().equals(ScriptLex::CONST))
{
Expand Down
2 changes: 2 additions & 0 deletions doomsday/libdeng2/src/scriptsys/scriptlex.cpp
Expand Up @@ -38,6 +38,7 @@ String const ScriptLex::FOR("for");
String const ScriptLex::DEF("def");
String const ScriptLex::TRY("try");
String const ScriptLex::IMPORT("import");
String const ScriptLex::EXPORT("export");
String const ScriptLex::RECORD("record");
String const ScriptLex::DEL("del");
String const ScriptLex::PASS("pass");
Expand Down Expand Up @@ -370,6 +371,7 @@ bool ScriptLex::isKeyword(Token const &token)
FOR,
IF,
IMPORT,
EXPORT,
IN,
NOT,
OR,
Expand Down
37 changes: 37 additions & 0 deletions doomsday/tests/script/kitchen_sink.de
Expand Up @@ -168,6 +168,16 @@ if False: print "Lots of useless stuff here"
elsif True
print "From the elsif"
end

if False
print 'a)'
elsif False
print 'b)'
elsif True > False
print 'c)'
end
print 'After the a), b), c)'

if True: print 'Everything on one line.'; else: print 'Goes unseen.'
if False; print 'Goes unseen again.'; else; print 'And with semicolons (requires "end").'; end

Expand Down Expand Up @@ -599,6 +609,33 @@ end
globalAssign()
print 'a =', a

sections.subsection('Exporting variable from a function.')
def exporting()
abc = 'Value from exporting()'
export abc
end
abc = 'Value in global namespace'
print 'Before: abc =', abc
exporting()
print 'After: abc =', abc

sections.subsection('Exporting a record.')
def exporting2()
record bunny
bunny.ear = 'value'
export bunny
end
exporting2()
print 'bunny =', bunny

sections.subsection('Exporting while assigning.')
del abc
def exporting3()
export abc = 'Value from exporting3()'
end
exporting3()
print 'abc =', abc

sections.subsection('Built-in function locals() returns the local namespace as a record.')
print 'At global level:'
print locals()
Expand Down

0 comments on commit b43e176

Please sign in to comment.