Added ability to define multiple functions (without parameters) and
[zilutils.git] / zilasm / parser.cpp
index 31e257fd444623db965366e8b293d130f8b79032..1ea4aa72aa3385af85d0a5437a1259f9c41c98fe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * parser.c -- part of ZilUtils/ZilAsm
  *
- * Copyright (C) 2016, 2019 Jason Self <j@jxself.org>
+ * Copyright (C) 2016, 2019, 2020 Jason Self <j@jxself.org>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
  *
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
-#include <stdlib.h>
-#include <stdio.h>             /* fopen, fgets */
-#include <string.h>            /* strlen */
-#include <ctype.h>
-#include <string>
-using namespace std;
 
-
-#include "header.h"
-#include "parser.h"
-
-extern "C"
-{
-#include "directives.h"
-#include "opcodes.h"
-}
-#include "labels.h"
-#include "string_table.h"
+#include "include_all.h"
 
 
 #define iscomment(c)   ((c) == ';')
 #define isbindigit(c)  ((c) == '0' || (c) == '1')
 
 /* !!! TODO !!! */
-#define fatal_error(errmsg) printf(errmsg)
+//#define fatal_error(errmsg) printf(errmsg)
 #define PC NULL
 
-unsigned g_numberOfInstructions = 0;
 
 
-ZMemblock (*g_codes[MAX_NUMBER_OF_INSTRUCTIONS]);
+stack<Parsing_Context> g_parsing_contexts;
+
+
+void
+CParser::add_function(const char *s)
+{
+       string name = "";
+       s = pass_spaces(s);
+       while (*s != 0 && *s != ' ' && *s != '\n')
+       {
+               name += *s;
+               s++;
+       }
+       
+       unsigned memory_for_function = 1;
+       unsigned alignment = 0;
+       if (m_current_address & 3)
+       {
+               alignment = 4 - (m_current_address & 3);
+               memory_for_function += alignment;
+       }
+       
+       g_codes[g_number_of_instructions] = zmem_init(memory_for_function);
+       for ( int i = 0; i < memory_for_function; ++i)
+               zmem_putbyte(g_codes[g_number_of_instructions], 0);  // output number of local variables
+       
+
+       m_function_addresses.push_back(m_current_address + alignment);
+       Function f;
+       f.address = m_current_address + alignment;
+       f.index = g_number_of_instructions;
+       m_functions.insert(make_pair(name, f)); /// add key pair: function name and current instruction index
+
+       m_current_address += memory_for_function;
+       ++g_number_of_instructions;
+}
+
+
+string
+CParser::build_error_message(const char *message)
+{
+       char buff[300];
+       sprintf(buff, "error at %s%s line %d: %s", g_parsing_contexts.top().current_directory.c_str(),
+               g_parsing_contexts.top().current_file_name.c_str(), m_current_line_number, message);
+       return string(buff);
+}
+
+
+void
+CParser::fatal_error(const char *errmsg)
+{
+       printf( "%s\n", errmsg);
+       g_haveErrors = true;
+}
+
 
 
 void
-checksep (const char *p)
+CParser::checksep (const char *p)
 {
   if (!*p || iscomment (*p) || isspace (*p))
     return;
-  fatal_error ("wrong chars");
+  fatal_error (build_error_message ("wrong chars").c_str());
 }
 
 
 const char *
-pass_spaces (const char *p)
+CParser::pass_spaces (const char *p)
 {
   while (p && isspace (*p))
     p++;
@@ -74,27 +111,47 @@ pass_alnums (const char *p)
 {
   while (p && isalnum (*p))
     p++;
-  return (p && *p) ? p : NULL;
+  //return (p && *p) ? p : NULL;
+  return p;
 }
 
 
-int tryparse_instruction (const char *a);
-
 int
-tryparse_directive (const char *p)
+CParser::tryparse_directive (const char *p)
 {
   if (*p != '.')
     return 0;
   const char *a = p + 1;
   const char *b = pass_alnums (a);
   checksep (b);
-  Directive_handler f = directive_lookup (a, b - a);
+  Directive_handler f = m_pdirectives->directive_lookup (a, b - a);
   if (!f)
     return 0;
-  return (*f) (b);
+  return (b-a) + (*f) (b, this);
+}
+
+
+int
+CParser::tryparse_startup_directive(const char *p)
+{
+       const char *a = p;
+       string s = "%START::";
+       if (memcmp(p, s.c_str(), s.length()) == 0)
+       {
+               p += s.length();
+               while (isspace(*p))
+                       p++;
+               m_start_function_name = "";
+               while (isalnum(*p))
+                       m_start_function_name += *p++;
+
+       }
+
+       return p - a;
 }
 
 
+
 int
 tryparse_assignment (const char *a, const char *b, const char *c)
 {
@@ -103,7 +160,7 @@ tryparse_assignment (const char *a, const char *b, const char *c)
 
 
 int
-tryparse_label (const char *a, const char *b, const char *c)
+CParser::tryparse_label (const char *a, const char *b, const char *c)
 {
   if (*(c + 1) != ':')
     {
@@ -115,7 +172,7 @@ tryparse_label (const char *a, const char *b, const char *c)
     }
   else
     {
-      fatal_error ("wrong label type");
+      fatal_error (build_error_message("wrong label type").c_str());
     }
 
   while (*c++ == ':');
@@ -126,7 +183,7 @@ tryparse_label (const char *a, const char *b, const char *c)
 
 
 int
-tryparse_name (const char *a)
+CParser::tryparse_name (const char *a)
 {
   const char *b = pass_alnums (a);
   const char *c = pass_spaces (b);
@@ -141,75 +198,134 @@ tryparse_name (const char *a)
 }
 
 
-int
-tryparse_instruction (const char *a)
+int 
+CParser::read_instructions_parameter(char *a, string& str)
 {
-  const char *b = pass_alnums (a);
-  if (b != a)
-    {
-      int len = b ? b - a : strlen (a);
-      ZOpcode *op = (ZOpcode *) symtable_lookup2 (Opcodes, a, len);
-      if (!op)
-       return 0;
-      ZOpcode_flags flags = op->flags;
-      /* !!! TODO !!! */
-
-
-      ZMemblock *mem_additional = NULL;
-
-      switch (op->opcode)
+       int len = 0;
+       str = "";
+       char *p = (char *)pass_spaces(a);
+       if (*p == '\"')
        {
-       case Opcode_CRLF:
-       case Opcode_PRINT:
-       case Opcode_QUIT:
-         break;
-
-       case Opcode_PRINTI:
-       case Opcode_PRINTR:
-         {
-           char *p = (char *) a + len;
-           p = (char *) pass_spaces (p);
-           if (*p == '\"')
-             {
                p++;
-               string str;
                while (*p != '\"')
-                 {
-                   str += *p;
-                   ++p;
-                 }
+               {
+                       str += *p;
+                       ++p;
+               }
                len = p - a;
-               mem_additional =
-                 String_table::encrypt_string (str.c_str (), NULL);
-             }
-           break;
-         }
-       default:
-         fatal_error ("error! instruction not supported");
-         return 0;
        }
+       return len;
+}
 
-      int instruction_size = 1;
-      if (mem_additional)
-       instruction_size += mem_additional->used_size;
-      //printf("instruction %s", a);
 
-      g_codes[g_numberOfInstructions] = zmem_init (instruction_size);
-      zmem_putbyte (g_codes[g_numberOfInstructions], op->opcode);
+int
+CParser::read_instructions_parameter2(char *a, string& str)
+{
+       int len = 0;
+       str = "";
+       char *p = (char *)pass_spaces(a);
+       str="";
+       
+       const char *b = pass_alnums(p);
+       str = string(p).substr(0, b - p);
+
+       len = b - a;
+       return len;
+}
 
-      if (mem_additional)
+int
+CParser::tryparse_instruction (const char *a)
+{
+       bool display_error = false;
+       int len = 0;
+       const char *b = pass_alnums(a);
+       if (b != a)
        {
-         for (int i = 0; i < mem_additional->used_size; ++i)
-           zmem_putbyte (g_codes[g_numberOfInstructions],
-                         mem_additional->contents[i]);
-         zmem_destroy (mem_additional);
+               string call_function_name = "";
+
+               len = b ? b - a : strlen(a);
+               ZOpcode *op = (ZOpcode *)symtable_lookup2(Opcodes, a, len);
+               if (op)
+               {
+                       ZOpcode_flags flags = op->flags;
+                       ZMemblock *mem_additional = NULL;
+
+                       switch (op->opcode)
+                       {
+                               case Opcode_CRLF:
+                               case Opcode_PRINT:
+                               case Opcode_QUIT:
+                               case Opcode_RTRUE:
+                                       break;
+
+                               case Opcode_PRINTI:
+                               case Opcode_PRINTR:
+                               {
+                                       string str;
+                                       len += read_instructions_parameter((char *)a + len, str);
+                                       if ( str.length())
+                                               mem_additional = String_table::encrypt_string(str.c_str(), NULL);
+                                       break;
+                               }
+                               case Opcode_ICALL1:
+                               {
+                                       len += read_instructions_parameter2((char *)a + len, call_function_name);
+                                       m_calls.push_back(call_function_name);
+                                       mem_additional = zmem_init(2);   // reserve two bytes of memory for the address
+                                       zmem_putbyte(mem_additional, 0); // and write there some two bytes, e.g. zeros
+                                       zmem_putbyte(mem_additional, 0);
+                               }
+                               break;
+                               default:
+                               {
+                                       display_error = true;
+                               }
+                       }
+
+                       if (display_error == false)
+                       {
+                               int instruction_size = 1;
+                               if (mem_additional)
+                                       instruction_size += mem_additional->used_size;
+
+                               g_codes[g_number_of_instructions] = zmem_init(instruction_size);
+                               zmem_putbyte(g_codes[g_number_of_instructions], op->opcode);
+                               m_current_address += instruction_size;
+                               if (mem_additional)
+                               {
+                                       for (int i = 0; i < mem_additional->used_size; ++i)
+                                               zmem_putbyte(g_codes[g_number_of_instructions],
+                                                       mem_additional->contents[i]);
+                                       zmem_destroy(mem_additional);
+                               }
+
+                               
+
+                               ++g_number_of_instructions;
+                               return len;
+                       }
+               }
+               else
+               {
+                       string message = "wrong line \"" + string(a) + string("\"");
+                       fatal_error(build_error_message(message.c_str()).c_str());
+               }
        }
 
-      ++g_numberOfInstructions;
-      return len;
-    }
+       if (display_error)
+       {
+               char buff[300];
+               if (len == 0)
+                       len = strlen(a);
+               char *instrcution_name = (char *)malloc(len + 1);
+               memcpy(instrcution_name, a, len);
+               instrcution_name[len] = 0;
+               string message = "instruction " + string(instrcution_name) + string(" is not supported yet");
+               fatal_error(build_error_message(message.c_str()).c_str());
+               free(instrcution_name);
+       }
 
-  return 0;
+       return strlen(a);
 }
 
 
@@ -217,7 +333,7 @@ tryparse_instruction (const char *a)
  *  Line can be one from: Comment, Global label, Local label, Directive, Name=Value, Instruction
  */
 int
-parse_line (const char *p)
+CParser::parse_line (const char *p)
 {
   for (; *p; p++)
     {
@@ -227,13 +343,20 @@ parse_line (const char *p)
        continue;
       if (iscomment (c))
        return 0;
+         if (c == '%')
+         {
+                 if (n = tryparse_startup_directive(p))
+                         return n;
+         }
+
+
       if (n = tryparse_directive (p))
        return n;
       if (n = tryparse_name (p))
        return n;               // ..label or assignment
       if (n = tryparse_instruction (p))
        return n;
-      fatal_error ("wrong line");
+      //fatal_error ("wrong line");
     }
   return 0;
 }
@@ -241,73 +364,170 @@ parse_line (const char *p)
 #define MAX_LINESIZE 1024
 
 int
-parse_file (const char *filename)
+CParser::parse_file ()
 {
-  FILE *fp = fopen (filename, "r");
-  if (!fp)
-    fatal_error ("wrong file");
+       if (g_parsing_contexts.size() > 0)
+       {
+               string filename = g_parsing_contexts.top().current_directory + g_parsing_contexts.top().current_file_name;
 
-  //const int MAX_LINESIZE = 1024;
-  char line[MAX_LINESIZE];
-  int newline_missing = 0;
+               FILE *fp = fopen(filename.c_str(), "r");
+               if (fp)
+               {
 
-  while (fgets (line, MAX_LINESIZE, fp))
-    {
-      if (newline_missing)
-       fatal_error ("line too long");
+                       m_current_line_number = 0;
 
-      int n = strlen (line);
-      if (!n)
-       continue;
+                       //const int MAX_LINESIZE = 1024;
+                       char line[MAX_LINESIZE];
+                       int newline_missing = 0;
 
-      parse_line (line);
+                       while (g_stopParsing == 0 && fgets(line, MAX_LINESIZE, fp))
+                       {
+                               ++m_current_line_number;
 
-      newline_missing = (line[n - 1] != '\n');
-    }
+                               if (newline_missing)
+                                       fatal_error(build_error_message("line too long").c_str());
 
-  fclose (fp);
-  return 0;
+                               int n = strlen(line);
+                               if (!n)
+                                       continue;
+
+                               parse_line(line);
+
+                               newline_missing = (line[n - 1] != '\n');
+                       }
+
+                       fclose(fp);
+               }
+               else
+               {
+                       string message = string("can't open file ") + filename;
+                       fatal_error(message.c_str());
+               }
+
+               g_parsing_contexts.pop();
+       }
+       return 0;
 }
 
-/*
 
-line_passed() {
-    skip_spaces();
-    return (current_token == LINE_END || current_token == LINE_COMMENT);
+CParser::CParser() 
+{
+  g_number_of_instructions = 0;
+  m_current_line_number = 0;
+  m_current_address = 0;
+  g_haveErrors = false;
+  m_pdirectives = new CDirectives(this);
 }
 
-if (line_passed()) continue;
-if (current_token == DIRECTIVE) {
-    if (!try_next_token(NAME))
-        fatal_error("directive contains incorrect chars")
-    handler = get_directive_handler(current_token);
-    if (!handler)
-        fatal error("unknown directive");
-    (*handler)(remaining_line);
-    if (line_passed()) continue;
-    fatal_error("unexpected line tail");
-} else if (current_token == NAME) {
-    skip_spaces();
-    if (current_token == ASSIGNMENT)
+
+CParser::~CParser ()
+{
+       for (int i = 0; i < g_number_of_instructions; ++i)
+       {
+               zmem_destroy(g_codes[i]);
+       }
+       g_number_of_instructions = 0;
+       delete m_pdirectives;
 }
-    
 
-*/
+
+ZMemblock **
+CParser::get_codes()
+{
+       return g_codes;
+}
 
 
-void
-init_parser ()
+unsigned 
+CParser::get_number_of_instructions()
 {
-  g_numberOfInstructions = 0;
+       return g_number_of_instructions;
 }
 
 
-void
-relase_parser ()
+bool 
+CParser::have_errors()
 {
-  for (int i = 0; i < g_numberOfInstructions; ++i)
-    {
-      zmem_destroy (g_codes[i]);
-    }
-  g_numberOfInstructions = 0;
+       return g_haveErrors;
 }
+
+
+void 
+CParser::calculate_function_addresses()
+{
+       //unsigned address = 0;
+       ////map<string, Function>::iterator function_iter = m_functions.begin();
+       //unsigned j = 0;
+
+       //
+       //for (int i = 0; i < get_number_of_instructions(); ++i)
+       //{
+       //      //if (function_iter == m_functions.end())
+       //      //{
+       //      //      break;
+       //      //}
+
+       //      if (m_function_addresses[j] == address)
+       //      {
+       //              if (address & 3)
+       //                      address += 4 - (address & 3);
+       //              
+
+       //              for (map<string, Function>::iterator function_iter = m_functions.begin(); function_iter != m_functions.end(); ++function_iter)
+       //              {
+       //                      if (function_iter->second.index == i)
+       //                      {
+       //                              function_iter->second.address = address;
+       //                              break;
+       //                      }
+       //      
+       //              }
+       //              m_function_addresses[j] = address;
+       //              ++j;
+       //      }
+       //      address += g_codes[i]->used_size;
+       //}
+}
+
+
+unsigned
+CParser::output_codes(ZMemblock *zmem_code)
+{
+       unsigned code_size = 0;
+       map<string, Function>::iterator function_iter = m_functions.begin();
+       unsigned address = 0;
+
+       unsigned calls_index = 0;
+       for (int i = 0; i < get_number_of_instructions(); ++i)
+       {
+               ////if ( parser.)
+               //if (function_iter != m_functions.end())
+               //{
+               //      if (function_iter->second.index == i)
+               //      {
+               //              /// write few bytes for alignment purposes
+               //              for (int j = 0; j < function_iter->second.address - address; ++j)
+               //                      zmem_putbyte(zmem_code, 0);
+               //      }
+               //}
+
+               // write actual addresses to calls
+               if (g_codes[i]->contents[0] == Opcode_ICALL1)
+               {
+                       //vector<unsigned>::iterator iter = find(m_function_addresses.begin(), m_function_addresses.end(), address);
+                       unsigned target_addr = m_functions.find(m_calls[calls_index])->second.address >> 2;
+                       g_codes[i]->contents[1] = (target_addr >> 8) & 255;
+                       g_codes[i]->contents[2] = target_addr & 255;
+                       ++calls_index;
+               }
+
+
+               for (int j = 0; j < g_codes[i]->used_size; ++j)
+               {
+                       zmem_putbyte(zmem_code, g_codes[i]->contents[j]);
+                       ++code_size;
+               }
+       }
+
+       return code_size;
+}
\ No newline at end of file