Writing an EasyCoder plugin

This article is written specifically for the anagram finder plugin, which uses only part of the EasyCoder language feature set. It is a support page for a series of articles at dev.to.

An EasyCoder plugin has a standard form. There's a range of them in the plugins folder of the EasyCoder installation, including a dummy one called dummy.js that's sometimes useful as a starting point for a new one. You can also see all the files on the EasyCoder GitHub repository. We're only using part of the functionality provided by a plugin so I'll just show what the new one looks like. It's be calling it anagrams.js. You can put it wherever you like as it's accessed by its URL, but the best place for custom files is at the top of the site directory, in the easycoder folder. When EasyCoder is installed into WordPress it sets up this folder and puts a couple of special files in it, but you can add whatever files you like.


const EasyCoder_Anagrams = {

  value: {

    compile: (compiler) => {
      if (compiler.tokenIs(`anagrams`)) {
        if (compiler.nextTokenIs(`of`)) {
          const value = compiler.getNextValue();
          return {
            domain: `anagrams`,
            type: `getAnagrams`,
            value
          };
        }
      }
      return null;
    },

    get: (program, value) => {
      switch (value.type) {
        case `getAnagrams`:
          return {
            type: `constant`,
            numeric: false,
            content: JSON.stringify(Anagrams.getAnagrams(program.getValue(value.value),
              EasyCoder_words))
          };
      }
      return null;
    }
  },

  getHandler: () => {}
};

There are two parts to the value handler; one to compile the incoming script and the other to get the value at runtime. These are compile() and get() respectively. The syntax we want to handle is the expression anagrams of Text. This is not a command; it's a value. Most plugins handle commands, values and conditions but here we only have to handle a single value expression. The main compiler has a number of helper functions that we can use to compile the new item, some of which are used in the code above.

The incoming script is in the form of a stream of tokens (words). Walking through it, the code first checks if the current token is anagrams and if so, if the one after that is of. After that we go and fetch a "value", which here is likely to be a variable but might be any sequence of instructions that returns a string or a number. Many of the functions advance the token pointer so we don't need to do it manually. The value isn't a simple number; it's a specification of how to get a number, which allows variables or constants to be used as appropriate. Evaluation is not done until runtime because some values, such as the time or the size of a window, aren't known until then.

Once we've extracted all the meaning we construct a small object that names the domain (package), a "type" that identifies what function is being performed and the value. This data returns to the main compiler where it's stored in the compiled program.

When the script runs it will at some point arrive at the compiled value. The runtime manager will look up which package it's from and what action is needed and call get(program, value) with a reference to the program and the value object we created in the compiler. So in the runtime handler - the get() function - we can now look for our anagrams.

The real work of looking for anagrams is done by getAnagrams(), a function in the external JavaScript library we wrote in Part 1, that we call with the text the user typed into the box on the screen. The library is completely independent of EasyCoder; in larger projects it may well be a React component but here it is vanilla JavaScript.

As I mentioned above, evaluation of values is done as late as possible, so up to this point all we've been passing about is the name of the variable holding the text. There's an internal symbol table that points to the variable itself and the data it contains. The text is retrieved using program.getValue() before it's sent to the library function.

The reason why EasyCoder can claim to handle almost any level of complexity or size of website is because something else is doing all the heavy lifting. EasyCoder itself deals with language syntax and looking after data, but complex calculations and algorithms are done by library JavaScript modules. Because of this I don't have to explain here how anagrams are found as it really has nothing to do with the way the plugin is used.

Adding the new plugin to EasyCoder

There's just one more step needed; to tell EasyCoder about the new plugin. The easycoder folder I mentioned above, located at the root of the WordPress installation, contains a file called plugins.js, which controls which plugins are available to be loaded. The relevant part of the default file looks like this:


const EasyCoder_Plugins = {

  getGlobalPlugins: (timestamp, path, setPluginCount, getPlugin, addPlugin) => {

    console.log(`${Date.now() - timestamp} ms: Load plugins`);

    setPluginCount(2); // *** IMPORTANT *** the number of plugins you will be adding

    getPlugin(`browser`,
      `${window.location.origin}${path}/wp-content/plugins/easycoder/plugins/browser.js`,
        function () {
          addPlugin(`browser`, EasyCoder_Browser);
        });

    getPlugin(`json`,
      `${window.location.origin}${path}/wp-content/plugins/easycoder/plugins/json.js`,
        function () {
          addPlugin(`json`, EasyCoder_Json);
        });

  }
};

exports = {
  EasyCoder_Plugins
};

In this case, 2 plugins are loaded globally (the rest of the file deals with loading plugins on demand, which doesn't concern us here). We need to add our anagram finder to this list, so first we'll decide where to put it. In the same folder seems as good a place as any, so the script now looks like this:


const EasyCoder_Plugins = {

  getGlobalPlugins: (timestamp, path, setPluginCount, getPlugin, addPlugin) => {

    console.log(`${Date.now() - timestamp} ms: Load plugins`);

    setPluginCount(3); // *** IMPORTANT *** the number of plugins you will be adding

    getPlugin(`browser`,
      `${window.location.origin}${path}/wp-content/plugins/easycoder/plugins/browser.js`,
        function () {
          addPlugin(`browser`, EasyCoder_Browser);
        });

    getPlugin(`json`,
      `${window.location.origin}${path}/wp-content/plugins/easycoder/plugins/json.js`,
        function () {
          addPlugin(`json`, EasyCoder_Json);
        });

    getPlugin(`anagrams`,
       `${window.location.origin}${path}/easycoder/anagrams.js`,
        function () {
         addPlugin(`json`, EasyCoder_Anagrams);
        });

   }
};

exports = {
  EasyCoder_Plugins
};

Our new plugin will now be loaded when EasyCoder starts up and will be ready to do its job.