Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Yksinkertainen tulkki

Sivun loppuun

Hipo [16.10.2002 18:58:14]

#

Joops, väsäsin tommosen yksinkertasen tulkin yksinkertaselle kielelle. Koodia on aika runsaasti, mutta mukana onkin lausekkeen laskija laskujärjestyksineen, muuttujat ja joitakin sisäänrakennettuja funktioita. Muuttujien tyyppejä on numero, stringi. Esimerkeistä varmaan näkee kielen syntaksin.

tulkki.cpp

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <ctype.h>
#include <stdlib.h>

/* Tyypit */

class Any {
public:
  enum AnyType {
    TypeNumber,
    TypeString,
    TypeName,
    TypeVector,
    TypeOperator
  } type;

  Any(AnyType t) { type = t; }
  virtual ~Any() {}
  virtual Any *clone() { return 0; }
};

class Number : public Any {
public:
  Number(double n) : Any(TypeNumber) { number = n; }
  ~Number() {}
  Any *clone() { return new Number(number); }
  double number;
};

class String : public Any {
public:
  String(const string &s) : Any(TypeString) { str = s; }
  ~String() {}
  Any *clone() { return new String(str); }
  string str;
};

class Name : public Any {
public:
  Name(const string &n) : Any(TypeName) { name = n; }
  ~Name() {}
  string name;
};

class Vector : public Any {
public:
  Vector() : Any(TypeVector) {}
  ~Vector() {}
  Any *clone() {
    Vector *v = new Vector();
    for(int i=0; i<(int) vec.size(); i++) vec.push_back(vec[i]->clone());
    return v;
  }
  vector<Any *> vec;
};

class Operator : public Any {
public:
  Operator(const string &o) : Any(TypeOperator) { oper = o; }
  ~Operator() {}
  string oper;
};

/* Parserit */

Vector *parse_vector(char *b, char **end=0);

const string operchars = "*/+-<>=!";
const string groupedopers[] = { "*", "/", "",
                                "+", "-", "",
                                "<", ">", "==", ">=", "<=", "!=", "",
                                "=", "", "" };

Any *parse(char *b, char **end) {
  static bool lastwasoper = false;

  while(*b && (isspace(*b) || *b == '#')) {
    if(*b == '#') {
      while(*b != '\n' && *b) b++;
      continue;
    }
    b++;
  }
  *end = b;
  if(!*b) return 0;

  string s;

  if(isdigit(*b) || (lastwasoper && (*b=='+' || *b=='-'))) {
    return new Number(strtod(b, end));
  }
  lastwasoper = false;

  if(*b == '"') {
    while((*b) && (*(++b) != '"')) {
      if(*b == '\\') {
        b++;
        if(*b == 'n') s += '\n';
        else s += *b;
      } else s += *b;
    }
    *end = b+1;
    return new String(s);
  }

  if(isalpha(*b)) {
    while(*b && isalpha(*b)) {
      s += *(b++);
    }
    *end = b;
    return new Name(s);
  }

  if(*b == ',') {
    *end = b+1;
    return new Operator(string(","));
  }

  if(operchars.find(*b) != string::npos) {
    while(operchars.find(*b) != string::npos) s += *(b++);
    *end = b;
    lastwasoper = true;
    return new Operator(s);
  }

  if(*b == '(') return parse_vector(b+1, end);

  if(*b == ')') return 0;

  throw string("Hassu merkki: ") + *b;
}

Vector *parse_vector(char *b, char **end=0) {
  Vector *v = new Vector();

  while(*b) {
    Any *a = parse(b, &b);
    if(!a) break;
    v->vec.push_back(a);
  }

  if(end) *end = b+1;
  return v;
}

/* Ympäristö / muuttujat */

class Variable {
public:
  Variable(string &n, Any *a) { name = n; var = a; };
  string name;
  Any *var;
};

vector<Variable> variables;

