Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Commit

Permalink
[[ Bug 23149 ]] Replace mode syntax factory functions
Browse files Browse the repository at this point in the history
This patch replaces the current mode syntax factory functions:
`MCModeNewCommand` and `MCModeNewFunction` with a more general mechanism based
around instantiating a class as a global-variable, constructed using a syntax
factory function pointer.

There are three global classes which can be instantiated in this manner -
`MCNewStatementFactory`, `MCNewFunctionFactory` and `MCNewOperatorFactory` -
corresponding to the top-level factory functions `MCN_new_statement`,
`MCN_new_function` and `MCN_new_operator`.

The classes 'hide' a linked list of factory functions, which are invoked
in turn if the top-level functions do not recognise an id.
  • Loading branch information
runrevmark committed Apr 4, 2021
1 parent 9d4dc77 commit 4e8ac6a
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 64 deletions.
7 changes: 0 additions & 7 deletions engine/src/mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,6 @@ bool MCModeCanLoadHome(void);
//
void MCModeSetupCrashReporting(void);

// These hooks are used to create mode-specific syntax objects.
//
// They are called by the MCN_new_* methods.
//
MCStatement *MCModeNewCommand(int2 which);
MCExpression *MCModeNewFunction(int2 which);

// This hook is used to determine whether to queue stacks that are wanting to
// be opened (e.g. via an AppleEvent OpenDoc event).
//
Expand Down
37 changes: 19 additions & 18 deletions engine/src/mode_development.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
#include "player.h"
#include "objptr.h"
#include "osspec.h"
#include "newobj.h"

#include "globals.h"
#include "license.h"
Expand Down Expand Up @@ -245,6 +246,24 @@ void MCRevRelicense::exec_ctxt(MCExecContext& ctxt)
atexit(restart_livecode);
}

/* Define the factory function handling development mode syntax. */
static MCStatement *MCN_new_statement_development(int2 which)
{
switch(which)
{
case S_REV_RELICENSE:
return new MCRevRelicense;
default:
break;
}

return NULL;
}

/* Define a new statement factory wrapping the factory function. */
static MCNewStatementFactory
MCdevelopmentmodestatementfactory(MCN_new_statement_development);

////////////////////////////////////////////////////////////////////////////////
//
// Implementation of MCDispatch::startup method for DEVELOPMENT mode.
Expand Down Expand Up @@ -602,24 +621,6 @@ bool MCModeCanLoadHome(void)
return true;
}

MCStatement *MCModeNewCommand(int2 which)
{
switch(which)
{
case S_REV_RELICENSE:
return new MCRevRelicense;
default:
break;
}

return NULL;
}

MCExpression *MCModeNewFunction(int2 which)
{
return NULL;
}

bool MCModeShouldQueueOpeningStacks(void)
{
return MCscreen == NULL || MCenvironmentactive;
Expand Down
10 changes: 0 additions & 10 deletions engine/src/mode_installer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1603,16 +1603,6 @@ bool MCModeCanLoadHome(void)
return false;
}

MCStatement *MCModeNewCommand(int2 which)
{
return NULL;
}

MCExpression *MCModeNewFunction(int2 which)
{
return NULL;
}

MCObject *MCModeGetU3MessageTarget(void)
{
return MCdefaultstackptr -> getcard();
Expand Down
10 changes: 0 additions & 10 deletions engine/src/mode_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,16 +301,6 @@ bool MCModeCanLoadHome(void)
return false;
}

MCStatement *MCModeNewCommand(int2 which)
{
return NULL;
}

MCExpression *MCModeNewFunction(int2 which)
{
return NULL;
}

bool MCModeShouldQueueOpeningStacks(void)
{
return false;
Expand Down
10 changes: 0 additions & 10 deletions engine/src/mode_standalone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1222,16 +1222,6 @@ bool MCModeCanLoadHome(void)
return false;
}

MCStatement *MCModeNewCommand(int2 which)
{
return NULL;
}

MCExpression *MCModeNewFunction(int2 which)
{
return NULL;
}

