OpenGL game
Vlastní skriptovací jazyk

Zadání hodnot skriptu příkazem z konzole, nebo načtěním hodnot ze souboru.

zdroják ke stažení (6kB)

24.03.2006

Nová verze skriptovacího jazyka vypíše do konzole toto:

***TALK SCRIPT***
I am talking.
Hello World

***VARIABLE STATES***
0: 11
1: 11
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0

Oproti předchozímu skriptu se nový skript nijak nemění, jen vstupní hodnoty se zadávají do konzole, nebo se načtou ze souboru. Vstupní hodnoty uložíme do souboru
sample.kscr

talk
print 72 101 108 108 111 32 87 111 114 108 100
set 0 12
set 1 10
dec 0
inc 1
end

Nyní si vytvoříme list příkazů

string strCodeList[K_NUM_CODES] =
{
	"talk",
	"print",
	"set",
	"inc",
	"dec",
	"add",
	"end"
};

A uložíme do třídy
CScanner

CScanner kScanner(strCodeList, K_NUM_CODES);

Třída vypadá takto

typedef std::vector<char> TCharArray;
typedef std::vector<std::string> TStringArray;

class CScanner
{
	TStringArray m_tCodeNameArray;
	TCharArray m_tTokArray;
	size_t m_tOffset;
public:
	CScanner(const TStringArray& _rtCodeNames) : m_tCodeNameArray(_rtCodeNames)   {}
	CScanner(const std::string* _pCodeNameArray, size_t _tNumNames)
	{
		m_tCodeNameArray.resize(_tNumNames);
		const std::string* pStrArray = _pCodeNameArray+_tNumNames;
		std::copy(_pCodeNameArray, pStrArray, m_tCodeNameArray.begin());
	}
	///\brief Tokenize a line and return success
	bool ScanLine(const std::string& _rstrLine);
	///\brief Get tokens from most recent scan
	const TCharArray& GetTokens() const
	{ 
		return m_tTokArray; 
	}  
private:
	///\brief Skipping space in string
	///\param _rstrLine Reference to string line
	///\retval bool True if not empty string
	bool SkipSpacing(const std::string& _rLine)
	{
		while (isspace(_rLine.c_str()[m_tOffset]))
		{
			++m_tOffset;
		}
		if (_rLine.c_str()[m_tOffset] == 0)
		{
			return false;
		}
		return true;
	}
	///\brief Scan alphabetic string
	///\param _rstrLine Reference to string line
	///\retval bool True if string line is found in code
	bool ScanCode(const std::string& _rLine)
	{
		size_t tBegin = m_tOffset;
		// Test alphabetic string
		while (isalpha(_rLine.c_str()[m_tOffset]))
		{
			++m_tOffset;
		}
		return FindCode(std::string(_rLine, tBegin, m_tOffset-tBegin));
	}
	///\brief Scan numeric string
	///\param _rstrLine Reference to string line
	///\retval bool True if string line is found in code
	bool ScanNum(const std::string&_rLine)
	{
		size_t tBegin = m_tOffset;
		// Test numeric string
		while (isdigit(_rLine.c_str()[m_tOffset]))
		{
			++m_tOffset;
		}
		// Were any digits scanned?
		if (m_tOffset == tBegin)
		{
			return false;
		}
		std::string strNumber(_rLine, tBegin, m_tOffset-tBegin);
		m_tTokArray.push_back(static_cast<char>(atoi(strNumber.c_str())));
		return true;
	}
	///\brief Find string in code
	///\param _rstr Reference to search string
	///\retval bool True if string is found in code
	bool FindCode(const std::string& _rStr)
	{
		char cCodeVal;
		TStringArray::iterator it = m_tCodeNameArray.begin();
		for (cCodeVal = 0; it != m_tCodeNameArray.end(); ++it, ++cCodeVal)
		{
			if (_rStr == *it)
			{
				m_tTokArray.push_back(cCodeVal);
				return true;
			}
		}
		return false;
	}
};

Zadání hodnot skriptu příkazem z konzole


vector<CInstruction> vInstrList;
// Simulate some external data
char strBuffer[K_BUFFER_SIZE];