Any *getVariable(Name *n) {
  for(int i=0; i<(int) variables.size(); i++) {
    if(variables[i].name == n->name) return variables[i].var;
  }
  throw string("Muuttujaa ei määritelty: ") + n->name;
}

void setVariable(Name *n, Any *a) {
  for(int i=0; i<(int) variables.size(); i++) {
    if(variables[i].name == n->name) {
      variables[i].var = a->clone();
      return;
    }
  }
  variables.push_back(Variable(n->name, a->clone()));
}

/* Ajo */

Any *interpret(Any *a);

void print(Any *any) {
  switch(any->type) {
    case Any::TypeNumber: cout << ((Number *) any)->number; break;
    case Any::TypeString: cout << ((String *) any)->str; break;
    case Any::TypeName: print(getVariable((Name *) any)); break;
    case Any::TypeVector: {
      for(int i=0; i<(int) ((Vector *) any)->vec.size(); i++)
        print(((Vector *) any)->vec[i]);
      } break;
    case Any::TypeOperator: cout << ((Operator *) any)->oper; break;
  }
}

Any *function(Name *n, Vector *args) {
  if(n->name == "if") {
    Any *c = interpret(args->vec[0]);
    if(c->type != Any::TypeNumber) throw string("if tahtoo numeron");

    if(((Number *) c)->number) {
      if(args->vec.size() >= 2) return args->vec[1];
    } else if(args->vec.size() >= 3) return args->vec[2];
    return 0;
  }

  if(n->name == "while") {
    while(1) {
      Any *c = interpret(args->vec[0]);
      if(c->type != Any::TypeNumber) throw string("while tahtoo numeron");
      if(!((Number *) c)->number) break;

      interpret(args->vec[1]);
    }
    return 0;
  }

  Any *a = interpret(args);

  if(n->name == "rand")
    return new Number(rand() / (double) RAND_MAX);

  if(n->name == "int")
    return new Number((int) ((Number *) a)->number);

  if(n->name == "print") {
    print(a);
    return 0;
  }

  if(n->name == "readnumber") {
    double n;
    cin >> n;
    return new Number(n);
  }

  if(n->name == "readstring") {
    string s;
    cin >> s;
    return new String(s);
  }

  throw string("Hassun niminen funktio") + n->name;
}

void calculateExpression(vector<Any *> &operands, vector<string *> &operators) {
  Name *let = 0;
  if(operators.size() >= 1) {
    if(*operators[0] == "=") {
      if(operands[0]->type != Any::TypeName) throw string("= tahtoo nimen");
      let = (Name *) operands[0];
      operands.erase(operands.begin());
      operators.erase(operators.begin());
    }
  }

  int i;
  for(i=0; i<(int) operands.size(); i++) {
    switch(operands[i]->type) {
      case Any::TypeName: operands[i] = getVariable((Name *) operands[i]); break;
      case Any::TypeVector: operands[i]= interpret((Vector *) operands[i]); break;
      default:;
    }
  }

  int s = 0;
  while(groupedopers[s] != "") {
    for(i=0; i<(int) operators.size(); i++) {
      for(int j=s; groupedopers[j] != ""; j++) {
        if(groupedopers[j] == *operators[i]) {
          if(operands[i]->type != operands[i+1]->type) throw string("Tyyppivirhe");

          Any *r = 0;

          switch(operands[i]->type) {
            case Any::TypeNumber: {
              double a = ((Number *) operands[i])->number,
                     b = ((Number *) operands[i+1])->number;

              if(*operators[i] == "+") r = new Number(a + b);
              if(*operators[i] == "-") r = new Number(a - b);
              if(*operators[i] == "*") r = new Number(a * b);
              if(*operators[i] == "/") r = new Number(a / b);
              if(*operators[i] == "<") r = new Number(a < b);
              if(*operators[i] == ">") r = new Number(a > b);
              if(*operators[i] == "==") r = new Number(a == b);
              if(*operators[i] == "<=") r = new Number(a <= b);
              if(*operators[i] == ">=") r = new Number(a >= b);
              if(*operators[i] == "!=") r = new Number(a != b);
            }
            break;

            case Any::TypeString:
              if(*operators[i] == "==")
                r = new Number(((String *) operands[i])->str == ((String *) operands[i+1])->str);
              if(*operators[i] == "+")
                r = new String(((String *) operands[i])->str + ((String *) operands[i+1])->str);

            default:;
          }

          if(!r) throw string("Hassu operaattori tyypille");

          operands.erase(operands.begin()+i, operands.begin()+i+2);
          operators.erase(operators.begin()+i);
          operands.insert(operands.begin()+i, r);

          i--;
          break;
        }
      }
    }

    while(groupedopers[s++] != "");
  }

  if(let) {
    setVariable(let, operands[0]);
    operands.erase(operands.begin());
  }
}

