..
    Decision
    Copyright (C) 2019-2020  Benjamin Beddows

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#######################
Running Decision from C
#######################

.. warning::

   If you are embedding into a C++ program, it is very, very recommended that
   when you include a C header file, you encapsulate it with an ``extern``
   block, like so:

   .. code-block:: cpp

      extern "C" {
          #include <decision.h>
          #include <dsheet.h>
      }

Decision Strings
================

Loading Decision Strings
------------------------

To load a string with Decision source code in into a ``Sheet``, use:

.. doxygenfunction:: d_load_string
   :no-link:

You can then run any compiled ``Sheet`` with:

.. doxygenfunction:: d_run_sheet
   :no-link:

.. code-block:: c

   #include <decision.h>
   #include <dsheet.h>

   int main() {
       // Load the string.
       Sheet *sheet = d_load_string("Start~#1 ; Print(#1, 'Hello, world!')", NULL, NULL);

       // Run the sheet.
       d_run_sheet(sheet);

       // Free the sheet from memory.
       d_sheet_free(sheet);

       return 0;
   }

.. note::

   For safety, you can check to see whether a sheet has failed to compile
   (usually because there was an error in the code) by checking to see if
   ``sheet->hasErrors`` is true.

   If a sheet does have an error, ``d_run_sheet`` will not run it.

Running Decision Strings
------------------------

To run a Decision source code string directly, use:

.. doxygenfunction:: d_run_string
   :no-link:

.. code-block:: c

   #include <decision.h>

   int main() {
       // Run the string.
       d_run_string("Start~#1 ; Print(#1, 'Hello, world!')", NULL, NULL);

       return 0;
   }

Decision Files
==============

Loading Decision Files
----------------------

To load a Decision file from the disk into a ``Sheet``, use:

.. doxygenfunction:: d_load_file
   :no-link:

.. code-block:: c

   #include <decision.h>
   #include <dsheet.h>

   int main() {
       // Load the sheet into memory.
       Sheet *sheet = d_load_file("hello_world.dc", NULL);

       // Run the sheet.
       d_run_sheet(sheet);

       // Free the sheet from memory.
       d_sheet_free(sheet);

       return 0;
   }

Running Decision Files
----------------------

To run a Decision file directly, use:

.. doxygenfunction:: d_run_file
   :no-link:

.. code-block:: c

   #include <decision.h>

   int main() {
       // Run the file.
       d_run_file("hello_world.dc", NULL);

       return 0;
   }

.. _decision-functions:

Decision Functions
==================

In order to run specific functions and subroutines, you first need to know how
to pass arguments and get return values from them.

Passing Arguments
-----------------

To pass arguments to a function or subroutine, you need to push the values
onto the Decision Virtual Machine's stack **in the correct order**.
This can be done with the following functions:

.. doxygenfunction:: d_vm_push
   :no-link:

.. doxygenfunction:: d_vm_push_float
   :no-link:

.. doxygenfunction:: d_vm_push_ptr
   :no-link:

Getting Return Values
---------------------

To get return values from a function or subroutine, you need to pop the values
from the Decision Virtual Machine's stack **in reverse order**.
This can be done with the following functions:

.. doxygenfunction:: d_vm_pop
   :no-link:

.. doxygenfunction:: d_vm_pop_float
   :no-link:

.. doxygenfunction:: d_vm_pop_ptr
   :no-link:

Calling Decision Functions
--------------------------

Finally, you can call the function or subroutine itself using:

.. doxygenfunction:: d_run_function
   :no-link:

.. code-block:: decision

   > decision.dc

   [Function(IsEven)]
   [FunctionInput(IsEven, Integer)]
   [FunctionOutput(IsEven, Boolean)]

   Define(IsEven)~#1
   Mod(#1, 2)~#2
   Equal(#2, 0)~#3
   Return(IsEven, #3)

   [Subroutine(SayHi)]

   Define(SayHi)~#10
   Print(#10, "Hi! :3")

.. code-block:: c

   // main.c
   #include <dcfg.h>
   #include <decision.h>
   #include <dsheet.h>
   #include <dvm.h>

   #include <stdio.h>

   int main() {
       // Load the sheet into memory.
       Sheet *sheet = d_load_file("decision.dc", NULL);

       // Create a Decision Virtual Machine.
       DVM vm = d_vm_create();

       // Calling a function/subroutine with no inputs or outputs is easy:
       d_run_function(&vm, sheet, "SayHi");

       // It's good practice to reset the VM once you've run something.
       d_vm_reset(&vm);

       dint value = 531780;

       // To call the IsEven function, we first need to push the argument onto
       // the stack.
       d_vm_push(&vm, value);

       // REMEMBER: If you have more than one argument, push the arguments IN
       // THE CORRECT ORDER.

       // Then call the function:
       d_run_function(&vm, sheet, "IsEven");

       // Then pop the return values out IN REVERSE ORDER.
       dint isEven = d_vm_pop(&vm);

       // dcfg.h has some handy macros for when you want to print dint or
       // dfloat types:
       if (isEven) {
           printf("%" DINT_PRINTF_d " is even!\n", value);
       } else {
           printf("%" DINT_PRINTF_d " is odd!\n", value);
       }

       // Free the VM.
       d_vm_free(&vm);

       d_sheet_free(sheet);
       return 0;
   }