/* This program is distributed under the terms of the 'MIT license'. The text of this licence follows... Copyright (c) 2005 J.D.Medhurst (a.k.a. Tixy) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** @file @brief Test code for forth virtual machine. */ /** @defgroup forth_test Test - Test code for forth virtual machine @ingroup forth @{ */ #include "common.h" #include "..\forth.h" /** Path for source code location */ #define SOURCE_ROOT_PATH "forth\\" #include /** ForthIo implementation which uses C standard library console i/o functions. */ class StdForthIo : public ForthIo { public: inline StdForthIo() { NewLine[0]=2; NewLine[1]=13; NewLine[2]=10; } virtual void ConsoleOut(const CHAR* text,UCELL textLength) { while(textLength--) putch(*text++); } virtual CELL ConsoleIn() { return getch(); } }; #include #include #include /** Read a files contents into memory. The file is assumed to contain characters of 8 bit size but they are stored in memory as characters the same size as the Forth VM's \c CHAR. @param name The file name. @param address On success, this is the address of the memory cell containing the read contents. This memory must be freed when it is no longer required. @param size On success, this is the number of characters in the file. @return True, if the read was successful, false otherwise. */ bool ReadFile(const char* name,void*& address, unsigned& size) { FILE* f=fopen(name,"rb"); if(!f) return false; if(fseek(f,0,SEEK_END)) { fclose(f); return false; } size = ftell(f); if(fseek(f,0,SEEK_SET)) { fclose(f); return false; } address = malloc(size*sizeof(CHAR)); if(!address) { fclose(f); return false; } if(size!=fread(address,sizeof(uint8_t),size,f)) { free(address); fclose(f); return false; } fclose(f); CHAR* d=(CHAR*)((CELL)address+size*sizeof(CHAR)/sizeof(uint8_t)); uint8_t* s=(uint8_t*)((CELL)address+size*sizeof(uint8_t)); while((void*)d>(void*)s) *--d=*--s; return true; } #if BITS_PER_CHAR==16 /** Macro to include string constants in source which match the width of the \c CHAR type.*/ #define STRING(string) L##string /** Macro to include a line of text as an in-source constant .*/ #define LINE(string) L##string L"\n" #else /** Macro to include string constants in source which match the width of the \c CHAR type.*/ #define STRING(string) string /** Macro to include a line of text as an in-source constant .*/ #define LINE(string) string "\n" #endif /** Test Forth VM derivation to add file \c INCLUDE functionality. */ class ForthTest : public Forth { public: /** Load the contents of a file and \c EVALUATE it. @param fileName The name of the file. @return 0 or a forth execption value. */ CELL Include(const char* fileName); /** Load the contents of a file and \c EVALUATE it a line at a time. @param fileName The name of the file. @return 0 or a forth execption value. */ CELL IncludeLines(const char* fileName); }; CELL ForthTest::Include(const char* fileName) { void* file; unsigned size; if(ReadFile(fileName,file,size)) { CELL result = Evaluate((const CHAR*)file,size); free(file); return result; } else return -38; // non-existent file } CELL ForthTest::IncludeLines(const char* fileName) { static CELL XT_INTERPRET_LINES = 0; static const CHAR InterpretLines[] = { // Function which interprets source a line at a time... LINE("DECIMAL") LINE(":NONAME ( c-addr u -- )") LINE(" (source) 2! 0 >IN !") LINE(" SOURCE CHARS + >R") LINE(" BEGIN") LINE(" SOURCE DROP >IN @ CHARS +") LINE(" DUP R@ U<") LINE(" WHILE") LINE(" R@ OVER - 1 CHARS / (source) 2!") // LINE(" BASE @ HEX R@ . (source) 2@ CHARS + . CR BASE !") LINE(" 0 >IN ! 10 PARSE 2DROP >IN @ (source) ! 0 >IN !") // LINE(" SOURCE TYPE KEY DROP") LINE(" INTERPRET") LINE(" REPEAT") LINE(" R> 2DROP") LINE(";") }; if(!XT_INTERPRET_LINES) { // Create word to interpret a file a line at a time... CELL result = Evaluate(InterpretLines,sizeof(InterpretLines)/sizeof(CHAR)); if(result) return result; XT_INTERPRET_LINES = Pop(1)[0]; // Pop xt for INTERPRET-LINES } void* file; unsigned size; if(ReadFile(fileName,file,size)) { // Interpret file a line at a time... Push((CELL*)&file,1); Push((CELL*)&size,1); CELL result = Execute(XT_INTERPRET_LINES); free(file); return result; } else return -38; // non-existent file } /** Construct counted string arguments in the form (CHAR*)string,(CELL)stringLength @param string A quoted string, e.g. "A string" */ #define COUNTED_STRING(string) (CHAR*)STRING(string),(sizeof(string)-1) /** Top level test function. Call this to perform all tests. */ void TestForth() { // construct test VM... unsigned size = 1<<20; // 1Meg of RAM void* start = malloc(size); StdForthIo io; ForthTest* forth = (ForthTest*)Forth::Construct(start,size,&io); // test Push and Pop... CELL args[2]; args[0] = 888; forth->Push(args,1); args[0] = 999; forth->Push(args,1); args[0] = 11; args[1] = 123; forth->Push(args,2); const CELL* out; out=forth->Pop(0); // pop zero arguments, i.e. just get a peek at the stack ASSERT(out[0]==11) ASSERT(out[1]==123) ASSERT(out[2]==999) ASSERT(out[3]==888) ASSERT(!forth->Evaluate(COUNTED_STRING("- SWAP"))); // stack effect is: ( 888 999 123 11 -- 888 112 999 ) out=forth->Pop(0); ASSERT(out[0]==999) ASSERT(out[1]==112) ASSERT(out[2]==888) out=forth->Pop(2); ASSERT(out[0]==999) ASSERT(out[1]==112) out=forth->Pop(1); ASSERT(out[0]==888) // load additional forth source... ASSERT(0==forth->Include(SOURCE_ROOT_PATH "core-ext.f")); ASSERT(0==forth->Include(SOURCE_ROOT_PATH "search.f")); ASSERT(0==forth->Include(SOURCE_ROOT_PATH "programming-tools.f")); // ANS wordset tests... ASSERT(0==forth->Include(SOURCE_ROOT_PATH "test\\tester.f")); ASSERT(0==forth->IncludeLines(SOURCE_ROOT_PATH "test\\core.f")); ASSERT(0==forth->IncludeLines(SOURCE_ROOT_PATH "test\\core-ext.f")); ASSERT(0==forth->IncludeLines(SOURCE_ROOT_PATH "test\\search.f")); ASSERT(0==forth->IncludeLines(SOURCE_ROOT_PATH "test\\search-ext.f")); #if 0 // set true to test ARM assembler and disassembler... ASSERT(0==forth->Include(SOURCE_ROOT_PATH "arm-disasm.f")); ASSERT(0==forth->Include(SOURCE_ROOT_PATH "arm-asm.f")); ASSERT(0==forth->Include(SOURCE_ROOT_PATH "test\\arm-asm-disasm-test.f")); #endif // forth->Quit(); free(forth); } /** @} */ // End of group