Any *interpret(Any *a) {
  if(a->type != Any::TypeVector) return a;
  Vector *v = (Vector *) a;

  Vector *ret = new Vector();

  vector<Any *>::iterator i;
  for(i=v->vec.begin(); i<v->vec.end();) {
    vector<Any *> operands;
    vector<string *> operators;

    bool readoperand = true;

    for(; i<v->vec.end(); i++) {
      if(readoperand) { // Operandi
        if(i+1 < v->vec.end()) {
          if((*i)->type==Any::TypeName && (*(i+1))->type==Any::TypeVector) {
            Any *r = function((Name *) (*i), (Vector *) *(i+1));
            if(r) operands.push_back(r);
            i++;
          } else operands.push_back(*i);
        } else operands.push_back(*i);
      } else { // Operi
        if((*i)->type == Any::TypeOperator) {
          if(((Operator *) *i)->oper == ",") { i++; break; }
          operators.push_back(&((Operator *) *i)->oper);
        } else throw string("Operaattori da?");
      }
      readoperand = readoperand ? false : true;
    }

    calculateExpression(operands, operators);
    if(operands.size()) ret->vec.push_back(operands[0]);
  }

  if(ret->vec.size() == 0) return 0;
  if(ret->vec.size() == 1) return ret->vec[0];
  return ret;
}

int main(int argc, char *argv[]) {
  try {
    if(argc < 2) throw string("Käyttö: tulkki <file>");

    ifstream f(argv[1]);
    if(!f) throw string("Enpä voi avata tiedostoa.");

    string s;
    while(f.peek() != -1) s += f.get();
    f.close();

    interpret(parse_vector(strdup(s.c_str())));
  } catch(string &s) {
    cerr << "Exception: " << s << endl;
    return 1;
  }
  return 0;
}

laskupeli

oikein = 0,
vaarin = 0,

while ((oikein < 5) (
  print (oikein, " oikein ja ", vaarin, " väärin.\n"),  # Tilastoja

  x = int(rand() * 10),   # Arvotaan luvut
  y = int(rand() * 10),
  print (x, " + ", y, " = "),

  if ( (readnumber() == x+y)       # Vertailu
       (oikein = oikein + 1,       # Tosi
        print ("Oikein.\n") )
       (vaarin = vaarin + 1,       # Epätosi
        print ("Väärin.\n") )
     )
)),

print ("Da.\n")

nimi

print ("Antaisitko nimesi: "),  # Kommentti
print ("No moi " + readstring() + ".\n")

progo [17.10.2002 10:58:11]

#

No tohan näyttäisi ihan siltä, että tätä yritetään tosissaan. :) Ihan oikeasti! Vielä kun lisäät tuen luokille/malleille/muuttujien ylikuormitukselle/windows-tuen/ja siihen samaan myös DirectX-tuki :D

Menisipäs pitkään, mutta sitten voisit julkaista kielen vaikka nimellä D. =D
Heh.. eipäs olekaan ollut hauskaa näin pitkään aikaan.. Heh

thefox [17.10.2002 13:16:35]

