🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Angelscript Parser

Started by
8 comments, last by GameDev.net 19 years ago
Does the parser keep in memory anywhere the parse tree? The reason is I would like to attempt (emphasis on attempt) to extract information from a script regarding object names, function names, global variables, local varibles, member functions and properties in order to provide something like intellisense and better syntax highlighting. Oh, and I would like to have have to write my own parser because its already been written by WL.
Advertisement
He uses a recursive descent parser that, if I'm not totally mistaken, spits out byte code directly with no intermediate parse tree. My preprocessor has an intermediate token list, but it's not a parse tree either.

However, you can still get intellisense in most IDEs! Just name your scripts .c or .cpp, and stick them in a project. Don't ever try to build this project of course. That's all you need to get syntax hilighting and auto-formatting. Now, create a dummy header that contains all the classes and functions you have bound to angelscript. Remember, you don't need to define anything - just declare it. You also don't need to declare any operators. Include this header from your script, and you have intellisense and autocomplete. This works in VS - should work anywhere else too.
Since I assume you are using my preprocessor - well, I'm working on another feature that cropped up; I need to be able to specify things that should be defined, things like 'DEBUG' in MSVC, that will allow you to do something like define 'COMPILING_IN_ANGELSCRIPT' and conditionally compile out all the dummy declarations. In the mean time, just remove the include from the file - or edit the dummy header - before running it through the preprocessor.

Here's my dummy header. Note that I do have the app-specified defines working, but right now it's an ugly hack and part of my app, not the preprocessor.
#ifndef JM_SCRIPT_VS_INTELLISENSE_H#define JM_SCRIPT_VS_INTELLISENSE_H/*	Include this file to make intellisense work inside visual studio.	This file preprocesses to a BLANK FILE when compiling inside angelscript.	This is a complete list of every type and function bound to angelscript.*/#ifndef COMPILING_ANGELSCRIPTclass string{public:	unsigned int length() {}};class Object {}class ParameterIterator {}void Print(const string&);string SubString(const string&,int,int);int FindFirstOf(const string&,const string&);string PopFirstWord(string&);string Replace(const string&,const string&,const string&);string CaselessStringEqual(const string&,const string&);string ToUpper(const string&);bool HasKey(Object, const string&);string GetValue(Object, const string&);void SetValue(Object, const string&, const string&);void RemoveKey(Object, const string&);int ContentsSize(Object);string GetContents(Object, int);bool IsGood(Object);void Send(Object, const string&);string GetName(Object);bool IsConnected(Object);bool AreEqual(Object,Object);string Location(Object);void Advance(ParameterIterator&);string Key(ParameterIterator&);string Value(ParameterIterator&);ParameterIterator ParmBegin(Object);ParameterIterator ParmEnd(Object);bool GetObject(Object&, const string&);bool CloneObect(const Object,Object&,const string&);bool DoesObjectExist(const string&);bool CreateObject(Object&,const string&);bool DestroyObject(Object&);bool InstantiateObject(const Object,Object&);bool HandOffConnection(Object,Object);void SaveDatabase();bool DisconnectObject(Object);void MoveObject(Object, const string&);int GetConnectionCount();Object GetConnection(int);void AsyncCallScript(Object,const string&,const string&,Object,const string&,uint32);void DiscardScript(const string&);void SHUTDOWN();Object GetActiveObject();#endif //end ifndef COMPILING_ANGELSCRIPT#endif
Actually, the asCParser does indeed build a parse tree, where each node is a asCScriptNode. This is needed because the compiler is a multi-pass compiler.

If you wish to base your parser upon mine, that's fine with me. I cannot give you any documentation on how it works though. You're welcome to ask me, but I'd like to ask you to study the code first.

Also, I change the way the parser works as needed, so it would be a good idea for you to not rely on it to always be compatible with earlier versions. I suggest you take what is implemented now and break it out into a separate module that you can use for your editor.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Deyja:

My application acts as the IDE for its users and is geared towards non-programmers so expecting someone to have access to other IDE's is not really feasible.


