Sona 0.50 Source

Sona 0.50/tools/mml2sona/mml_macros.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "main.h"
#include "mml_macros.h"
#include "blob.h"
#include "text.h"

// Macro limits
#define MACRO_MAX_NUMBER      999
#define MACRO_UPPER_START     (MACRO_MAX_NUMBER+1)
#define MACRO_LOWER_START     (MACRO_UPPER_START+26)
#define NUM_MACROS            (MACRO_LOWER_START+26)

// Where every macro is stored
char *macros[NUM_MACROS] = { 0 };

//***************************************************************************
// parse_macro_id
// Parses the name of a macro following the ! and returns its internal ID.
//---------------------------------------------------------------------------
// param text ....... pointer to pointer to string
// param filename ... file name (for error reporting)
// param line_num ... current line (for error reporting)
// param col_num .... current column (for error reporting)
//---------------------------------------------------------------------------
// return ........... macro ID on success (0 or larger), -1 on failure
//---------------------------------------------------------------------------
// *text should point to the first character after the ! and on return will
// be advanced to point to the first character following the macro's name.
// Valid macro names are !0 to !999, !A to !Z and !a to !z.
//***************************************************************************

int parse_macro_id(const char **text,
                   const char *filename,
                   unsigned line_num,
                   unsigned col_num)
{
   int id;
   const char *ptr = *text;
   
   // No spaces allowed between ! and macro name
   if (*ptr == ' ') {
      fprintf(stderr, ERRORMSG_NOMACROID,
              filename, line_num, col_num);
      return -1;
   }
   
   // Macro named by letter?
   if (*ptr >= 'A' && *ptr <= 'Z') {
      id = *ptr - 'A' + MACRO_UPPER_START;
      *text = ptr + 1;
      return id;
   }
   if (*ptr >= 'a' && *ptr <= 'z') {
      id = *ptr - 'a' + MACRO_LOWER_START;
      *text = ptr + 1;
      return id;
   }
   
   // Macro named by number?
   int result = get_number(text, &id);
   if (result) {
      fprintf(stderr, ERRORMSG_NOMACROID,
              filename, line_num, col_num);
      return -1;
   }
   if (id < 0 || id > MACRO_MAX_NUMBER) {
      fprintf(stderr, ERRORMSG_MACRORANGE,
              filename, line_num, col_num, id);
      return -1;
   }
   
   return id;
}

//***************************************************************************
// set_macro
// Changes the definition for a MML macro.
//---------------------------------------------------------------------------
// param id ..... macro ID (see parse_macro_id())
// param text ... pointer to new text
//***************************************************************************

void set_macro(int id, const char *text)
{
   if (id < 0 || id >= NUM_MACROS) abort();
   
   // Get rid of previous definition, if any
   // (free() does nothing if pointer was NULL)
   free(macros[id]);
   
   // Allocate room to store a copy of the text to store
   macros[id] = malloc(strlen(text)+1);
   if (macros[id] == NULL) {
      fprintf(stderr, ERRORMSG_NOMEMORY);
      abort();
   }
   
   // Copy it over
   strcpy(macros[id], text);
}

//***************************************************************************
// get_macro
// Retrieves the definition for a MML macro.
//---------------------------------------------------------------------------
// param id ... macro ID (see parse_macro_id())
//---------------------------------------------------------------------------
// return ..... pointer to macro's text (NULL if not defined)
//***************************************************************************

const char *get_macro(int id)
{
   if (id < 0 || id >= NUM_MACROS) abort();
   return macros[id];
}

//***************************************************************************
// expand_macros
// Parses a string and expands all the macros in it and returns a pointer to
// a new string with all the macros replaced. You should call free() when
// you're done with this string.
//---------------------------------------------------------------------------
// param text ....... pointer to string
// param filename ... file name (for error reporting)
// param line_num ... current line (for error reporting)
// param col_num .... current column (for error reporting)
//---------------------------------------------------------------------------
// return ........... pointer to new string
//***************************************************************************

char *expand_macros(const char *text,
                    const char *filename,
                    unsigned line_num,
                    unsigned col_num)
{
   const char *text_start = text;
   Blob *blob = create_blob();
   
   while (*text != '\0') {
      // Compute real column number at this point in the string
      unsigned real_col_num = (text - text_start) + col_num;
      
      // Text to copy as-is
      if (*text != '!') {
         const char *start = text;
         while (*text != '\0' && *text != '!') text++;
         size_t length = text - start;
         
         append_to_blob(blob, (const uint8_t *)(start), length);
      }
      
      // Macro replacement
      else {
         text++;
         
         int id = parse_macro_id(&text, filename, line_num, real_col_num);
         if (id < 0) continue;
         
         const char *data = macros[id];
         if (data == NULL) {
            fprintf(stderr, ERRORMSG_NOMACRODEF,
                    filename, line_num, real_col_num);
            continue;
         }
         
         size_t length = strlen(data);
         append_to_blob(blob, (const uint8_t *)(data), length);
      }
   }
   
   char *result = malloc(blob->len + 1);
   memcpy(result, blob->data, blob->len);
   result[blob->len] = '\0';
   delete_blob(blob);
   
   //printf("\"%s\" -> \"%s\"\n", text_start, result);
   return result;
}