MCObject *MCModeGetU3MessageTarget(void)
{
return MCdefaultstackptr -> getcard();
Expand Down
32 changes: 23 additions & 9 deletions engine/src/newobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */

#include "mode.h"

/* Define the roots of the new object factory lists - one for each kind. These
* are used internally by the MCNewObjFactory derived classes. */
MCNewStatementFactory::Base *MCNewStatementFactory::s_functions = nullptr;
MCNewFunctionFactory::Base *MCNewFunctionFactory::s_functions = nullptr;
MCNewOperatorFactory::Base *MCNewOperatorFactory::s_functions = nullptr;

MCStatement *MCN_new_statement(int2 which)
{
switch (which)
Expand Down Expand Up @@ -309,8 +315,8 @@ MCStatement *MCN_new_statement(int2 which)
break;
}

MCStatement *t_new_statement;
t_new_statement = MCModeNewCommand(which);
MCStatement *t_new_statement
= MCNewStatementFactory::New(which);
if (t_new_statement != NULL)
return t_new_statement;

Expand Down Expand Up @@ -871,12 +877,11 @@ MCExpression *MCN_new_function(int2 which)
break;
}

MCExpression *t_new_function;
t_new_function = MCModeNewFunction(which);

// SN-2014-11-25: [[ Bug 14088 ]] A NULL pointer is returned if no function exists.
// (that avoids to get a MCFunction which does not implement eval_ctxt).
return t_new_function;
/* Unlike statements and operators, functions return nullptr if a function
* cannot be instantiated. This handles special-cases like 'templateField()'
* which only makes sense in container context (where it is instantiated
* explicitly). */
return MCNewFunctionFactory::New(which);
}

MCExpression *MCN_new_operator(int2 which)
Expand Down Expand Up @@ -942,6 +947,15 @@ MCExpression *MCN_new_operator(int2 which)
case O_ENDS_WITH:
return new MCEndsWith;
default:
return new MCExpression;
break;
}

MCExpression *t_new_operator
= MCNewOperatorFactory::New(which);
if (t_new_operator != nullptr)
{
return t_new_operator;
}

return new MCExpression;
}
122 changes: 122 additions & 0 deletions engine/src/newobj.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,131 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
//
// create new script objects
//
#ifndef NEWOBJ_H
#define NEWOBJ_H

class MCStatement;
class MCExpression;

/* The MCNewObjFactory class is designed to be derived from to form the three
* lists of newobj functions for statements, functions and operators.
*
* Derivations of the class are declared as global instances which automatically
* hook themselves into a globally-linked list via global constructors.
*
* As there is no guaranteed order to global constructors, care
* must be taken to ensure the syntax ids handled by each registerd function are
* distinct.
*
* The template base class exploits the fact that template arguments can be
* a derived class. This allows all the code to be in the base-class, with
* derived classes only needing to add a class variable `s_functions` which
* holds the root of the list and a constructor. */
template<typename Derived, typename Node>
class MCNewObjFactory
{
public:
typedef Derived Self;
typedef MCNewObjFactory<Derived, Node> Base;
typedef Node *(*Function)(int2 p_which);

MCNewObjFactory(
Function p_function)
: m_function(p_function)
{
m_next = Derived::s_functions;
Derived::s_functions = this;
}

static Node *
New(int2 p_which)
{
if (Derived::s_functions == nullptr)
{
return nullptr;
}
return Derived::s_functions->_New(p_which);
}

private:
Node *_New(
int2 p_which)
{
Node *t_instance
= m_function(p_which);
if (t_instance != nullptr)
{
return t_instance;
}

if (m_next == nullptr)
{
return nullptr;
}

return m_next->New(p_which);
}

Base *m_next;
Function m_function;
};

/* The MCNewStatementFactory class should be globally instantiated with
* a factory function for statement ids. */
class MCNewStatementFactory
: public MCNewObjFactory<MCNewStatementFactory, MCStatement>
{
public:
MCNewStatementFactory(
Function p_function)
: Base(p_function)
{
}

private:
static Base *s_functions;
friend Base;
};

/* The MCNewFunctionFactory class should be globally instantiated with a factory
* function for function ids. */
class MCNewFunctionFactory
: public MCNewObjFactory<MCNewFunctionFactory, MCExpression>
{
public:
MCNewFunctionFactory(
Function p_function)
: Base(p_function)
{
}

private:
static Base *s_functions;
friend Base;
};

/* The MCNewOperatorFactory class should be globally instantiated with a factory
* function for operator ids. */
class MCNewOperatorFactory
: public MCNewObjFactory<MCNewOperatorFactory, MCExpression>
{
public:
MCNewOperatorFactory(
Function p_function)
: Base(p_function)
{
}

private:
static Base *s_functions;
friend Base;
};

/* The new methods for each type of syntax id which can be instantiated. These
* functions handle the main syntax ids first, and then pass on responsibility
* to the list of factories for the syntax type. */
extern MCStatement *MCN_new_statement(int2 which);
extern MCExpression *MCN_new_function(int2 which);
extern MCExpression *MCN_new_operator(int2 which);

#endif

0 comments on commit 4e8ac6a

Please sign in to comment.