WL:

I'm just doing some initial research on what exactly is needed in order to do the things I listed. As such, the plans for the features I want to implement are going to be at least a month or two away. I have other features I am working on implementing 1st. I think your parser will be what I need to get started though.
I had the same needs. I have took a different approach : using Scintilla that already have a parser for C/C++ .
I have next make a new descendant class based on asIScriptEngine that overload all the methods needed to get informations about the registered func/vars

class CAsScriptEngineWrapper : public asIScriptEngine{protected :	asIScriptEngine *m_pAsIScriptEngine;public :	CAsScriptEngineWrapper() : m_pAsIScriptEngine(NULL) {};	CAsScriptEngineWrapper(asIScriptEngine *in_pAsIScriptEngine) : m_pAsIScriptEngine(in_pAsIScriptEngine) {};	void SetEngine(asIScriptEngine *in_pAsIScriptEngine) {m_pAsIScriptEngine=in_pAsIScriptEngine;};	asIScriptEngine *GetEngine() {return m_pAsIScriptEngine;};	virtual int AddRef() {return m_pAsIScriptEngine->AddRef();};	virtual int Release() {return m_pAsIScriptEngine->Release();};	virtual int RegisterObjectType(const char *name, int byteSize, asDWORD flags) {return m_pAsIScriptEngine->RegisterObjectType(name, byteSize, flags);};	virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) {return m_pAsIScriptEngine->RegisterObjectProperty(obj, declaration, byteOffset);};	virtual int RegisterObjectMethod(const char *obj, const char *declaration, asUPtr funcPointer, asDWORD callConv) {return m_pAsIScriptEngine->RegisterObjectMethod(obj, declaration, funcPointer, callConv);};	virtual int RegisterObjectBehaviour(const char *datatype, asDWORD behaviour, const char *declaration, asUPtr funcPointer, asDWORD callConv) {return m_pAsIScriptEngine->RegisterObjectBehaviour(datatype, behaviour, declaration, funcPointer, callConv);};	virtual int RegisterGlobalProperty(const char *declaration, void *pointer) {return m_pAsIScriptEngine->RegisterGlobalProperty(declaration, pointer);};	virtual int RegisterGlobalFunction(const char *declaration, asUPtr funcPointer, asDWORD callConv) {return m_pAsIScriptEngine->RegisterGlobalFunction(declaration, funcPointer, callConv);};	virtual int RegisterStringFactory(const char *datatype, asUPtr factoryFunc, asDWORD callConv)  {return m_pAsIScriptEngine->RegisterStringFactory(datatype, factoryFunc, callConv);};	virtual int AddScriptSection(const char *module, const char *name, const char *code, int codeLength, int lineOffset) {return m_pAsIScriptEngine->AddScriptSection(module, name, code, codeLength, lineOffset);};	virtual int Build(const char *module, asIOutputStream *out)  {return m_pAsIScriptEngine->Build(module, out);};  virtual int Discard(const char *module)   {return m_pAsIScriptEngine->Discard(module);};//	virtual int GetModuleID(const char *module)   {return m_pAsIScriptEngine->GetModuleID(module);};	virtual int GetModuleIndex(const char *module)    {return m_pAsIScriptEngine->GetModuleIndex(module);};	virtual const char *GetModuleNameFromIndex(int index, int *length = 0)    {return m_pAsIScriptEngine->GetModuleNameFromIndex(index, length);};	virtual int GetFunctionCount(const char *module) {return m_pAsIScriptEngine->GetFunctionCount(module);};	virtual int GetFunctionIDByIndex(const char *module, int index) {return m_pAsIScriptEngine->GetFunctionIDByIndex(module, index);};	virtual int GetFunctionIDByName(const char *module, const char *name) {return m_pAsIScriptEngine->GetFunctionIDByName(module, name);};	virtual int GetFunctionIDByDecl(const char *module, const char *decl) {return m_pAsIScriptEngine->GetFunctionIDByDecl(module, decl);};	virtual const char *GetFunctionDeclaration(int funcID, int *length = 0) {return m_pAsIScriptEngine->GetFunctionDeclaration(funcID, length);};;	virtual const char *GetFunctionName(int funcID, int *length = 0) {return m_pAsIScriptEngine->GetFunctionName(funcID, length);};#ifdef AS_DEPRECATED	virtual int GetFunctionDeclaration(int funcID, char *buffer, int bufferSize) {return m_pAsIScriptEngine->GetFunctionDeclaration(funcID, buffer, bufferSize);};	virtual int GetFunctionName(int funcID, char *buffer, int bufferSize) {return m_pAsIScriptEngine->GetFunctionName(funcID, buffer, bufferSize);};#endif	virtual int GetGlobalVarCount(const char *module) {return m_pAsIScriptEngine->GetFunctionCount(module);};	virtual int GetGlobalVarIDByIndex(const char *module, int index) {return m_pAsIScriptEngine->GetGlobalVarIDByIndex(module, index);};	virtual int GetGlobalVarIDByName(const char *module, const char *name) {return m_pAsIScriptEngine->GetGlobalVarIDByName(module, name);};	virtual int GetGlobalVarIDByDecl(const char *module, const char *decl) {return m_pAsIScriptEngine->GetGlobalVarIDByDecl(module, decl);};	virtual const char *GetGlobalVarDeclaration(int gvarID, int *length = 0) {return m_pAsIScriptEngine->GetGlobalVarDeclaration(gvarID, length);};	virtual const char *GetGlobalVarName(int gvarID, int *length = 0)  {return m_pAsIScriptEngine->GetGlobalVarName(gvarID, length);};#ifdef AS_DEPRECATED	virtual int GetGlobalVarDeclaration(int gvarID, char *buffer, int bufferSize) {return m_pAsIScriptEngine->GetGlobalVarDeclaration(gvarID, buffer, bufferSize);};	virtual int GetGlobalVarName(int gvarID, char *buffer, int bufferSize) {return m_pAsIScriptEngine->GetGlobalVarName(gvarID, buffer, bufferSize);};#endif	virtual int GetGlobalVarPointer(int gvarID, void **pointer) {return m_pAsIScriptEngine->GetGlobalVarPointer(gvarID, pointer);};	virtual int SetDefaultContextStackSize(asUINT initial, asUINT maximum) {return m_pAsIScriptEngine->SetDefaultContextStackSize(initial, maximum);};	virtual int CreateContext(asIScriptContext **context) {return m_pAsIScriptEngine->CreateContext(context);};	virtual int ExecuteString(const char *module, const char *script, asIOutputStream *out = 0, asIScriptContext **ctx = 0, asDWORD flags = 0) {return m_pAsIScriptEngine->ExecuteString(module, script, out, ctx, flags);};#ifdef AS_DEPRECATED	virtual int ExecuteString(const char *module, const char *script, asIOutputStream *out, asDWORD flags) {return m_pAsIScriptEngine->ExecuteString(module, script, out, flags);};	virtual asIScriptContext *GetContextForExecuteString() {return m_pAsIScriptEngine->GetContextForExecuteString();};#endif	virtual int SaveByteCode(const char *module, asIBinaryStream *out) {return m_pAsIScriptEngine->SaveByteCode(module, out);};	virtual int LoadByteCode(const char *module, asIBinaryStream *in) {return m_pAsIScriptEngine->LoadByteCode(module, in);};// 1.9.0 WIP4	// Dynamic binding between modules	virtual int GetImportedFunctionCount(const char *module) {return m_pAsIScriptEngine->GetImportedFunctionCount(module);};	virtual int GetImportedFunctionIndexByDecl(const char *module, const char *decl) {return m_pAsIScriptEngine->GetImportedFunctionIndexByDecl(module, decl);};	virtual const char *GetImportedFunctionDeclaration(const char *module, int importIndex, int *length = 0)  {return m_pAsIScriptEngine->GetImportedFunctionDeclaration(module, importIndex, length);};#ifdef AS_DEPRECATED	virtual int GetImportedFunctionDeclaration(const char *module, int importIndex, char *buffer, int bufferSize) {return m_pAsIScriptEngine->GetImportedFunctionDeclaration(module, importIndex, buffer, bufferSize);};#endif	virtual const char *GetImportedFunctionSourceModule(const char *module, int importIndex, int *length = 0) {return m_pAsIScriptEngine->GetImportedFunctionSourceModule(module, importIndex, length);};	virtual int BindImportedFunction(const char *module, int importIndex, int funcID) {return m_pAsIScriptEngine->BindImportedFunction(module, importIndex, funcID);};	virtual int UnbindImportedFunction(const char *module, int importIndex) {return m_pAsIScriptEngine->UnbindImportedFunction(module, importIndex);};	virtual int BindAllImportedFunctions(const char *module) {return m_pAsIScriptEngine->BindAllImportedFunctions(module);};	virtual int UnbindAllImportedFunctions(const char *module) {return m_pAsIScriptEngine->UnbindAllImportedFunctions(module);};//// 1.10.1	virtual int RegisterGlobalBehaviour(asDWORD behaviour, const char *declaration, asUPtr funcPointer, asDWORD callConv) {return m_pAsIScriptEngine->RegisterGlobalBehaviour(behaviour, declaration, funcPointer, callConv);};};


