Viikko vierähti ja valmista tuli. Seuraavaksi esittelen G-Scriptin, olkaa hyvä. Se on yksi luokka, joka lisää ohjelmaan scriptituen.
Pääperiaatteittain koodi toimii siten, että scriptikoodi parsitaan Parse()-funktiolla ja aliparsereilla sanoiksi(Word), jonka jälkeen suoritetaan sanat. Koodiin kannattaa lähteä tutustumaan "script.h":n kautta, sillä siellä se on nidottu loogiseksi kokonaisuudeksi. Jos ei halua tutustua koodiin, vaan ottaa sen suoraan käyttöön, kannattaa tutustua manual.txt:n sisältöön(löytyy alempaa ladattavasta paketista). Siellä on kerrottu tarkemmat tiedot ja ominaisuudet G-Scriptistä.
Koko paketin voi ladata tästä.
Ja kyllä, koodia saa käyttää vapaasti suoraan tai muokattuna omassa ohjelmassa, eikä tarvitse mainita alkuperäistä tekijää. Tietty olis kiva tietää, jos tätä käytetään jossain projektissa.
Nykyään sivulla näkyy vain script.h ja example.cpp. Script.cpp löytyy edelleen ladattavasta paketista(kts. yllä). Tämä koodin suuresta koosta johtuen.
Uusin versio 1.3
16.12.2009 — Päivitin vinkin, koska skriptitulkkiin oli tullut korjauksia ja parannuksia. Käytän tulkkia itse eräässä pitkään tekeillä olleessa roolipeliprojektissani ja siinä se on toiminut moitteitta.
script.h
// G-Script versio 1.3
//
#include <string>
#include <vector>
#ifndef script_h
#define script_h
using namespace std;
// Muuttujatyypit
const int VOID = -1; // Ei mikään
const int INT = 0; // Kokonaisluku
const int FLOAT = 1; // Liukuluku
const int STRING = 2; // Teksti
// Palautusarvot
const int ILLEGAL_PLACEMENT = -4; // Laiton sijoitus
const int UNDEFINED_OPERATOR = -3; // Tunnistamaton operaattori
const int DIVIDED_BY_ZERO = -2; // Jaettiin nollalla
const int FAILED = -1; // Epäonnistui
const int SUCCESS = 0; // Onnistui
struct Var {
string name; // Nimi
int type; // Tyyppi
int members; // alkioiden määri
void *address; // osoite
};
struct Func {
string name; // Nimi
void* (*address)(vector<void*> param); // Osoite
int type; // Palautusarvon tyyppi
};
struct Word {
int wtype; // Sanan tyyppi
int vtype; // Arvon tai operaatorin tyyppi
void *value; // Arvon osoitin
string name; // Nimi
};
struct Segment {
bool run; // Suoritetaanko lohko
int type; // Tyyppi
vector<Word> condition; // Ehto
int wstart; // silmukan alku
};
class Script {
vector<Var> var; // Muuttujat
vector<Func> func; // Funktiot
vector<Segment> segment; // Lohkopino
// Parserit
int Parse(string sentence, int cstart); // Parsii lauseen
int ExeWords(vector<Word>& word); // Suorittaa sanat
// Laskuoperaatiot
Word CalculateClause(vector<Word>& word, int start, int len); // Laskee lausekkeen
Word CalculateTwoValue(Word a, Word oper, Word b); // Laskee kaksi lukua
// Aliparserit
Word Define(string word, int start, int len); // Muuttujamäärittely
Word Value(string word, int start, int len); // Arvo
Word String(string owrd, int start, int len); // Teksti
Word Variable(string word, int start, int len); // Muuttuja
Word Function(string word, int start, int len); // Funktio
Word Operator(string word, int *start); // Mikä operaattori
int Segments(string word, int start, int len); // If/while
string Script::trim(string& text, int start, int length = -1); // Välilyöntien poisto
// Muistin vapauttajat
void FreeWords(vector<Word>& word); // Vapauttaa sanojen sisäisen muistin
void FreeWord(Word& word); // Vapauttaa sanan sisäisen muistin
void FreeString(void*& pointer); // Vapauttaa string-muuttujan
public:
Script(); // Constructori
~Script(); // Destructori
int RegVar(string name, int type, int members, void *address); // Registeröi muuttujan
int RegFunc(string name, int type, void* (*address)(vector<void*> param)); // Registeröi funktion
int Execute(string script, int *error); // Suorittaa scriptin
};
#endifexample.cpp
#include <iostream>
#include "script.h"
void* Print(vector<void*> param);
int main() {
Script script;
string command;
int *err;
// Scriptistä käsiteltävät muuttujat
string name;
int age;
// Registeröidään muuttujat
script.RegVar("name", STRING, 1, &name);
script.RegVar("age", INT, 1, &age);
// Registeröidään funktio
script.RegFunc("Print", STRING, Print);
// Määritetään suoritettava scripti
command = "name = \"Matti\"; age = 15; Print(\"Terve \" + name + \", \" + age + \"v!\");";
// Suoritetaan scripti
if(script.Execute(command, err) > 0)
std::cout << "ERROR: " << *err << std::endl;
else
std::cout << "SUCCESS" << std::endl;
// Korvaa jollain, jos kiinnostaa
system("pause");
return 0;
}
// Tulostus funktio
void* Print(vector<void*> param) {
std::string *str = (std::string*)param.at(0); // Luodaan osoitin ensimmäiseen parametriin
std::cout << str->c_str() << std::endl; // Tulostetaan parametrin arvo
}Näyttää todella hyvältä, varmasti on minulle ainakin tulevaisuudessa käyttöä. Valitettavasti olen vielä sen verran aloitteleva C++ kanssa että en osaa koodin kannalta minkäänlaista komenttia oikein antaa, muuta kuin että siististi on kirjoitettu ja hyvältä näyttää. :}
EDIT: Millähän kääntäjällä tämä on kääntynyt oikein? Oma mingw32 herjaa "This application has requested the Runtime to terminate it in an unusual way. Please contact the apllication's support team for more information.".
Aika hyvältä näyttää. Tämä ei taida olla enää ohjelmoinnin alkeet kurssilta.
Kirjotusvirheitähän löytyy (tosin varmaan pienin ongelmista)
DIVINED_BY_ZERO --> Jumaloitu zerolla manual.txt:ssä on registeröityä
Makeelta toi kyl näyttää :D Melkeempä Luakin jää tälle kakkoseksi :D
temu92 kirjoitti:
DIVINED_BY_ZERO --> Jumaloitu zerolla
Eikös se olisi pikemminkin "ennustettu nollalla" tai vastaavaa?
Koodipätkä näyttää siistiltä, täytyy pitää mielessä jos/kun sattuu itse tarvimaan. Aina plussaa kun ei tarvitse alkaa itse väsäämään :P
matpit kirjoitti:
EDIT: Millähän kääntäjällä tämä on kääntynyt oikein? Oma mingw32 herjaa "This application has requested the Runtime to terminate it in an unusual way. Please contact the apllication's support team for more information.".
www.bloodshed.net kirjoitti:
Dev-C++ version 4.9.9.2, includes full Mingw compiler system with GCC 3.4.2 and GDB 5.2.1
Tollasella paketilla ei tuu mitään herjauksia. Lisäksi olen testaillu koodia aika lailla, enkä ole havainnut siinä virheitä pitkään aikaan. Virheet ovat löytyneet skriptin puolelta.
Edit: DIVINED -> DIVIDED :)
Parseri on toteutettu huonosti.
name = "Matti"; age = 15; Print("Terve " + name + "; " + age + "v!");Kun suoritan koodin samalla tavalla kuin esimerkissä, ohjelma tulostaa:
Terve Matti Muistialueen ylitys
Tämä johtuu siitä, että tulkki ajattelee, että merkkijonon sisällä oleva puolipiste päättää lauseen. Mitään virhettä ei myöskään synny siitä, että funktiokutsulta puuttuu toinen sulku.
Tulkki käyttää lekseriä (joka on hämäävästi funktiossa Parse), mutta vain laskulausekkeisiin. Ehdot, silmukat, puolipisteet, yms parsitaan yksinkertaisesti etsimällä merkkejä ja vertaamalla merkkijonoja.
Myöskin seuraava koodi aiheuttaa virheen samasta syystä.
name = "Matti"; age = 15; Print("Terve " + name + "{ " + age + "v!");Aihe on jo aika vanha, joten et voi enää vastata siihen.