blob: 9603c5a17fc9a48ed4f8f451a6a4cdcf6883ad5e [file] [log] [blame]
/*
* gen.c - Generate code to "treecc" output files.
*
* Copyright (C) 2001 Southern Storm Software, Pty Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "system.h"
#include "input.h"
#include "info.h"
#include "gen.h"
#include "errors.h"
#ifdef __cplusplus
extern "C" {
#endif
void TreeCCGenerate(TreeCCContext *context)
{
switch(context->language)
{
case TREECC_LANG_C:
{
TreeCCGenerateC(context);
}
break;
case TREECC_LANG_CPP:
{
TreeCCGenerateCPP(context);
}
break;
case TREECC_LANG_JAVA:
{
TreeCCGenerateJava(context);
}
break;
case TREECC_LANG_CSHARP:
{
TreeCCGenerateCSharp(context);
}
break;
case TREECC_LANG_RUBY:
{
TreeCCGenerateRuby(context);
}
break;
case TREECC_LANG_PHP:
{
TreeCCGeneratePHP(context);
}
break;
}
}
/*
* Generate selectors for one case of a "switch" statement.
*/
static int GenerateSelectors(TreeCCContext *context, TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt, TreeCCNode *node,
int markBit, int triggerNum)
{
/* If the mark bit is already set, then we've already handled this case */
if((node->flags & markBit) != 0)
{
return 0;
}
/* Mark the node as already visited */
node->flags |= markBit;
/* Generate a selector for this node */
(*(nonVirt->genSelector))(context, stream, node, triggerNum);
/* Generate selectors for the children */
node = node->firstChild;
while(node != 0)
{
GenerateSelectors(context, stream, nonVirt, node, markBit, triggerNum);
node = node->nextSibling;
}
/* We have done something non-trivial */
return 1;
}
/*
* Determine if the outer trigger levels of two operation cases match.
*/
static int LevelsMatch(TreeCCOperationCase *operCase1,
TreeCCOperationCase *operCase2,
int triggerNum)
{
TreeCCTrigger *trigger1 = operCase1->triggers;
TreeCCTrigger *trigger2 = operCase2->triggers;
while(triggerNum > 0)
{
if(trigger1->node != trigger2->node)
{
return 0;
}
trigger1 = trigger1->next;
trigger2 = trigger2->next;
}
return 1;
}
/*
* Generate code for a "switch" on a trigger.
*/
static TreeCCOperationCase *GenerateSwitch
(TreeCCContext *context, TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt, TreeCCOperation *oper,
TreeCCOperationCase *operCase, int triggerNum)
{
TreeCCParam *param;
int num, markBit;
int paramNum;
TreeCCOperationCase *firstCase;
TreeCCTrigger *trigger;
TreeCCNode *node;
int isEnum;
/* Generate the head of the switch for this level */
param = oper->params;
num = 0;
paramNum = 1;
while(param != 0)
{
if((param->flags & TREECC_PARAM_TRIGGER) != 0)
{
if(num == triggerNum)
{
break;
}
++num;
}
if(!(param->name))
{
++paramNum;
}
param = param->next;
}
if(!param)
{
/* Output the code for the case */
if((oper->flags & TREECC_OPER_INLINE) != 0)
{
(*(nonVirt->genCaseInline))(context, stream, operCase,
triggerNum - 1);
}
else
{
(*(nonVirt->genCaseCall))(context, stream, operCase,
operCase->number, triggerNum - 1);
}
return operCase->next;
}
node = TreeCCNodeFindByType(context, param->type);
isEnum = ((node->flags & TREECC_NODE_ENUM) != 0);
if(param->name)
{
(*(nonVirt->genSwitchHead))(context, stream, param->name,
triggerNum, isEnum);
}
else
{
char paramName[64];
sprintf(paramName, "P%d__", paramNum);
(*(nonVirt->genSwitchHead))(context, stream, paramName,
triggerNum, isEnum);
}
/* Recurse to handle the next-inner level of switch statements */
markBit = TREECC_NODE_MARK(triggerNum);
TreeCCNodeClearMarking(context, markBit);
firstCase = operCase;
do
{
/* Output the "case" statements to match this operation case */
trigger = operCase->triggers;
num = 0;
while(trigger != 0 && num < triggerNum)
{
++num;
trigger = trigger->next;
}
if(trigger)
{
if(!GenerateSelectors(context, stream, nonVirt,
trigger->node, markBit, triggerNum))
{
/* We already output code for this with another case */
TreeCCErrorOnLine(context->input, operCase->filename,
operCase->linenum,
"this operation case duplicates another");
}
}
(*(nonVirt->genEndSelectors))(context, stream, triggerNum);
/* Generate the next level of "switch"'s */
operCase = GenerateSwitch(context, stream, nonVirt,
oper, operCase, triggerNum + 1);
/* Terminate the "case" statement */
(*(nonVirt->genEndCase))(context, stream, triggerNum);
}
while(operCase != 0 && LevelsMatch(firstCase, operCase, triggerNum));
/* Generate the end of the switch for this level */
(*(nonVirt->genEndSwitch))(context, stream, triggerNum);
/* Return the next case to be handled to the caller */
return operCase;
}
/*
* Assign positions to the node types in a non-virtual operation.
*/
static void AssignTriggerPosns(TreeCCContext *context, TreeCCOperation *oper)
{
TreeCCParam *param;
TreeCCNode *type;
param = oper->params;
while(param != 0)
{
if((param->flags & TREECC_PARAM_TRIGGER) != 0)
{
type = TreeCCNodeFindByType(context, param->type);
if(type)
{
TreeCCNodeAssignPositions(type);
}
}
param = param->next;
}
}
/*
* Forward declaration.
*/
static void GenerateMultiSwitch(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum);
/*
* Scan down the hierarchy to generate cases for a multi-trigger operation.
*/
static void GenerateMultiScan(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum,
TreeCCNode *node)
{
/* Ignore abstract node types */
if((node->flags & TREECC_NODE_ABSTRACT) == 0)
{
/* Generate a selector for this node */
(*(nonVirt->genSelector))(context, stream, node, triggerNum);
(*(nonVirt->genEndSelectors))(context, stream, triggerNum);
/* Go in one level for this node */
GenerateMultiSwitch(context, stream, nonVirt, oper, sortedCases,
base + node->position * multiplier,
multiplier * nextParam->size, nextParam->next,
triggerNum + 1);
/* Terminate the case for the above selector */
(*(nonVirt->genEndCase))(context, stream, triggerNum);
}
/* Scan all of the children */
node = node->firstChild;
while(node != 0)
{
GenerateMultiScan(context, stream, nonVirt, oper, sortedCases,
base, multiplier, nextParam, triggerNum, node);
node = node->nextSibling;
}
}
/*
* Generate code for a "switch" on a multi-trigger operation.
*/
static void GenerateMultiSwitch(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum)
{
TreeCCNode *type;
int isEnum;
/* Scan for the next trigger parameter */
while(nextParam != 0 && (nextParam->flags & TREECC_PARAM_TRIGGER) == 0)
{
nextParam = nextParam->next;
}
/* If we are out of triggers, then we need to output the case code */
if(!nextParam)
{
if((oper->flags & TREECC_OPER_INLINE) != 0)
{
(*(nonVirt->genCaseInline))(context, stream, sortedCases[base],
triggerNum - 1);
}
else
{
(*(nonVirt->genCaseCall))(context, stream, sortedCases[base],
sortedCases[base]->number,
triggerNum - 1);
}
return;
}
/* Generate the head of the switch for this level */
type = TreeCCNodeFindByType(context, nextParam->type);
isEnum = ((type->flags & TREECC_NODE_ENUM) != 0);
if(nextParam->name)
{
(*(nonVirt->genSwitchHead))(context, stream, nextParam->name,
triggerNum, isEnum);
}
else
{
char paramName[64];
int paramNum = 0;
TreeCCParam *param = oper->params;
while(param != 0 && param != nextParam)
{
if(!(param->name))
{
++paramNum;
}
param = param->next;
}
sprintf(paramName, "P%d__", paramNum);
(*(nonVirt->genSwitchHead))(context, stream, paramName,
triggerNum, isEnum);
}
/* Scan down the node type hierarchy for this trigger level */
GenerateMultiScan(context, stream, nonVirt, oper, sortedCases,
base, multiplier, nextParam, triggerNum, type);
/* Generate the end of the switch for this level */
(*(nonVirt->genEndSwitch))(context, stream, triggerNum);
}
/*
* Forward declaration.
*/
static void GenerateSplitMultiSwitch(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum);
/*
* Scan down the hierarchy to generate cases for a multi-trigger operation
* that is split across multiple functions.
*/
static void GenerateSplitMultiScan(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum,
TreeCCNode *node)
{
int number;
/* Ignore abstract node types */
if((node->flags & TREECC_NODE_ABSTRACT) == 0)
{
/* Generate a selector for this node */
(*(nonVirt->genSelector))(context, stream, node, 0);
(*(nonVirt->genEndSelectors))(context, stream, 0);
/* Generate the code for the case */
number = base + node->position * multiplier;
if(triggerNum == (oper->numTriggers - 1))
{
/* We are at the inner-most level, so insert the case's code */
if((oper->flags & TREECC_OPER_INLINE) != 0)
{
(*(nonVirt->genCaseInline))(context, stream,
sortedCases[number], 0);
}
else
{
(*(nonVirt->genCaseCall))(context, stream,
sortedCases[number],
sortedCases[number]->number, 0);
}
}
else
{
/* Generate a call to a split function */
(*(nonVirt->genCaseSplit))(context, stream,
sortedCases[number], number, 0);
}
/* Terminate the case for the above selector */
(*(nonVirt->genEndCase))(context, stream, 0);
}
/* Scan all of the children */
node = node->firstChild;
while(node != 0)
{
GenerateSplitMultiScan(context, stream, nonVirt, oper, sortedCases,
base, multiplier, nextParam, triggerNum, node);
node = node->nextSibling;
}
}
/*
* Scan down the hierarchy to generate split functions for
* a multi-trigger operation.
*/
static void GenerateSplitMultiScanFunc(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum,
TreeCCNode *node)
{
/* Ignore abstract node types */
if((node->flags & TREECC_NODE_ABSTRACT) == 0)
{
/* Go in one level for this node */
GenerateSplitMultiSwitch(context, stream, nonVirt, oper, sortedCases,
base + node->position * multiplier,
multiplier * nextParam->size, nextParam->next,
triggerNum + 1);
}
/* Scan all of the children */
node = node->firstChild;
while(node != 0)
{
GenerateSplitMultiScanFunc(context, stream, nonVirt, oper,
sortedCases, base, multiplier,
nextParam, triggerNum, node);
node = node->nextSibling;
}
}
/*
* Generate code for a "switch" on a multi-trigger operation
* that is split across multiple functions.
*/
static void GenerateSplitMultiSwitch(TreeCCContext *context,
TreeCCStream *stream,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper,
TreeCCOperationCase **sortedCases,
int base, int multiplier,
TreeCCParam *nextParam,
int triggerNum)
{
TreeCCNode *type;
int isEnum;
/* Scan for the next trigger parameter */
while(nextParam != 0 && (nextParam->flags & TREECC_PARAM_TRIGGER) == 0)
{
nextParam = nextParam->next;
}
/* Generate the next level of split functions */
type = TreeCCNodeFindByType(context, nextParam->type);
if(triggerNum != (oper->numTriggers - 1))
{
GenerateSplitMultiScanFunc
(context, stream, nonVirt, oper, sortedCases,
base, multiplier, nextParam, triggerNum, type);
}
/* Output the entry point for this switch function */
if(triggerNum != 0)
{
(*(nonVirt->genSplitEntry))(context, stream, oper, base);
}
else
{
(*(nonVirt->genEntry))(context, stream, oper);
}
/* Generate the head of the switch for this level */
isEnum = ((type->flags & TREECC_NODE_ENUM) != 0);
if(nextParam->name)
{
(*(nonVirt->genSwitchHead))(context, stream, nextParam->name,
0, isEnum);
}
else
{
char paramName[64];
int paramNum = 0;
TreeCCParam *param = oper->params;
while(param != 0 && param != nextParam)
{
if(!(param->name))
{
++paramNum;
}
param = param->next;
}
sprintf(paramName, "P%d__", paramNum);
(*(nonVirt->genSwitchHead))(context, stream, paramName, 0, isEnum);
}
/* Scan down the node type hierarchy for this trigger level */
GenerateSplitMultiScan(context, stream, nonVirt, oper, sortedCases,
base, multiplier, nextParam, triggerNum, type);
/* Generate the end of the switch for this level */
(*(nonVirt->genEndSwitch))(context, stream, 0);
/* Generate the exit point for this switch function */
(*(nonVirt->genExit))(context, stream, oper);
}
/*
* Generate code for a specific non-virtual operation.
*/
static void GenerateNonVirtual(TreeCCContext *context,
const TreeCCNonVirtual *nonVirt,
TreeCCOperation *oper)
{
TreeCCStream *stream;
TreeCCOperationCase *operCase;
int number;
/* Determine which stream to write to */
if(context->language == TREECC_LANG_JAVA)
{
if(oper->className)
{
stream = TreeCCStreamGetJava(context, oper->className);
}
else
{
stream = TreeCCStreamGetJava(context, oper->name);
}
}
else
{
stream = oper->source;
}
/* Output start declarations for the operation */
(*(nonVirt->genStart))(context, stream, oper);
/* If the operation is not inline, then output functions for all cases */
if((oper->flags & TREECC_OPER_INLINE) == 0)
{
number = 1;
operCase = oper->firstCase;
while(operCase != 0)
{
(*(nonVirt->genCaseFunc))(context, stream, operCase, number);
operCase->number = number++;
operCase = operCase->next;
}
}
/* Process split non-virtuals */
if(oper->numTriggers > 1 && (oper->flags & TREECC_OPER_SPLIT) != 0)
{
/* Split the switch statement over multiple levels of functions,
so that no single function grows too large */
AssignTriggerPosns(context, oper);
GenerateSplitMultiSwitch(context, stream, nonVirt, oper,
oper->sortedCases, 0, 1, oper->params, 0);
(*(nonVirt->genEnd))(context, stream, oper);
return;
}
/* Output the entry point for the operation */
(*(nonVirt->genEntry))(context, stream, oper);
/* Generate the switch statement for the outer-most level */
if(oper->numTriggers <= 1)
{
GenerateSwitch(context, stream, nonVirt, oper, oper->firstCase, 0);
}
else
{
AssignTriggerPosns(context, oper);
GenerateMultiSwitch(context, stream, nonVirt, oper,
oper->sortedCases, 0, 1, oper->params, 0);
}
/* Output the exit point for the operation */
(*(nonVirt->genExit))(context, stream, oper);
/* Output the end declarations for the operation */
(*(nonVirt->genEnd))(context, stream, oper);
}
void TreeCCGenerateNonVirtuals(TreeCCContext *context,
const TreeCCNonVirtual *nonVirt)
{
unsigned int hash;
TreeCCOperation *oper;
for(hash = 0; hash < TREECC_HASH_SIZE; ++hash)
{
oper = context->operHash[hash];
while(oper != 0)
{
if((oper->flags & TREECC_OPER_VIRTUAL) == 0)
{
GenerateNonVirtual(context, nonVirt, oper);
}
oper = oper->nextHash;
}
}
}
#ifdef __cplusplus
};
#endif