with an example for RegisterObjectType implementation :

int CAsScriptEngineWrapperXPN::RegisterObjectType(const char *name, int byteSize, asDWORD flags){	int r = m_pAsIScriptEngine->RegisterObjectType(name, byteSize, flags);	if (r == asSUCCESS) {				m_csObjectType += name; m_csObjectType += " ";		m_mapTypes.SetAt(name, new _stObjectDef);	}	else {		TRACE("RegisterObjectType(%s) FAILED\n", name);	}	return r;}


Next, I have used it like that :

	m_pAsEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION);	m_asWrapper.SetEngine(m_pAsEngine);	RegisterAsStringS(&m_asWrapper);	RegisterAsXML(&m_asWrapper);	...


This way, my IDE application if full compatible with the "runtime" application since all of my RegisterAs... are using an asIScriptEngine pointer as parameter.

The "runtime" programm simply use :

	m_pAsEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION);	RegisterAsStringS(m_pAsEngine);	RegisterAsXML(m_pAsEngine);	...


In the IDE application, the informations grabbed into the overloadded methods (Register...) are simply passed to Scintilla to highlight the added script functions/ script types.

This works under AS 1.10 but I think could be implemented the same way using AS 2.x .

You can look at the IDE snapshot :


AbrKen
Does your application do real time highlighting? It seems that proper syntax highlighting would only be triggered when a script was built and executed, hence calls to RegisterObjectType would be made so that you would add to the keywords of the scintilla word lists.

For the most part, code completion and highlighting of host registered objects would be relatively simple: I could simple make a hardcoded tree for them.

One of my main concerns is the processing time it might take to account for user changes. Sure something like ReSharper or VS.NET provide pretty good real time code analysis, but they have rather large teams of epxerienced developers writing what is going to be a lot better code than what I will come up with.
Does your application do real time highlighting?

Yes, thank's Scintilla !

Please take a look at Scintilla I'm sure it will clarify what I was trying to explain.

AbrKen.
Yeah, My app currently uses Scintilla. In fact, I'm currently using SciTE as the IDE with AS embedded.


What I meant as far as real time is concerned is it seemd to me by the code you used that the application had to be built and executed before there could be script specific highlighting and intellisense such as highlighting the different class methods and properties.

[Edited by - Rain Dog on June 28, 2005 3:56:38 PM]
Quote: My application acts as the IDE for its users and is geared towards non-programmers so expecting someone to have access to other IDE's is not really feasible.


Ah well. Clearly I misunderstood your question. Good luck with that. An IDE geared toward non-programmers might be interesting. Is it for level-designers, so they can write their own scripts?
I dont see any other alternative ;)

This topic is closed to new replies.

Advertisement