#

Mitään luokkia tai malleja tuollaisiin.. Helmi tulkki, hyvää työtä ;)

Gevil [17.10.2002 13:26:11]

#

1337-C0D3 :D

Antti Laaksonen [17.10.2002 14:23:20]

#

Mielenkiintoista, mielenkiintoista. Tuolla kielellä voi jo oikeasti tehdä jotain. Pitänee vastata tuohon omalla QBasic-pohjaisella ohjelmointikielellä ;)

(nimetön) [02.11.2002 01:49:58]

#

Jaha omaa ohjelmointikieltäkin tekemässä :).. Hieno systeemi!

Teme [26.01.2003 21:17:49]

#

Hieno ja hyvä tulkki...!

nomic [16.07.2003 03:29:31]

#

saisiko tota mistään ihan erillisenä ohjelmana kun oon systerillä ja täällä ei ole mitää muuta kuin QB :)
no onhan QB:kin mukava mutta kun oon alkanu taas käyttää borlandin tuotteita ja niitä ei täällä systeril oo niin ajattelin jos jostain löytyisi tämä erillisenä ohjelmana
ja voisin sitten kommentoida tätä
ja kertoo että mitä pitäisi lisäillä ja mitä ei jne...
mutta vaikuttaa mukavalta...

Heikki [04.05.2004 21:33:33]

#

Aika laiskasti kommentoitu, muuten hienon näköinen viritys.

GorkkiusSuuri [13.01.2005 18:35:51]

#

Aika hyvä. iostreamia ei ois tarttenu pistää koska fstream sisältää sen.

aWW [30.01.2005 20:14:41]

#

Tulee tosi monta virhettä...

teppu [21.06.2005 14:24:38]

#

Ei toimi.

Deewiant [13.07.2005 16:09:18]

#

Jeh, using namespace std puuttuu.

Ja miksi parse_vector-funktion määrittelyssä on char **end=0? Se =0 annetaan deklaraatiossa - jossa se jo on. Turhat kaksi kirjainta jotka aiheuttavat virheen.

Megant92 [15.07.2005 15:02:40]

#

Aivan mahtava.Tuosta on apua minulle :)

lainaus:

Mielenkiintoista, mielenkiintoista. Tuolla kielellä voi jo oikeasti tehdä jotain. Pitänee vastata tuohon omalla QBasic-pohjaisella ohjelmointikielellä ;)

Tulkilla tulkki jolla silläkin voidaan tehdä tulkki ;)

ville-v [11.09.2005 20:17:16]

#

Parannusehdotuksia:

-Aaltosulut lohkoihin tavallisten sulkujen tilalle
-Puolipisteet rivin loppuun
-else
-goto

zorm [19.01.2006 16:17:24]

#

Olisi kyllä kiva jos tuon saisi hyvällä kommentoinnilla.

Grandi [07.07.2008 12:15:27]

#

ville-v: Voihan ne itsekkin tehdä ;) Eikös koodivinkit ole sitä varten, että niitä voi muokkailla?

Ja itse tulkki on todella hieno. Eipä itseltäni tuollainen onnistuisi, vaikka jotain tuollaista yritän itsekkin väsätä.

fergusq [24.06.2013 20:16:48]

#

Koodissa on muutamia ongelmia:

  1. Any-tyypin käyttäminen sekä parsitun koodin että tulkkauksenaikaisten muuttujien ja muiden arvojen säilömiseen on harhaanjohtavaa. Parserin tulisi käyttää puunsa säilömiseen omaa luokkaansa ja tulkin omaa luokkaansa.
  2. Jotkut selvästi syntaksiin kuuluvat elementit kuten =-operaattori käsitellään kokonaan tulkin puolella. Tämä ei vaikuta ohjelman toimintaan, mutta tulkki ja parseri on hyvä pitää erossa toisistaan selkeyden vuoksi.

Muokkaus:

Lisäksi:


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta