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
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
#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();