|
//
// Parago Media GmbH & Co. KG, J�rgen B�urle (jbaurle@parago.de)
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
namespace Parago.DALComp.Compiler {
/// <summary>
/// The Parser class represents the second phase of the small compiler application
/// and analyzes the tokens created by the Tokenizer class.
/// </summary>
public class Parser {
Tokenizer tokenizer;
Token token;
DALC dalc;
Function function;
public AST DAL;
/// <summary>
/// Initializes a new instance of the Parser class when given a Tokenizer
/// instance and starts parsing.
/// </summary>
public Parser(Tokenizer tokenizer) {
this.tokenizer=tokenizer;
token=new Token(TokenKind.EOT);
DAL=new AST();
ParseDAL();
}
/// <summary>
/// dal = config dalc { dalc }
/// </summary>
void ParseDAL() {
ParseConfig();
do {
ParseDALC();
} while(Taste(TokenKind.KeywordDALC));
Eat(TokenKind.EOT);
}
/// <summary>
/// config = "Config" "{" config-setting { "," config-setting } "}"
/// </summary>
void ParseConfig() {
Eat(TokenKind.KeywordConfig);
Eat(TokenKind.CurlyBracketOpen);
ParseConfigSetting();
while(true) {
if(!Taste(TokenKind.Comma))
break;
Eat();
ParseConfigSetting();
}
Eat(TokenKind.CurlyBracketClose);
if(DAL.Settings.Count!=3)
Abort("The 'Config' section must contain values for 'Namespace', 'DatabaseType' and 'ConnectionString'");
foreach(KeyValuePair<string, string> item in DAL.Settings)
if(item.Value==String.Empty)
Abort(String.Format("Value for {0} cannot be empty", item.Key));
else if(item.Key=="DATABASETYPE"&&item.Value.ToUpper()!="MSSQL")
Abort("The allowed value for 'DATABASETYPE' is currently restricted to 'MSSQL'");
}
/// <summary>
/// config-setting = ( "Namespace" | "DatabaseType" | "Connectionstring" ) "=" string
/// </summary>
void ParseConfigSetting() {
string value=Eat(TokenKind.Identifier).ToUpper();
if(value!="NAMESPACE"&&value!="DATABASETYPE"&&value!="CONNECTIONSTRING")
Abort("Values are currently only allowed for 'Namespace', 'DatabaseType' and 'ConnectionString'");
if(DAL.Settings.ContainsKey(value))
Abort(String.Format("Value for '{0}' is already defined", value));
Eat(TokenKind.Equal);
DAL.Settings.Add(value, Eat(TokenKind.String));
}
/// <summary>
/// dalc = "DALC" dalc-name [ dalc-table ] "{" [ dalc-mapping ] dalc-function { dalc-function } "}"
/// </summary>
void ParseDALC() {
dalc=new DALC();
Eat(TokenKind.KeywordDALC);
dalc.Name=Eat(TokenKind.Identifier);
foreach(DALC item in DAL.DALCs)
if(item.Name==dalc.Name)
Abort(String.Format("DALC '{0}' is already defined", dalc.Name));
if(Taste(TokenKind.ParenthesisOpen))
ParseDALCTable();
else
dalc.Table=dalc.Name;
Eat(TokenKind.CurlyBracketOpen);
if(Taste(TokenKind.KeywordMapping))
ParseDALCMapping();
ParseDALCFunction();
while(true) {
if(!Taste(TokenKind.Identifier))
break;
ParseDALCFunction();
}
Eat(TokenKind.CurlyBracketClose);
DAL.DALCs.Add(dalc);
}
/// <summary>
/// dalc-table = "(" "=" ( identifier | string ) ")"
/// </summary>
void ParseDALCTable() {
Eat(TokenKind.ParenthesisOpen);
Eat(TokenKind.Equal);
dalc.Table=EatAny(new TokenKind[] { TokenKind.Identifier, TokenKind.String });
Eat(TokenKind.ParenthesisClose);
}
/// <summary>
/// dalc-mapping = "Mapping" "{" dalc-mapping-field { "," dalc-mapping-field } "}"
/// </summary>
void ParseDALCMapping() {
Eat(TokenKind.KeywordMapping);
Eat(TokenKind.CurlyBracketOpen);
ParseDALCMappingField();
while(true) {
if(!Taste(TokenKind.Comma))
break;
Eat();
ParseDALCMappingField();
}
Eat(TokenKind.CurlyBracketClose);
}
/// <summary>
/// dalc-mapping-field = ( identifier | string ) "=>" identifier
/// </summary>
void ParseDALCMappingField() {
string fieldToMap=EatAny(new TokenKind[] { TokenKind.Identifier, TokenKind.String });
if(String.IsNullOrEmpty(fieldToMap))
Abort("Field to map cannot be empty");
Eat(TokenKind.Assign);
string field=Eat(TokenKind.Identifier);
dalc.Mapping.Add(fieldToMap.ToUpper(), field);
}
/// <summary>
/// dalc-function = identifier "(" [ dalc-function-parameter-list ] ")"
/// </summary>
void ParseDALCFunction() {
function=new Function(Eat(TokenKind.Identifier));
Eat(TokenKind.ParenthesisOpen);
if(!Taste(TokenKind.ParenthesisClose))
ParseDALCFunctionParameterList();
Eat(TokenKind.ParenthesisClose);
dalc.Functions.Add(function);
}
/// <summary>
/// dalc-function-parameter-list = dalc-function-parameter { "," dalc-function-parameter }
/// </summary>
void ParseDALCFunctionParameterList() {
ParseDALCFunctionParameter();
while(true) {
if(!Taste(TokenKind.Comma))
break;
Eat();
ParseDALCFunctionParameter();
}
}
/// <summary>
/// dalc-function-parameter = ( "string" | "int" ) identifier "[" identifier | string "]"
/// </summary>
void ParseDALCFunctionParameter() {
string type=Eat(TokenKind.Type);
string name=Eat(TokenKind.Identifier);
foreach(Parameter item in function.Parameters)
if(item.Name==name)
Abort(String.Format("Parameter name '{0}' is already defined", item.Name));
Eat(TokenKind.BracketOpen);
string field=EatAny(new TokenKind[] { TokenKind.Identifier, TokenKind.String });
foreach(Parameter item in function.Parameters)
if(item.Field==field)
Abort(String.Format("Parameter field '{0}' is already defined", item.Field));
Eat(TokenKind.BracketClose);
function.Parameters.Add(new Parameter(type, name, field));
}
/// <summary>
/// Looks ahead and returns the next token type.
/// </summary>
bool Taste(TokenKind type) {
return tokenizer.PeekTokenType()==type;
}
/// <summary>
/// Returns the next token.
/// </summary>
string Eat() {
token=tokenizer.GetNextToken();
return token.Value;
}
/// <summary>
/// Returns the next token of type, otherwise aborts.
/// </summary>
string Eat(TokenKind type) {
token=tokenizer.GetNextToken();
if(token.Type!=type)
Abort();
return token.Value;
}
/// <summary>
/// Returns the next token of any of the passed array of types, otherwise aborts.
/// </summary>
string EatAny(TokenKind[] types) {
token=tokenizer.GetNextToken();
foreach(TokenKind type in types)
if(token.Type==type)
return token.Value;
Abort();
return String.Empty;
}
/// <summary>
/// Aborts the parsing.
/// </summary>
void Abort() {
throw new Exception("DALComp syntax error");
}
/// <summary>
/// Aborts the parsing with specific message.
/// </summary>
void Abort(string message) {
throw new Exception(String.Format("DALComp syntax error: {0}", message));
}
}
/// <summary>
/// Represents the Abstract Syntax Tree (AST).
/// </summary>
public class AST {
public Dictionary<string, string> Settings;
public List<DALC> DALCs;
public AST() {
Settings=new Dictionary<string, string>();
DALCs=new List<DALC>();
}
}
/// <summary>
/// Represents the DALC defintion with functions and field mappings.
/// </summary>
public class DALC {
public string Name;
public string Table;
public Dictionary<string, string> Mapping;
public List<Function> Functions;
public DALC() {
Mapping=new Dictionary<string, string>();
Functions=new List<Function>();
}
}
/// <summary>
/// Represents a DALC function definition including the parameter list.
/// </summary>
public class Function {
public string Name;
public List<Parameter> Parameters;
public Function(string name) {
Name=name;
Parameters=new List<Parameter>();
}
}
/// <summary>
/// Represents a function parameter definition including the parameter list.
/// </summary>
public class Parameter {
public string Type;
public string Name;
public string Field;
public Parameter(string type, string name, string field) {
Type=type;
Name=name;
Field=field;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.