Designfrage (viel Code)



  • Hi!

    Ich habe ein kleines Design für einen Codeparser entwickelt, und möchte nun
    gerne Euer Feedback darüber hören.
    Meine Ersten beiden Versuche waren eher bescheiden (parsen zwar gut, aber sehr stranger Code 😉 ) ..
    naja, und ich habe mir halt überlegt wie man soetwas verbessert und allgemeiner programmieren kann.
    Die Kernidee ist, dass mögliche Syntaxstrukturen mit baumartig aufgebauten Regeln erkannt werden.
    Nach "int foo" kann z.b. ein "=" folgen, ein "(", ";" oder ",", also vier verschiedene Möglichkeiten wies weiter gehen kann.
    In meinem Parser währen das 6 Rules, 2 mal beliebiger Text ("int" -> "foo"), und von der 2. Regel folgen 4 Verzweigungen. Dort wird weiter in die Unterregeln gegangen.

    Ich freue mich auf jedes Feedback 🙂
    Danke für Hilfe schonmal jetzt, ich kann mir vorstellen, dass das ein wenig viel Code auf einmal ist..

    //
    struct character
    {
    	char _c;	// what is saved
    	int _pos;	// where it is in the file
    
    	bool _comment;	// inside comment?
    	bool _string; 	// inside string?
    	bool _whitespace;// is whitespace?
    };
    
    namespace syntax
    {
    
    	// all syntax elements we are supposed to parse
    	enum ELEMENT
    	{ 
    
    		ANY_WORD, // one single word, ended by either a whitespace or a delimiter
    			  // word is somehow a token, =, ;, hello etc are examples
    
    		ANY_TEXT, // several words, ended by a userdefined character. if something
    		          // is ANY_TEXT, it contains parsable data.
    
    		ANY_BODY, // a function call or implementation body or something else.. starts with an
    			  // opening bracket of some kind. Might contain parsable variables
    
    		ANY_FUNCTION_MODIFIER, // const or nothrow. CAN THERE BE MORE?
    
    		ANY_OPERATOR, 	    // , ., ->, [], (), ++, --, !, ~,
    				    // new, new[], delete, delete [],
    				    // *, /, %, +, -, <<, >>, <, <=, >, >=,
    				    // ==, !=, &, ^, |, &&, ||, ?, =, +=
    				    // -=, *=, /=, %=, &=, ^=, , 
    
    		WHITESPACE	    // Asciicode < 33
    	};
    
    	// returns all kind of syntaxelements
    	// !does not throw anything!
    	class parser
    	{
    	public:
    
    		// load file into buffer and do a preparse!
    		bool loadFile (string filename);
    
    		// try to read the next word from file and return
    		// true if i could do it. does not advance in file!
    		bool readAnyword (string& word) const;
    
    		// reads into word until we find something to stop.
    		// most propably a ';' or '{'
    		bool readAnytext (string& word, char until) const;
    
    		// try to read const or nothrow
    		bool readFunctionModifier (string& word) const;
    
    		// readsAnyword -> return if in operators
    		bool readAnyoperator (string& word) const;
    
    		// skips to after { some implentation } or int stuff ( bla that )
    		bool eatBody (char opening, char closing);
    
    		// remove ' ' until we find a real character
    		void eatSomeWhitespaces ();
    
    		// add howMuch to our current reading position, return false
    		// if on end of file
    		// call with read word's size
    		bool advance (unsigned int howMuch);
    
    	private:
    
    		// convert whitespaces, set if comment/in string etc
    		void preparse (string& what);
    
    		// make    the      file 	look nice
    		void removeObsoleteWhitespaces ();
    
    		// where are we in the file?
    		unsigned int _readPos;
    
    		// first worked with by preparse
    		vector <character> _characters;
    
    	};
    
    	// this describes how the syntaxelements must be arranged to form something
    	// parsable. use this to define your language!
    	//
    	// A rule is said to be finished (and thus a variable or function or whatever
    	// was parsed using the rule) when _positives.size() == _negatives.size() == 0
    	// check _success to see if the rule was succesfull or not. 
    	// !does not throw anything!
    	class rule
    	{
    	public:
    
    		//
    		rule ();
    
    		//
    		void addPositive (const rule&);
    		void addNegative (const rule&);
    
    		// read&write access
    		rule& getPositive (int n)t;
    		rule& getNegative (int n);
    
    		//
    		void setType (TYPE);
    		void setDetail (string);
    
    		//
    		TYPE getType () const;
    		//
    		string getDetail () const;
    		// !!
    		bool wasSuccesfull () const;
    
    	private:
    
    		// expect what to read for this rule to apply
    		TYPE _what;
    
    		// if it's not only enough to read ANY_WORD but something 
    		// specific (like language keywords), set them here. they will
    		// be and'ed to the _what check
    		string _detail;
    
    		// true if this rules applies, false otherwise.
    		// check after rule is finished!
    		bool _succesful;
    
    		// all rules we check if the rule applies!
    		vector <rule> _positives;
    
    		// rules to check when this rule does not apply.
    		vector <rule> _negatives;
    
    	};
    
    	// just saves some rules
    	struct ruleset
    	{
    		//
    		void add (const rule&);
    
    		// develop your syntax from here!
    		vector <rule> _rules;
    	};
    
    	//
    	class parsetree
    	{
    	public:
    
    		// does all the action and puts the result in output
    		// (whatever that might be)
    		bool parseFile (string filename, SOMEOUTPUT& output,
    					ruleset& rules // parse any language  you want..
    				);
    
    	private:
    
    	};
    
    /*
    
    	Anwendungsbeispiel
    
       syntax::rule root, cur;
       syntax::ruleset set;
    
       // rule um folgendes zu erkennen:
       // int a = 3; 
       // int b;
       // class blub;
       // 
    
       // wenn er class  liest ist eine rule schon komplett
       cur.setType ( ANY_WORD );
       cur.setDetail ( "class " );
       set.add ( cur );
    
       Alternative rule die sonst ausgeführt wird:
       Zuerst hinzugefügte rules werden stärker beachtet als später hinzugefügte
    
       //
       root.setType ( ANY_WORD ); // kann nur mit nem wort starten hier
       |
       |
       |
       ------> cur.setType ( ANY_WORD ); // wenn root regel gilt, setze die neue cur regel
       |	   root.addPositive ( cur ); // und speichere sie als einzigen möglichen weg.
       |	   |
       |	   |	   cur.setType ( WANT_CHAR ); // regel für int a =  erstellen
       |	   |----   cur.setDetail ("=");	
       |	   |	   root.getPositive (0).addPositive ( cur ); // wenn variablen typ und name bekannt ist
       |	   | 					     // kann eine zuweisung erfolgen
       |	   |
       |	   |
       |	   | 	   cur.setType ( WANT_CHAR ); // regel für int a;
       |	   |----   cur.setDetail (";");
       |		   root.getPositive (0).addPositive ( cur ); // oder einfach nur ;
    
     set.add (root); // auch hinzufügen
    
       parseTree parse;
       if ( parse.parseFile ( "bla.hpp", OUTPUT, set ) ) 
        ...
    
    */
    

Anmelden zum Antworten