Sun, 26 Jan 2014 23:18:48 +0200
- merged object writer into parser
--- a/CMakeLists.txt Sun Jan 19 20:39:30 2014 +0200 +++ b/CMakeLists.txt Sun Jan 26 23:18:48 2014 +0200 @@ -9,7 +9,6 @@ src/lexer.cc src/lexer_scanner.cc src/main.cc - src/object_writer.cc src/parser.cc src/str.cc src/stringtable.cc
--- a/botc_defs.bts Sun Jan 19 20:39:30 2014 +0200 +++ b/botc_defs.bts Sun Jan 26 23:18:48 2014 +0200 @@ -4,6 +4,7 @@ // Do not edit unless you know what you are doing! // ============================================================================= +// // Function definitions // Syntax: number:name:returntype:numargs:maxargs[:argumentlist] // @@ -102,6 +103,7 @@ funcdef 92:ChatSectionExistsInChatLump:bool:1:1:str(section); // ============================================================================= +// // Events: // eventdef <number>:<name>(); //
--- a/src/containers.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/containers.h Sun Jan 26 23:18:48 2014 +0200 @@ -264,7 +264,7 @@ for (const element_type& hay : *this) { - if (hay == needle) + if (&hay == &needle) return i; i++;
--- a/src/data_buffer.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/data_buffer.cc Sun Jan 26 23:18:48 2014 +0200 @@ -30,46 +30,46 @@ // ============================================================================ // -void data_buffer::merge (data_buffer* other) +data_buffer::data_buffer (int size) +{ + set_writepos (get_buffer()); + set_buffer (new byte[size]); + set_allocated_size (size); +} + +// ============================================================================ +// +data_buffer::~data_buffer() +{ + assert (count_marks() == 0 && count_refs() == 0); + delete get_buffer(); +} + +// ============================================================================ +// +void data_buffer::merge_and_destroy (data_buffer* other) { if (!other) return; - int oldsize = writesize; - - for (int x = 0; x < other->writesize; x++) - write (* (other->buffer + x)); - - // Merge its marks and references - int u = 0; - - for (u = 0; u < MAX_MARKS; u++) - { - if (other->marks[u]) - { - // Merge the mark and offset its position. - if (marks[u]) - error ("DataBuffer: duplicate mark %d!\n"); + int oldsize = get_write_size(); + copy_buffer (other); - marks[u] = other->marks[u]; - marks[u]->pos += oldsize; - - // The original mark becomes null so that the deconstructor - // will not delete it prematurely. (should it delete any - // marks in the first place since there is no such thing - // as at temporary mark?) - other->marks[u] = null; - } - - if (other->refs[u]) - { - // Same for references - // TODO: add a g_NextRef system like here, akin to marks! - int r = add_reference (other->refs[u]->num, false); - refs[r]->pos = other->refs[u]->pos + oldsize; - } + // Assimilate in its marks and references + for (byte_mark* mark : other->get_marks()) + { + mark->pos += oldsize; + push_to_marks (mark); } + for (mark_reference* ref : other->get_refs()) + { + ref->pos += oldsize; + push_to_refs (ref); + } + + clear_marks(); + clear_refs(); delete other; } @@ -78,182 +78,166 @@ data_buffer* data_buffer::clone() { data_buffer* other = new data_buffer; - - for (int x = 0; x < writesize; x++) - other->write (* (buffer + x)); - + other->copy_buffer (this); return other; } // ============================================================================ // -int data_buffer::add_mark (string name) +void data_buffer::copy_buffer (const data_buffer* buf) { - // Find a free slot for the mark - int u = g_NextMark++; - - if (marks[u]) - error ("DataBuffer: attempted to re-create mark %u!\n", u); - - if (u >= MAX_MARKS) - error ("mark quota exceeded, all labels, if-structs and loops add marks\n"); - - ScriptMark* m = new ScriptMark; - m->name = name; - m->pos = writesize; - marks[u] = m; - return u; + check_space (buf->get_write_size()); + memcpy (m_writepos, buf->get_buffer(), buf->get_write_size()); + m_writepos += buf->get_write_size(); } // ============================================================================ // -int data_buffer::add_reference (int marknum, bool placeholder) +byte_mark* data_buffer::add_mark (string name) { - int u; - - for (u = 0; u < MAX_MARKS; u++) - if (!refs[u]) - break; - - // TODO: get rid of this - if (u == MAX_MARKS) - error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n"); - - ScriptMarkReference* r = new ScriptMarkReference; - r->num = marknum; - r->pos = writesize; - refs[u] = r; - - // Write a dummy placeholder for the reference - if (placeholder) - write (0xBEEFCAFE); - - return u; + byte_mark* mark = new byte_mark; + mark->name = name; + mark->pos = get_write_size(); + push_to_marks (mark); + return mark; } // ============================================================================ // -void data_buffer::delete_mark (int marknum) +mark_reference* data_buffer::add_reference (byte_mark* mark, bool write_placeholder) { - if (!marks[marknum]) - return; - - // Delete the mark - delete marks[marknum]; - marks[marknum] = null; + mark_reference* ref = new mark_reference; + ref->target = mark; + ref->pos = get_write_size(); + push_to_refs (ref); - // Delete its references - for (int u = 0; u < MAX_MARKS; u++) - { - if (refs[u]->num == marknum) - { - delete refs[u]; - refs[u] = null; - } - } + // Write a dummy placeholder for the reference + if (write_placeholder) + write_dword (0xBEEFCAFE); + + return ref; } // ============================================================================ // -void data_buffer::move_mark (int i) +void data_buffer::adjust_mark (byte_mark* mark) { - if (!marks[i]) - return; - - marks[i]->pos = writesize; -} - -// ============================================================================ -// -void data_buffer::offset_mark (int mark, int offset) -{ - if (!marks[mark]) - return; - - marks[mark]->pos += offset; + mark->pos = get_write_size(); } // ============================================================================ // -int data_buffer::count_marks() +void data_buffer::offset_mark (byte_mark* mark, int offset) { - int count = 0; - - for (int u = 0; u < MAX_MARKS; u++) - count += !!marks[u]; - - return count; + mark->pos += offset; } // ============================================================================ // -int data_buffer::count_references() +void data_buffer::write_float (float a) { - int count = 0; + // TODO: Find a way to store the number without decimal loss. + write_dword (dh_push_number); + write_dword (abs (a)); - for (int u = 0; u < MAX_MARKS; u++) - count += !!refs[u]; - - return count; + if (a < 0) + write_dword (dh_unary_minus); } // ============================================================================ // -void data_buffer::write_float (string floatstring) +void data_buffer::write_string_index (const string& a) { - // TODO: Casting float to word causes the decimal to be lost. - // Find a way to store the number without such loss. - float val = atof (floatstring); - write (dh_push_number); - write (static_cast<word> (abs (val))); - - if (val < 0) - write (dh_unary_minus); -} - -// ============================================================================ -// -void data_buffer::write_string (string a) -{ - write (dh_push_string_index); - write (get_string_table_index (a)); + write_dword (dh_push_string_index); + write_dword (get_string_table_index (a)); } // ============================================================================ // void data_buffer::dump() { - for (int x = 0; x < writesize; x++) - printf ("%d. [%d]\n", x, * (buffer + x)); + for (int i = 0; i < get_write_size(); ++i) + printf ("%d. [%d]\n", i, get_buffer()[i]); } // ============================================================================ // -data_buffer::~data_buffer() +void data_buffer::check_space (int bytes) { - delete buffer; + int writesize = get_write_size(); + + if (writesize + bytes < get_allocated_size()) + return; + + // We don't have enough space in the buffer to write + // the stuff - thus resize. First, store the old + // buffer temporarily: + char* copy = new char[get_allocated_size()]; + memcpy (copy, get_buffer(), get_allocated_size()); - // Delete any marks and references - for (int i = 0; i < MAX_MARKS; i++) - { - delete marks[i]; - delete refs[i]; - } + // Remake the buffer with the new size. Have enough space + // for the stuff we're going to write, as well as a bit + // of leeway so we don't have to resize immediately again. + size_t newsize = get_allocated_size() + bytes + 512; + + delete get_buffer(); + set_buffer (new byte[newsize]); + set_allocated_size (newsize); + + // Now, copy the stuff back. + memcpy (m_buffer, copy, get_allocated_size()); + set_writepos (get_buffer() + writesize); + delete copy; +} + +// ============================================================================= +// +void data_buffer::write_byte (int8_t data) +{ + check_space (1); + *m_writepos++ = data; } -// ============================================================================ +// ============================================================================= // -data_buffer::data_buffer (int size) +void data_buffer::write_word (int16_t data) { - writesize = 0; + check_space (2); + + for (int i = 0; i < 2; ++i) + *m_writepos++ = (data >> (i * 8)) & 0xFF; +} + +// ============================================================================= +// +void data_buffer::write_dword (int32_t data) +{ + check_space (4); + + for (int i = 0; i < 4; ++i) + *m_writepos++ = (data >> (i * 8)) & 0xFF; +} - buffer = new unsigned char[size]; - allocsize = size; +// ============================================================================= +// +void data_buffer::write_string (const string& a) +{ + check_space (a.length() + 1); + + for (char c : a) + write_byte (c); + + write_byte ('\0'); +} - // Clear the marks table out - for (int u = 0; u < MAX_MARKS; u++) - { - marks[u] = null; - refs[u] = null; - } -} + +// ============================================================================= +// +byte_mark* data_buffer::find_mark_by_name (const string& target) +{ + for (byte_mark* mark : get_marks()) + if (mark->name == target) + return mark; + + return null; +} \ No newline at end of file
--- a/src/data_buffer.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/data_buffer.h Sun Jan 26 23:18:48 2014 +0200 @@ -36,112 +36,60 @@ #define MAX_MARKS 512 -extern int g_NextMark; - // ============================================================================ -// DataBuffer: A dynamic data buffer. +// data_buffer: A dynamic data buffer. +// +// Notes: +// +// - A mark is a "pointer" to a particular position in the bytecode. The actual +// permanent position cannot be predicted in any way or form, thus these things +// are used to "mark" a position like that for future use. +// +// - A reference is another "mark" that references a mark. When the bytecode +// is written to file, they are changed into their marks' current +// positions. Marks themselves are never written to files, only refs are +// class data_buffer { -public: - // The actual buffer - byte* buffer; - - // Allocated size of the buffer - int allocsize; + PROPERTY (private, byte*, buffer, NO_OPS, STOCK_WRITE) + PROPERTY (private, int, allocated_size, NUM_OPS, STOCK_WRITE) + PROPERTY (private, byte*, writepos, NO_OPS, STOCK_WRITE) + PROPERTY (private, list<byte_mark*>, marks, LIST_OPS, STOCK_WRITE) + PROPERTY (private, list<mark_reference*>, refs, LIST_OPS, STOCK_WRITE) - // Written size of the buffer - int writesize; - - // Marks and references - ScriptMark* marks[MAX_MARKS]; - ScriptMarkReference* refs[MAX_MARKS]; - - data_buffer (int size = 128); - ~data_buffer (); + public: + data_buffer (int size = 128); + ~data_buffer(); - // ==================================================================== - // Write stuff to the buffer - // TODO: un-template and remove the union, move to source file - template<class T> void write (T stuff) - { - if (writesize + sizeof (T) >= allocsize) - { - // We don't have enough space in the buffer to write - // the stuff - thus resize. First, store the old - // buffer temporarily: - char* copy = new char[allocsize]; - memcpy (copy, buffer, allocsize); + // ==================================================================== + // Merge another data buffer into this one. + // Note: @other is destroyed in the process! + void merge_and_destroy (data_buffer* other); - // Remake the buffer with the new size. Have enough space - // for the stuff we're going to write, as well as a bit - // of leeway so we don't have to resize immediately again. - size_t newsize = allocsize + sizeof (T) + 128; - - delete buffer; - buffer = new unsigned char[newsize]; - allocsize = newsize; + // Clones this databuffer to a new one and returns it. + data_buffer* clone (); - // Now, copy the stuff back. - memcpy (buffer, copy, allocsize); - delete copy; - } - - // Buffer is now guaranteed to have enough space. - // Write the stuff one byte at a time. - generic_union<T> uni; - uni.as_t = stuff; + byte_mark* add_mark (string name); - for (int x = 0; x < sizeof (T); x++) - { - if (writesize >= allocsize) // should NEVER happen because resizing is done above - error ("DataBuffer: written size exceeds allocated size!\n"); - - buffer[writesize] = uni.as_bytes[x]; - writesize++; - } - } - - // ==================================================================== - // Merge another data buffer into this one. - void merge (data_buffer* other); - - // Clones this databuffer to a new one and returns it. - data_buffer* clone (); - - // ==================================================================== - // Adds a mark to the buffer. A mark is a "pointer" to a particular - // position in the bytecode. The actual permanent position cannot - // be predicted in any way or form, thus these things will be used - // to "mark" a position like that for future use. - int add_mark (string name); + mark_reference* add_reference (byte_mark* mark, bool write_placeholder = true); + void check_space (int bytes); + void delete_mark (int marknum); + void adjust_mark(byte_mark* mark); + void offset_mark (byte_mark* mark, int offset); + byte_mark* find_mark_by_name (const string& target); + void dump(); + void write_float (float a); + void write_string_index (const string& a); + void write_string (const string& a); + void write_byte (int8_t data); + void write_word (int16_t data); + void write_dword (int32_t data); + void copy_buffer (const data_buffer* buf); - // ==================================================================== - // A ref is another "mark" that references a mark. When the bytecode - // is written to file, they are changed into their marks' current - // positions. Marks themselves are never written to files, only refs are - int add_reference (int marknum, bool placeholder = true); - - // Delete a mark and all references to it. - void delete_mark (int marknum); - - // Adjusts a mark to the current position - void move_mark(int i); - - void offset_mark (int mark, int offset); - - // Dump the buffer (for debugging purposes) - void dump(); - - // Count the amount of marks - int count_marks (); - - // Count the amount of refs - int count_references (); - - // Write a float into the buffer - void write_float (string floatstring); - - void write_string (string a); + inline int get_write_size() const + { + return m_writepos - get_buffer(); + } }; #endif // BOTC_DATABUFFER_H
--- a/src/format.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/format.cc Sun Jan 26 23:18:48 2014 +0200 @@ -33,7 +33,7 @@ // ============================================================================= // -static void draw_pos (const string& fmt, int pos) +static void draw_position (const string& fmt, int pos) { string rep (fmt); rep.replace ("\n", "↵"); @@ -47,6 +47,8 @@ fprintf (stderr, "^\n"); } +// ============================================================================= +// string format_args (const list<format_arg>& args) { const string& fmtstr = args[0].as_string(); @@ -82,7 +84,7 @@ { fprintf (stderr, "bad format string, expected digit with optional " "modifier after '%%':\n"); - draw_pos (fmt, pos); + draw_position (fmt, pos); return fmt; } @@ -123,7 +125,7 @@ // void do_error (string msg) { - lexer* lx = lexer::get_main_lexer(); + lexer* lx = lexer::get_current_lexer(); string fileinfo; if (lx != null && lx->has_valid_token())
--- a/src/format.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/format.h Sun Jan 26 23:18:48 2014 +0200 @@ -35,11 +35,20 @@ class format_arg { public: - format_arg (const string& a) : m_string (a) {} - format_arg (char a) : m_string (a) {} - format_arg (int a) : m_string (string::from_number (a)) {} - format_arg (long a) : m_string (string::from_number (a)) {} - format_arg (const char* a) : m_string (a) {} + format_arg (const string& a) : + m_string (a) {} + + format_arg (char a) : + m_string (a) {} + + format_arg (int a) : + m_string (string::from_number (a)) {} + + format_arg (long a) : + m_string (string::from_number (a)) {} + + format_arg (const char* a) : + m_string (a) {} format_arg (void* a) { @@ -88,30 +97,6 @@ return out; } -inline string hex (ulong a) -{ - return custom_format (a, "0x%X"); -} - -inline string charnum (char a) -{ - return custom_format (a, "%d"); -} - -class script_error : public std::exception -{ - public: - script_error (const string& msg) : m_msg (msg) {} - - inline const char* what() const throw() - { - return m_msg.c_str(); - } - - private: - string m_msg; -}; - string format_args (const list<format_arg>& args); void print_args (FILE* fp, const list<format_arg>& args); void do_fatal (const list<format_arg>& args);
--- a/src/lexer.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/lexer.cc Sun Jan 26 23:18:48 2014 +0200 @@ -285,7 +285,7 @@ // ============================================================================= // -lexer* lexer::get_main_lexer() +lexer* lexer::get_current_lexer() { return g_main_lexer; }
--- a/src/lexer.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/lexer.h Sun Jan 26 23:18:48 2014 +0200 @@ -91,7 +91,7 @@ return describe_token_private (tok->type, tok); } - static lexer* get_main_lexer(); + static lexer* get_current_lexer(); inline void skip (int a = 1) {
--- a/src/main.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/main.cc Sun Jan 26 23:18:48 2014 +0200 @@ -27,52 +27,15 @@ */ #include "main.h" -#include "object_writer.h" #include "events.h" #include "commands.h" #include "stringtable.h" #include "variables.h" #include "data_buffer.h" -#include "object_writer.h" #include "parser.h" #include "lexer.h" #include "gitinfo.h" -// List of keywords -const string_list g_Keywords = -{ - "bool", - "break", - "case", - "continue", - "const", - "default", - "do", - "else", - "event", - "for", - "goto", - "if", - "int", - "mainloop", - "onenter", - "onexit", - "state", - "switch", - "str" - "void", - "while", - - // These ones aren't implemented yet but I plan to do so, thus they are - // reserved. Also serves as a to-do list of sorts for me. >:F - "enum", // Would enum actually be useful? I think so. - "func", // Would function support need external support from zandronum? - "return", -}; - -// databuffer global variable -int g_NextMark = 0; - int main (int argc, char** argv) { try @@ -102,7 +65,7 @@ header += " (debug build)"; #endif - for (int i = 0; i < header.len() / 2; ++i) + for (int i = 0; i < header.length() / 2; ++i) headerline += "-="; headerline += '-'; @@ -122,62 +85,26 @@ else outfile = argv[2]; - // If we'd end up writing into an existing file, - // ask the user if we want to overwrite it - if (fexists (outfile)) - { - // Additional warning if the paths are the same - string warning; -#ifdef FILE_CASEINSENSITIVE - - if (+outfile == +string (argv[1])) -#else - if (outfile == argv[1]) -#endif - { - warning = "\nWARNING: Output file is the same as the input file. "; - warning += "Answering yes here will destroy the source!\n"; - warning += "Continue nevertheless?"; - } - - printf ("output file `%s` already exists! overwrite?%s (y/n) ", outfile.chars(), warning.chars()); - - char ans; - fgets (&ans, 1, stdin); - - if (ans != 'y') - { - printf ("abort\n"); - exit (1); - } - } - // Prepare reader and writer - botscript_parser* r = new botscript_parser; - object_writer* w = new object_writer; + botscript_parser* parser = new botscript_parser; // We're set, begin parsing :) - printf ("Parsing script...\n"); - r->parse_botscript (argv[1], w); - printf ("Script parsed successfully.\n"); + print ("Parsing script...\n"); + parser->parse_botscript (argv[1]); + print ("Script parsed successfully.\n"); // Parse done, print statistics and write to file int globalcount = g_GlobalVariables.size(); int stringcount = num_strings_in_table(); - int NumMarks = w->MainBuffer->count_marks(); - int NumRefs = w->MainBuffer->count_references(); print ("%1 / %2 strings written\n", stringcount, g_max_stringlist_size); print ("%1 / %2 global variables\n", globalcount, g_max_global_vars); - print ("%1 / %2 bytecode marks\n", NumMarks, MAX_MARKS); // TODO: nuke - print ("%1 / %2 bytecode references\n", NumRefs, MAX_MARKS); // TODO: nuke - print ("%1 / %2 events\n", g_NumEvents, g_max_events); - print ("%1 state%s1\n", g_NumStates); + print ("%1 / %2 events\n", parser->get_num_events(), g_max_events); + print ("%1 state%s1\n", parser->get_num_states()); - w->write_to_file (outfile); + parser->write_to_file (outfile); // Clear out the junk - delete r; - delete w; + delete parser; // Done! exit (0); @@ -211,30 +138,14 @@ // Locate the extension and chop it out int extdot = s.last ("."); - if (extdot >= s.len() - 4) - s -= (s.len() - extdot); + if (extdot >= s.length() - 4) + s -= (s.length() - extdot); s += ".o"; return s; } // ============================================================================ -// Is the given argument a reserved keyword? -bool IsKeyword (string s) -{ - for (int u = 0; u < NumKeywords(); u++) - if (s.to_uppercase() == g_Keywords[u].to_uppercase()) - return true; - - return false; -} - -int NumKeywords() -{ - return sizeof (g_Keywords) / sizeof (const char*); -} - -// ============================================================================ type_e GetTypeByName (string t) { t = t.to_lowercase();
--- a/src/main.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/main.h Sun Jan 26 23:18:48 2014 +0200 @@ -26,8 +26,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BOTC_COMMON_H -#define BOTC_COMMON_H +#ifndef BOTC_MAIN_H +#define BOTC_MAIN_H #if !defined (__cplusplus) || __cplusplus < 201103L # error botc requires a C++11-compliant compiler to be built @@ -36,6 +36,7 @@ #include <cstdio> #include <cstdarg> #include <cstdint> +#include "property.h" #include "types.h" #include "containers.h" #include "str.h" @@ -57,25 +58,6 @@ #define FILE_CASEINSENSITIVE #endif -// Parser mode: where is the parser at? -enum parsermode_e -{ - MODE_TOPLEVEL, // at top level - MODE_EVENT, // inside event definition - MODE_MAINLOOP, // inside mainloop - MODE_ONENTER, // inside onenter - MODE_ONEXIT, // inside onexit -}; - -enum type_e -{ - TYPE_UNKNOWN = 0, - TYPE_VOID, - TYPE_INT, - TYPE_STRING, - TYPE_BOOL, -}; - #define elif else if #define CHECK_FILE(pointer,path,action) \ @@ -101,60 +83,9 @@ string get_version_string (form_length_e len); string make_version_string (int major, int minor, int patch); -// Make the parser's variables globally available -extern int g_NumStates; -extern int g_NumEvents; -extern parsermode_e g_current_mode; -extern string g_CurState; - #ifndef __GNUC__ #define __attribute__(X) #endif #define deprecated __attribute__ ((deprecated)) -// Byte datatype -typedef int32_t word; -typedef unsigned char byte; - -// Keywords -extern const string_list g_Keywords; - -bool IsKeyword (string s); -int NumKeywords (); - -// Script mark and reference -struct ScriptMark -{ - string name; - size_t pos; -}; - -struct ScriptMarkReference -{ - int num; - size_t pos; -}; - -// ==================================================================== -// Generic union -template <class T> union generic_union -{ - T as_t; - byte as_bytes[sizeof (T)]; - char as_chars[sizeof (T)]; - double as_double; - float as_float; - int as_int; - word as_word; -}; - -// ==================================================================== -// Finds a byte in the given value. -template <class T> inline unsigned char GetByteIndex (T a, int b) -{ - generic_union<T> uni; - uni.as_t = a; - return uni.as_bytes[b]; -} - -#endif // BOTC_COMMON_H +#endif // BOTC_MAIN_H
--- a/src/object_writer.cc Sun Jan 19 20:39:30 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - Copyright 2012-2014 Santeri Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "main.h" -#include "object_writer.h" -#include "data_buffer.h" -#include "stringtable.h" - -extern bool g_GotMainLoop; - -object_writer::object_writer() -{ - MainBuffer = new data_buffer; - MainLoopBuffer = new data_buffer; - OnEnterBuffer = new data_buffer; - SwitchBuffer = null; // created on demand - numWrittenBytes = 0; - numWrittenReferences = 0; -} - -void object_writer::write_string (string a) -{ - write (a.length()); - - for (int i = 0; i < a.length(); i++) - write (a[i]); -} - -void object_writer::write_buffer (data_buffer* buf) -{ - get_current_buffer()->merge (buf); -} - -void object_writer::write_member_buffers() -{ - // If there was no mainloop defined, write a dummy one now. - if (!g_GotMainLoop) - { - MainLoopBuffer->write (dh_main_loop); - MainLoopBuffer->write (dh_end_main_loop); - } - - // Write the onenter and mainloop buffers, IN THAT ORDER - for (int i = 0; i < 2; i++) - { - data_buffer** buf = (!i) ? &OnEnterBuffer : &MainLoopBuffer; - write_buffer (*buf); - - // Clear the buffer afterwards for potential next state - *buf = new data_buffer; - } - - // Next state definitely has no mainloop yet - g_GotMainLoop = false; -} - -// Write string table -void object_writer::write_string_table() -{ - int stringcount = num_strings_in_table(); - - if (!stringcount) - return; - - // Write header - write (dh_string_list); - write (stringcount); - - // Write all strings - for (int a = 0; a < stringcount; a++) - write_string (get_string_table()[a]); -} - -// Write main buffer to file -void object_writer::write_to_file (string filepath) -{ - fp = fopen (filepath, "w"); - CHECK_FILE (fp, filepath, "writing"); - - // First, resolve references - numWrittenReferences = 0; - - for (int u = 0; u < MAX_MARKS; u++) - { - ScriptMarkReference* ref = MainBuffer->refs[u]; - - if (!ref) - continue; - - // Substitute the placeholder with the mark position - generic_union<word> uni; - uni.as_word = static_cast<word> (MainBuffer->marks[ref->num]->pos); - - for (int v = 0; v < (int) sizeof (word); v++) - memset (MainBuffer->buffer + ref->pos + v, uni.as_bytes[v], 1); - - /* - printf ("reference %u at %d resolved to %u at %d\n", - u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos); - */ - numWrittenReferences++; - } - - // Then, dump the main buffer to the file - for (int x = 0; x < MainBuffer->writesize; x++) - write_data_to_file<byte> (*(MainBuffer->buffer + x)); - - print ("-- %1 byte%s1 written to %2\n", numWrittenBytes, filepath); - fclose (fp); -} - -data_buffer* object_writer::get_current_buffer() -{ - return SwitchBuffer ? SwitchBuffer : - (g_current_mode == MODE_MAINLOOP) ? MainLoopBuffer : - (g_current_mode == MODE_ONENTER) ? OnEnterBuffer : - MainBuffer; -} - -ScriptMark* g_ScriptMark = null; - -// Adds a mark -int object_writer::add_mark (string name) -{ - return get_current_buffer()->add_mark (name); -} - -// Adds a reference -int object_writer::add_reference (int mark) -{ - data_buffer* b = get_current_buffer(); - return b->add_reference (mark); -} - -// Finds a mark -int object_writer::find_byte_mark (string name) -{ - data_buffer* b = get_current_buffer(); - - for (int u = 0; u < MAX_MARKS; u++) - { - if (b->marks[u] && b->marks[u]->name.to_uppercase() == name.to_uppercase()) - return u; - } - - return MAX_MARKS; -} - -// Moves a mark to the current position -void object_writer::move_mark (int mark) -{ - get_current_buffer()->move_mark (mark); -} - -// Deletes a mark -void object_writer::delete_mark (int mark) -{ - get_current_buffer()->delete_mark (mark); -} - -void object_writer::offset_mark (int mark, int offset) -{ - get_current_buffer()->offset_mark (mark, offset); -}
--- a/src/object_writer.h Sun Jan 19 20:39:30 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -/* - Copyright 2012-2014 Santeri Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef BOTC_OBJWRITER_H -#define BOTC_OBJWRITER_H - -#include <stdio.h> -#include <typeinfo> -#include <string.h> -#include "main.h" -#include "str.h" -#include "data_buffer.h" - -class object_writer -{ -public: - // ==================================================================== - // MEMBERS - - // Pointer to the file we're writing to - FILE* fp; - - // The main buffer - the contents of this is what we - // write to file after parsing is complete - data_buffer* MainBuffer; - - // onenter buffer - the contents of the onenter{} block - // is buffered here and is merged further at the end of state - data_buffer* OnEnterBuffer; - - // Mainloop buffer - the contents of the mainloop{} block - // is buffered here and is merged further at the end of state - data_buffer* MainLoopBuffer; - - // Switch buffer - switch case data is recorded to this - // buffer initially, instead of into main buffer. - data_buffer* SwitchBuffer; - - // How many bytes have we written to file? - int numWrittenBytes; - - // How many references did we resolve in the main buffer? - int numWrittenReferences; - - // ==================================================================== - // METHODS - object_writer(); - void write_string (string s); - void write_buffer (data_buffer* buf); - void write_member_buffers(); - void write_string_table(); - void write_to_file (string filepath); - data_buffer* get_current_buffer(); - - int add_mark (string name); - int find_byte_mark (string name); - int add_reference (int mark); - void move_mark (int mark); - void offset_mark (int mark, int offset); - void delete_mark (int mark); - - template <class T> void write (T stuff) - { - get_current_buffer()->write (stuff); - } - -private: - // Write given data to file. - template <class T> void write_data_to_file (T stuff) - { - // One byte at a time - generic_union<T> uni; - uni.as_t = stuff; - - for (int x = 0; x < sizeof (T); x++) - { - fwrite (&uni.as_bytes[x], 1, 1, fp); - numWrittenBytes++; - } - } -}; - -#endif // BOTC_OBJWRITER_H
--- a/src/parser.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/parser.cc Sun Jan 26 23:18:48 2014 +0200 @@ -26,7 +26,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "object_writer.h" #include "parser.h" #include "events.h" #include "commands.h" @@ -34,21 +33,9 @@ #include "variables.h" #include "containers.h" #include "lexer.h" - -#define SCOPE(n) scopestack[g_ScopeCursor - n] +#include "data_buffer.h" -// TODO: make these static -int g_NumStates = 0; -int g_NumEvents = 0; -parsermode_e g_current_mode = MODE_TOPLEVEL; -string g_CurState = ""; -bool g_stateSpawnDefined = false; -bool g_GotMainLoop = false; -int g_ScopeCursor = 0; -data_buffer* g_IfExpression = null; -bool g_CanElse = false; -static string* g_undefined_labels[MAX_MARKS]; // TODO: make a list -list<constant_info> g_ConstInfo; +#define SCOPE(n) (m_scope_stack[m_scope_cursor - n]) // ============================================================================ // @@ -66,7 +53,7 @@ // void botscript_parser::check_toplevel() { - if (g_current_mode != MODE_TOPLEVEL) + if (m_current_mode != MODE_TOPLEVEL) error ("%1-statements may only be defined at top level!", token_string()); } @@ -74,7 +61,7 @@ // void botscript_parser::check_not_toplevel() { - if (g_current_mode == MODE_TOPLEVEL) + if (m_current_mode == MODE_TOPLEVEL) error ("%1-statements must not be defined at top level!", token_string()); } @@ -82,29 +69,33 @@ // Main parser code. Begins read of the script file, checks the syntax of it // and writes the data to the object file via Objwriter - which also takes care // of necessary buffering so stuff is written in the correct order. -void botscript_parser::parse_botscript (string file_name, object_writer* w) +void botscript_parser::parse_botscript (string file_name) { - m_writer = w; - // Lex and preprocess the file m_lx->process_file (file_name); + m_current_mode = MODE_TOPLEVEL; + m_num_states = 0; + m_num_events = 0; + m_scope_cursor = 0; + m_state_spawn_defined = false; + m_got_main_loop = false; + m_if_expression = null; + m_can_else = false; + // Zero the entire block stack first // TODO: this shouldn't be necessary for (int i = 0; i < MAX_SCOPE; i++) - ZERO (scopestack[i]); - - for (int i = 0; i < MAX_MARKS; i++) - g_undefined_labels[i] = null; + ZERO (m_scope_stack[i]); while (m_lx->get_next()) { // Check if else is potentically valid - if (token_is (tk_else) && !g_CanElse) + if (token_is (tk_else) && !m_can_else) error ("else without preceding if"); if (!token_is (tk_else)) - g_CanElse = false; + m_can_else = false; switch (m_lx->get_token()->type) { @@ -209,7 +200,7 @@ if (comm) { - m_writer->get_current_buffer()->merge (ParseCommand (comm)); + buffer()->merge_and_destroy (parse_command (comm)); m_lx->must_get_next (tk_semicolon); continue; } @@ -220,7 +211,7 @@ if (!b) error ("unknown token `%1`", token_string()); - m_writer->write_buffer (b); + buffer()->merge_and_destroy (b); m_lx->must_get_next (tk_semicolon); } break; @@ -229,22 +220,29 @@ // =============================================================================== // Script file ended. Do some last checks and write the last things to main buffer - if (g_current_mode != MODE_TOPLEVEL) + if (m_current_mode != MODE_TOPLEVEL) error ("script did not end at top level; a `}` is missing somewhere"); // stateSpawn must be defined! - if (!g_stateSpawnDefined) + if (!m_state_spawn_defined) error ("script must have a state named `stateSpawn`!"); - for (int i = 0; i < MAX_MARKS; i++) - if (g_undefined_labels[i]) - error ("label `%1` is referenced via `goto` but isn't defined\n", g_undefined_labels[i]); + // Ensure no goto target is left undefined + if (m_undefined_labels.is_empty() == false) + { + string_list names; + + for (undefined_label& undf : m_undefined_labels) + names << undf.name; + + error ("labels `%1` are referenced via `goto` but are not defined\n", names); + } // Dump the last state's onenter and mainloop - m_writer->write_member_buffers(); + write_member_buffers(); // String table - m_writer->write_string_table(); + write_string_table(); } // ============================================================================ @@ -262,24 +260,24 @@ // stateSpawn is special - it *must* be defined. If we // encountered it, then mark down that we have it. if (-statename == "statespawn") - g_stateSpawnDefined = true; + m_state_spawn_defined = true; // Must end in a colon m_lx->must_get_next (tk_colon); // write the previous state's onenter and // mainloop buffers to file now - if (g_CurState.is_empty() == false) - m_writer->write_member_buffers(); + if (m_current_state.is_empty() == false) + write_member_buffers(); - m_writer->write (dh_state_name); - m_writer->write_string (statename); - m_writer->write (dh_state_index); - m_writer->write (g_NumStates); + buffer()->write_dword (dh_state_name); + buffer()->write_string (statename); + buffer()->write_dword (dh_state_index); + buffer()->write_dword (m_num_states); - g_NumStates++; - g_CurState = statename; - g_GotMainLoop = false; + m_num_states++; + m_current_state = statename; + m_got_main_loop = false; } // ============================================================================ @@ -295,10 +293,10 @@ error ("bad event, got `%1`\n", token_string()); m_lx->must_get_next (tk_brace_start); - g_current_mode = MODE_EVENT; - m_writer->write (dh_event); - m_writer->write (e->number); - g_NumEvents++; + m_current_mode = MODE_EVENT; + buffer()->write_dword (dh_event); + buffer()->write_dword (e->number); + m_num_events++; } // ============================================================================ @@ -308,9 +306,8 @@ check_toplevel(); m_lx->must_get_next (tk_brace_start); - // Mode must be set before dataheader is written here! - g_current_mode = MODE_MAINLOOP; - m_writer->write (dh_main_loop); + m_current_mode = MODE_MAINLOOP; + m_main_loop_buffer->write_dword (dh_main_loop); } // ============================================================================ @@ -321,10 +318,8 @@ bool onenter = (token_is (tk_onenter)); m_lx->must_get_next (tk_brace_start); - // Mode must be set before dataheader is written here, - // because onenter goes to a separate buffer. - g_current_mode = onenter ? MODE_ONENTER : MODE_ONEXIT; - m_writer->write (onenter ? dh_on_enter : dh_on_exit); + m_current_mode = onenter ? MODE_ONENTER : MODE_ONEXIT; + buffer()->write_dword (onenter ? dh_on_enter : dh_on_exit); } // ============================================================================ @@ -332,7 +327,7 @@ void botscript_parser::parse_variable_declaration() { // For now, only globals are supported - if (g_current_mode != MODE_TOPLEVEL || g_CurState.is_empty() == false) + if (m_current_mode != MODE_TOPLEVEL || m_current_state.is_empty() == false) error ("variables must only be global for now"); type_e type = (token_is (tk_int)) ? TYPE_INT : @@ -362,18 +357,20 @@ // Find the mark this goto statement points to string target = token_string(); - int m = m_writer->find_byte_mark (target); + byte_mark* mark = buffer()->find_mark_by_name (target); // If not set, define it - if (m == MAX_MARKS) + if (!mark) { - m = m_writer->add_mark (target); - g_undefined_labels[m] = new string (target); + undefined_label undf; + undf.name = target; + undf.target = buffer()->add_mark (target); + m_undefined_labels << undf; } // Add a reference to the mark. - m_writer->write (dh_goto); - m_writer->add_reference (m); + buffer()->write_dword (dh_goto); + buffer()->add_reference (mark); m_lx->must_get_next (tk_semicolon); } @@ -390,22 +387,22 @@ // Read the expression and write it. m_lx->must_get_next(); data_buffer* c = parse_expression (TYPE_INT); - m_writer->write_buffer (c); + buffer()->merge_and_destroy (c); m_lx->must_get_next (tk_paren_end); m_lx->must_get_next (tk_brace_start); // Add a mark - to here temporarily - and add a reference to it. // Upon a closing brace, the mark will be adjusted. - int marknum = m_writer->add_mark (""); + byte_mark* mark = buffer()->add_mark (""); // Use dh_if_not_goto - if the expression is not true, we goto the mark // we just defined - and this mark will be at the end of the scope block. - m_writer->write (dh_if_not_goto); - m_writer->add_reference (marknum); + buffer()->write_dword (dh_if_not_goto); + buffer()->add_reference (mark); // Store it - SCOPE (0).mark1 = marknum; + SCOPE (0).mark1 = mark; SCOPE (0).type = e_if_scope; } @@ -417,9 +414,9 @@ m_lx->must_get_next (tk_brace_start); // Don't use PushScope as it resets the scope - g_ScopeCursor++; + m_scope_cursor++; - if (g_ScopeCursor >= MAX_SCOPE) + if (m_scope_cursor >= MAX_SCOPE) error ("too deep scope"); if (SCOPE (0).type != e_if_scope) @@ -427,14 +424,14 @@ // write down to jump to the end of the else statement // Otherwise we have fall-throughs - SCOPE (0).mark2 = m_writer->add_mark (""); + SCOPE (0).mark2 = buffer()->add_mark (""); // Instruction to jump to the end after if block is complete - m_writer->write (dh_goto); - m_writer->add_reference (SCOPE (0).mark2); + buffer()->write_dword (dh_goto); + buffer()->add_reference (SCOPE (0).mark2); // Move the ifnot mark here and set type to else - m_writer->move_mark (SCOPE (0).mark1); + buffer()->adjust_mark (SCOPE (0).mark1); SCOPE (0).type = e_else_scope; } @@ -449,8 +446,8 @@ // end. The condition is checked at the very start of the loop, if it fails, // we use goto to skip to the end of the loop. At the end, we loop back to // the beginning with a go-to statement. - int mark1 = m_writer->add_mark (""); // start - int mark2 = m_writer->add_mark (""); // end + byte_mark* mark1 = buffer()->add_mark (""); // start + byte_mark* mark2 = buffer()->add_mark (""); // end // Condition m_lx->must_get_next (tk_paren_start); @@ -460,11 +457,11 @@ m_lx->must_get_next (tk_brace_start); // write condition - m_writer->write_buffer (expr); + buffer()->merge_and_destroy (expr); // Instruction to go to the end if it fails - m_writer->write (dh_if_not_goto); - m_writer->add_reference (mark2); + buffer()->write_dword (dh_if_not_goto); + buffer()->add_reference (mark2); // Store the needed stuff SCOPE (0).mark1 = mark1; @@ -509,16 +506,16 @@ m_lx->must_get_next (tk_brace_start); // First, write out the initializer - m_writer->write_buffer (init); + buffer()->merge_and_destroy (init); // Init two marks - int mark1 = m_writer->add_mark (""); - int mark2 = m_writer->add_mark (""); + byte_mark* mark1 = buffer()->add_mark (""); + byte_mark* mark2 = buffer()->add_mark (""); // Add the condition - m_writer->write_buffer (cond); - m_writer->write (dh_if_not_goto); - m_writer->add_reference (mark2); + buffer()->merge_and_destroy (cond); + buffer()->write_dword (dh_if_not_goto); + buffer()->add_reference (mark2); // Store the marks and incrementor SCOPE (0).mark1 = mark1; @@ -534,7 +531,7 @@ check_not_toplevel(); push_scope(); m_lx->must_get_next (tk_brace_start); - SCOPE (0).mark1 = m_writer->add_mark (""); + SCOPE (0).mark1 = buffer()->add_mark (""); SCOPE (0).type = e_do_scope; } @@ -559,11 +556,11 @@ push_scope(); m_lx->must_get_next (tk_paren_start); m_lx->must_get_next(); - m_writer->write_buffer (parse_expression (TYPE_INT)); + buffer()->merge_and_destroy (parse_expression (TYPE_INT)); m_lx->must_get_next (tk_paren_end); m_lx->must_get_next (tk_brace_start); SCOPE (0).type = e_switch_scope; - SCOPE (0).mark1 = m_writer->add_mark (""); // end mark + SCOPE (0).mark1 = buffer()->add_mark (""); // end mark SCOPE (0).buffer1 = null; // default header } @@ -584,17 +581,19 @@ if (SCOPE (0).casenumbers[i] == num) error ("multiple case %d labels in one switch", num); - // write down the expression and case-go-to. This builds + // Write down the expression and case-go-to. This builds // the case tree. The closing event will write the actual // blocks and move the marks appropriately. - // AddSwitchCase will add the reference to the mark + // + // AddSwitchCase will add the reference to the mark // for the case block that this heralds, and takes care // of buffering setup and stuff like that. - // null the switch buffer for the case-go-to statement, + // + // We null the switch buffer for the case-go-to statement as // we want it all under the switch, not into the case-buffers. - m_writer->SwitchBuffer = null; - m_writer->write (dh_case_goto); - m_writer->write (num); + m_switch_buffer = null; + buffer()->write_dword (dh_case_goto); + buffer()->write_dword (num); add_switch_case (null); SCOPE (0).casenumbers[SCOPE (0).casecursor] = num; } @@ -620,8 +619,8 @@ // a default. data_buffer* b = new data_buffer; SCOPE (0).buffer1 = b; - b->write (dh_drop); - b->write (dh_goto); + b->write_dword (dh_drop); + b->write_dword (dh_goto); add_switch_case (b); } @@ -629,10 +628,10 @@ // void botscript_parser::parse_break() { - if (!g_ScopeCursor) + if (!m_scope_cursor) error ("unexpected `break`"); - m_writer->write (dh_goto); + buffer()->write_dword (dh_goto); // switch and if use mark1 for the closing point, // for and while use mark2. @@ -641,13 +640,13 @@ case e_if_scope: case e_switch_scope: { - m_writer->add_reference (SCOPE (0).mark1); + buffer()->add_reference (SCOPE (0).mark1); } break; case e_for_scope: case e_while_scope: { - m_writer->add_reference (SCOPE (0).mark2); + buffer()->add_reference (SCOPE (0).mark2); } break; default: @@ -669,16 +668,16 @@ bool found = false; // Fall through the scope until we find a loop block - for (curs = g_ScopeCursor; curs > 0 && !found; curs--) + for (curs = m_scope_cursor; curs > 0 && !found; curs--) { - switch (scopestack[curs].type) + switch (m_scope_stack[curs].type) { case e_for_scope: case e_while_scope: case e_do_scope: { - m_writer->write (dh_goto); - m_writer->add_reference (scopestack[curs].mark1); + buffer()->write_dword (dh_goto); + buffer()->add_reference (m_scope_stack[curs].mark1); found = true; } break; @@ -698,36 +697,36 @@ { // Closing brace // If we're in the block stack, we're descending down from it now - if (g_ScopeCursor > 0) + if (m_scope_cursor > 0) { switch (SCOPE (0).type) { case e_if_scope: // Adjust the closing mark. - m_writer->move_mark (SCOPE (0).mark1); + buffer()->adjust_mark (SCOPE (0).mark1); // We're returning from if, thus else can be next - g_CanElse = true; + m_can_else = true; break; case e_else_scope: // else instead uses mark1 for itself (so if expression // fails, jump to else), mark2 means end of else - m_writer->move_mark (SCOPE (0).mark2); + buffer()->adjust_mark (SCOPE (0).mark2); break; case e_for_scope: // write the incrementor at the end of the loop block - m_writer->write_buffer (SCOPE (0).buffer1); + buffer()->merge_and_destroy (SCOPE (0).buffer1); // fall-thru case e_while_scope: // write down the instruction to go back to the start of the loop - m_writer->write (dh_goto); - m_writer->add_reference (SCOPE (0).mark1); + buffer()->write_dword (dh_goto); + buffer()->add_reference (SCOPE (0).mark1); // Move the closing mark here since we're at the end of the while loop - m_writer->move_mark (SCOPE (0).mark2); + buffer()->adjust_mark (SCOPE (0).mark2); break; case e_do_scope: @@ -740,9 +739,9 @@ m_lx->must_get_next (tk_semicolon); // If the condition runs true, go back to the start. - m_writer->write_buffer (expr); - m_writer->write (dh_if_goto); - m_writer->add_reference (SCOPE (0).mark1); + buffer()->merge_and_destroy (expr); + buffer()->write_dword (dh_if_goto); + buffer()->add_reference (SCOPE (0).mark1); break; } @@ -751,21 +750,21 @@ // Switch closes. Move down to the record buffer of // the lower block. if (SCOPE (1).casecursor != -1) - m_writer->SwitchBuffer = SCOPE (1).casebuffers[SCOPE (1).casecursor]; + m_switch_buffer = SCOPE (1).casebuffers[SCOPE (1).casecursor]; else - m_writer->SwitchBuffer = null; + m_switch_buffer = null; // If there was a default in the switch, write its header down now. // If not, write instruction to jump to the end of switch after // the headers (thus won't fall-through if no case matched) if (SCOPE (0).buffer1) - m_writer->write_buffer (SCOPE (0).buffer1); - else - { - m_writer->write (dh_drop); - m_writer->write (dh_goto); - m_writer->add_reference (SCOPE (0).mark1); - } + buffer()->merge_and_destroy (SCOPE (0).buffer1); + else + { + buffer()->write_dword (dh_drop); + buffer()->write_dword (dh_goto); + buffer()->add_reference (SCOPE (0).mark1); + } // Go through all of the buffers we // recorded down and write them. @@ -774,12 +773,12 @@ if (!SCOPE (0).casebuffers[u]) continue; - m_writer->move_mark (SCOPE (0).casemarks[u]); - m_writer->write_buffer (SCOPE (0).casebuffers[u]); + buffer()->adjust_mark (SCOPE (0).casemarks[u]); + buffer()->merge_and_destroy (SCOPE (0).casebuffers[u]); } // Move the closing mark here - m_writer->move_mark (SCOPE (0).mark1); + buffer()->adjust_mark (SCOPE (0).mark1); break; } @@ -788,14 +787,14 @@ } // Descend down the stack - g_ScopeCursor--; + m_scope_cursor--; return; } - int dataheader = (g_current_mode == MODE_EVENT) ? dh_end_event : - (g_current_mode == MODE_MAINLOOP) ? dh_end_main_loop : - (g_current_mode == MODE_ONENTER) ? dh_end_on_enter : - (g_current_mode == MODE_ONEXIT) ? dh_end_on_exit : -1; + int dataheader = (m_current_mode == MODE_EVENT) ? dh_end_event : + (m_current_mode == MODE_MAINLOOP) ? dh_end_main_loop : + (m_current_mode == MODE_ONENTER) ? dh_end_on_enter : + (m_current_mode == MODE_ONEXIT) ? dh_end_on_exit : -1; if (dataheader == -1) error ("unexpected `}`"); @@ -803,8 +802,8 @@ // Data header must be written before mode is changed because // onenter and mainloop go into special buffers, and we want // the closing data headers into said buffers too. - m_writer->write (dataheader); - g_current_mode = MODE_TOPLEVEL; + buffer()->write_dword (dataheader); + m_current_mode = MODE_TOPLEVEL; m_lx->get_next (tk_semicolon); } @@ -846,7 +845,7 @@ } info.val = m_lx->get_token()->text; - g_ConstInfo << info; + m_constants << info; m_lx->must_get_next (tk_semicolon); } @@ -857,6 +856,7 @@ { check_not_toplevel(); string label_name = token_string(); + byte_mark* mark = null; // want no conflicts.. if (find_command_by_name (label_name)) @@ -866,24 +866,22 @@ error ("label name `%1` conflicts with variable\n", label_name); // See if a mark already exists for this label - int mark = -1; - - for (int i = 0; i < MAX_MARKS; i++) + for (undefined_label& undf : m_undefined_labels) { - if (g_undefined_labels[i] && *g_undefined_labels[i] == label_name) - { - mark = i; - m_writer->move_mark (i); + if (undf.name != label_name) + continue; - // No longer undefinde - delete g_undefined_labels[i]; - g_undefined_labels[i] = null; - } + mark = undf.target; + buffer()->adjust_mark (mark); + + // No longer undefined + m_undefined_labels.remove (undf); + break; } // Not found in unmarked lists, define it now - if (mark == -1) - m_writer->add_mark (label_name); + if (mark == null) + buffer()->add_mark (label_name); m_lx->must_get_next (tk_colon); } @@ -921,9 +919,6 @@ m_lx->must_get_next (tk_symbol); comm->name = m_lx->get_token()->text; - if (IsKeyword (comm->name)) - error ("function name `%1` conflicts with keyword", comm->name); - m_lx->must_get_next (tk_colon); // Return value @@ -994,11 +989,11 @@ // ============================================================================ // Parses a command call -data_buffer* botscript_parser::ParseCommand (command_info* comm) +data_buffer* botscript_parser::parse_command (command_info* comm) { data_buffer* r = new data_buffer (64); - if (g_current_mode == MODE_TOPLEVEL) + if (m_current_mode == MODE_TOPLEVEL) error ("command call at top level"); m_lx->must_get_next (tk_paren_start); @@ -1022,7 +1017,7 @@ error ("too many arguments passed to %1\n\tprototype: %2", comm->name, get_command_signature (comm)); - r->merge (parse_expression (comm->args[curarg].type)); + r->merge_and_destroy (parse_expression (comm->args[curarg].type)); m_lx->must_get_next(); if (curarg < comm->numargs - 1) @@ -1051,14 +1046,14 @@ // If the script skipped any optional arguments, fill in defaults. while (curarg < comm->maxargs) { - r->write (dh_push_number); - r->write (comm->args[curarg].defvalue); + r->write_dword (dh_push_number); + r->write_dword (comm->args[curarg].defvalue); curarg++; } - r->write (dh_command); - r->write (comm->number); - r->write (comm->maxargs); + r->write_dword (dh_command); + r->write_dword (comm->number); + r->write_dword (comm->maxargs); return r; } @@ -1145,7 +1140,7 @@ data_buffer* retbuf = new data_buffer (64); // Parse first operand - retbuf->merge (parse_expr_value (reqtype)); + retbuf->merge_and_destroy (parse_expr_value (reqtype)); // Parse any and all operators we get int oper; @@ -1173,22 +1168,22 @@ // It also is handled differently: there isn't a dataheader for ternary // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. // Behold, big block of writing madness! :P - int mark1 = retbuf->add_mark (""); // start of "else" case - int mark2 = retbuf->add_mark (""); // end of expression - retbuf->write (dh_if_not_goto); // if the first operand (condition) + byte_mark* mark1 = retbuf->add_mark (""); // start of "else" case + byte_mark* mark2 = retbuf->add_mark (""); // end of expression + retbuf->write_dword (dh_if_not_goto); // if the first operand (condition) retbuf->add_reference (mark1); // didn't eval true, jump into mark1 - retbuf->merge (rb); // otherwise, perform second operand (true case) - retbuf->write (dh_goto); // afterwards, jump to the end, which is + retbuf->merge_and_destroy (rb); // otherwise, perform second operand (true case) + retbuf->write_dword (dh_goto); // afterwards, jump to the end, which is retbuf->add_reference (mark2); // marked by mark2. - retbuf->move_mark (mark1); // move mark1 at the end of the true case - retbuf->merge (tb); // perform third operand (false case) - retbuf->move_mark (mark2); // move the ending mark2 here + retbuf->adjust_mark (mark1); // move mark1 at the end of the true case + retbuf->merge_and_destroy (tb); // perform third operand (false case) + retbuf->adjust_mark (mark2); // move the ending mark2 here } else { // write to buffer - retbuf->merge (rb); - retbuf->write (get_data_header_by_operator (null, oper)); + retbuf->merge_and_destroy (rb); + retbuf->write_dword (get_data_header_by_operator (null, oper)); } } @@ -1325,8 +1320,8 @@ if (reqtype != TYPE_INT) error ("strlen returns int but %1 is expected\n", GetTypeName (reqtype)); - b->write (dh_push_number); - b->write (constant->val.len()); + b->write_dword (dh_push_number); + b->write_dword (constant->val.len()); m_lx->must_get_next (tk_paren_end); } @@ -1336,7 +1331,7 @@ // Expression m_lx->must_get_next(); data_buffer* c = parse_expression (reqtype); - b->merge (c); + b->merge_and_destroy (c); m_lx->must_get_next (tk_paren_end); } else if (command_info* comm = find_command_by_name (token_string())) @@ -1347,7 +1342,7 @@ if (reqtype && comm->returnvalue != reqtype) error ("%1 returns an incompatible data type", comm->name); - b = ParseCommand (comm); + b = parse_command (comm); } else if (constant_info* constant = find_constant (token_string())) { @@ -1361,12 +1356,12 @@ { case TYPE_BOOL: case TYPE_INT: - b->write (dh_push_number); - b->write (atoi (constant->val)); + b->write_dword (dh_push_number); + b->write_dword (atoi (constant->val)); break; case TYPE_STRING: - b->write_string (constant->val); + b->write_string_index (constant->val); break; case TYPE_VOID: @@ -1377,8 +1372,8 @@ else if ((g = find_global_variable (token_string()))) { // Global variable - b->write (dh_push_global_var); - b->write (g->index); + b->write_dword (dh_push_global_var); + b->write_dword (g->index); } else { @@ -1397,13 +1392,13 @@ // All values are written unsigned - thus we need to write the value's // absolute value, followed by an unary minus for negatives. - b->write (dh_push_number); + b->write_dword (dh_push_number); long v = token_string().to_long(); - b->write (static_cast<word> (abs (v))); + b->write_dword (static_cast<word> (abs (v))); if (v < 0) - b->write (dh_unary_minus); + b->write_dword (dh_unary_minus); break; } @@ -1413,14 +1408,14 @@ // string if it finds it in the table, or writes it to the // table and returns it index if it doesn't find it there. m_lx->must_be (tk_string); - b->write_string (token_string()); + b->write_string_index (token_string()); break; } } // Negate it now if desired if (negate) - b->write (dh_negate_logical); + b->write_dword (dh_negate_logical); return b; } @@ -1432,8 +1427,6 @@ // data_buffer* botscript_parser::parse_assignment (script_variable* var) { - bool global = !var->statename.len(); - // Get an operator m_lx->must_get_next(); int oper = parse_operator(); @@ -1441,7 +1434,7 @@ if (!is_assignment_operator (oper)) error ("expected assignment operator"); - if (g_current_mode == MODE_TOPLEVEL) + if (m_current_mode == MODE_TOPLEVEL) error ("can't alter variables at top level"); // Parse the right operand @@ -1454,19 +1447,19 @@ // a >>= b -> a = a >> b if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) { - retbuf->write (global ? dh_push_global_var : dh_push_local_var); - retbuf->write (var->index); - retbuf->merge (expr); - retbuf->write ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift); - retbuf->write (global ? dh_assign_global_var : dh_assign_local_var); - retbuf->write (var->index); + retbuf->write_dword (var->is_global() ? dh_push_global_var : dh_push_local_var); + retbuf->write_dword (var->index); + retbuf->merge_and_destroy (expr); + retbuf->write_dword ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift); + retbuf->write_dword (var->is_global() ? dh_assign_global_var : dh_assign_local_var); + retbuf->write_dword (var->index); } else { - retbuf->merge (expr); + retbuf->merge_and_destroy (expr); long dh = get_data_header_by_operator (var, oper); - retbuf->write (dh); - retbuf->write (var->index); + retbuf->write_dword (dh); + retbuf->write_dword (var->index); } return retbuf; @@ -1476,21 +1469,21 @@ // void botscript_parser::push_scope() { - g_ScopeCursor++; + m_scope_cursor++; - if (g_ScopeCursor >= MAX_SCOPE) + if (m_scope_cursor >= MAX_SCOPE) error ("too deep scope"); ScopeInfo* info = &SCOPE (0); info->type = e_unknown_scope; - info->mark1 = 0; - info->mark2 = 0; + info->mark1 = null; + info->mark2 = null; info->buffer1 = null; info->casecursor = -1; for (int i = 0; i < MAX_CASE; i++) { - info->casemarks[i] = MAX_MARKS; + info->casemarks[i] = null; info->casebuffers[i] = null; info->casenumbers[i] = -1; } @@ -1522,28 +1515,28 @@ error ("too many cases in one switch"); // Init a mark for the case buffer - int m = m_writer->add_mark (""); - info->casemarks[info->casecursor] = m; + byte_mark* casemark = buffer()->add_mark (""); + info->casemarks[info->casecursor] = casemark; // Add a reference to the mark. "case" and "default" both // add the necessary bytecode before the reference. if (b) - b->add_reference (m); + b->add_reference (casemark); else - m_writer->add_reference (m); + buffer()->add_reference (casemark); // Init a buffer for the case block and tell the object // writer to record all written data to it. - info->casebuffers[info->casecursor] = m_writer->SwitchBuffer = new data_buffer; + info->casebuffers[info->casecursor] = m_switch_buffer = new data_buffer; } // ============================================================================ // -constant_info* find_constant (const string& tok) +constant_info* botscript_parser::find_constant (const string& tok) { - for (int i = 0; i < g_ConstInfo.size(); i++) - if (g_ConstInfo[i].name == tok) - return &g_ConstInfo[i]; + for (int i = 0; i < m_constants.size(); i++) + if (m_constants[i].name == tok) + return &m_constants[i]; return null; } @@ -1569,3 +1562,99 @@ lexer::token* tok = m_lx->get_token(); return tok->file + ":" + string (tok->line) + ":" + string (tok->column); } + +// ============================================================================ +// +data_buffer* botscript_parser::buffer() +{ + if (m_switch_buffer != null) + return m_switch_buffer; + + if (m_current_mode == MODE_MAINLOOP) + return m_main_loop_buffer; + + if (m_current_mode == MODE_ONENTER) + return m_on_enter_buffer; + + return m_main_buffer; +} + +// ============================================================================ +// +void botscript_parser::write_member_buffers() +{ + // If there was no mainloop defined, write a dummy one now. + if (!m_got_main_loop) + { + m_main_loop_buffer->write_dword (dh_main_loop); + m_main_loop_buffer->write_dword (dh_end_main_loop); + } + + // Write the onenter and mainloop buffers, in that order in particular. + for (data_buffer** bufp : list<data_buffer**> ({&m_on_enter_buffer, &m_main_loop_buffer})) + { + buffer()->merge_and_destroy (*bufp); + + // Clear the buffer afterwards for potential next state + *bufp = new data_buffer; + } + + // Next state definitely has no mainloop yet + m_got_main_loop = false; +} + +// ============================================================================ +// +// Write string table +// +void botscript_parser::write_string_table() +{ + int stringcount = num_strings_in_table(); + + if (!stringcount) + return; + + // Write header + m_main_buffer->write_dword (dh_string_list); + m_main_buffer->write_dword (stringcount); + + // Write all strings + for (int i = 0; i < stringcount; i++) + m_main_buffer->write_string (get_string_table()[i]); +} + +// ============================================================================ +// +// Write the compiled bytecode to a file +// +void botscript_parser::write_to_file (string outfile) +{ + FILE* fp = fopen (outfile, "w"); + CHECK_FILE (fp, outfile, "writing"); + + // First, resolve references + for (int u = 0; u < MAX_MARKS; u++) + { + mark_reference* ref = m_main_buffer->get_refs()[u]; + + if (!ref) + continue; + + // Substitute the placeholder with the mark position + generic_union<word> uni; + uni.as_word = static_cast<word> (ref->target->pos); + + for (int v = 0; v < (int) sizeof (word); v++) + memset (m_main_buffer->get_buffer() + ref->pos + v, uni.as_bytes[v], 1); + + /* + printf ("reference %u at %d resolved to %u at %d\n", + u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos); + */ + } + + // Then, dump the main buffer to the file + fwrite (m_main_buffer->get_buffer(), 1, m_main_buffer->get_write_size(), fp); + print ("-- %1 byte%s1 written to %2\n", m_main_buffer->get_write_size(), outfile); + fclose (fp); +} \ No newline at end of file
--- a/src/parser.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/parser.h Sun Jan 26 23:18:48 2014 +0200 @@ -32,18 +32,26 @@ #include <stdio.h> #include "main.h" #include "commands.h" -#include "object_writer.h" #include "lexer_scanner.h" #include "tokens.h" -#define MAX_FILESTACK 8 #define MAX_SCOPE 32 #define MAX_CASE 64 +#define MAX_MARKS 512 // TODO: get rid of this +class data_buffer; class lexer; class script_variable; +struct undefined_label +{ + string name; + byte_mark* target; +}; + +// ============================================================================ // Operators +// enum operator_e { OPER_ADD, @@ -76,6 +84,8 @@ OPER_STRLEN, }; +// ============================================================================ +// struct operator_info { operator_e opercode; @@ -83,7 +93,9 @@ e_token token; }; +// ============================================================================ // Mark types +// enum marktype_e { e_label_mark, @@ -91,7 +103,9 @@ e_internal_mark, // internal structures }; -// Block types +// ============================================================================ +// Scope types +// enum scopetype_e { e_unknown_scope, @@ -104,32 +118,34 @@ }; // ============================================================================ -// Meta-data about blocks +// Meta-data about scopes +// struct ScopeInfo { - int mark1; - int mark2; - scopetype_e type; - data_buffer* buffer1; + byte_mark* mark1; + byte_mark* mark2; + scopetype_e type; + data_buffer* buffer1; // switch-related stuff // Which case are we at? - short casecursor; + int casecursor; // Marks to case-blocks - int casemarks[MAX_CASE]; + byte_mark* casemarks[MAX_CASE]; // Numbers of the case labels - int casenumbers[MAX_CASE]; + int casenumbers[MAX_CASE]; // actual case blocks - data_buffer* casebuffers[MAX_CASE]; + data_buffer* casebuffers[MAX_CASE]; // What is the current buffer of the block? - data_buffer* recordbuffer; + data_buffer* recordbuffer; }; // ============================================================================ +// struct constant_info { string name; @@ -138,71 +154,105 @@ }; // ============================================================================ -// The script reader reads the script, parses it and tells the object writer -// the bytecode it needs to write to file. +// class botscript_parser { public: // ==================================================================== - // TODO: make private - FILE* fp[MAX_FILESTACK]; - string filepath[MAX_FILESTACK]; - int fc; - - int pos[MAX_FILESTACK]; - int curline[MAX_FILESTACK]; - int curchar[MAX_FILESTACK]; - ScopeInfo scopestack[MAX_SCOPE]; - long savedpos[MAX_FILESTACK]; // filepointer cursor position - int commentmode; - long prevpos; - string prevtoken; - - // ==================================================================== // METHODS botscript_parser(); ~botscript_parser(); - void parse_botscript (string file_name, object_writer* w); - data_buffer* ParseCommand (command_info* comm); - data_buffer* parse_expression (type_e reqtype); - data_buffer* parse_assignment (script_variable* var); - int parse_operator (bool peek = false); - data_buffer* parse_expr_value (type_e reqtype); - string parse_float (); - void push_scope (); - data_buffer* parse_statement (); - void add_switch_case (data_buffer* b); - void check_toplevel(); - void check_not_toplevel(); - bool token_is (e_token a); - string token_string(); - string describe_position() const; + void parse_botscript (string file_name); + data_buffer* parse_command (command_info* comm); + data_buffer* parse_expression (type_e reqtype); + data_buffer* parse_assignment (script_variable* var); + int parse_operator (bool peek = false); + data_buffer* parse_expr_value (type_e reqtype); + string parse_float(); + void push_scope(); + data_buffer* parse_statement(); + void add_switch_case (data_buffer* b); + void check_toplevel(); + void check_not_toplevel(); + bool token_is (e_token a); + string token_string(); + string describe_position() const; + void write_to_file (string outfile); + + inline int get_num_events() const + { + return m_num_events; + } + + inline int get_num_states() const + { + return m_num_states; + } private: - lexer* m_lx; - object_writer* m_writer; + // The lexer we're using. + lexer* m_lx; + + // The main buffer - the contents of this is what we + // write to file after parsing is complete + data_buffer* m_main_buffer; + + // onenter buffer - the contents of the onenter{} block + // is buffered here and is merged further at the end of state + data_buffer* m_on_enter_buffer; + + // Mainloop buffer - the contents of the mainloop{} block + // is buffered here and is merged further at the end of state + data_buffer* m_main_loop_buffer; + + // Switch buffer - switch case data is recorded to this + // buffer initially, instead of into main buffer. + data_buffer* m_switch_buffer; + + int m_num_states; + int m_num_events; + parsermode_e m_current_mode; + string m_current_state; + bool m_state_spawn_defined; + bool m_got_main_loop; + int m_scope_cursor; + data_buffer* m_if_expression; + bool m_can_else; + list<undefined_label> m_undefined_labels; + list<constant_info> m_constants; - void parse_state_block(); - void parse_event_block(); - void parse_mainloop(); - void parse_on_enter_exit(); - void parse_variable_declaration(); - void parse_goto(); - void parse_if(); - void parse_else(); - void parse_while_block(); - void parse_for_block(); - void parse_do_block(); - void parse_switch_block(); - void parse_switch_case(); - void parse_switch_default(); - void parse_break(); - void parse_continue(); - void parse_block_end(); - void parse_const(); - void parse_label(); - void parse_eventdef(); - void parse_funcdef(); + // How many bytes have we written to file? + int m_num_written_bytes; + + // Scope data + // TODO: make a list + ScopeInfo m_scope_stack[MAX_SCOPE]; + + data_buffer* buffer(); + constant_info* find_constant (const string& tok); + void parse_state_block(); + void parse_event_block(); + void parse_mainloop(); + void parse_on_enter_exit(); + void parse_variable_declaration(); + void parse_goto(); + void parse_if(); + void parse_else(); + void parse_while_block(); + void parse_for_block(); + void parse_do_block(); + void parse_switch_block(); + void parse_switch_case(); + void parse_switch_default(); + void parse_break(); + void parse_continue(); + void parse_block_end(); + void parse_const(); + void parse_label(); + void parse_eventdef(); + void parse_funcdef(); + void write_member_buffers(); + void write_string_table(); }; constant_info* find_constant (const string& tok);
--- a/src/str.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/str.h Sun Jan 26 23:18:48 2014 +0200 @@ -82,9 +82,9 @@ bool starts_with (const string& other); string strip (list< char > unwanted); string substring (long a, long b = -1) const; - double to_double (bool* ok = null) const; - float to_float (bool* ok = null) const; - long to_long (bool* ok = null, int base = 10) const; + double to_double (bool* ok = nullptr) const; + float to_float (bool* ok = nullptr) const; + long to_long (bool* ok = nullptr, int base = 10) const; void trim (length_type n); string to_uppercase() const; @@ -173,11 +173,6 @@ m_string.insert (m_string.begin() + pos, c); } - inline length_type len() const - { - return length(); - } - inline length_type length() const { return m_string.length();
--- a/src/types.h Sun Jan 19 20:39:30 2014 +0200 +++ b/src/types.h Sun Jan 26 23:18:48 2014 +0200 @@ -31,9 +31,73 @@ #include <cstdlib> #include <stdexcept> +#include "str.h" static const std::nullptr_t null = nullptr; +// Byte datatype +typedef int32_t word; +typedef unsigned char byte; + +// Parser mode: where is the parser at? +enum parsermode_e +{ + MODE_TOPLEVEL, // at top level + MODE_EVENT, // inside event definition + MODE_MAINLOOP, // inside mainloop + MODE_ONENTER, // inside onenter + MODE_ONEXIT, // inside onexit +}; + +enum type_e +{ + TYPE_UNKNOWN = 0, + TYPE_VOID, + TYPE_INT, + TYPE_STRING, + TYPE_BOOL, +}; + +// Script mark and reference +struct byte_mark +{ + string name; + int pos; +}; + +struct mark_reference +{ + byte_mark* target; + int pos; +}; + +class script_error : public std::exception +{ + public: + script_error (const string& msg) : m_msg (msg) {} + + inline const char* what() const throw() + { + return m_msg.c_str(); + } + + private: + string m_msg; +}; + +// ==================================================================== +// Generic union +template <class T> union generic_union +{ + T as_t; + byte as_bytes[sizeof (T)]; + char as_chars[sizeof (T)]; + double as_double; + float as_float; + int as_int; + word as_word; +}; + template<class T> inline T abs (T a) { return (a >= 0) ? a : -a;
--- a/src/variables.cc Sun Jan 19 20:39:30 2014 +0200 +++ b/src/variables.cc Sun Jan 26 23:18:48 2014 +0200 @@ -29,7 +29,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "object_writer.h" #include "stringtable.h" #include "variables.h" #include "parser.h" @@ -50,9 +49,6 @@ if (find_command_by_name (name)) error ("name of variable-to-be `%s` conflicts with that of a command", name.chars()); - if (IsKeyword (name)) - error ("name of variable-to-be `%s` is a keyword", name.chars()); - if (g_GlobalVariables.size() >= g_max_global_vars) error ("too many global variables!");