gecko-dev/dom/tools/IdlParser.cpp
1998-05-19 05:21:21 +00:00

886 lines
24 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/**
IDL syntax
specification = definition < specification > .
definition = ( type_dcl / const_dcl / except_dcl / interface / module ) ";" .
interface = interface_dcl / forward_dcl .
forward_dcl = "interface" identifier .
interface_dcl = interface_header "{" interface_body "}" .
interface_header = "interface" identifier [ inheritance_spec ] .
interface_body = < export > .
export = ( type_dcl / const_dcl / except_dcl / attr_dcl / op_dcl ) ";" .
inheritance_spec = ":" scoped_name < "," scoped_name > .
scoped_name = identifier
/ ( "::" identifier )
/ ( scoped_name "::" identifier ) .
*/
// initialize cout
#include <ostream.h>
#include "IdlParser.h"
#include "IdlScanner.h"
#include "Exceptions.h"
#include "IdlSpecification.h"
#include "IdlInterface.h"
#include "IdlEnum.h"
#include "IdlVariable.h"
#include "IdlAttribute.h"
#include "IdlFunction.h"
#include "IdlParameter.h"
// not defined yet. We don't need this types for now
// so the forward declaration in IdlParser.h is good enough
/*
#include "IdlTypedef.h"
#include "IdlStruct.h"
#include "IdlUnion.h"
#include "IdlConst.h"
#include "IdlException.h"
*/
/**
* constructor
*/
IdlParser::IdlParser()
{
mScanner = new IdlScanner();
}
/**
* destructor
*/
IdlParser::~IdlParser()
{
if (mScanner) {
delete mScanner;
}
}
/**
* specification = definition < specification > .
* definition = ( type_dcl / const_dcl / except_dcl / interface / module ) ";" .
*
* We only understand interfaces so go directly to process interfaces.
* A more complex parsing of specification requires changes in this method
* (or subclassing if you like and you can let this class process the interfaces only)
*/
void IdlParser::Parse(char *aFileName, IdlSpecification &aSpecification)
{
// connect the scanner to the file in input
if (mScanner->Open(aFileName)) {
try {
// read all the interfaces in the idl file
IdlInterface *idlInterface;
while (mScanner->CanReadMoreData()) {
idlInterface = ParseInterface(aSpecification);
if (idlInterface)
aSpecification.AddInterface(idlInterface);
}
} catch (ParserException &exc) {
cout << exc;
throw AbortParser(mScanner->GetFileName(), mScanner->GetLineNumber());
}
}
else {
throw FileNotFoundException(aFileName);
}
}
/**
* interface = interface_dcl / forward_dcl
* forward_dcl = "interface" identifier
* interface_dcl = interface_header "{" interface_body "}"
* interface_header = "interface" identifier [ inheritance_spec ]
* interface_body = < export >
*
* We look for
* "interface" identifier
* and if followed by "{" or ":" we expect a full interface declaration
* otherwise it is just a forward declaration
*
*/
IdlInterface* IdlParser::ParseInterface(IdlSpecification &aSpecification)
{
IdlInterface *interfaceObj = (IdlInterface*)0;
// go through all comments and assume the last one is a comment for the
// interface
Token *token = (Token*)0;
while (mScanner->CanReadMoreData() && COMMENT_TOKEN == mScanner->PeekToken()->id) {
token = mScanner->NextToken();
}
if (token) {
//XXX save last comment somewhere...
}
if (mScanner->CanReadMoreData()) {
try {
// must be "interface" identifier
if (INTERFACE_TOKEN == mScanner->NextToken()->id) {
TrimComments();
token = mScanner->NextToken();
if (IDENTIFIER_TOKEN == token->id) {
interfaceObj = new IdlInterface();
interfaceObj->SetName(token->stringID);
}
else {
throw InterfaceParsingException("Missing identifier. Interface name undefined.");
}
}
else {
throw InterfaceParsingException("Interface expected. We only process interfaces specification.");
}
TrimComments();
// look for ":"
int inheritanceSpec = 0;
token = mScanner->PeekToken();
if (INHERITANCE_SPEC_TOKEN == token->id) {
mScanner->NextToken(); // read ":"
TrimComments();
//XXX we deal only with an interface_header structured as follow
// interface_header = ":" identifier < "," identifier >
token = mScanner->NextToken(); // this must be the base class
while (IDENTIFIER_TOKEN == token->id) {
interfaceObj->InheritsFrom(token->stringID);
TrimComments();
token = mScanner->PeekToken();
if (SEPARATOR_TOKEN == token->id) {
mScanner->NextToken(); // eat ","
TrimComments();
token = mScanner->NextToken(); // this must be the next base class
}
else {
// next token isn't a ","
// we are done with interface_header
inheritanceSpec = 1;
break;
}
}
if (0 == inheritanceSpec) {
delete interfaceObj;
throw InterfaceParsingException("Bad inheritance specification.");
}
}
// got a "{"
if (BEGIN_BLOCK_TOKEN == token->id) {
ParseInterfaceBody(aSpecification, *interfaceObj);
}
else if (inheritanceSpec) {
delete interfaceObj;
// if interface_header was found a interface_body must be defined
throw InterfaceParsingException("Missing interface body.");
}
else {
//XXX it's a forward declaration what do we do?
}
} catch (EndOfFileException &) {
if (interfaceObj != 0) {
delete interfaceObj;
}
// unexpected end of file. Trying to read after a EOF has
// been returned
throw ParserException("Unexpected End of File.");
}
// read the final ";"
token = mScanner->NextToken();
if (TERMINATOR_TOKEN != token->id) {
throw InterfaceParsingException("Missing ';'.");
}
}
return interfaceObj;
}
/**
* interface_body = < export >
* export = ( type_dcl / const_dcl / except_dcl / attr_dcl / op_dcl ) ";"
* type_dcl = "typedef" type_declarator
* / struct_type
* / union_type
* / enum_type
* struct_type = "struct" identifier "{" member_list "}"
* union_type = "union" identifier "switch" "(" switch_type_spec ")"
* "{" switch_body "}"
* enum_type = "enum" identifier "{" enumerator < "," enumerator > "}"
* const_dcl = "const" const_type identifier "=" const_exp
* except_dcl = "exception" identifier "{" < member > "}"
* attr_dcl = [ "readonly" ] "attribute" param_type_spec identifier
* op_dcl = [ op_attribute ] op_type_spec identifier parameter_dcls
* [ raises_expr ] [ context_expr ]
* op_attribute = "oneway" .
* op_type_spec = param_type_spec / "void"
* param_type_spec = base_type_spec / string_type / scoped_name
* base_type_spec = floating_pt_type
* / integer_type
* / char_type
* / boolean_type
* / octet_type
* / any_type
* scoped_name = identifier
* / ( "::" identifier )
* / ( scoped_name "::" identifier )
*/
void IdlParser::ParseInterfaceBody(IdlSpecification &aSpecification, IdlInterface &aInterface)
{
// eat "{"
mScanner->NextToken();
TrimComments();
// untill "}" parse the interface body
Token *token = (Token*)0;
while (END_BLOCK_TOKEN != mScanner->PeekToken()->id) {
token = mScanner->NextToken();
TrimComments();
// parse the proper valid type
switch (token->id) {
case TYPEDEF_TOKEN:
aInterface.AddTypedef(ParseTypedef(aSpecification));
break;
case STRUCT_TOKEN:
aInterface.AddStruct(ParseStruct(aSpecification));
break;
case ENUM_TOKEN:
aInterface.AddEnum(ParseEnum(aSpecification));
break;
case UNION_TOKEN:
aInterface.AddUnion(ParseUnion(aSpecification));
break;
case CONST_TOKEN:
aInterface.AddConst(ParseConst(aSpecification));
break;
case EXCEPTION_TOKEN:
aInterface.AddException(ParseException(aSpecification));
break;
case READONLY_TOKEN:
case ATTRIBUTE_TOKEN:
aInterface.AddAttribute(ParseAttribute(aSpecification, token->id));
break;
default:
// it's either a function or an error
aInterface.AddFunction(ParseFunction(aSpecification, *token));
break;
}
TrimComments();
// read the final ";"
token = mScanner->NextToken();
if (TERMINATOR_TOKEN != token->id) {
throw InterfaceParsingException("Missing ';'.");
}
TrimComments();
}
// eat "}"
mScanner->NextToken();
}
/**
* XXX NOT IMPLEMENTED YET
*/
IdlTypedef* IdlParser::ParseTypedef(IdlSpecification &aSpecification)
{
throw NotImplementedException();
return (IdlTypedef*)0;
}
/**
* XXX NOT IMPLEMENTED YET
*/
IdlStruct* IdlParser::ParseStruct(IdlSpecification &aSpecification)
{
throw NotImplementedException();
return (IdlStruct*)0;
}
/**
* enum_type = "enum" identifier "{" enumerator < "," enumerator > "}"
* enumerator = identifier
*
* We semplify enum's and define them as
* enum_type = "enum" identifier "{" enumerator < "," integer_literal > "}"
* enumerator = identifier
*/
IdlEnum* IdlParser::ParseEnum(IdlSpecification &aSpecification)
{
IdlEnum *enumObj = new IdlEnum();
Token *token;
// can be an identifier
token = mScanner->NextToken();
if (IDENTIFIER_TOKEN == token->id) {
enumObj->SetName(token->stringID);
TrimComments();
token = mScanner->NextToken();
}
// a "{" must be present anyway
if (BEGIN_BLOCK_TOKEN == token->id) {
// untill "}" parse the enum
token = mScanner->PeekToken();
while (END_BLOCK_TOKEN != token->id) {
// must be an identifier
if (IDENTIFIER_TOKEN == token->id) {
IdlVariable* enumerator = new IdlVariable();
enumerator->SetType(TYPE_INT); // it does not really matter which type
enumerator->SetName(mScanner->NextToken()->stringID);
enumObj->AddEnumerator(enumerator);
TrimComments();
// if "=" is specified an integer must follow
// NOTE: this is an ad hoc solution for the w3c dom. Enum can
// be more complex than this
token = mScanner->PeekToken();
if (ASSIGNEMENT_TOKEN == token->id) {
mScanner->NextToken(); // eat "="
TrimComments();
// must be an integer
token = mScanner->NextToken();
if (token->id == INTEGER_CONSTANT) {
enumerator->SetValue(token->value.vLong);
TrimComments();
token = mScanner->PeekToken();
}
else {
delete enumObj;
throw EnumParsingException("The specified enumerator value is not an integer.");
}
}
if (SEPARATOR_TOKEN == token->id) {
mScanner->NextToken(); // eat ","
TrimComments();
token = mScanner->PeekToken();
}
}
else {
delete enumObj;
throw EnumParsingException("Missing identifier in enum body.");
}
}
mScanner->NextToken(); // eat "}"
}
else {
delete enumObj;
throw EnumParsingException("Missing enum body.");
}
return enumObj;
}
/**
* XXX NOT IMPLEMENTED YET
*/
IdlUnion* IdlParser::ParseUnion(IdlSpecification &aSpecification)
{
throw NotImplementedException();
return (IdlUnion*)0;
}
/**
*
*/
IdlVariable* IdlParser::ParseConst(IdlSpecification &aSpecification)
{
IdlVariable *constObj = new IdlVariable();
Token *token;
TrimComments();
token = mScanner->NextToken();
// must be the type
switch(token->id) {
// base type
case BOOLEAN_TOKEN:
constObj->SetType(TYPE_BOOLEAN);
break;
case FLOAT_TOKEN:
constObj->SetType(TYPE_FLOAT);
break;
case DOUBLE_TOKEN:
constObj->SetType(TYPE_DOUBLE);
break;
case LONG_TOKEN:
constObj->SetType(TYPE_LONG);
break;
case SHORT_TOKEN:
constObj->SetType(TYPE_SHORT);
break;
case ULONG_TOKEN:
constObj->SetType(TYPE_ULONG);
break;
case USHORT_TOKEN:
constObj->SetType(TYPE_USHORT);
break;
case CHAR_TOKEN:
constObj->SetType(TYPE_CHAR);
break;
case INT_TOKEN:
constObj->SetType(TYPE_INT);
break;
case UINT_TOKEN:
constObj->SetType(TYPE_UINT);
break;
// string type
case STRING_TOKEN:
constObj->SetType(TYPE_STRING);
break;
// scoped name
case IDENTIFIER_TOKEN:
//if (aSpecification.ContainInterface(aToken.stringID)) {
constObj->SetType(TYPE_OBJECT);
constObj->SetTypeName(token->stringID);
break;
//}
default:
delete constObj;
throw ConstParsingException("Unknown type.");
}
TrimComments();
token = mScanner->NextToken();
// an identifier must follow
if (IDENTIFIER_TOKEN == token->id) {
constObj->SetName(token->stringID);
}
else {
delete constObj;
throw ConstParsingException("Missing identifire. Const name undefined.");
}
// the "=" sign
TrimComments();
token = mScanner->NextToken();
if (ASSIGNEMENT_TOKEN != token->id) {
delete constObj;
throw ConstParsingException("Missing identifire. Const name undefined.");
}
TrimComments();
token = mScanner->NextToken();
// must be some kind of constant. Constants token ids start at
// position INTEGER_CONSTANT
if (token->id < INTEGER_CONSTANT) {
delete constObj;
throw ConstParsingException("Missing identifire. Const name undefined.");
}
else if (INTEGER_CONSTANT == token->id) {
constObj->SetValue(token->value.vLong);
}
else if (STRING_CONSTANT == token->id) {
constObj->SetValue(token->value.vString);
}
return constObj;
}
/**
* XXX NOT IMPLEMENTED YET
*/
IdlException* IdlParser::ParseException(IdlSpecification &aSpecification)
{
throw NotImplementedException();
return (IdlException*)0;
}
/**
* attr_dcl = [ "readonly" ] "attribute" param_type_spec identifier
* param_type_spec = base_type_spec / string_type / scoped_name
*/
IdlAttribute* IdlParser::ParseAttribute(IdlSpecification &aSpecification, int aTokenID)
{
Token *token;
int isReadOnly = 0;
// if it was a readonly keyword read the attribute keyword
if (READONLY_TOKEN == aTokenID) {
isReadOnly = 1;
TrimComments();
token = mScanner->NextToken();
if (ATTRIBUTE_TOKEN != token->id) {
throw AttributeParsingException("Missing attribute specifier.");
}
}
TrimComments();
// create the attribute object
IdlAttribute *attrObj = new IdlAttribute();
attrObj->SetReadOnly(isReadOnly);
// this must be the attribute type
token = mScanner->NextToken();
switch(token->id) {
// base type
case BOOLEAN_TOKEN:
attrObj->SetType(TYPE_BOOLEAN);
break;
case FLOAT_TOKEN:
attrObj->SetType(TYPE_FLOAT);
break;
case DOUBLE_TOKEN:
attrObj->SetType(TYPE_DOUBLE);
break;
case LONG_TOKEN:
attrObj->SetType(TYPE_LONG);
break;
case SHORT_TOKEN:
attrObj->SetType(TYPE_SHORT);
break;
case ULONG_TOKEN:
attrObj->SetType(TYPE_ULONG);
break;
case USHORT_TOKEN:
attrObj->SetType(TYPE_USHORT);
break;
case CHAR_TOKEN:
attrObj->SetType(TYPE_CHAR);
break;
case INT_TOKEN:
attrObj->SetType(TYPE_INT);
break;
case UINT_TOKEN:
attrObj->SetType(TYPE_UINT);
break;
// string type
case STRING_TOKEN:
attrObj->SetType(TYPE_STRING);
break;
// scoped name
case IDENTIFIER_TOKEN:
//if (aSpecification.ContainInterface(token->stringID)) {
attrObj->SetType(TYPE_OBJECT);
attrObj->SetTypeName(token->stringID);
break;
//}
default:
delete attrObj;
throw AttributeParsingException("Unknow attribute type.");
}
TrimComments();
// an identifier must follow
token = mScanner->NextToken();
if (IDENTIFIER_TOKEN == token->id) {
attrObj->SetName(token->stringID);
}
else {
delete attrObj;
throw AttributeParsingException("Missing identifire. Attribute name undefined.");
}
return attrObj;
}
IdlFunction* IdlParser::ParseFunction(IdlSpecification &aSpecification, Token &aToken)
{
// NOTE: we don't process the function specifier "oneway"
IdlFunction *funcObj = new IdlFunction();
// a function name starts with the return value
switch(aToken.id) {
// base type
case BOOLEAN_TOKEN:
funcObj->SetReturnValue(TYPE_BOOLEAN);
break;
case FLOAT_TOKEN:
funcObj->SetReturnValue(TYPE_FLOAT);
break;
case DOUBLE_TOKEN:
funcObj->SetReturnValue(TYPE_DOUBLE);
break;
case LONG_TOKEN:
funcObj->SetReturnValue(TYPE_LONG);
break;
case SHORT_TOKEN:
funcObj->SetReturnValue(TYPE_SHORT);
break;
case ULONG_TOKEN:
funcObj->SetReturnValue(TYPE_ULONG);
break;
case USHORT_TOKEN:
funcObj->SetReturnValue(TYPE_USHORT);
break;
case CHAR_TOKEN:
funcObj->SetReturnValue(TYPE_CHAR);
break;
case INT_TOKEN:
funcObj->SetReturnValue(TYPE_INT);
break;
case UINT_TOKEN:
funcObj->SetReturnValue(TYPE_UINT);
break;
// string type
case STRING_TOKEN:
funcObj->SetReturnValue(TYPE_STRING);
break;
// scoped name
case IDENTIFIER_TOKEN:
//if (aSpecification.ContainInterface(aToken.stringID)) {
funcObj->SetReturnValue(TYPE_OBJECT, aToken.stringID);
break;
//}
default:
delete funcObj;
throw AttributeParsingException("Unknown type.");
}
Token *token;
TrimComments();
// must be the function name
token = mScanner->NextToken();
if (IDENTIFIER_TOKEN == token->id) {
funcObj->SetName(token->stringID);
TrimComments();
// must be "("
token = mScanner->PeekToken();
if (FUNC_PARAMS_SPEC_BEGIN_TOKEN == token->id) {
try {
while (FUNC_PARAMS_SPEC_END_TOKEN != token->id &&
(FUNC_PARAMS_SPEC_BEGIN_TOKEN == token->id ||
SEPARATOR_TOKEN == token->id)
) {
mScanner->NextToken(); // eat the last peeked symbol
TrimComments();
if (FUNC_PARAMS_SPEC_END_TOKEN == mScanner->PeekToken()->id) {
break;
}
// parse all arguments
funcObj->AddParameter(ParseFunctionParameter(aSpecification));
TrimComments();
token = mScanner->PeekToken();
}
mScanner->NextToken(); // eat ')'
}
catch (ParameterParsingException &) {
delete funcObj;
throw;
}
// is it throwing an exception?
TrimComments();
token = mScanner->PeekToken();
//XXX this is how exceptions are declared now. This should change
// to the correct way so that this while loop can be deleted
while (INTERFACE_TOKEN == token->id) {
mScanner->NextToken(); // eat "interface"
TrimComments();
token = mScanner->NextToken(); // must be the exception name
if (IDENTIFIER_TOKEN == token->id) {
funcObj->AddException(token->stringID);
TrimComments();
mScanner->NextToken(); // eat '{'
TrimComments();
mScanner->NextToken(); // eat '}'
TrimComments();
token = mScanner->PeekToken();
if (SEPARATOR_TOKEN == token->id) {
mScanner->NextToken(); // eat ','
TrimComments();
token = mScanner->PeekToken(); // should be next exception
}
}
else {
delete funcObj;
throw FunctionParsingException("Undeclared exception name.");
}
}
if (RAISES_TOKEN == token->id) {
mScanner->NextToken(); // eat "raises"
TrimComments();
token = mScanner->NextToken(); // must be '('
while (FUNC_PARAMS_SPEC_BEGIN_TOKEN == token->id ||
SEPARATOR_TOKEN == token->id) {
TrimComments();
token = mScanner->NextToken(); // must be the exception name
if (IDENTIFIER_TOKEN == token->id) {
funcObj->AddException(token->stringID);
TrimComments();
token = mScanner->NextToken(); // must be ',' or ')'
}
else {
delete funcObj;
throw FunctionParsingException("Undeclared exception name.");
}
}
if (FUNC_PARAMS_SPEC_END_TOKEN != token->id) {
delete funcObj;
throw FunctionParsingException("Missing ')'. Exceptions declaration non terminated.");
}
TrimComments();
token = mScanner->PeekToken();
}
}
else {
delete funcObj;
throw FunctionParsingException("Missing '('. Parameters list undefined.");
}
}
else {
delete funcObj;
throw FunctionParsingException("Missing identifier. Function name undefined.");
}
return funcObj;
}
/**
* Parse a function argument
*
*/
IdlParameter* IdlParser::ParseFunctionParameter(IdlSpecification &aSpecification)
{
IdlParameter *argObj = new IdlParameter();
Token *token = mScanner->NextToken();
// the paramenter attribute (in, out, inout)
switch(token->id) {
// base type
case INPUT_PARAM_TOKEN:
argObj->SetAttribute(IdlParameter.INPUT);
break;
case OUTPUT_PARAM_TOKEN:
argObj->SetAttribute(IdlParameter.OUTPUT);
break;
case INOUT_PARAM_TOKEN:
argObj->SetAttribute(IdlParameter.INOUT);
break;
default:
delete argObj;
throw ParameterParsingException("Parameter attribute (in, out or inout) undefined.");
}
TrimComments();
token = mScanner->NextToken();
// must be a known type
switch(token->id) {
// base type
case BOOLEAN_TOKEN:
argObj->SetType(TYPE_BOOLEAN);
break;
case FLOAT_TOKEN:
argObj->SetType(TYPE_FLOAT);
break;
case DOUBLE_TOKEN:
argObj->SetType(TYPE_DOUBLE);
break;
case LONG_TOKEN:
argObj->SetType(TYPE_LONG);
break;
case SHORT_TOKEN:
argObj->SetType(TYPE_SHORT);
break;
case ULONG_TOKEN:
argObj->SetType(TYPE_ULONG);
break;
case USHORT_TOKEN:
argObj->SetType(TYPE_USHORT);
break;
case CHAR_TOKEN:
argObj->SetType(TYPE_CHAR);
break;
case INT_TOKEN:
argObj->SetType(TYPE_INT);
break;
case UINT_TOKEN:
argObj->SetType(TYPE_UINT);
break;
// string type
case STRING_TOKEN:
argObj->SetType(TYPE_STRING);
break;
// scoped name
case IDENTIFIER_TOKEN:
//if (aSpecification.ContainInterface(token->stringID)) {
argObj->SetType(TYPE_OBJECT);
argObj->SetTypeName(token->stringID);
break;
//}
default:
delete argObj;
throw ParameterParsingException("Unknow type in parameters list.");
}
TrimComments();
// must be the argument name
token = mScanner->NextToken();
if (IDENTIFIER_TOKEN == token->id) {
argObj->SetName(token->stringID);
}
else {
delete argObj;
throw ParameterParsingException("Missing identifier. Parameter name undefined.");
}
return argObj;
}
/**
* Skip all following comments
*
*/
void IdlParser::TrimComments()
{
while (COMMENT_TOKEN == mScanner->PeekToken()->id) {
mScanner->NextToken();
}
}