#ifdef K_LOAD_FROM_CONSOLE
string strInput;
// Begin reading text lines from user	
cout << "Input script instructions:" << endl;
while (strInput != "end")
{
	cin.getline(strBuffer, K_BUFFER_SIZE);
	// May as well use the string
	strInput = strBuffer; 
	if (kScanner.ScanLine(strInput))
	{
		const TCharArray& tTokens = kScanner.GetTokens();
		EScriptOpCode eCode = static_cast<EScriptOpCode>(tTokens[0]);
		vInstrList.push_back(CInstruction(eCode, &tTokens[1], tTokens.size()-1));
	}
	else
	{
		cout << "Invalid Instruction!" << endl;
	}
	}
	// as a safety precaution, it couldn't hurt to have redundant ends
	vInstrList.push_back(CInstruction(K_OP_END));
	
	// obtain a variable count
	size_t tNumVars;
	cout << "Input required number of variables: ";
	cin >> tNumVars;
#endif

Prochází vstupní hodnoty, dokud nenarazí na hodnotu
end

while (strInput != "end")
{
	...
}

V každém cyklu nás konzole vyzve pro zapis hodnot

cin.getline(strBuffer, K_BUFFER_SIZE);

Zadanou hodnotu vyhodnotí funkce ScanLine

strInput = strBuffer; 
if (kScanner.ScanLine(strInput))
{
	...
}

Funkce vypadá takto:


///\brief Scanning script line
///\param _rstrLine Reference to string line
///\retval bool True if string line is scanned
bool CScanner::ScanLine(const std::string& _rLine)
{
    // Reset offset and token buffer
    m_tOffset = 0;
    m_tTokArray.clear();

    // Check for an empty line
    if (_rLine.empty())
        return false;

    // Check for valid line content
    if (!SkipSpacing(_rLine))
        return false;

    // Check for a valid opcode
    if (!ScanCode(_rLine))
        return false;

    size_t tLen = _rLine.length();
	// Scan args until the end of line
    while (m_tOffset < tLen)   
    {
        // Get to next arg
		if (!SkipSpacing(_rLine)) 
            return true;        // Unless we're done
        if (!ScanNum(_rLine))
            return false;
    }

    return true;
}

Funkce čte řádek a výsledky ukládá do pole řetězců

TCharArray m_tTokArray;

První index
0
obsahuje příkaz
druhý index
1
obsahuje hodnoty

Vytvoříme novou instrukci do které uložíme načtený příkaz a hodnoty

EScriptOpCode eCode = static_cast<EScriptOpCode>(tTokens[0]);
vInstrList.push_back(CInstruction(eCode, &tTokens[1], tTokens.size()-1));

Načtění hodnot ze souboru


#ifdef K_LOAD_FROM_FILE
	cout << "Enter path/name of script file: ";
	cin.getline(strBuffer, K_BUFFER_SIZE);
	
	// Attempt to open the file
	CTextReaderFile kFile(strBuffer);
	if (kFile.Bad())
	{
		cout << "Could not open file!" << endl;
		return;
	}
	
	// Read in file data
	string strFileData;
	strFileData.resize(kFile.Length());
	kFile.Read(strFileData, strFileData.length());
	
	size_t tBegin = 0, tEnd = 0, tNumLines = 1, tNumVars = K_NUM_VARIABLES;
    // Feed data into scanner and build instructions
	while (tEnd != string::npos)
	{
		// Grab a line from the file data
		tEnd = strFileData.find_first_of("\n", tBegin);
		string strLine(strFileData, tBegin, tEnd-tBegin);
		tBegin = tEnd+1;  // Move past '\n' character
		
		// Scan the line
		if (kScanner.ScanLine(strLine))
		{
			const TCharArray& tTokens = kScanner.GetTokens();
			EScriptOpCode eCode = static_cast<EScriptOpCode>(tTokens[0]);
			vInstrList.push_back(CInstruction(eCode, &tTokens[1], tTokens.size()-1));
		}
		else
		{
			cout << "Invalid Instruction!" << endl;
			cout << "Line number: " << tNumLines << endl;
			cout << "Line: " << strLine << endl;
		}
		++tNumLines;
	}
	
	// as a safety precaution, it couldn't hurt to have redundant ends
	vInstrList.push_back(CInstruction(K_OP_END));
#endif

Nové instrukce se vytváří stejným způsobem, data se však načítají ze souboru. Konzole nás vyzve, abychom zadali cestu a jméno tohoto souboru.

Nakonec skript načteme a spustíme

CVirtualMachine kVM;
CScript kScript(vInstrList, tNumVars);

// Load the scripts and save the id
size_t tScriptId = kVM.LoadScript(kScript);

// Execute each script by its id
cout << "***TALK SCRIPT***" << endl;
kVM.ExecuteScript(tScriptId);
cout <<"\n***VARIABLE STATES***" << endl;
// Check out the variable states
kVM.ShowVariableState();



home / opengl game


Valid XHTML 1.0 Transitional