From fb28f6800864176be2ffca29e8e664b641f33170 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 21 Dec 2009 18:04:02 +0000 Subject: m3 is copied into trunk, we should be good to go, now. --- src/action.cpp | 107 +++++ src/action.h | 26 ++ src/ast.cpp | 98 +++++ src/ast.h | 44 +++ src/astbranch.cpp | 44 +++ src/astbranch.h | 27 ++ src/astleaf.cpp | 137 +++++++ src/astleaf.h | 41 ++ src/astnode.cpp | 113 ++++++ src/astnode.h | 108 ++++++ src/build.l | 216 +++++++++++ src/build.y | 741 +++++++++++++++++++++++++++++++++++ src/buildparser.cpp | 127 ++++++ src/buildparser.h | 40 ++ src/condition.cpp | 10 + src/condition.h | 16 + src/conditionalways.cpp | 21 + src/conditionalways.h | 16 + src/conditionfiletime.cpp | 73 ++++ src/conditionfiletime.h | 16 + src/conditionnever.cpp | 21 + src/conditionnever.h | 16 + src/context.cpp | 524 +++++++++++++++++++++++++ src/context.h | 101 +++++ src/function.cpp | 16 + src/function.h | 23 ++ src/functionast.cpp | 33 ++ src/functionast.h | 21 + src/functiondirname.cpp | 38 ++ src/functiondirname.h | 17 + src/functiondirs.cpp | 61 +++ src/functiondirs.h | 17 + src/functionexecute.cpp | 68 ++++ src/functionexecute.h | 16 + src/functionexists.cpp | 34 ++ src/functionexists.h | 17 + src/functionfilename.cpp | 36 ++ src/functionfilename.h | 17 + src/functionfiles.cpp | 61 +++ src/functionfiles.h | 17 + src/functiongetmakedeps.cpp | 59 +++ src/functiongetmakedeps.h | 16 + src/functionmatches.cpp | 141 +++++++ src/functionmatches.h | 23 ++ src/functionreplace.cpp | 47 +++ src/functionreplace.h | 16 + src/functiontargets.cpp | 39 ++ src/functiontargets.h | 16 + src/functiontostring.cpp | 50 +++ src/functiontostring.h | 16 + src/functionunlink.cpp | 51 +++ src/functionunlink.h | 16 + src/main.cpp | 255 ++++++++++++ src/profile.cpp | 116 ++++++ src/profile.h | 30 ++ src/rule.cpp | 167 ++++++++ src/rule.h | 53 +++ src/runner.cpp | 922 ++++++++++++++++++++++++++++++++++++++++++++ src/runner.h | 43 +++ src/target.cpp | 402 +++++++++++++++++++ src/target.h | 89 +++++ src/types.h | 26 ++ src/variable.cpp | 892 ++++++++++++++++++++++++++++++++++++++++++ src/variable.h | 115 ++++++ src/view.cpp | 10 + src/view.h | 46 +++ src/viewdefault.cpp | 170 ++++++++ src/viewdefault.h | 42 ++ src/viewmake.cpp | 86 +++++ src/viewmake.h | 36 ++ src/viewplugger.cpp | 14 + src/viewplugger.h | 20 + 72 files changed, 7199 insertions(+) create mode 100644 src/action.cpp create mode 100644 src/action.h create mode 100644 src/ast.cpp create mode 100644 src/ast.h create mode 100644 src/astbranch.cpp create mode 100644 src/astbranch.h create mode 100644 src/astleaf.cpp create mode 100644 src/astleaf.h create mode 100644 src/astnode.cpp create mode 100644 src/astnode.h create mode 100644 src/build.l create mode 100644 src/build.y create mode 100644 src/buildparser.cpp create mode 100644 src/buildparser.h create mode 100644 src/condition.cpp create mode 100644 src/condition.h create mode 100644 src/conditionalways.cpp create mode 100644 src/conditionalways.h create mode 100644 src/conditionfiletime.cpp create mode 100644 src/conditionfiletime.h create mode 100644 src/conditionnever.cpp create mode 100644 src/conditionnever.h create mode 100644 src/context.cpp create mode 100644 src/context.h create mode 100644 src/function.cpp create mode 100644 src/function.h create mode 100644 src/functionast.cpp create mode 100644 src/functionast.h create mode 100644 src/functiondirname.cpp create mode 100644 src/functiondirname.h create mode 100644 src/functiondirs.cpp create mode 100644 src/functiondirs.h create mode 100644 src/functionexecute.cpp create mode 100644 src/functionexecute.h create mode 100644 src/functionexists.cpp create mode 100644 src/functionexists.h create mode 100644 src/functionfilename.cpp create mode 100644 src/functionfilename.h create mode 100644 src/functionfiles.cpp create mode 100644 src/functionfiles.h create mode 100644 src/functiongetmakedeps.cpp create mode 100644 src/functiongetmakedeps.h create mode 100644 src/functionmatches.cpp create mode 100644 src/functionmatches.h create mode 100644 src/functionreplace.cpp create mode 100644 src/functionreplace.h create mode 100644 src/functiontargets.cpp create mode 100644 src/functiontargets.h create mode 100644 src/functiontostring.cpp create mode 100644 src/functiontostring.h create mode 100644 src/functionunlink.cpp create mode 100644 src/functionunlink.h create mode 100644 src/main.cpp create mode 100644 src/profile.cpp create mode 100644 src/profile.h create mode 100644 src/rule.cpp create mode 100644 src/rule.h create mode 100644 src/runner.cpp create mode 100644 src/runner.h create mode 100644 src/target.cpp create mode 100644 src/target.h create mode 100644 src/types.h create mode 100644 src/variable.cpp create mode 100644 src/variable.h create mode 100644 src/view.cpp create mode 100644 src/view.h create mode 100644 src/viewdefault.cpp create mode 100644 src/viewdefault.h create mode 100644 src/viewmake.cpp create mode 100644 src/viewmake.h create mode 100644 src/viewplugger.cpp create mode 100644 src/viewplugger.h (limited to 'src') diff --git a/src/action.cpp b/src/action.cpp new file mode 100644 index 0000000..23c24cd --- /dev/null +++ b/src/action.cpp @@ -0,0 +1,107 @@ +#include "action.h" +#include "ast.h" +#include "astbranch.h" +#include "astleaf.h" +#include "runner.h" +#include "variable.h" + +Action::Action( const class AstBranch *pRoot ) : + pRoot( pRoot ), + pAst( NULL ) +{ + sName = dynamic_cast( + *(*pRoot->getBranchBegin()).begin() + )->getStrValue(); +} + +Action::~Action() +{ + delete pAst; + pAst = NULL; +} + +const Bu::FString &Action::getName() const +{ + return sName; +} + +void Action::call( class Runner *pRunner ) +{ + pRunner->run( (*(pRoot->getBranchBegin()+1)).begin() ); +} + +Action *Action::genDefaultAll() +{ + Ast *pAst = new Ast(); + pAst->addNode( AstNode::typeActionDef ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "all" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeProcessTarget ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "build" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeFunction ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "targets" ); + pAst->closeNode(); + pAst->closeNode(); + pAst->closeNode(); + Action *pRet = new Action( + dynamic_cast( *pAst->getNodeBegin() ) + ); + pRet->pAst = pAst; + + return pRet; +} + +Action *Action::genDefaultClean() +{ + Ast *pAst = new Ast(); + pAst->addNode( AstNode::typeActionDef ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "clean" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeProcessTarget ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "clean" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeFunction ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "targets" ); + pAst->closeNode(); + pAst->closeNode(); + pAst->closeNode(); + Action *pRet = new Action( + dynamic_cast( *pAst->getNodeBegin() ) + ); + pRet->pAst = pAst; + + return pRet; +} + +Action *Action::genDefaultDefault() +{ + Ast *pAst = new Ast(); + pAst->addNode( AstNode::typeActionDef ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "default" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeProcessTarget ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "build" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeFunction ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "targets" ); + pAst->closeNode(); + pAst->closeNode(); + pAst->closeNode(); + Action *pRet = new Action( + dynamic_cast( *pAst->getNodeBegin() ) + ); + pRet->pAst = pAst; + + return pRet; +} + diff --git a/src/action.h b/src/action.h new file mode 100644 index 0000000..520f2f1 --- /dev/null +++ b/src/action.h @@ -0,0 +1,26 @@ +#ifndef ACTION_H +#define ACTION_H + +#include + +class Action +{ +public: + Action( const class AstBranch *pRoot ); + virtual ~Action(); + + const Bu::FString &getName() const; + + void call( class Runner *pRunner ); + + static Action *genDefaultAll(); + static Action *genDefaultClean(); + static Action *genDefaultDefault(); + +private: + Bu::FString sName; + const class AstBranch *pRoot; + class Ast *pAst; +}; + +#endif diff --git a/src/ast.cpp b/src/ast.cpp new file mode 100644 index 0000000..03ed4b6 --- /dev/null +++ b/src/ast.cpp @@ -0,0 +1,98 @@ +#include "ast.h" + +#include "astleaf.h" +#include "astbranch.h" + +Ast::Ast() +{ +} + +Ast::~Ast() +{ +} + +void Ast::addNode( AstNode::Type eType ) +{ + switch( eType&AstNode::typeClassMask ) + { + case AstNode::typeBranch: + { + AstBranch *pNode = new AstBranch( eType ); + addNode( pNode ); + sBranch.push( pNode ); + } + break; + + case AstNode::typeLeaf: + { + AstLeaf *pNode = new AstLeaf( eType ); + addNode( pNode ); + } + break; + + default: + throw Bu::ExceptionBase("You got it wrong."); + break; + } +} + +void Ast::addNode( AstNode::Type eType, int iVal ) +{ + addNode( new AstLeaf( eType, iVal ) ); +} + +void Ast::addNode( AstNode::Type eType, float fVal ) +{ + addNode( new AstLeaf( eType, fVal ) ); +} + +void Ast::addNode( AstNode::Type eType, bool bVal ) +{ + addNode( new AstLeaf( eType, bVal ) ); +} + +void Ast::addNode( AstNode::Type eType, const Bu::FString &sVal ) +{ + addNode( new AstLeaf( eType, sVal ) ); +} + +void Ast::addNode( AstNode::Type eType, const char *sVal ) +{ + addNode( new AstLeaf( eType, sVal ) ); +} + +void Ast::addNode( AstNode *pNode ) +{ + if( sBranch.isEmpty() ) + lNode.append( pNode ); + else + sBranch.peek()->addNode( pNode ); +} + +void Ast::openBranch() +{ + sBranch.peek()->addBranch(); +} + +void Ast::closeNode() +{ + sBranch.pop(); +} + +Ast::NodeList::const_iterator Ast::getNodeBegin() const +{ + return lNode.begin(); +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Ast &a ) +{ + f << "Abstract Syntax Tree:"; + f.incIndent(); + f << f.nl; + for( Ast::NodeList::const_iterator i = a.getNodeBegin(); i; i++ ) + f << **i << f.nl; + f << f.nl; + f.decIndent(); + return f; +} + diff --git a/src/ast.h b/src/ast.h new file mode 100644 index 0000000..a4f9361 --- /dev/null +++ b/src/ast.h @@ -0,0 +1,44 @@ +#ifndef AST_H +#define AST_H + +#include "bu/list.h" +#include "bu/stack.h" +#include "bu/fstring.h" +#include "bu/formatter.h" + +#include "astnode.h" + +/** + * Abstract Symbol Tree. This is the thing that the parser builds for us. In + * the end, this is also what we "run" when we run build files. + */ +class Ast +{ +public: + typedef Bu::List NodeList; + Ast(); + virtual ~Ast(); + + void addNode( AstNode::Type eType ); + void addNode( AstNode::Type eType, int iVal ); + void addNode( AstNode::Type eType, float fVal ); + void addNode( AstNode::Type eType, bool bVal ); + void addNode( AstNode::Type eType, const Bu::FString &sVal ); + void addNode( AstNode::Type eType, const char *sVal ); + void addNode( AstNode *pNode ); + + void openBranch(); + + void closeNode(); + + NodeList::const_iterator getNodeBegin() const; + +private: + NodeList lNode; + typedef Bu::Stack BranchStack; + BranchStack sBranch; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const Ast &a ); + +#endif diff --git a/src/astbranch.cpp b/src/astbranch.cpp new file mode 100644 index 0000000..a6aa21c --- /dev/null +++ b/src/astbranch.cpp @@ -0,0 +1,44 @@ +#include "astbranch.h" + +AstBranch::AstBranch( Type eType ) : + AstNode( eType ) +{ +} + +AstBranch::~AstBranch() +{ +} + +void AstBranch::addBranch() +{ + lBranch.append( NodeList() ); +} + +void AstBranch::addNode( AstNode *pNode ) +{ + lBranch.last().append( pNode ); +} + +AstBranch::BranchList::const_iterator AstBranch::getBranchBegin() const +{ + return lBranch.begin(); +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstBranch &l ) +{ + f.incIndent(); + f << ":"; + for( AstBranch::BranchList::const_iterator i = l.getBranchBegin(); i; i++ ) + { + f << f.nl << "Branch:"; + f.incIndent(); + for( AstBranch::NodeList::const_iterator j = i->begin(); j; j++ ) + { + f << f.nl << **j; + } + f.decIndent(); + } + f.decIndent(); + return f; +} + diff --git a/src/astbranch.h b/src/astbranch.h new file mode 100644 index 0000000..5ff8606 --- /dev/null +++ b/src/astbranch.h @@ -0,0 +1,27 @@ +#ifndef AST_BRANCH_H +#define AST_BRANCH_H + +#include "bu/list.h" +#include "astnode.h" +#include "bu/formatter.h" + +class AstBranch : public AstNode +{ +public: + typedef Bu::List NodeList; + typedef Bu::List BranchList; + AstBranch( Type eType ); + virtual ~AstBranch(); + + void addBranch(); + void addNode( AstNode *pNode ); + + BranchList::const_iterator getBranchBegin() const; + +private: + BranchList lBranch; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstBranch &l ); + +#endif diff --git a/src/astleaf.cpp b/src/astleaf.cpp new file mode 100644 index 0000000..62773ce --- /dev/null +++ b/src/astleaf.cpp @@ -0,0 +1,137 @@ +#include "astleaf.h" + +AstLeaf::AstLeaf( Type eType ) : + AstNode( eType ), + sVal( NULL ) +{ +} + +AstLeaf::AstLeaf( Type eType, int iNew ) : + AstNode( eType ), + sVal( NULL ) +{ + setIntValue( iNew ); +} + +AstLeaf::AstLeaf( Type eType, float fNew ) : + AstNode( eType ), + sVal( NULL ) +{ + setFloatValue( fNew ); +} + +AstLeaf::AstLeaf( Type eType, bool bNew ) : + AstNode( eType ), + sVal( NULL ) +{ + setBoolValue( bNew ); +} + +AstLeaf::AstLeaf( Type eType, const Bu::FString &sNew ) : + AstNode( eType ), + sVal( NULL ) +{ + setStrValue( sNew ); +} + +AstLeaf::AstLeaf( Type eType, const char *sNew ) : + AstNode( eType ), + sVal( NULL ) +{ + setStrValue( sNew ); +} + +AstLeaf::~AstLeaf() +{ + if( getDataType() == typeDataString ) + delete sVal; +} + +void AstLeaf::setIntValue( int iNew ) +{ + if( getDataType() != typeDataInt ) + throw Bu::ExceptionBase("Type is not int."); + iVal = iNew; +} + +void AstLeaf::setFloatValue( float fNew ) +{ + if( getDataType() != typeDataFloat ) + throw Bu::ExceptionBase("Type is not float."); + fVal = fNew; +} + +void AstLeaf::setBoolValue( bool bNew ) +{ + if( getDataType() != typeDataBool ) + throw Bu::ExceptionBase("Type is not bool."); + bVal = bNew; +} + +void AstLeaf::setStrValue( const Bu::FString &sNew ) +{ + if( getDataType() != typeDataString ) + throw Bu::ExceptionBase("Type is not string."); + if( sVal == NULL ) + sVal = new Bu::FString( sNew ); + else + *sVal = sNew; +} + +int AstLeaf::getIntValue() const +{ + if( getDataType() != typeDataInt ) + throw Bu::ExceptionBase("Type is not int."); + return iVal; +} + +float AstLeaf::getFloatValue() const +{ + if( getDataType() != typeDataFloat ) + throw Bu::ExceptionBase("Type is not float."); + return fVal; +} + +bool AstLeaf::getBoolValue() const +{ + if( getDataType() != typeDataBool ) + throw Bu::ExceptionBase("Type is not bool."); + return bVal; +} + +Bu::FString &AstLeaf::getStrValue() const +{ + if( getDataType() != typeDataString ) + throw Bu::ExceptionBase("Type is not string."); + return *sVal; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstLeaf &l ) +{ + switch( l.getDataType() ) + { + case AstNode::typeDataInt: + f << ": " << l.getIntValue(); + break; + + case AstNode::typeDataFloat: + f << ": " << l.getFloatValue(); + break; + + case AstNode::typeDataBool: + f << ": " << l.getBoolValue(); + break; + + case AstNode::typeDataString: + f << ": '" << l.getStrValue() << "'"; + break; + + case AstNode::typeDataNone: + break; + + default: + f << ": " << "!! Invalid Type !!"; + } + return f; +} + diff --git a/src/astleaf.h b/src/astleaf.h new file mode 100644 index 0000000..0c8911b --- /dev/null +++ b/src/astleaf.h @@ -0,0 +1,41 @@ +#ifndef AST_LEAF_H +#define AST_LEAF_H + +#include "astnode.h" +#include "bu/fstring.h" +#include "bu/formatter.h" + +class AstLeaf : public AstNode +{ +public: + AstLeaf( Type eType ); + AstLeaf( Type eType, int iNew ); + AstLeaf( Type eType, float fNew ); + AstLeaf( Type eType, bool bNew ); + AstLeaf( Type eType, const Bu::FString &sNew ); + AstLeaf( Type eType, const char *sNew ); + virtual ~AstLeaf(); + + void setIntValue( int iNew ); + void setFloatValue( float fNew ); + void setBoolValue( bool bNew ); + void setStrValue( const Bu::FString &sNew ); + + int getIntValue() const; + float getFloatValue() const; + bool getBoolValue() const; + Bu::FString &getStrValue() const; + +private: + union + { + int iVal; + float fVal; + bool bVal; + Bu::FString *sVal; + }; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstLeaf &l ); + +#endif diff --git a/src/astnode.cpp b/src/astnode.cpp new file mode 100644 index 0000000..5adb3ee --- /dev/null +++ b/src/astnode.cpp @@ -0,0 +1,113 @@ +#include "astnode.h" +#include "astleaf.h" +#include "astbranch.h" + +AstNode::AstNode( Type eType ) : + eType( eType ) +{ +} + +AstNode::~AstNode() +{ +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode &n ) +{ + f << n.getType(); + if( n.getClass() == AstNode::typeBranch ) + { + f << *dynamic_cast(&n); + } + else + { + f << *dynamic_cast(&n); + } + return f; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode::Type &t ) +{ + switch( t ) + { + case AstNode::typeFunction: f << "Function"; break; + case AstNode::typeSet: f << "Set"; break; + case AstNode::typeUnset: f << "Unset"; break; + case AstNode::typeIf: f << "If"; break; + case AstNode::typeInclude: f << "Include"; break; + case AstNode::typeTarget: f << "Target"; break; + case AstNode::typeRule: f << "Rule"; break; + case AstNode::typeConfig: f << "Config"; break; + case AstNode::typeList: f << "List"; break; + case AstNode::typeInlineFunc: f << "InlineFunc"; break; + case AstNode::typeRequires: f << "Requires"; break; + case AstNode::typeFor: f << "For"; break; + case AstNode::typeFunctionDef: f << "FunctionDef"; break; + case AstNode::typeReturn: f << "Return"; break; + case AstNode::typeProfile: f << "Profile"; break; + case AstNode::typeInput: f << "Input"; break; + case AstNode::typeRuleDef: f << "RuleDef"; break; + case AstNode::typeOutput: f << "Output"; break; + case AstNode::typeAutoConfig: f << "AutoConfig"; break; + case AstNode::typeGlobalConfig: f << "GlobalConfig"; break; + case AstNode::typeType: f << "Type"; break; + case AstNode::typeValue: f << "Value"; break; + case AstNode::typeAllow: f << "Allow"; break; + case AstNode::typeDefault: f << "Default"; break; + case AstNode::typeExport: f << "Export"; break; + case AstNode::typeExpr: f << "Expr"; break; + case AstNode::typeActionDef: f << "ActionDef"; break; + case AstNode::typeProcessTarget:f << "ProcessTarget"; break; + case AstNode::typeTag: f << "Tag"; break; + + case AstNode::typeVariable: f << "Variable"; break; + case AstNode::typeString: f << "String"; break; + case AstNode::typeInt: f << "Int"; break; + case AstNode::typeFloat: f << "Float"; break; + case AstNode::typeBool: f << "Bool"; break; + case AstNode::typeVersion: f << "Version"; break; + case AstNode::typeOpEq: f << "Operator ="; break; + case AstNode::typeOpPlusEq: f << "Operator +="; break; + case AstNode::typeOpPlusEqRaw: f << "Operator <<"; break; + case AstNode::typeError: f << "Error"; break; + case AstNode::typeWarning: f << "Warning"; break; + case AstNode::typeNotice: f << "Notice"; break; + case AstNode::typeTypeString: f << "Type String"; break; + case AstNode::typeTypeInt: f << "Type Int"; break; + case AstNode::typeTypeFloat: f << "Type Float"; break; + case AstNode::typeTypeBool: f << "Type Bool"; break; + case AstNode::typeTypeVersion: f << "Type Version"; break; + case AstNode::typeCmpEq: f << "Compare ="; break; + case AstNode::typeCmpLt: f << "Compare <"; break; + case AstNode::typeCmpGt: f << "Compare >"; break; + case AstNode::typeCmpNe: f << "Compare !="; break; + case AstNode::typeCmpLtEq: f << "Compare <="; break; + case AstNode::typeCmpGtEq: f << "Compare >="; break; + case AstNode::typeCondition: f << "Condition"; break; + case AstNode::typeDisplay: f << "Display"; break; + case AstNode::typeCache: f << "Cache"; break; + case AstNode::typePushPrefix: f << "Push Prefix"; break; + case AstNode::typePopPrefix: f << "Pop Prefix"; break; + case AstNode::typeNull: f << "Null"; break; + case AstNode::typeVariableRef: f << "VariableRef"; break; + case AstNode::typeOpPlus: f << "Operator +"; break; + case AstNode::typeOpMinus: f << "Operator -"; break; + case AstNode::typeOpMultiply: f << "Operator *"; break; + case AstNode::typeOpDivide: f << "Operator /"; break; + case AstNode::typeOpNegate: f << "Operator negate"; break; + case AstNode::typeOpNot: f << "Operator not"; break; + + case AstNode::typeBranch: f << "Branch"; break; + case AstNode::typeLeaf: f << "Leaf"; break; + case AstNode::typeClassMask: f << "ClassMask"; break; + + case AstNode::typeDataNone: f << ""; break; + case AstNode::typeDataString: f << ""; break; + case AstNode::typeDataInt: f << ""; break; + case AstNode::typeDataFloat: f << ""; break; + case AstNode::typeDataBool: f << ""; break; + case AstNode::typeDataVersion: f << ""; break; + case AstNode::typeDataMask: f << ""; break; + } + return f; +} + diff --git a/src/astnode.h b/src/astnode.h new file mode 100644 index 0000000..6ade25b --- /dev/null +++ b/src/astnode.h @@ -0,0 +1,108 @@ +#ifndef AST_NODE_H +#define AST_NODE_H + +#include "bu/formatter.h" + +class AstNode +{ +public: + enum Type + { + // Branching types + typeFunction = 0x100001, + typeSet = 0x100002, + typeUnset = 0x100003, + typeIf = 0x100004, + typeInclude = 0x100005, + typeTarget = 0x100006, + typeRule = 0x100007, + typeConfig = 0x100008, + typeList = 0x100009, + typeInlineFunc = 0x10000A, + typeRequires = 0x10000B, + typeFor = 0x10000C, + typeFunctionDef = 0x10000D, + typeReturn = 0x10000E, + typeProfile = 0x10000F, + typeInput = 0x100010, + typeRuleDef = 0x100011, + typeOutput = 0x100012, + typeAutoConfig = 0x100013, + typeGlobalConfig = 0x100014, + typeType = 0x100015, + typeValue = 0x100016, + typeAllow = 0x100017, + typeDefault = 0x100018, + typeExport = 0x100019, + typeExpr = 0x10001A, /***< Stack based compound expression.*/ + typeActionDef = 0x10001B, + typeProcessTarget = 0x10001C, + typeTag = 0x10001D, + + // Leaf types + typeVariable = 0x210001, + typeString = 0x210002, + typeInt = 0x220003, + typeFloat = 0x230004, + typeBool = 0x240005, + typeVersion = 0x250006, + typeOpEq = 0x200007, + typeOpPlusEq = 0x200008, + typeOpPlusEqRaw = 0x200009, + typeError = 0x21000A, + typeWarning = 0x21000B, + typeNotice = 0x21000C, + typeTypeString = 0x20000D, + typeTypeInt = 0x20000E, + typeTypeFloat = 0x20000F, + typeTypeBool = 0x200010, + typeTypeVersion = 0x200011, + typeCmpEq = 0x200012, + typeCmpLt = 0x200013, + typeCmpGt = 0x200014, + typeCmpNe = 0x200015, + typeCmpLtEq = 0x200016, + typeCmpGtEq = 0x200017, + typeCondition = 0x210018, + typeDisplay = 0x210019, + typeCache = 0x24001A, + typePushPrefix = 0x21001B, + typePopPrefix = 0x20001C, + typeNull = 0x20001D, + typeVariableRef = 0x21001E, + typeOpPlus = 0x20001F, + typeOpMinus = 0x200020, + typeOpMultiply = 0x200021, + typeOpDivide = 0x200022, + typeOpNegate = 0x200023, + typeOpNot = 0x200024, + + typeBranch = 0x100000, + typeLeaf = 0x200000, + typeClassMask = 0x300000, + + typeDataNone = 0x000000, + typeDataString = 0x010000, + typeDataInt = 0x020000, + typeDataFloat = 0x030000, + typeDataBool = 0x040000, + typeDataVersion = 0x050000, + + typeDataMask = 0x0F0000 + }; +public: + AstNode( Type eType ); + virtual ~AstNode(); + + Type getType() const { return eType; } + Type getClass() const { return (Type)(eType&typeClassMask); } + Type getDataType() const { return (Type)(eType&typeDataMask); } + +private: + Type eType; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode &n ); +Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode::Type &t ); + +#endif diff --git a/src/build.l b/src/build.l new file mode 100644 index 0000000..dc4ddda --- /dev/null +++ b/src/build.l @@ -0,0 +1,216 @@ +%{ + #include "buildparser.h" + #include "bu/fstring.h" + +char *fstrdup( const Bu::FString &s ) +{ + char *sRet = new char[s.getSize()+1]; + memcpy( sRet, s.getStr(), s.getSize()+1 ); + return sRet; +} + +char *rstrdup( const char *sIn ) +{ + char *sRet = new char[strlen(sIn)+1]; + strcpy( sRet, sIn ); + return sRet; +} +void build_error( YYLTYPE *locp, yyscan_t yyscanner, BuildParser &bld, const char *msg ); + +Bu::FString sBuf; +int iStrDepth = 0; +%} + +%{ + #define YY_USER_ACTION yylloc->last_column += yyleng; +%} + +%x strdq +%x BlockComment +%option noyywrap nounput batch debug bison-bridge bison-locations reentrant +%option prefix="build_" +%option outfile="src/build.yy.c" +%option header-file="src/build.yy.h" +%% +\n+ { + yylloc->last_line += yyleng; + yylloc->first_line = yylloc->last_line; + yylloc->first_column = yylloc->last_column = 0; +} +[ \t\r]+ { + yylloc->first_line = yylloc->last_line; + yylloc->first_column = yylloc->last_column+1; +} +"#".* /* eol comment */ +"//".* /* eol comment */ + +"/*" { + BEGIN( BlockComment ); +} + +"*/" { + BEGIN( INITIAL ); +} + +\n+ { + yylloc->last_column = -yyleng; + yylloc->last_line += yyleng; +} +. { } + /* +[^*\n/]+ { } +"*"[^/\n]+ { } +"*"|"/" { } + */ + +[(){}[\],.;=<>!+*/-] return yytext[0]; +"+=" return OP_ADDSETP; +"<<" return OP_ADDSETR; +"==" return OP_CMPEQUAL; +"!=" return OP_INEQUAL; +"<=" return OP_LTEQUAL; +">=" return OP_GTEQUAL; +"target" return TOK_TARGET; +"input" return TOK_INPUT; +"output" return TOK_OUTPUT; +"unset" return TOK_UNSET; +"set" return TOK_SET; +"condition" return TOK_CONDITION; +"requires" return TOK_REQUIRES; +"auto" return TOK_AUTO; +"config" return TOK_CONFIG; +"display" return TOK_DISPLAY; +"type" return TOK_TYPE; +"int" return TOK_INT; +"float" return TOK_FLOAT; +"bool" return TOK_BOOL; +"version" return TOK_VERSION; +"string" return TOK_STRING; +"default" return TOK_DEFAULT; +"allow" return TOK_ALLOW; +"rule" return TOK_RULE; +"action" return TOK_ACTION; +"profile" return TOK_PROFILE; +"if" return TOK_IF; +"then" return TOK_THEN; +"else" return TOK_ELSE; +"include" return TOK_INCLUDE; +"warning" return TOK_WARNING; +"error" return TOK_ERROR; +"notice" return TOK_NOTICE; +"cache" return TOK_CACHE; +"always" return TOK_ALWAYS; +"never" return TOK_NEVER; +"global" return TOK_GLOBAL; +"local" return TOK_LOCAL; +"for" return TOK_FOR; +"in" return TOK_IN; +"do" return TOK_DO; +"return" return TOK_RETURN; +"function" return TOK_FUNCTION; +"continue" return TOK_CONTINUE; +"break" return TOK_BREAK; +"value" return TOK_VALUE; +"all" return TOK_ALL; +"export" return TOK_EXPORT; +"tag" return TOK_TAG; +"null" return TOK_NULL; + +"true" { + yylval->bVal = true; + return BOOL; +} +"false" { + yylval->bVal = false; + return BOOL; +} + +[a-zA-Z_][a-zA-Z0-9_]*: { + yytext[yyleng-1] = '\0'; + yylval->sVal = rstrdup( yytext ); + return PROFILE; +} + +[a-zA-Z_][a-zA-Z0-9_]* { + yylval->sVal = rstrdup( yytext ); + if( b.isKeyword( yylval->sVal ) ) + return KEYWORD; + else if( b.isCond( yylval->sVal ) ) + return CONDITION; + return UNDEF; +} + +-?([1-9][0-9]*)|(0) { + yylval->iVal = strtol( yytext, NULL, 10 ); + return INT; +} + +(0\.0+)|(-?(([1-9][0-9]*)|(0))\.[0-9]*) { + yylval->fVal = strtof( yytext, NULL ); + return FLOAT; +} + +\" { + BEGIN( strdq ); + sBuf.clear(); + iStrDepth = 0; +} +[^\\\n\"$()]+ { + sBuf += yytext; +} +\$\$ { + sBuf += "$$"; +} +\$\( { + iStrDepth++; // TODO: Should this really count depth? I dunno... + sBuf += "$("; +} +\\\) sBuf += "\\)"; +\) { + if( iStrDepth > 0 ) + iStrDepth--; + sBuf += ")"; +} +[$(] { + sBuf += yytext; +} +\n { + build_error( yylloc, yyscanner, b, "newline encountered in string"); +} +\\n sBuf += "\n"; +\\t sBuf += "\t"; +\\r sBuf += "\r"; +\\b sBuf += "\b"; +\\f sBuf += "\f"; +\\\\ sBuf += "\\"; +\\\" sBuf += "\""; +\\\' sBuf += "\'"; +\\\( sBuf += "("; +\\\` sBuf += "`"; +\\. printf("Invalid escape sequence.\n"); +\"[ \t\r\n]*\" {/* Ignore spaces between strings. */} +\" { + if( iStrDepth > 0 ) + { + sBuf += "\""; + } + else + { + BEGIN( INITIAL ); + yylval->sVal = fstrdup( sBuf ); + return STRING; + } +} + +<> { + build_pop_buffer_state( yyscanner ); + if( !YY_CURRENT_BUFFER ) + yyterminate(); + else + b.endInclude( yylloc ); +} + +. { + build_error( yylloc, yyscanner, b, "invalid character"); +} +%% diff --git a/src/build.y b/src/build.y new file mode 100644 index 0000000..a6618e4 --- /dev/null +++ b/src/build.y @@ -0,0 +1,741 @@ +%defines +%{ /* Prologue -- decls and stuff */ + #include "buildparser.h" + #include "ast.h" +void yyerror( YYLTYPE *locp, yyscan_t yyscanner, BuildParser &bld, const char *msg ); +%} +/* Bison declerations */ + +%parse-param { yyscan_t yyscanner } +%parse-param { BuildParser &bld } +%lex-param { yyscan_t yyscanner } +%lex-param { BuildParser &bld } +%pure-parser + +%locations + +%debug +%error-verbose +%name-prefix="build_" + +%union { + int iVal; + char *sVal; + float fVal; + bool bVal; +} + +%token STRING "string literal" +%token KEYWORD "keyword" +%token CONDITION "condition term" +%token VARIABLE "variable name" +%token FUNCTION "function name" +%token UNDEF "undefined identifier" +%token PROFILE "profile execute" +%token INT "integer literal" +%token FLOAT "floating point literal" +%token BOOL "boolean literal" + +%token TOK_TARGET "target" +%token TOK_INPUT "input" +%token TOK_OUTPUT "output" +%token TOK_UNSET "unset" +%token TOK_SET "set" +%token TOK_CONDITION "condition" +%token TOK_REQUIRES "requires" +%token TOK_AUTO "auto" +%token TOK_CONFIG "config" +%token TOK_DISPLAY "display" +%token TOK_TYPE "type" +%token TOK_INT "int" +%token TOK_FLOAT "float" +%token TOK_BOOL "boolean" +%token TOK_VERSION "version" +%token TOK_STRING "string" +%token TOK_DEFAULT "default" +%token TOK_ALLOW "allow" +%token TOK_RULE "rule" +%token TOK_ACTION "action" +%token TOK_PROFILE "profile" +%token TOK_IF "if" +%token TOK_THEN "then" +%token TOK_ELSE "else" +%token TOK_INCLUDE "include" +%token TOK_ERROR "error" +%token TOK_WARNING "warning" +%token TOK_NOTICE "notice" +%token TOK_CACHE "cache" +%token TOK_ALWAYS "always" +%token TOK_NEVER "never" +%token TOK_GLOBAL "global" +%token TOK_LOCAL "local" +%token TOK_FOR "for" +%token TOK_IN "in" +%token TOK_DO "do" +%token TOK_RETURN "return" +%token TOK_FUNCTION "function" +%token TOK_CONTINUE "continue" +%token TOK_BREAK "break" +%token TOK_VALUE "value" +%token TOK_ALL "all" +%token TOK_EXPORT "export" +%token TOK_TAG "tag" +%token TOK_NULL "null" + +%token OP_ADDSETP "+=" +%token OP_ADDSETR "<<" +%token OP_CMPEQUAL "==" +%token OP_INEQUAL "!=" +%token OP_LTEQUAL "<=" +%token OP_GTEQUAL ">=" + +%token '(' ')' '{' '}' '[' ']' ',' ';' '=' '.' '<' '>' '+' '-' '*' '/' + +%right '=' OP_ADDSETP OPADDSETR +%left OP_CMPEQUAL '<' '>' OP_INEQUAL OP_LTEQUAL OP_GTEQUAL '+' '-' '*' '/' +%left '(' ')' '{' '}' '[' ']' +%left IINEG IINOT + +%destructor { delete[] $$; } STRING +%destructor { delete[] $$; } KEYWORD +%destructor { delete[] $$; } CONDITION +%destructor { delete[] $$; } VARIABLE +%destructor { delete[] $$; } FUNCTION +%destructor { delete[] $$; } UNDEF +%destructor { delete[] $$; } PROFILE + +%% /* Grammar rules */ + +/* + * root stuff + */ + +root: +// | root set + | root line_expr + | root unset + | root target + | root rule + | root config + | root root_if + | root root_for + | root include + | root notify + | root export + | root function_def + | root action_def + ; + +root_sub_exprs: +// | root set + | root line_expr + | root unset + | root target + | root rule + | root config + | root root_if + | root root_for + | root include + | root notify + | root export + ; + +include: TOK_INCLUDE STRING ';' { bld.include( $2, yyscanner, &yylloc ); } + ; + +/* + * data related + */ + +string: STRING { bld.xAst.addNode( AstNode::typeString, $1 ); } + ; + +int: INT { bld.xAst.addNode( AstNode::typeInt, $1 ); } + ; + +float: FLOAT { bld.xAst.addNode( AstNode::typeFloat, $1 ); } + ; + +bool: BOOL { bld.xAst.addNode( AstNode::typeBool, (bool)$1 ); } + ; + +null: TOK_NULL { bld.xAst.addNode( AstNode::typeNull ); } + +literal: string + | int + | float + | bool + | null + ; + +variable: UNDEF /*VARIABLE*/ { bld.xAst.addNode( AstNode::typeVariable, $1 ); } + +list_core: + | { bld.xAst.openBranch(); } expr + | list_core ',' { bld.xAst.openBranch(); } expr + ; + +list: '[' { + bld.xAst.addNode( AstNode::typeList ); + } list_core ']' { + bld.xAst.closeNode(); + } + ; + +value_mods: + | value_mods '.' function + ; + +value_core: variable + | literal + | function + | list + ; + +value: value_core value_mods + ; + +/* + * misc global things + */ + +notify: TOK_ERROR STRING ';' { bld.xAst.addNode( AstNode::typeError, $2 ); } + | TOK_WARNING STRING ';' { bld.xAst.addNode( AstNode::typeWarning, $2 ); } + | TOK_NOTICE STRING ';' { bld.xAst.addNode( AstNode::typeNotice, $2 ); } + ; +/* +set_rhs: '=' { bld.xAst.addNode( AstNode::typeOpEq ); } value + | OP_ADDSETP { bld.xAst.addNode( AstNode::typeOpPlusEq ); } value + | OP_ADDSETR { bld.xAst.addNode( AstNode::typeOpPlusEqRaw ); } string + ; + +set: TOK_SET { + bld.xAst.addNode( AstNode::typeSet ); + bld.xAst.openBranch(); + } variable set_rhs ';' { + bld.xAst.closeNode(); + } + ;*/ + +unset: TOK_UNSET { + bld.xAst.addNode( AstNode::typeUnset ); + bld.xAst.openBranch(); + } variable ';' { + bld.xAst.closeNode(); + } + ; + +export_rhs: '=' value + | + ; + +export: TOK_EXPORT { + bld.xAst.addNode( AstNode::typeExport ); + bld.xAst.openBranch(); + } variable export_rhs ';' { + bld.xAst.closeNode(); + } + ; + +func_params: + | func_param_list + ; + +func_param_list: { bld.xAst.openBranch(); } value + | func_param_list ',' { bld.xAst.openBranch(); } value + ; + +function: UNDEF '(' { + bld.xAst.addNode( AstNode::typeFunction ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, $1 ); + } func_params ')' { + bld.xAst.closeNode(); + } + ; + +requires: TOK_REQUIRES { + bld.xAst.addNode( AstNode::typeRequires ); + bld.xAst.openBranch(); + } value ';' { + bld.xAst.closeNode(); + } + ; + +type: TOK_STRING { bld.xAst.addNode( AstNode::typeTypeString ); } + | TOK_INT { bld.xAst.addNode( AstNode::typeTypeInt ); } + | TOK_FLOAT { bld.xAst.addNode( AstNode::typeTypeFloat ); } + | TOK_BOOL { bld.xAst.addNode( AstNode::typeTypeBool ); } + | TOK_VERSION { bld.xAst.addNode( AstNode::typeTypeVersion ); } + ; + +/* + * comparisons + */ + +expr: value + | '(' expr ')' + | UNDEF '=' { + bld.xAst.addNode( AstNode::typeVariableRef, $1 ); + } expr { + bld.xAst.addNode( AstNode::typeOpEq ); + } + | UNDEF OP_ADDSETP { + bld.xAst.addNode( AstNode::typeVariableRef, $1 ); + } expr { + bld.xAst.addNode( AstNode::typeOpPlusEq ); + } + | expr OP_CMPEQUAL expr + { + bld.xAst.addNode( AstNode::typeCmpEq ); + } + | expr '<' expr + { + bld.xAst.addNode( AstNode::typeCmpLt ); + } + | expr '>' expr + { + bld.xAst.addNode( AstNode::typeCmpGt ); + } + | expr OP_INEQUAL expr + { + bld.xAst.addNode( AstNode::typeCmpNe ); + } + | expr OP_LTEQUAL expr + { + bld.xAst.addNode( AstNode::typeCmpLtEq ); + } + | expr OP_GTEQUAL expr + { + bld.xAst.addNode( AstNode::typeCmpGtEq ); + } + | expr '+' expr + { + bld.xAst.addNode( AstNode::typeOpPlus ); + } + | expr '-' expr + { + bld.xAst.addNode( AstNode::typeOpMinus ); + } + | expr '*' expr + { + bld.xAst.addNode( AstNode::typeOpMultiply ); + } + | expr '/' expr + { + bld.xAst.addNode( AstNode::typeOpDivide ); + } + | '-' expr %prec IINEG + { + bld.xAst.addNode( AstNode::typeOpNegate ); + } + | '!' expr %prec IINOT + { + bld.xAst.addNode( AstNode::typeOpNot ); + } + ; + +line_expr: { + bld.xAst.addNode( AstNode::typeExpr ); + bld.xAst.openBranch(); + } expr ';' + { + bld.xAst.closeNode(); + } + ; + +if_core: TOK_IF { + bld.xAst.addNode( AstNode::typeIf ); + bld.xAst.openBranch(); +// bld.xAst.addNode( AstNode::typeExpr ); +// bld.xAst.openBranch(); + } expr TOK_THEN { +// bld.xAst.closeNode(); + bld.xAst.openBranch(); + } + ; + +else: TOK_ELSE { bld.xAst.openBranch(); } + ; + +root_if: if_core '{' root_sub_exprs '}' root_else { bld.xAst.closeNode(); } + ; + +root_else: + | else '{' root_sub_exprs '}' + | else root_if + ; + +target_if: if_core '{' target_exprs '}' target_else { bld.xAst.closeNode(); } + ; + +target_else: + | else '{' target_exprs '}' + | else target_if + ; + +rule_if: if_core '{' rule_exprs '}' rule_else { bld.xAst.closeNode(); } + ; + +rule_else: + | else '{' rule_exprs '}' + | else rule_if + ; + +function_if: if_core '{' function_exprs '}' function_else + { bld.xAst.closeNode(); } + ; + +function_else: + | else '{' function_exprs '}' + | else function_if + ; + +/* + * loops + */ + +for_base: TOK_FOR { + bld.xAst.addNode( AstNode::typeFor ); + bld.xAst.openBranch(); + } variable TOK_IN { + bld.xAst.openBranch(); + } value TOK_DO { + bld.xAst.openBranch(); + } + ; + +root_for: for_base '{' root_sub_exprs '}' { bld.xAst.closeNode(); } + ; + +target_for: for_base '{' target_exprs '}' { bld.xAst.closeNode(); } + ; + +rule_for: for_base '{' rule_exprs '}' { bld.xAst.closeNode(); } + ; + +function_for: for_base '{' function_exprs '}' { bld.xAst.closeNode(); } + ; + +/* + * functions + */ + +function_def: TOK_FUNCTION UNDEF { + bld.xAst.addNode( AstNode::typeFunctionDef ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, $2 ); + bld.xAst.openBranch(); + } '(' param_defs ')' { + bld.xAst.openBranch(); + } '{' function_exprs '}' { + bld.xAst.closeNode(); + } + ; + +param_defs: + | param_def_list + ; + +param_def_list: variable + | param_def_list ',' variable + ; + +function_exprs: +// | function_exprs function ';' +// | function_exprs set + | function_exprs unset + | function_exprs line_expr + | function_exprs export + | function_exprs notify + | function_exprs function_if + | function_exprs function_for + | function_exprs return + | function_exprs process_target + ; + +return: TOK_RETURN { + bld.xAst.addNode( AstNode::typeReturn ); + bld.xAst.openBranch(); + } expr { + bld.xAst.closeNode(); + } ';' + ; + +/* + * Actions, they're basically functions, no parameters + */ + +action_def: TOK_ACTION STRING { + bld.xAst.addNode( AstNode::typeActionDef ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, $2 ); + bld.xAst.openBranch(); + } '{' function_exprs '}' { + bld.xAst.closeNode(); + } + ; + +/* + * profiles + */ + +profile: TOK_PROFILE { + bld.xAst.addNode( AstNode::typeProfile ); + bld.xAst.openBranch(); + } string { + bld.xAst.openBranch(); + } '{' profile_exprs '}' { + bld.xAst.closeNode(); + } /* in-line function */ + ; + +profile_exprs: +// | profile_exprs function ';' +// | profile_exprs set + | profile_exprs unset + | profile_exprs line_expr + | profile_exprs export + | profile_exprs notify + | profile_exprs function_if + | profile_exprs function_for + | profile_exprs return + | profile_exprs process_target + | profile_exprs condition + ; +/* + * targets + */ + +target: TOK_TARGET { + bld.xAst.addNode( AstNode::typeTarget ); + bld.xAst.openBranch(); + } expr { + bld.xAst.openBranch(); + } '{' target_exprs '}' { + bld.xAst.closeNode(); + } + ; + +target_exprs: +// | target_exprs set + | target_exprs unset + | target_exprs line_expr + | target_exprs export + | target_exprs target_input + | target_exprs requires + | target_exprs profile + | target_exprs target_if + | target_exprs target_for + | target_exprs notify + | target_exprs target_rule + | target_exprs tag + | target_exprs display + ; + +target_input: TOK_INPUT { + bld.xAst.addNode( AstNode::typeInput ); + bld.xAst.openBranch(); + } expr ';' { + bld.xAst.closeNode(); + } + ; + +target_rule: TOK_RULE { + bld.xAst.addNode( AstNode::typeRule ); + bld.xAst.openBranch(); + } string ';' { + bld.xAst.closeNode(); + } + ; + +condition: TOK_CONDITION CONDITION ';' { + bld.xAst.addNode( AstNode::typeCondition, $2 ); + } + | TOK_CONDITION TOK_ALWAYS ';'{ + bld.xAst.addNode( AstNode::typeCondition, "always" ); + } + | TOK_CONDITION TOK_NEVER ';'{ + bld.xAst.addNode( AstNode::typeCondition, "never" ); + } + ; + +/* + * rules + */ + +rule: TOK_RULE { + bld.xAst.addNode( AstNode::typeRuleDef ); + bld.xAst.openBranch(); + } string { + bld.xAst.openBranch(); + } '{' rule_exprs '}' { + bld.xAst.closeNode(); + } + ; + +rule_exprs: + | rule_exprs rule_input + | rule_exprs output + | rule_exprs requires + | rule_exprs profile + | rule_exprs rule_if + | rule_exprs rule_for + | rule_exprs notify + | rule_exprs display + | rule_exprs tag +// | rule_exprs set + ; + +rule_input_func: function + | STRING { + /* In this case, when the input is just a string, + lets actually turn it into a call to the matches function. + */ + bld.xAst.addNode( AstNode::typeFunction ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, "matches" ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, $1 ); + bld.xAst.closeNode(); + } +/* | string */ + ; + +rule_input: TOK_INPUT { + bld.xAst.addNode( AstNode::typeInput ); + bld.xAst.openBranch(); + } rule_input_func ';' { + bld.xAst.closeNode(); + } + ; + +output: TOK_OUTPUT { + bld.xAst.addNode( AstNode::typeOutput ); + bld.xAst.openBranch(); + } value ';' { + bld.xAst.closeNode(); + } + ; + +/* + * config + */ +config: TOK_CONFIG { + bld.xAst.addNode( AstNode::typeConfig ); + bld.xAst.openBranch(); + } string { + bld.xAst.openBranch(); + } '{' config_exprs '}' { + bld.xAst.closeNode(); + } + | TOK_AUTO TOK_CONFIG { + bld.xAst.addNode( AstNode::typeAutoConfig ); + bld.xAst.openBranch(); + } string { + bld.xAst.openBranch(); + } '{' config_exprs '}' { + bld.xAst.closeNode(); + } + | TOK_GLOBAL TOK_CONFIG { + bld.xAst.addNode( AstNode::typeGlobalConfig ); + bld.xAst.openBranch(); + } string { + bld.xAst.openBranch(); + } '{' config_exprs '}' { + bld.xAst.closeNode(); + } + ; + +config_exprs: + | config_exprs display + | config_exprs config_type + | config_exprs default + | config_exprs value_key + | config_exprs allow + | config_exprs cache + ; + +display: TOK_DISPLAY STRING ';' { + bld.xAst.addNode( AstNode::typeDisplay, $2 ); + } + ; + +config_type: TOK_TYPE { + bld.xAst.addNode( AstNode::typeType ); + bld.xAst.openBranch(); + } type ';' { + bld.xAst.closeNode(); + } + ; + +default: TOK_DEFAULT { + bld.xAst.addNode( AstNode::typeDefault ); + bld.xAst.openBranch(); + } literal ';' { + bld.xAst.closeNode(); + } + ; + +value_key_val: value ';' + | '{' function_exprs '}' /* inline function */ + +value_key: TOK_VALUE { + bld.xAst.addNode( AstNode::typeValue ); + bld.xAst.openBranch(); + } value_key_val { + bld.xAst.closeNode(); + } + ; + +allow: TOK_ALLOW { + bld.xAst.addNode( AstNode::typeAllow ); + bld.xAst.openBranch(); + } value ';' { + bld.xAst.closeNode(); + } + ; + +cache: TOK_CACHE TOK_ALWAYS ';' + { bld.xAst.addNode( AstNode::typeCache, true ); } + | TOK_CACHE TOK_NEVER ';' + { bld.xAst.addNode( AstNode::typeCache, false ); } + ; + +/* + * target/profile execute + */ +process_target: PROFILE + { + bld.xAst.addNode( AstNode::typeProcessTarget ); + bld.xAst.openBranch(); + bld.xAst.addNode( AstNode::typeString, $1 ); + bld.xAst.openBranch(); + } value ';' { + bld.xAst.closeNode(); + } + ; + +tag: TOK_TAG + { + bld.xAst.addNode( AstNode::typeTag ); + bld.xAst.openBranch(); + } value ';' { + bld.xAst.closeNode(); + } + ; + + +%% + +/* Epilogue -- whatever you want, functions mainly */ + +void build_error( YYLTYPE *locp, yyscan_t, BuildParser &bld, const char *msg ) +{ + bld.error( + locp->first_line, locp->last_line, + locp->first_column, locp->last_column, + msg + ); +} + diff --git a/src/buildparser.cpp b/src/buildparser.cpp new file mode 100644 index 0000000..e391523 --- /dev/null +++ b/src/buildparser.cpp @@ -0,0 +1,127 @@ +#include "buildparser.h" +#include "ast.h" +#include "build.yy.h" + +#include "bu/sio.h" +using Bu::sio; + +BuildParser::BuildParser( Ast &rAst ) : + xAst( rAst ) +{ + lIncludePaths.append("./"); +} + +BuildParser::~BuildParser() +{ +} + +int build_parse( yyscan_t yyscanner, BuildParser &bld ); + +void BuildParser::load( const Bu::FString &sFile ) +{ + yyscan_t scanner; + + sFilename.push( sFile ); + FILE *fIn = fopen( sFile.getStr(), "rt" ); + build_lex_init( &scanner ); + // build_set_debug( true, scanner ); + build_set_in( fIn, scanner ); + + build_parse( scanner, *this ); + + build_lex_destroy( scanner ); + fclose( fIn ); + + // Bu::sio << xAst; +} + +bool BuildParser::isKeyword( const Bu::FString &sStr ) +{ + if( sStr == "important" ) + return true; + if( sStr == "normal" ) + return true; + if( sStr == "hidden" ) + return true; + if( sStr == "autogenerated" ) + return true; + return false; +} + +bool BuildParser::isCond( const Bu::FString &sStr ) +{ + if( sStr == "filetime" ) + return true; + if( sStr == "always" ) + return true; + if( sStr == "never" ) + return true; + return false; +} + +void BuildParser::include( const Bu::FString &sStr, void *scanner, YYLTYPE *loc ) +{ + for( StrList::iterator pi = lIncludePaths.begin(); pi; pi++ ) + { + FILE *fIn = fopen( (*pi + sStr).getStr(), "rt" ); + if( fIn == NULL ) + { + continue; + } + sFilename.push( sStr ); + sLocation.push( *loc ); + loc->first_line = loc->last_line = 1; + loc->first_column = loc->last_column = 0; + build_push_buffer_state( + build__create_buffer( fIn, YY_READ_BUF_SIZE, scanner ), + scanner + ); + Bu::FString::const_iterator i = sStr.find('/'); + if( i ) + { + for(;;) + { + Bu::FString::const_iterator j = i.find('/'); + if( !j ) + break; + i = j+1; + } + sio << "Hey, found it from here: " << sStr.getSubStr( sStr.begin(), i ) << sio.nl; + xAst.addNode( AstNode::typePushPrefix, sStr.getSubStr( sStr.begin(), i ) ); + } + else + { + xAst.addNode( AstNode::typePushPrefix, "" ); + } + return; + } + Bu::FString msg("Could not open include file: "); + msg += sStr; + error( + loc->first_line, loc->last_line, + loc->first_column, loc->last_column, + msg + ); +} + +void BuildParser::endInclude( YYLTYPE *loc ) +{ + sFilename.pop(); + memcpy( loc, &sLocation.peek(), sizeof(YYLTYPE) ); + sLocation.pop(); + xAst.addNode( AstNode::typePopPrefix ); +} + +void BuildParser::error( int iLine1, int iLine2, int iCol1, int iCol2, + const Bu::FString &sMsg ) +{ + throw Bu::ExceptionBase("%s: %d-%d:%d-%d: %s", + sFilename.peek().getStr(), iLine1, iLine2, iCol1, iCol2, sMsg.getStr() + ); +} + +void BuildParser::addIncludePath( const Bu::FString &sPath ) +{ + lIncludePaths.append( sPath + "/" ); +} + diff --git a/src/buildparser.h b/src/buildparser.h new file mode 100644 index 0000000..8e2af6c --- /dev/null +++ b/src/buildparser.h @@ -0,0 +1,40 @@ +#ifndef BUILD_PARSER_H +#define BUILD_PARSER_H + +#include "build.tab.h" + +#include "bu/stack.h" +#include "bu/fstring.h" +#include "types.h" + +class BuildParser +{ +public: + BuildParser( class Ast &rAst ); + virtual ~BuildParser(); + + void load( const Bu::FString &sFile ); + + bool isKeyword( const Bu::FString &sStr ); + bool isCond( const Bu::FString &sStr ); + void include( const Bu::FString &sStr, void *scanner, YYLTYPE *loc ); + void endInclude( YYLTYPE *loc ); + + void error( int iLine1, int iLine2, int iCol1, int iCol2, + const Bu::FString &sMsg ); + + class Ast &xAst; + + void addIncludePath( const Bu::FString &sPath ); + +private: + Bu::Stack sFilename; + Bu::Stack sLocation; + StrList lIncludePaths; +}; + +typedef void * yyscan_t; +#define YY_DECL int build_lex( YYSTYPE *yylval_param, YYLTYPE *yylloc_param, yyscan_t yyscanner, BuildParser &b ) +YY_DECL; + +#endif diff --git a/src/condition.cpp b/src/condition.cpp new file mode 100644 index 0000000..ec87c0e --- /dev/null +++ b/src/condition.cpp @@ -0,0 +1,10 @@ +#include "condition.h" + +Condition::Condition() +{ +} + +Condition::~Condition() +{ +} + diff --git a/src/condition.h b/src/condition.h new file mode 100644 index 0000000..2e9b26a --- /dev/null +++ b/src/condition.h @@ -0,0 +1,16 @@ +#ifndef CONDITION_H +#define CONDITION_H + +class Condition +{ +public: + Condition(); + virtual ~Condition(); + + virtual bool shouldExec( class Runner &r, class Target &rTarget )=0; + virtual Condition *clone()=0; + +private: +}; + +#endif diff --git a/src/conditionalways.cpp b/src/conditionalways.cpp new file mode 100644 index 0000000..077b5b5 --- /dev/null +++ b/src/conditionalways.cpp @@ -0,0 +1,21 @@ +#include "conditionalways.h" +#include "target.h" + +ConditionAlways::ConditionAlways() +{ +} + +ConditionAlways::~ConditionAlways() +{ +} + +bool ConditionAlways::shouldExec( class Runner &/*r*/, Target &/*rTarget*/ ) +{ + return true; +} + +Condition *ConditionAlways::clone() +{ + return new ConditionAlways(); +} + diff --git a/src/conditionalways.h b/src/conditionalways.h new file mode 100644 index 0000000..1eeeb71 --- /dev/null +++ b/src/conditionalways.h @@ -0,0 +1,16 @@ +#ifndef CONDITION_ALWAYS_H +#define CONDITION_ALWAYS_H + +#include "condition.h" + +class ConditionAlways : public Condition +{ +public: + ConditionAlways(); + virtual ~ConditionAlways(); + + virtual bool shouldExec( class Runner &r, class Target &rTarget ); + virtual Condition *clone(); +}; + +#endif diff --git a/src/conditionfiletime.cpp b/src/conditionfiletime.cpp new file mode 100644 index 0000000..224caf1 --- /dev/null +++ b/src/conditionfiletime.cpp @@ -0,0 +1,73 @@ +#include "conditionfiletime.h" +#include "target.h" + +#include +#include +#include + +#include +using namespace Bu; + +ConditionFileTime::ConditionFileTime() +{ +} + +ConditionFileTime::~ConditionFileTime() +{ +} + +bool ConditionFileTime::shouldExec( class Runner &r, Target &rTarget ) +{ + for( StrList::const_iterator j = rTarget.getOutputList().begin(); j; j++ ) + { + if( access( (*j).getStr(), F_OK ) ) + { + //sio << "-- Target processed because '" << *j << "' doesn't exist." + // << sio.nl; + // Output doesn't exist + return true; + } + } + + time_t tOut = 0; + struct stat s; + for( StrList::const_iterator j = rTarget.getOutputList().begin(); + j; j++ ) + { + stat( (*j).getStr(), &s ); + if( tOut == 0 || tOut > s.st_mtime ) + { + tOut = s.st_mtime; + } + } + for( StrList::const_iterator j = rTarget.getInputList().begin(); + j; j++ ) + { + stat( (*j).getStr(), &s ); + if( tOut < s.st_mtime ) + { + //sio << "-- Target processed because '" << *j + // << "' is newer than output." << sio.nl; + return true; + } + } + rTarget.buildRequires( r ); + for( StrList::const_iterator j = rTarget.getRequiresList().begin(); + j; j++ ) + { + stat( (*j).getStr(), &s ); + if( tOut < s.st_mtime ) + { + //sio << "-- Target processed because '" << *j + // << "' is newer than output." << sio.nl; + return true; + } + } + return false; +} + +Condition *ConditionFileTime::clone() +{ + return new ConditionFileTime(); +} + diff --git a/src/conditionfiletime.h b/src/conditionfiletime.h new file mode 100644 index 0000000..6fb2e9d --- /dev/null +++ b/src/conditionfiletime.h @@ -0,0 +1,16 @@ +#ifndef CONDITION_FILE_TIME_H +#define CONDITION_FILE_TIME_H + +#include "condition.h" + +class ConditionFileTime : public Condition +{ +public: + ConditionFileTime(); + virtual ~ConditionFileTime(); + + virtual bool shouldExec( class Runner &r, class Target &rTarget ); + virtual Condition *clone(); +}; + +#endif diff --git a/src/conditionnever.cpp b/src/conditionnever.cpp new file mode 100644 index 0000000..1ab4375 --- /dev/null +++ b/src/conditionnever.cpp @@ -0,0 +1,21 @@ +#include "conditionnever.h" +#include "target.h" + +ConditionNever::ConditionNever() +{ +} + +ConditionNever::~ConditionNever() +{ +} + +bool ConditionNever::shouldExec( class Runner &/*r*/, Target &/*rTarget*/ ) +{ + return false; +} + +Condition *ConditionNever::clone() +{ + return new ConditionNever(); +} + diff --git a/src/conditionnever.h b/src/conditionnever.h new file mode 100644 index 0000000..b7e5e92 --- /dev/null +++ b/src/conditionnever.h @@ -0,0 +1,16 @@ +#ifndef CONDITION_NEVER_H +#define CONDITION_NEVER_H + +#include "condition.h" + +class ConditionNever : public Condition +{ +public: + ConditionNever(); + virtual ~ConditionNever(); + + virtual bool shouldExec( class Runner &r, class Target &rTarget ); + virtual Condition *clone(); +}; + +#endif diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000..c257a75 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,524 @@ +#include "context.h" +#include "target.h" +#include "rule.h" +#include "function.h" +#include "runner.h" +#include "action.h" +#include "profile.h" +#include "view.h" + +#include "functionreplace.h" +#include "functionexists.h" +#include "functionfiles.h" +#include "functionexecute.h" +#include "functionmatches.h" +#include "functiontostring.h" +#include "functionunlink.h" +#include "functiontargets.h" +#include "functiondirs.h" +#include "functiongetmakedeps.h" +#include "functionfilename.h" +#include "functiondirname.h" + +#include +#include +using namespace Bu; + +Context::Context() : + pView( NULL ) +{ + addFunction( new FunctionReplace() ); + addFunction( new FunctionExists() ); + addFunction( new FunctionFiles() ); + addFunction( new FunctionExecute() ); + addFunction( new FunctionMatches() ); + addFunction( new FunctionToString() ); + addFunction( new FunctionUnlink() ); + addFunction( new FunctionTargets() ); + addFunction( new FunctionDirs() ); + addFunction( new FunctionGetMakeDeps() ); + addFunction( new FunctionFileName() ); + addFunction( new FunctionDirName() ); + pushScope(); +} + +Context::~Context() +{ +} + +void Context::addTarget( Target *pTarget ) +{ + for( StrList::const_iterator i = pTarget->getOutputList().begin(); i; i++ ) + { + hTarget.insert( (*i).getStr(), pTarget ); + } +} + +void Context::addRule( Rule *pRule ) +{ + hRule.insert( pRule->getName(), pRule ); +} + +void Context::addFunction( Function *pFunction ) +{ + pFunction->setContext( this ); + hFunction.insert( pFunction->getName(), pFunction ); +} + +void Context::addVariable( const Bu::FString &sName, const Variable &vValue ) +{ + for( ScopeStack::iterator i = sVars.begin(); i; i++ ) + { + if( (*i).has( sName ) ) + { +// sio << "Replacing higher scope variable \"" << sName << "\" with value \"" << (*i).get( sName ) << "\" with new value \"" << vValue << "\"" << sio.nl; + (*i).insert( sName, vValue ); + return; + } + } + sVars.first().insert( sName, vValue ); +} + +void Context::addAction( Action *pAction ) +{ + hAction.insert( pAction->getName(), pAction ); +} + +Action *Context::getAction( const Bu::FString &sName ) +{ + return hAction.get( sName ); +} + +void Context::addTargetToTag( Target *pTarget, const Bu::FString &sTag ) +{ + if( !hTag.has( sTag ) ) + { + hTag.insert( sTag, TargetList() ); + } + hTag.get( sTag ).append( pTarget ); +} + +void Context::addTargetToTags( Target *pTarget, const StrList &sTags ) +{ + for( StrList::const_iterator i = sTags.begin(); i; i++ ) + { + addTargetToTag( pTarget, *i ); + } +} + +TargetList &Context::getTag( const Bu::FString &sTag ) +{ + return hTag.get( sTag ); +} + +Variable &Context::getVariable( const Bu::FString &sName ) +{ + for( ScopeStack::iterator i = sVars.begin(); i; i++ ) + { + if( (*i).has( sName ) ) + { + return (*i).get( sName ); + } + } + throw Bu::ExceptionBase("No such variable."); +} + +void Context::delVariable( const Bu::FString &sName ) +{ + for( ScopeStack::iterator i = sVars.begin(); i; i++ ) + { + if( (*i).has( sName ) ) + { + (*i).erase( sName ); + } + } +} + +void Context::pushScope() +{ + VarHash h; + if( !sVars.isEmpty() ) + h = sVars.peek(); + sVars.push( h ); +} + +void Context::pushScope( const VarHash &hNewVars ) +{ + VarHash h = hNewVars; + if( !sVars.isEmpty() ) + { + for( VarHash::iterator i = sVars.peek().begin(); i; i++ ) + { + if( !h.has( i.getKey() ) ) + h.insert( i.getKey(), i.getValue() ); + } + } + sVars.push( h ); +} + +VarHash &Context::getScope() +{ + return sVars.first(); +} + +void Context::popScope() +{ + sVars.pop(); +} + +Variable Context::call( const Bu::FString &sName, Variable &input, + VarList lParams ) +{ + if( !hFunction.has( sName ) ) + { + throw Bu::ExceptionBase("Unknown function called: %s", sName.getStr() ); + } + return hFunction.get( sName )->call( input, lParams ); +} + +#include +using namespace Bu; +Bu::FString Context::expand( const Bu::FString &sInS ) +{ + Bu::FString sRet; + Bu::FString sIn = sInS; + + for( int iPass = 0; iPass < 2; iPass++ ) + { + Bu::FString::const_iterator b = sIn.begin(); + sRet.clear(); + for(;;) + { + Bu::FString::const_iterator e = b.find('$'); + if( !e ) + { + sRet.append( b ); + break; + } + sRet.append( b, e ); + b = e+1; + if( !b ) + { + sRet.append('$'); + } + else if( *b == '{' ) + { + b++; + e = b.find('}'); + Bu::FString sVar( b, e ); + try + { + sRet.append( getVariable( sVar ).toString() ); + } catch(...) + { + // TODO: This may be handy debugging later... + //sio << "No variable named " << sVar << sio.nl; + //sio << "Vars: " << sVars << sio.nl << sio.nl; + } + b = e+1; + } + else if( *b == '(' && iPass == 1 ) + { + b++; + e = b.find(')'); + Bu::FString sCmd( b, e ); + Bu::FString sBuf; + try + { + //sio << "Executing command: >>>" << sCmd << "<<<" << sio.nl; + Process p( Process::StdOut, "/bin/bash", "/bin/bash", "-c", sCmd.getStr(), NULL ); + while( p.isRunning() ) + { + char buf[4096]; + sBuf.append( buf, p.read( buf, 4096 ) ); + } + sRet.append( sBuf ); + } catch(...) + { + // TODO: This may be handy debugging later... + //sio << "No variable named " << sVar << sio.nl; + //sio << "Vars: " << sVars << sio.nl << sio.nl; + } + b = e+1; + } + else + { + // Not a match, uh, just output the $ for now... + sRet.append('$'); + } + } + + sIn = sRet; + } + return sRet; +} + +Target *Context::getTarget( const Bu::FString &sOutput ) +{ + return hTarget.get( sOutput ); +} + +TargetList Context::getExplicitTargets() +{ + TargetList lRet; + for( TargetHash::iterator i = hTarget.begin(); i; i++ ) + { + if( (*i)->isExplicit() ) + lRet.append( *i ); + } + return lRet; +} + +void Context::buildTargetTree( Runner &r ) +{ + TargetList lTargets = hTarget.getValues(); + + for( TargetList::iterator i = lTargets.begin(); i; i++ ) + { + // I believe we only want to autogenerate targets for explicit targets + // that have rules defined. + if( !(*i)->isExplicit() || !(*i)->hasRule() ) + continue; + + StrList lNewIns; // The new "changed" inputs for this target + + Rule *pMaster = hRule.get( (*i)->getRule() ); + + for( StrList::const_iterator j = (*i)->getInputList().begin(); j; j++ ) + { + if( pMaster->ruleMatches( r, *j ) ) + { + lNewIns.append( *j ); + } + + if( hTarget.has( *j ) ) + { + // Find the existing dependancy + lNewIns.append( *j ); + } + //else + //{ + buildTargetTree( r, *i, *j, pMaster, lNewIns ); + //} + } + + pMaster->prepTarget( *i ); + (*i)->resetInputList( lNewIns ); + } + //sio << "Building dependancies: " << Fmt(3) << 0 << "%\r" << sio.flush; + //int iSize = hTarget.getSize(), iCur = 0; + for( TargetHash::iterator i = hTarget.begin(); i; i++ ) + { + // Before we can take a look at the requirements, we need to build + // them... + // (*i)->buildRequires( r ); + + // For every target we have to examine both it's inputs and it's + // additional requirements. Inputs first + StrList lDeps( (*i)->getInputList() ); + lDeps += (*i)->getRequiresList(); + for( StrList::const_iterator j = lDeps.begin(); j; j++ ) + { + try + { + (*i)->addDep( hTarget.get( *j ) ); + } + catch(...) + { + } + } + //iCur++; + // sio << "Building dependancies: " << Fmt(3) << (iCur*100/iSize) << "%\r" << sio.flush; + (*i)->collapseDeps(); + } +// sio << sio.nl; + + for( TargetHash::iterator i = hTarget.begin(); i; i++ ) + { + if( !(*i)->isExplicit() ) + continue; + (*i)->setDepCount(); + (*i)->resetRun( false ); + } +} + +void Context::buildTargetTree( class Runner &r, class Target * /*pTarget*/, const Bu::FString &sInput, Rule *pMaster, StrList &lNewIns ) +{ + Target *pNewTarget = NULL; + for( RuleHash::iterator i = hRule.begin(); i; i++ ) + { + if( (*i)->hasOutputs() && (*i)->ruleMatches( r, sInput ) ) + { + pNewTarget = (*i)->createTarget( r, sInput ); + + Bu::Hash hDone; + for( StrList::const_iterator oi = + pNewTarget->getOutputList().begin(); oi; oi++ ) + { + try + { + Target *pOver = hTarget.get( *oi ); + if( hDone.has( (ptrdiff_t)pOver ) ) + continue; + hDone.insert( (ptrdiff_t)pOver, true ); + if( !pOver->isExplicit() ) + { + delete pNewTarget; + pNewTarget = pOver; + break; + } + pOver->mergeUnder( pNewTarget ); + delete pNewTarget; +// sio << "Delete: " << Fmt::ptr() << (ptrdiff_t)pNewTarget << sio.nl; + pNewTarget = pOver; + break; + } + catch(...) + { + } + } + + // We actually want to add this either way, if the merge added new + // outputs, then we need to take them into account. + addTarget( pNewTarget ); + addTargetToTags( pNewTarget, (*i)->getTagList() ); + + // We have created a new target (or not, either way, we need to + // check if it matches.) + for( StrList::const_iterator m = + pNewTarget->getOutputList().begin(); m; m++ ) + { + // Does the new output match the master rule? + if( pMaster->ruleMatches( r, (*m) ) ) + { + lNewIns.append( (*m) ); + +// sio << "What?" << sio.nl; + // These relationships are difficult to pick up on except + // that one target was created by the other, I suppose. + // Anyway, that means that we need to add this while we + // can. +// pTarget->addDep( pNewTarget ); + } + // else + // { + buildTargetTree( r, pNewTarget, *m, pMaster, lNewIns ); + // } + } + + return; + } + } + if( !pNewTarget ) + { + //sio << "Incomplete tree created, trying to find purpose for \"" + // << sInput << "\"." << sio.nl; + return; + } +} + +void Context::attachDefaults() +{ + for( TargetHash::iterator i = hTarget.begin(); i; i++ ) + { + if( !(*i)->hasProfile("clean") ) + { + (*i)->addProfile( Profile::genDefaultClean() ); + } + } +} + +void Context::genDefaultActions() +{ + if( !hAction.has("all") ) + { + addAction( Action::genDefaultAll() ); + } + if( !hAction.has("clean") ) + { + addAction( Action::genDefaultClean() ); + } + if( !hAction.has("default") ) + { + addAction( Action::genDefaultDefault() ); + } +} + +void Context::writeTargetDot() +{ + Bu::Hash hDone; + sio << "digraph {" << sio.nl + << "\trankdir=LR;" << sio.nl; + for( TargetHash::iterator i = hTarget.begin(); i; i++ ) + { + if( hDone.has( (ptrdiff_t)*i ) ) + continue; + hDone.insert( (ptrdiff_t)*i, true ); + for( StrList::const_iterator l = (*i)->getOutputList().begin(); + l; l++ ) + { + for( StrList::const_iterator k = (*i)->getInputList().begin(); + k; k++ ) + { + sio << "\t\"" << *k << "\" -> \"" + << *l << "\";" << sio.nl; + } + for( StrList::const_iterator k = (*i)->getRequiresList().begin(); + k; k++ ) + { + sio << "\t\"" << *k << "\" -> \"" + << *l << "\" [color=red];" << sio.nl; + } + } + + } + sio << "}" << sio.nl; +} + +void Context::setView( View *pNewView ) +{ + delete pView; + pView = pNewView; +} + +View *Context::getView() +{ + return pView; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c ) +{ + f << "Variables: " << c.sVars << f.nl; + f << "Targets: " << c.hTarget << f.nl; + f << "Rules: " << c.hRule << f.nl; + + return f; +} + +void Context::printBasicInfo() +{ + sio << "Available actions:" << sio.nl << "\t"; + for( ActionHash::iterator i = hAction.begin(); i; i++ ) + { + if( i != hAction.begin() ) + sio << ", "; + sio << i.getKey(); + } + + TargetList lTargets = getExplicitTargets(); + sio << sio.nl << sio.nl << "Available targets:" << sio.nl << "\t"; + for( TargetList::iterator i = lTargets.begin(); i; i++ ) + { + if( i != lTargets.begin() ) + sio << ", "; + for( StrList::const_iterator j = (*i)->getOutputList().begin(); j; j++ ) + { + if( j != (*i)->getOutputList().begin() ) + sio << ", "; + sio << (*j); + } + } + + sio << sio.nl << sio.nl; +} + diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..0d9aaff --- /dev/null +++ b/src/context.h @@ -0,0 +1,101 @@ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "bu/hash.h" +#include "bu/fstring.h" + +#include "variable.h" + +class Target; +class Rule; +class Function; +class Action; +class View; + +class Context +{ + friend Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c ); +public: + Context(); + virtual ~Context(); + + void addTarget( Target *pTarget ); + void addRule( Rule *pRule ); + void addFunction( Function *pFunction ); + void addVariable( const Bu::FString &sName, const Variable &vValue ); + void addAction( Action *pAction ); + Action *getAction( const Bu::FString &sName ); + + void addTargetToTag( Target *pTarget, const Bu::FString &sTag ); + void addTargetToTags( Target *pTarget, const StrList &sTags ); + TargetList &getTag( const Bu::FString &sTag ); + + Variable &getVariable( const Bu::FString &sName ); + void delVariable( const Bu::FString &sName ); + + void pushScope(); + void pushScope( const VarHash &hNewVars ); + VarHash &getScope(); + void popScope(); + + Variable call( const Bu::FString &sName, Variable &input, VarList lParams ); + + Bu::FString expand( const Bu::FString &sIn ); + + Target *getTarget( const Bu::FString &sOutput ); + TargetList getExplicitTargets(); + + /** + * This function actually builds the dependancy tree, and is responsible + * for creating all of the auto-generated targets that are required by the + * explicitly created targets. + */ + void buildTargetTree( class Runner &r ); + + /** + * This is done as a final step, it goes through all targets and + * attaches things that they should have even if they haven't defined them, + * like a clean profile, they'll get that even if they haven't added one of + * their own. The defaults in this routine are added only if they aren't + * already defined in the target. It should be excetued after + * buildTargetTree, which means it doesn't need to affect rules. + */ + void attachDefaults(); + + /** + * This function generates some default actions if they don't already + * exist, pretty straight forward, it will create all, clean, and default + * (default is the same as all). + */ + void genDefaultActions(); + + void writeTargetDot(); + + void setView( View *pNewView ); + View *getView(); + + void printBasicInfo(); + +private: + void buildTargetTree( class Runner &r, class Target *pTarget, const Bu::FString &sInput, class Rule *pMaster, StrList &lNewIns ); + +private: + typedef Bu::Hash TargetHash; + typedef Bu::Hash RuleHash; + typedef Bu::Hash FunctionHash; + typedef Bu::Hash ActionHash; + typedef Bu::List ScopeStack; + typedef Bu::Hash TagHash; + + TargetHash hTarget; + RuleHash hRule; + FunctionHash hFunction; + ScopeStack sVars; + ActionHash hAction; + TagHash hTag; + View *pView; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c ); + +#endif diff --git a/src/function.cpp b/src/function.cpp new file mode 100644 index 0000000..f73f576 --- /dev/null +++ b/src/function.cpp @@ -0,0 +1,16 @@ +#include "function.h" + +Function::Function() : + pContext( NULL ) +{ +} + +Function::~Function() +{ +} + +void Function::setContext( class Context *p ) +{ + pContext = p; +} + diff --git a/src/function.h b/src/function.h new file mode 100644 index 0000000..9573bd3 --- /dev/null +++ b/src/function.h @@ -0,0 +1,23 @@ +#ifndef FUNCTION_H +#define FUNCTION_H + +#include "bu/fstring.h" +#include "variable.h" + +class Function +{ +public: + Function(); + virtual ~Function(); + + virtual Bu::FString getName() const=0; + + virtual Variable call( Variable &input, VarList lParams )=0; + + void setContext( class Context *p ); + +protected: + class Context *pContext; +}; + +#endif diff --git a/src/functionast.cpp b/src/functionast.cpp new file mode 100644 index 0000000..0d9a938 --- /dev/null +++ b/src/functionast.cpp @@ -0,0 +1,33 @@ +#include "functionast.h" +#include "astbranch.h" +#include "astleaf.h" +#include "runner.h" +#include "context.h" + +FunctionAst::FunctionAst( const AstBranch *pRoot, class Runner *pRunner ) : + pRoot( pRoot ), + pRunner( pRunner ) +{ + sName = dynamic_cast( + *(*pRoot->getBranchBegin()).begin() + )->getStrValue(); +} + +FunctionAst::~FunctionAst() +{ +} + +Bu::FString FunctionAst::getName() const +{ + return sName; +} + +Variable FunctionAst::call( Variable &input, VarList /*lParams*/ ) +{ + pContext->pushScope(); + pContext->addVariable("INPUT", input ); + Variable vRet = pRunner->run( (*(pRoot->getBranchBegin()+2)).begin() ); + pContext->popScope(); + return vRet; +} + diff --git a/src/functionast.h b/src/functionast.h new file mode 100644 index 0000000..b971683 --- /dev/null +++ b/src/functionast.h @@ -0,0 +1,21 @@ +#ifndef FUNCTION_AST_H +#define FUNCTION_AST_H + +#include "function.h" + +class FunctionAst : public Function +{ +public: + FunctionAst( const class AstBranch *pRoot, class Runner *pRunner ); + virtual ~FunctionAst(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +private: + Bu::FString sName; + const class AstBranch *pRoot; + class Runner *pRunner; +}; + +#endif diff --git a/src/functiondirname.cpp b/src/functiondirname.cpp new file mode 100644 index 0000000..e8b728b --- /dev/null +++ b/src/functiondirname.cpp @@ -0,0 +1,38 @@ +#include "functiondirname.h" + +FunctionDirName::FunctionDirName() +{ +} + +FunctionDirName::~FunctionDirName() +{ +} + +Bu::FString FunctionDirName::getName() const +{ + return "dirName"; +} + +Variable FunctionDirName::call( Variable &input, VarList /*lParams*/ ) +{ + Bu::FString sFile; + sFile = input.getString(); + + Bu::FString::const_iterator i = sFile.begin(); + Bu::FString::const_iterator io; + for(;;) + { + Bu::FString::const_iterator b = i.find('/'); + if( !b ) + { + return Variable( Bu::FString( sFile.begin(), io ) ); + } + io = b; + i = b+1; + if( !i ) + { + return Variable( Bu::FString( sFile.begin(), io ) ); + } + } +} + diff --git a/src/functiondirname.h b/src/functiondirname.h new file mode 100644 index 0000000..830a992 --- /dev/null +++ b/src/functiondirname.h @@ -0,0 +1,17 @@ +#ifndef FUNCTION_DIR_NAME_H +#define FUNCTION_DIR_NAME_H + +#include "function.h" + +class FunctionDirName : public Function +{ +public: + FunctionDirName(); + virtual ~FunctionDirName(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functiondirs.cpp b/src/functiondirs.cpp new file mode 100644 index 0000000..fb64ef3 --- /dev/null +++ b/src/functiondirs.cpp @@ -0,0 +1,61 @@ +#include "functiondirs.h" + +#include +#include +#include +#include + +FunctionDirs::FunctionDirs() +{ +} + +FunctionDirs::~FunctionDirs() +{ +} + +Bu::FString FunctionDirs::getName() const +{ + return "dirs"; +} + +Variable FunctionDirs::call( Variable &/*input*/, VarList lParams ) +{ + glob_t globbuf; + + int flags = 0; + + for( VarList::const_iterator i = lParams.begin(); i; i++ ) + { + switch( (*i).getType() ) + { + case Variable::typeString: + glob( (*i).getString().getStr(), flags, NULL, &globbuf ); + flags |= GLOB_APPEND; + break; + + case Variable::typeList: + throw Bu::ExceptionBase("Lists not supported in glob yet."); + break; + + default: + throw Bu::ExceptionBase( + "Cannot use a non-string or non-list as a parameter to glob" + ); + break; + } + } + + Variable vRet( Variable::typeList ); + struct stat s; + for( size_t j = 0; j < globbuf.gl_pathc; j++ ) + { + stat( globbuf.gl_pathv[j], &s ); + if( S_ISDIR( s.st_mode ) ) + vRet.append( globbuf.gl_pathv[j] ); + } + + globfree( &globbuf ); + + return vRet; +} + diff --git a/src/functiondirs.h b/src/functiondirs.h new file mode 100644 index 0000000..5edfaf9 --- /dev/null +++ b/src/functiondirs.h @@ -0,0 +1,17 @@ +#ifndef FUNCTION_DIRS_H +#define FUNCTION_DIRS_H + +#include "function.h" + +class FunctionDirs : public Function +{ +public: + FunctionDirs(); + virtual ~FunctionDirs(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functionexecute.cpp b/src/functionexecute.cpp new file mode 100644 index 0000000..619d2c2 --- /dev/null +++ b/src/functionexecute.cpp @@ -0,0 +1,68 @@ +#include "functionexecute.h" +#include "context.h" +#include "view.h" + +#include +#include +using namespace Bu; + +FunctionExecute::FunctionExecute() +{ +} + +FunctionExecute::~FunctionExecute() +{ +} + +Bu::FString FunctionExecute::getName() const +{ + return "execute"; +} + +Variable FunctionExecute::call( Variable &/*input*/, VarList lParams ) +{ + // TODO This is lame, really lame, we need to exec on our own and process + // output appropriately. + pContext->getView()->cmdStarted( lParams.first().getString() ); + Process pCmd( Process::Both, "/bin/bash", "/bin/bash", "-c", + lParams.first().getString().getStr(), NULL ); + FString sStdOut, sStdErr; + while( pCmd.isRunning() ) + { + char buf[4096]; + bool out, err; + pCmd.select( out, err ); + if( err ) + { + int iRead = pCmd.readErr( buf, 4096 ); + sStdErr.append( buf, iRead ); + //sio << "Read " << iRead << " bytes of stderr." << sio.nl; + } + if( out ) + { + int iRead = pCmd.read( buf, 4096 ); + sStdOut.append( buf, iRead ); + //sio << "Read " << iRead << " bytes of stdout." << sio.nl; + } + } + + pContext->getView()->cmdFinished( + sStdOut, sStdErr, pCmd.childExitStatus() + ); + if( pCmd.childExited() ) + { + if( pCmd.childExitStatus() != 0 ) + { + throw Bu::ExceptionBase("Command exited with errorcode %d.", pCmd.childExitStatus() ); + } + } + else + { + pContext->getView()->cmdFinished( + sStdOut, sStdErr, pCmd.childExitStatus() + ); + throw Bu::ExceptionBase("Command Failed"); + } + return Variable( pCmd.childExitStatus() ); +} + diff --git a/src/functionexecute.h b/src/functionexecute.h new file mode 100644 index 0000000..ebeaa7f --- /dev/null +++ b/src/functionexecute.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_EXECUTE_H +#define FUNCTION_EXECUTE_H + +#include "function.h" + +class FunctionExecute : public Function +{ +public: + FunctionExecute(); + virtual ~FunctionExecute(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/functionexists.cpp b/src/functionexists.cpp new file mode 100644 index 0000000..d2aa9e9 --- /dev/null +++ b/src/functionexists.cpp @@ -0,0 +1,34 @@ +#include "functionexists.h" + +#include + +FunctionExists::FunctionExists() +{ +} + +FunctionExists::~FunctionExists() +{ +} + +Bu::FString FunctionExists::getName() const +{ + return "exists"; +} + +Variable FunctionExists::call( Variable &input, VarList lParams ) +{ + Bu::FString sFile; + if( input.getType() != Variable::typeNone ) + { + sFile = input.toString(); + } + else + { + sFile = lParams.first().toString(); + } + if( access( sFile.getStr(), F_OK ) == 0 ) + return Variable( true ); + else + return Variable( false ); +} + diff --git a/src/functionexists.h b/src/functionexists.h new file mode 100644 index 0000000..8a3001a --- /dev/null +++ b/src/functionexists.h @@ -0,0 +1,17 @@ +#ifndef FUNCTION_EXISTS_H +#define FUNCTION_EXISTS_H + +#include "function.h" + +class FunctionExists : public Function +{ +public: + FunctionExists(); + virtual ~FunctionExists(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functionfilename.cpp b/src/functionfilename.cpp new file mode 100644 index 0000000..21a4655 --- /dev/null +++ b/src/functionfilename.cpp @@ -0,0 +1,36 @@ +#include "functionfilename.h" + +FunctionFileName::FunctionFileName() +{ +} + +FunctionFileName::~FunctionFileName() +{ +} + +Bu::FString FunctionFileName::getName() const +{ + return "fileName"; +} + +Variable FunctionFileName::call( Variable &input, VarList /*lParams*/ ) +{ + Bu::FString sFile; + sFile = input.getString(); + + Bu::FString::const_iterator i = sFile.begin(); + for(;;) + { + Bu::FString::const_iterator b = i.find('/'); + if( !b ) + { + return Variable( Bu::FString( i ) ); + } + i = b+1; + if( !i ) + { + return Variable( Bu::FString( i ) ); + } + } +} + diff --git a/src/functionfilename.h b/src/functionfilename.h new file mode 100644 index 0000000..1401fc7 --- /dev/null +++ b/src/functionfilename.h @@ -0,0 +1,17 @@ +#ifndef FUNCTION_FILE_NAME_H +#define FUNCTION_FILE_NAME_H + +#include "function.h" + +class FunctionFileName : public Function +{ +public: + FunctionFileName(); + virtual ~FunctionFileName(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functionfiles.cpp b/src/functionfiles.cpp new file mode 100644 index 0000000..e708d45 --- /dev/null +++ b/src/functionfiles.cpp @@ -0,0 +1,61 @@ +#include "functionfiles.h" + +#include +#include +#include +#include + +FunctionFiles::FunctionFiles() +{ +} + +FunctionFiles::~FunctionFiles() +{ +} + +Bu::FString FunctionFiles::getName() const +{ + return "files"; +} + +Variable FunctionFiles::call( Variable &/*input*/, VarList lParams ) +{ + glob_t globbuf; + + int flags = 0; + + for( VarList::const_iterator i = lParams.begin(); i; i++ ) + { + switch( (*i).getType() ) + { + case Variable::typeString: + glob( (*i).getString().getStr(), flags, NULL, &globbuf ); + flags |= GLOB_APPEND; + break; + + case Variable::typeList: + throw Bu::ExceptionBase("Lists not supported in glob yet."); + break; + + default: + throw Bu::ExceptionBase( + "Cannot use a non-string or non-list as a parameter to glob" + ); + break; + } + } + + Variable vRet( Variable::typeList ); + struct stat s; + for( size_t j = 0; j < globbuf.gl_pathc; j++ ) + { + stat( globbuf.gl_pathv[j], &s ); + if( S_ISREG( s.st_mode ) ) + vRet.append( globbuf.gl_pathv[j] ); + } + + globfree( &globbuf ); + + return vRet; +} + diff --git a/src/functionfiles.h b/src/functionfiles.h new file mode 100644 index 0000000..711288a --- /dev/null +++ b/src/functionfiles.h @@ -0,0 +1,17 @@ +#ifndef FUNCTION_FILES_H +#define FUNCTION_FILES_H + +#include "function.h" + +class FunctionFiles : public Function +{ +public: + FunctionFiles(); + virtual ~FunctionFiles(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functiongetmakedeps.cpp b/src/functiongetmakedeps.cpp new file mode 100644 index 0000000..1aded15 --- /dev/null +++ b/src/functiongetmakedeps.cpp @@ -0,0 +1,59 @@ +#include "functiongetmakedeps.h" + +#include +#include +using namespace Bu; + +FunctionGetMakeDeps::FunctionGetMakeDeps() +{ +} + +FunctionGetMakeDeps::~FunctionGetMakeDeps() +{ +} + +Bu::FString FunctionGetMakeDeps::getName() const +{ + return "getMakeDeps"; +} + +Variable FunctionGetMakeDeps::call( Variable &/*input*/, VarList lParams ) +{ + Process p( Process::StdOut, "/bin/bash", "/bin/bash", "-c", + lParams.first().getString().getStr(), NULL ); + + // Gather all data from the command. + Bu::FString sBuf; + while( !p.isEos() ) + { + char buf[4096]; + int iRead = p.read( buf, 4096 ); + sBuf.append( buf, iRead ); + } + + Variable vRet( Variable::typeList ); + + Bu::FString::iterator i, j; + i = sBuf.find(':')+2; + while( i ) + { + // Find whitespace at the end of the word, this one is easy, there's + // always a space after a word + for( j = i; j && *j != ' ' && *j != '\n' && *j != '\r'; j++ ) { } + + Bu::FString sTmp( i, j ); + vRet.append( sTmp ); + + // Find the begining of the next word, trickier, we don't want to go + // off the end, and we need to skip \ chars at the ends of lines, right + // now this is too stupid to do that, so it may not work on windows. + // TODO: perhaps make this only skip \ chars at the ends of lines, + // we'll see if it matters. + for( i = j+1; + i && (*i == ' ' || *i == '\\' || *i == '\n' || *i == '\r'); i++ ) + { } + } + + return vRet; +} + diff --git a/src/functiongetmakedeps.h b/src/functiongetmakedeps.h new file mode 100644 index 0000000..b8f20d5 --- /dev/null +++ b/src/functiongetmakedeps.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_GET_MAKE_DEPS_H +#define FUNCTION_GET_MAKE_DEPS_H + +#include "function.h" + +class FunctionGetMakeDeps : public Function +{ +public: + FunctionGetMakeDeps(); + virtual ~FunctionGetMakeDeps(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/functionmatches.cpp b/src/functionmatches.cpp new file mode 100644 index 0000000..4e4b7ff --- /dev/null +++ b/src/functionmatches.cpp @@ -0,0 +1,141 @@ +#include "functionmatches.h" + +#include + +FunctionMatches::FunctionMatches() +{ +} + +FunctionMatches::~FunctionMatches() +{ +} + +Bu::FString FunctionMatches::getName() const +{ + return "matches"; +} + +bool FunctionMatches::globcmp( const Bu::FString &sTxt, const Bu::FString &sMatches ) +{ + Bu::FString::const_iterator t, g; + t = sTxt.begin(); + g = sMatches.begin(); + + while( g && t ) + { + switch( *g ) + { + case '*': + // First, if the * is at the end, then we do match, it doesn't + // matter what is in sTxt + if( !(g+1) ) + return true; + // Now attempt to scan for the remainder as a matched set + { + Bu::FString::const_iterator tn = t+1, gn = g+1, gi=g+1; + bool bFoundMatch = false; + while( tn && gn ) + { + if( *gn == '*' ) + { + g = gn; + t = tn; + break; + } + if( *tn == *gn ) + { + g = gn; + t = tn; + tn++; + gn++; + bFoundMatch = true; + } + else + { + gn = gi; + tn++; + bFoundMatch = false; + } + } + if( bFoundMatch == false ) + return false; + if( !tn && !gn && bFoundMatch ) + return true; + } + break; + + case '?': + // Don't bother checking. + t++; + g++; + break; + + default: + if( *t != *g ) + return false; + t++; + g++; + break; + } + } + if( t || (g && *g != '*') ) + return false; + return true; +} + +bool FunctionMatches::matchlist( const Bu::FString &sTxt, VarList &lParams ) +{ + for( VarList::iterator i = lParams.begin(); i; i++ ) + { + if( (*i).getType() == Variable::typeList ) + { + for( VarList::iterator j = (*i).begin(); j; j++ ) + { + if( globcmp( sTxt, (*j).toString() ) ) + return true; + } + } + else + { + if( globcmp( sTxt, (*i).toString() ) ) + return true; + } + } + return false; +} + +Variable FunctionMatches::call( Variable &input, VarList lParams ) +{ + switch( input.getType() ) + { + case Variable::typeString: + { + Bu::FString sTxt = input.getString(); + return Variable( matchlist( sTxt, lParams ) ); + } + break; + + case Variable::typeList: + { + Variable vRet( Variable::typeList ); + for( VarList::iterator i = input.begin(); i; i++ ) + { + if( (*i).getType() != Variable::typeString ) + continue; + Bu::FString sTxt = (*i).getString(); + if( matchlist( sTxt, lParams ) ) + vRet.append( *i ); + } + return vRet; + } + break; + + default: + throw Bu::ExceptionBase("You can only use a string or list as the " + "input to matches."); + break; + } + + return Variable(); +} + diff --git a/src/functionmatches.h b/src/functionmatches.h new file mode 100644 index 0000000..7757a44 --- /dev/null +++ b/src/functionmatches.h @@ -0,0 +1,23 @@ +#ifndef FUNCTION_MATCHES_H +#define FUNCTION_MATCHES_H + +#include "function.h" + +class FunctionMatches : public Function +{ +public: + FunctionMatches(); + virtual ~FunctionMatches(); + + /** + * Really basic globbing function, it doesn't handle character classes, + * just * and ?. We can expand on it later, it may be handy. + */ + bool globcmp( const Bu::FString &sTxt, const Bu::FString &sMatches ); + bool matchlist( const Bu::FString &sTxt, VarList &lParams ); + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); + +}; + +#endif diff --git a/src/functionreplace.cpp b/src/functionreplace.cpp new file mode 100644 index 0000000..d269083 --- /dev/null +++ b/src/functionreplace.cpp @@ -0,0 +1,47 @@ +#include "functionreplace.h" + +FunctionReplace::FunctionReplace() +{ +} + +FunctionReplace::~FunctionReplace() +{ +} + +Bu::FString FunctionReplace::getName() const +{ + return "replace"; +} + +Variable FunctionReplace::call( Variable &input, VarList lParams ) +{ + Bu::FString sA, sB; + sA = lParams.first().getString(); + sB = lParams.last().getString(); + switch( input.getType() ) + { + case Variable::typeString: + { + Variable vOut( input.getString().replace( sA, sB ) ); + return vOut; + } + break; + + case Variable::typeList: + { + Variable vOut( Variable::typeList ); + for( VarList::iterator i = input.begin(); i; i++ ) + { + vOut.append( (*i).getString().replace( sA, sB ) ); + } + return vOut; + } + break; + + default: + break; + } + throw Bu::ExceptionBase( + "replace does not work on non-string or non-list types."); +} + diff --git a/src/functionreplace.h b/src/functionreplace.h new file mode 100644 index 0000000..1bf4dae --- /dev/null +++ b/src/functionreplace.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_REPLACE_H +#define FUNCTION_REPLACE_H + +#include "function.h" + +class FunctionReplace : public Function +{ +public: + FunctionReplace(); + virtual ~FunctionReplace(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/functiontargets.cpp b/src/functiontargets.cpp new file mode 100644 index 0000000..93fbb96 --- /dev/null +++ b/src/functiontargets.cpp @@ -0,0 +1,39 @@ +#include "functiontargets.h" +#include "context.h" +#include "target.h" + +FunctionTargets::FunctionTargets() +{ +} + +FunctionTargets::~FunctionTargets() +{ +} + +Bu::FString FunctionTargets::getName() const +{ + return "targets"; +} + +Variable FunctionTargets::call( Variable &/*input*/, VarList lParams ) +{ + Variable vRet( Variable::typeList ); + TargetList lTrg; + if( lParams.getSize() == 0 ) + { + lTrg = pContext->getExplicitTargets(); + } + else + { + lTrg = pContext->getTag( lParams.first().toString() ); + } + for( TargetList::const_iterator i = lTrg.begin(); i; i++ ) + { + for( StrList::const_iterator j = (*i)->getOutputList().begin(); j; j++ ) + { + vRet.append( *j ); + } + } + return vRet; +} + diff --git a/src/functiontargets.h b/src/functiontargets.h new file mode 100644 index 0000000..9b65d30 --- /dev/null +++ b/src/functiontargets.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_TARGETS_H +#define FUNCTION_TARGETS_H + +#include "function.h" + +class FunctionTargets : public Function +{ +public: + FunctionTargets(); + virtual ~FunctionTargets(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/functiontostring.cpp b/src/functiontostring.cpp new file mode 100644 index 0000000..0c04091 --- /dev/null +++ b/src/functiontostring.cpp @@ -0,0 +1,50 @@ +#include "functiontostring.h" + +#include +#include +using namespace Bu; + +FunctionToString::FunctionToString() +{ +} + +FunctionToString::~FunctionToString() +{ +} + +Bu::FString FunctionToString::getName() const +{ + return "toString"; +} + +Variable FunctionToString::call( Variable &input, VarList lParams ) +{ + Bu::FString sStr; + Bu::FString sSep; + if( lParams.getSize() == 0 ) + { + sSep = " "; + } + else + { + sSep = lParams.first().getString(); + } + switch( input.getType() ) + { + case Variable::typeString: + return input; + + case Variable::typeList: + for( VarList::iterator i = input.begin(); i; i++ ) + { + if( i != input.begin() ) + sStr += sSep; + sStr += (*i).getString(); + } + return Variable( sStr ); + + default: + return Variable( input.getString() ); + } +} + diff --git a/src/functiontostring.h b/src/functiontostring.h new file mode 100644 index 0000000..3c3ecc7 --- /dev/null +++ b/src/functiontostring.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_TO_STRING_H +#define FUNCTION_TO_STRING_H + +#include "function.h" + +class FunctionToString : public Function +{ +public: + FunctionToString(); + virtual ~FunctionToString(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/functionunlink.cpp b/src/functionunlink.cpp new file mode 100644 index 0000000..addcfd9 --- /dev/null +++ b/src/functionunlink.cpp @@ -0,0 +1,51 @@ +#include "functionunlink.h" + +#include +#include +#include +using namespace Bu; + +FunctionUnlink::FunctionUnlink() +{ +} + +FunctionUnlink::~FunctionUnlink() +{ +} + +Bu::FString FunctionUnlink::getName() const +{ + return "unlink"; +} + +Variable FunctionUnlink::call( Variable &/*input*/, VarList lParams ) +{ + //sio << "Unlink called: " << lParams << sio.nl; + for( VarList::iterator p = lParams.begin(); p; p++ ) + { + switch( (*p).getType() ) + { + case Variable::typeString: + //sio << " xx> " << (*p).getString() << sio.nl; + unlink( (*p).getString().getStr() ); + break; + + case Variable::typeList: + //sio << " xx>"; + for( VarList::iterator i = (*p).begin(); i; i++ ) + { + //sio << " " << (*i).getString(); + unlink( (*i).getString().getStr() ); + } + //sio << sio.nl; + break; + + default: + throw Bu::ExceptionBase("Hey, wrong type passed."); + break; + } + } + + return Variable(); +} + diff --git a/src/functionunlink.h b/src/functionunlink.h new file mode 100644 index 0000000..ac3f21e --- /dev/null +++ b/src/functionunlink.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_UNLINK_H +#define FUNCTION_UNLINK_H + +#include "function.h" + +class FunctionUnlink : public Function +{ +public: + FunctionUnlink(); + virtual ~FunctionUnlink(); + + virtual Bu::FString getName() const; + virtual Variable call( Variable &input, VarList lParams ); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..142927d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,255 @@ +#include "buildparser.h" +#include "context.h" +#include "ast.h" +#include "runner.h" +#include "target.h" + +#include "viewplugger.h" + +#include +#include +#include +#include +#include +#include + +extern char **environ; + +using namespace Bu; + +class Options : public Bu::OptParser +{ +public: + Options( int argc, char *argv[] ) : + sView("default"), + sAction("default"), + sConfig("default.bld"), + bDot( false ), + bDebug( false ), + bAutoInclude( true ), + bAstDump( false ), + bEnviron( true ), + iInfoLevel( 0 ) + { + bool bClean = false; + addHelpBanner("build mark 3\n"); + + Bu::FString sViews("Select a view from: "); + StrList lViews = ViewPlugger::getInstance().getPluginList(); + for( StrList::iterator i = lViews.begin(); i; i++ ) + { + if( i != lViews.begin() ) + sViews += ", "; + sViews += *i; + } + + addHelpBanner("The following options do things other than build:"); + addOption( iInfoLevel, 'i', "info", "Display some basic info about the " + "loaded build config, including available targets."); + + addHelpBanner("The following options control general execution:"); + addOption( sView, 'v', "view", sViews ); + addOption( sConfig, 'f', "file", "Select a different config file." ); + addOption( bClean, 'c', "Shorthand for running action 'clean'. If an " + "action is specified, this will modify it to run 'clean-action'."); + addOption( slot(this, &Options::onChdir), 'C', "chdir", + "Change to directory before doing anything else."); + + addHelpBanner("\nThe following options control debugging:"); + addOption( bEnviron, "no-env", "Do not import environment variables."); + addOption( bDot, "dot", "Generate a dot chart after execution." ); + addOption( bDebug, "debug", + "Dump massive amounts of hard to read debugging data." ); + addOption( bAstDump, "debug-ast", + "Display the raw AST that is computed from parsing the input. " + "You should probably never ever use this, it'll scare you." + ); + + setOverride( "no-env", "false" ); + setOverride( "dot", "true" ); + setOverride( "debug", "true" ); + setOverride( "debug-ast", "true" ); + setOverride( "info", "1" ); + setOverride( 'c', "true" ); + + addHelpOption(); + + setNonOption( slot( this, &Options::onNonOption ) ); + + parse( argc, argv ); + + if( bClean ) + { + if( sAction == "default" ) + sAction = "clean"; + else + sAction.prepend("clean-"); + } + } + + virtual ~Options() + { + } + + int onChdir( StrArray sParams ) + { + if( sParams.getSize() == 0 ) + { + sio << "You must specify a directory name!" << sio.nl << sio.nl; + exit(2); + } + chdir( sParams[1].getStr() ); + return 1; + } + + int onNonOption( StrArray sParams ) + { + sAction = sParams[0]; + return 0; + } + + Bu::FString sView; + Bu::FString sAction; + Bu::FString sConfig; + bool bDot; + bool bDebug; + bool bAutoInclude; + bool bAstDump; + bool bEnviron; + int iInfoLevel; +}; + +int main( int argc, char *argv[] ) +{ + typedef Bu::List StrList; + StrList lShareList; + lShareList.append("/usr/share/build/").append("./share/"); + Ast ast; + Context cnt; + BuildParser bp( ast ); + + for( StrList::iterator i = lShareList.begin(); i; i++ ) + { + bp.addIncludePath( *i + "include"); + } + + Options opts( argc, argv ); + + try + { + cnt.setView( ViewPlugger::getInstance().instantiate( opts.sView ) ); + } + catch( Bu::HashException &e ) + { + sio << "Error: Invalid view specified, please choose from the " + "following choices:" << sio.nl << sio.nl << "\t"; + + StrList lViews = ViewPlugger::getInstance().getPluginList(); + for( StrList::iterator i = lViews.begin(); i; i++ ) + { + if( i != lViews.begin() ) + sio << ", "; + sio << *i; + } + sio << sio.nl << sio.nl; + return 1; + } + + // Load up the environment as vars. + if( opts.bEnviron ) + { + for( char **env = environ; *env; env++ ) + { + int iSplit; + for( iSplit = 0; (*env)[iSplit] != '='; iSplit++ ) { } + cnt.addVariable( + FString( *env, iSplit ), + FString( *env+iSplit+1 ) + ); + } + } + + if( opts.bAutoInclude ) + { + DIR *d; + Bu::FString sAutoDir; + for( StrList::iterator i = lShareList.begin(); i; i++ ) + { + sAutoDir = *i + "autoinclude"; + d = opendir( sAutoDir.getStr() ); + if( d ) + break; + } + if( !d ) + { + cnt.getView()->sysWarning( + "Could not find an autoinclude directory." + ); + } + else + { + struct dirent *de; + while( (de = readdir( d )) ) + { + if( de->d_name[0] == '.' || (de->d_type != DT_REG) ) + continue; + //sio << "Auto-including: " << de->d_name << sio.nl; + bp.load( sAutoDir + "/" + de->d_name ); + } + } + } + + bp.load( opts.sConfig ); + + if( opts.bAstDump ) + { + sio << ast << sio.nl << sio.nl; + return 0; + } + +// sio << ast << sio.nl; + + Runner r( ast, cnt ); + r.initialize(); + + r.run(); + + switch( opts.iInfoLevel ) + { + case 0: + // Do nothing + break; + + case 1: + cnt.printBasicInfo(); + return 0; + } + + try + { + r.execAction( opts.sAction ); + } + catch( std::exception &e ) + { + cnt.getView()->sysError(e.what()); + } + catch( ... ) + { + cnt.getView()->sysError( + "Unknown error occured, this is probably bad..." + ); + } + + if( opts.bDot ) + { + cnt.writeTargetDot(); + } + + if( opts.bDebug ) + { + sio << "Final context:" << sio.nl << cnt << sio.nl << sio.nl; + } + + return 0; +} + diff --git a/src/profile.cpp b/src/profile.cpp new file mode 100644 index 0000000..878a6e9 --- /dev/null +++ b/src/profile.cpp @@ -0,0 +1,116 @@ +#include "profile.h" +#include "ast.h" +#include "astbranch.h" +#include "astleaf.h" +#include "condition.h" + +#include "conditionfiletime.h" +#include "conditionalways.h" +#include "conditionnever.h" + +#include +using namespace Bu; + +Profile::Profile( const class AstBranch *pRoot ) : + pRoot( pRoot ), + pCond( NULL ), + pAst( NULL ) +{ + sName = dynamic_cast( + (*pRoot->getBranchBegin()).first() + )->getStrValue(); + + setCondition(); +} + +Profile::Profile( const Profile &rSrc ) : + sName( rSrc.sName ), + pRoot( rSrc.pRoot ), + pCond( rSrc.pCond->clone() ), + pAst( NULL ) +{ +} + +Profile::~Profile() +{ + delete pAst; + pAst = NULL; +} + +const Bu::FString &Profile::getName() const +{ + return sName; +} + +const AstBranch *Profile::getRoot() const +{ + return pRoot; +} + +const Condition *Profile::getCond() const +{ + return pCond; +} + +bool Profile::shouldExec( class Runner &r, class Target &rTarget ) const +{ + return pCond->shouldExec( r, rTarget ); +} + +Profile *Profile::genDefaultClean() +{ + Ast *pAst = new Ast(); + pAst->addNode( AstNode::typeProfile ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "clean" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeCondition, "always" ); + pAst->addNode( AstNode::typeFunction ); + pAst->openBranch(); + pAst->addNode( AstNode::typeString, "unlink" ); + pAst->openBranch(); + pAst->addNode( AstNode::typeVariable, "OUTPUT" ); + pAst->closeNode(); + pAst->closeNode(); + //pAst->closeNode(); + Profile *pRet = new Profile( + dynamic_cast(*pAst->getNodeBegin()) + ); + pRet->pAst = pAst; + + return pRet; +} + +void Profile::setCondition() +{ + for( AstBranch::NodeList::const_iterator i = + (*(pRoot->getBranchBegin()+1)).begin(); i; i++ ) + { + if( (*i)->getType() == AstNode::typeCondition ) + { + Bu::FString sCond = dynamic_cast(*i)->getStrValue(); + if( sCond == "filetime" ) + { + delete pCond; + pCond = new ConditionFileTime(); + } + else if( sCond == "always" ) + { + delete pCond; + pCond = new ConditionAlways(); + } + else if( sCond == "never" ) + { + delete pCond; + pCond = new ConditionNever(); + } + } + } + + if( pCond == NULL ) + { + // The default condition + pCond = new ConditionFileTime(); + } +} + diff --git a/src/profile.h b/src/profile.h new file mode 100644 index 0000000..dbcc1ea --- /dev/null +++ b/src/profile.h @@ -0,0 +1,30 @@ +#ifndef PROFILE_H +#define PROFILE_H + +#include + +class Profile +{ +public: + Profile( const class AstBranch *pRoot ); + Profile( const Profile &rSrc ); + virtual ~Profile(); + + const Bu::FString &getName() const; + const class AstBranch *getRoot() const; + const class Condition *getCond() const; + bool shouldExec( class Runner &r, class Target &rTarget ) const; + + static Profile *genDefaultClean(); + +private: + void setCondition(); + +private: + Bu::FString sName; + const class AstBranch *pRoot; + class Condition *pCond; + class Ast *pAst; +}; + +#endif diff --git a/src/rule.cpp b/src/rule.cpp new file mode 100644 index 0000000..4c42346 --- /dev/null +++ b/src/rule.cpp @@ -0,0 +1,167 @@ +#include "rule.h" +#include "target.h" +#include "astbranch.h" +#include "astleaf.h" +#include "runner.h" +#include "variable.h" +#include "context.h" +#include "condition.h" +#include "profile.h" + +#include +using namespace Bu; + +Rule::Rule( const Bu::FString &sName ) : + sName( sName ), + pInput( NULL ) +{ +} + +Rule::~Rule() +{ +} + +const Bu::FString &Rule::getName() const +{ + return sName; +} + +void Rule::setInput( const AstBranch *pNewInput ) +{ + pInput = pNewInput; +} + +const AstBranch *Rule::getInput() const +{ + return pInput; +} + +bool Rule::hasOutputs() const +{ + return !lOutput.isEmpty(); +} + +void Rule::addOutput( const AstBranch *pNewOutput ) +{ + lOutput.append( pNewOutput ); +} + +void Rule::addProfile( const AstBranch *pProfRoot ) +{ + Profile *pProf = new Profile( pProfRoot ); + hProfiles.insert( pProf->getName(), pProf ); + /* + hProfiles.insert( + dynamic_cast( + (*pProfile->getBranchBegin()).first() + )->getStrValue(), + pProfile + ); + */ +} + +void Rule::prepTarget( class Target *pTarget ) +{ + pTarget->setDisplay( getDisplay() ); + for( ProfileHash::iterator i = hProfiles.begin(); i; i++ ) + { + pTarget->addProfile( *i ); + } + for( AstBranchList::iterator i = lRequires.begin(); i; i++ ) + { + pTarget->addRequires( *i ); + } +} + +Target *Rule::createTarget( class Runner &r, const Bu::FString &sInput ) +{ + r.getContext().pushScope(); + r.getContext().addVariable("INPUT", sInput ); + Target *pTrg = new Target( false ); + for( AstBranchList::iterator i = lOutput.begin(); i; i++ ) + { + Variable vOut = r.execExpr( + (*(*i)->getBranchBegin()).begin(), + Variable( sInput ) + ); + if( vOut.getType() == Variable::typeString ) + { + pTrg->addOutput( vOut.getString() ); + } + else if( vOut.getType() == Variable::typeList ) + { + for( VarList::iterator j = vOut.begin(); j; j++ ) + { + pTrg->addOutput( (*j).getString() ); + } + } + } + r.getContext().addVariable("OUTPUT", pTrg->getOutputList() ); + pTrg->addInput( sInput ); + pTrg->setDisplay( getDisplay() ); + for( ProfileHash::iterator i = hProfiles.begin(); i; i++ ) + { + pTrg->addProfile( *i ); + } + for( AstBranchList::iterator i = lRequires.begin(); i; i++ ) + { + pTrg->addRequires( *i ); + } + pTrg->setVars( r.getContext().getScope() ); + r.getContext().popScope(); + + return pTrg; +} + +bool Rule::ruleMatches( Runner &r, const Bu::FString &sInput ) +{ + r.getContext().pushScope(); + r.getContext().addVariable("INPUT", sInput ); + Variable vInput( sInput ); + Variable vOut = r.execExpr( + (*pInput->getBranchBegin()).begin(), + vInput + ); + r.getContext().popScope(); + if( vOut.getType() == Variable::typeBool ) + return vOut.getBool(); + else if( vOut.getType() == Variable::typeList ) + return vOut.begin(); + return false; +} + +void Rule::addTag( const Bu::FString &sTag ) +{ + lsTags.append( sTag ); +} + +const StrList &Rule::getTagList() const +{ + return lsTags; +} + +void Rule::setDisplay( const Bu::FString &sStr ) +{ + sDisplay = sStr; +} + +const Bu::FString &Rule::getDisplay() const +{ + return ((bool)sDisplay)?(sDisplay):(sName); +} + +void Rule::addRequires( const AstBranch *pBr ) +{ + lRequires.append( pBr ); +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &/*t*/ ) +{ + return f << "rule"; +} + +template<> Bu::Formatter &Bu::operator<< ( Bu::Formatter &f, const Rule *t ) +{ + return f << (*t); +} + diff --git a/src/rule.h b/src/rule.h new file mode 100644 index 0000000..a3c9344 --- /dev/null +++ b/src/rule.h @@ -0,0 +1,53 @@ +#ifndef RULE_H +#define RULE_H + +#include "types.h" +#include + +class Rule +{ + friend Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &t ); +public: + Rule( const Bu::FString &sName ); + virtual ~Rule(); + + const Bu::FString &getName() const; + + void setInput( const AstBranch *pNewInput ); + const AstBranch *getInput() const; + + bool hasOutputs() const; + + void addOutput( const AstBranch *pNewOutput ); + void addProfile( const AstBranch *pProfile ); + + void prepTarget( class Target *pTarget ); + class Target *createTarget( class Runner &r, const Bu::FString &sInput ); + bool ruleMatches( class Runner &r, const Bu::FString &sInput ); + + void addTag( const Bu::FString &sTag ); + const StrList &getTagList() const; + + void setDisplay( const Bu::FString &sStr ); + const Bu::FString &getDisplay() const; + + void addRequires( const AstBranch *pBr ); + +private: + Bu::FString sName; + Bu::FString sDisplay; + const AstBranch *pInput; + AstBranchList lOutput; + ProfileHash hProfiles; + StrList lsTags; + AstBranchList lRequires; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &t ); + +namespace Bu +{ + template<> Bu::Formatter &operator<< ( Bu::Formatter &f, const Rule *t ); +}; + +#endif diff --git a/src/runner.cpp b/src/runner.cpp new file mode 100644 index 0000000..ace9ce9 --- /dev/null +++ b/src/runner.cpp @@ -0,0 +1,922 @@ +#include "runner.h" + +#include "ast.h" +#include "astnode.h" +#include "astleaf.h" +#include "astbranch.h" +#include "context.h" +#include "functionast.h" +#include "rule.h" +#include "variable.h" +#include "target.h" +#include "action.h" +#include "profile.h" +#include "view.h" + +#include "bu/sio.h" +using Bu::sio; + +Runner::Runner( Ast &rAst, Context &rCont ) : + rAst( rAst ), + rCont( rCont ), + pCurTarget( NULL ), + pCurRule( NULL ) +{ +} + +Runner::~Runner() +{ +} + +void Runner::initialize() +{ + for( Ast::NodeList::const_iterator i = rAst.getNodeBegin(); i; i++ ) + { + if( (*i)->getType() == AstNode::typeFunctionDef ) + { + AstBranch *pFnc = dynamic_cast(*i); + rCont.addFunction( new FunctionAst( pFnc, this ) ); + } + else if( (*i)->getType() == AstNode::typeActionDef ) + { + AstBranch *pAction = dynamic_cast(*i); + rCont.addAction( new Action( pAction ) ); + } + } +} + +Variable Runner::execFunc( const AstBranch *pFunc, Variable &vIn ) +{ + Bu::FString sName = dynamic_cast( + (*pFunc->getBranchBegin()).first())->getStrValue(); + + VarList lParams; + for( AstBranch::BranchList::const_iterator p = + pFunc->getBranchBegin()+1; p; p++ ) + { + lParams.append( execExpr( (*p).begin() ) ); + } + + return rCont.call( sName, vIn, lParams ); +} + +Variable Runner::execExpr( AstBranch::NodeList::const_iterator e ) +{ + Variable vBlank; + return execExpr( e, vBlank ); +} + +Variable Runner::execExpr( AstBranch::NodeList::const_iterator e, + const Variable &vIn ) +{ +// Variable v( vIn ); + VarList lStack; + lStack.push( vIn ); + + for(; e; e++ ) + { + if( ((*e)->getType()&AstNode::typeClassMask) == AstNode::typeBranch ) + { + const AstBranch *pBranch = dynamic_cast( *e ); + switch( pBranch->getType() ) + { + case AstNode::typeFunction: + //sio << "FUNC: " << *pBranch << sio.nl << sio.nl; + { + Variable v = lStack.peekPop(); + lStack.push( execFunc( pBranch, v ) ); + } + break; + + case AstNode::typeSet: + lStack.push( doSet( pBranch ) ); + break; + + case AstNode::typeList: + { + Variable vLst( Variable::typeList ); + for( AstBranch::BranchList::const_iterator i = + pBranch->getBranchBegin(); i; i++ ) + { + vLst.append( execExpr( (*i).begin() ) ); + } + lStack.push( vLst ); + } + break; + + case AstNode::typeExpr: + { + sio << "!!! typeExpr in an expr maybe should be an error..." << sio.nl; + for( AstBranch::BranchList::const_iterator i = + pBranch->getBranchBegin(); i; i++ ) + { + lStack.push( + execExpr( (*i).begin() ) // Are they atomic? + ); + } + if( lStack.getSize() != 1 ) + { + throw Bu::ExceptionBase( + "Something went wrong, expression processing " + "left %d elements on stack, should be 1.", + lStack.getSize() ); + } + } + break; + + default: + sio << "?? branch ???: " + << (pBranch)->getType(); + break; + } + } + else + { + const AstLeaf *pLeaf = dynamic_cast( *e ); + switch( pLeaf->getType() ) + { + case AstNode::typeVariable: + try + { + lStack.push( + rCont.getVariable( pLeaf->getStrValue() ) + ); + } + catch(...) + { + lStack.push( Variable() ); + } + break; + + case AstNode::typeVariableRef: + lStack.push( + Variable::mkRef( pLeaf->getStrValue() ) + ); + break; + + case AstNode::typeString: + lStack.push( + rCont.expand( pLeaf->getStrValue() ) + ); + break; + + case AstNode::typeInt: + lStack.push( + pLeaf->getIntValue() + ); + break; + + case AstNode::typeFloat: + lStack.push( + pLeaf->getFloatValue() + ); + break; + + case AstNode::typeBool: + lStack.push( + pLeaf->getBoolValue() + ); + break; + + case AstNode::typeVersion: + break; + + case AstNode::typeNull: + lStack.push( + Variable() + ); + break; + + case AstNode::typeCmpEq: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( a == b ) ); + } + break; + + case AstNode::typeCmpLt: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b < a ) ); + } + break; + + case AstNode::typeCmpGt: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b > a ) ); + } + break; + + case AstNode::typeCmpNe: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( a != b ) ); + } + break; + + case AstNode::typeCmpLtEq: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b <= a ) ); + } + break; + + case AstNode::typeCmpGtEq: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b >= a ) ); + } + break; + + case AstNode::typeOpEq: + { + Variable ref, val; + val = lStack.peekPop(); + ref = lStack.peekPop(); + rCont.addVariable( ref.getString(), val ); + lStack.push( val ); + } + break; + + case AstNode::typeOpPlusEq: + { + Variable ref, val; + val = lStack.peekPop(); + ref = lStack.peekPop(); + try + { + Variable &nVal = rCont.getVariable( + ref.getString() + ); + nVal += val; + lStack.push( nVal ); + } catch(...) + { + rCont.addVariable( ref.getString(), val ); + lStack.push( val ); + } + } + break; + + case AstNode::typeOpPlusEqRaw: + { + Variable ref, val; + val = lStack.peekPop(); + ref = lStack.peekPop(); + try + { + Variable &nVal = rCont.getVariable( + ref.getString() + ); + nVal << val; + lStack.push( nVal ); + } catch(...) + { + rCont.addVariable( ref.getString(), val ); + lStack.push( val ); + } + } + break; + + case AstNode::typeOpPlus: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b + a ) ); + } + break; + + case AstNode::typeOpMinus: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b - a ) ); + } + break; + + case AstNode::typeOpMultiply: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b * a ) ); + } + break; + + case AstNode::typeOpDivide: + { + Variable a, b; + a = lStack.peekPop(); + b = lStack.peekPop(); + lStack.push( Variable( b / a ) ); + } + break; + + case AstNode::typeOpNegate: + lStack.peek().doNegate(); + break; + + case AstNode::typeOpNot: + lStack.peek().doNot(); + break; + + default: + sio << "?? leaf ???: " + << (pLeaf)->getType(); + break; + } + } + } + + return lStack.peek(); +} + +void Runner::run() +{ + run( rAst.getNodeBegin() ); + + rCont.buildTargetTree( *this ); + + rCont.attachDefaults(); + rCont.genDefaultActions(); + +// rCont.writeTargetDot(); +} + +Variable Runner::run( AstBranch::NodeList::const_iterator n ) +{ + /* Execute the top level code. */ + + Variable vReturn; + Bu::List sI; + sI.push( n ); +// for( Ast::NodeList::const_iterator i = rAst.getNodeBegin(); i; i++ ) + while( !sI.isEmpty() ) + { + while( !sI.isEmpty() && !(sI.peek()) ) + { + sI.pop(); + } + if( sI.isEmpty() ) + break; + Ast::NodeList::const_iterator &i = sI.peek(); + if( ((*i)->getType()&AstNode::typeClassMask) == AstNode::typeLeaf ) + { + const AstLeaf *pExpr = dynamic_cast( *i ); + switch( pExpr->getType() ) + { + case AstNode::typeError: + { + Bu::FString sMsg = rCont.expand( pExpr->getStrValue() ); + rCont.getView()->userError( sMsg.getStr() ); + throw Bu::ExceptionBase( sMsg.getStr() ); + } + break; + + case AstNode::typeWarning: + rCont.getView()->userWarning( + rCont.expand( pExpr->getStrValue() ) + ); + break; + + case AstNode::typeNotice: + rCont.getView()->userNotice( + rCont.expand( pExpr->getStrValue() ) + ); + break; + + case AstNode::typeCondition: + break; + + case AstNode::typeDisplay: + if( pCurTarget ) + { + pCurTarget->setDisplay( + rCont.expand( pExpr->getStrValue() ) + ); + } + else if( pCurRule ) + { + pCurRule->setDisplay( + rCont.expand( pExpr->getStrValue() ) + ); + } + break; +/* + case AstNode::typeCondition: + if( pCurTarget ) + { + if( pExpr->getStrValue() == "filetime" ) + { + pCurTarget->setCondition( + new ConditionFileTime() + ); + } + } + else if( pCurRule ) + { + if( pExpr->getStrValue() == "filetime" ) + { + pCurRule->setCondition( + new ConditionFileTime() + ); + } + } + else + { + throw Bu::ExceptionBase( + "You can only set a condition in a target or rule."); + } + break; +*/ + default: + sio << "Leaf? " << (*i)->getType() << sio.nl; + break; + } + } + else + { + const AstBranch *pExpr = dynamic_cast( *i ); + switch( pExpr->getType() ) + { + case AstNode::typeSet: + { + // This is effectively legacy, if we add the set + // keyword back in I want it to work. + doSet( pExpr ); + } + break; + + case AstNode::typeUnset: + { + AstBranch::NodeList::const_iterator n = + (*pExpr->getBranchBegin()).begin(); + Bu::FString sVar = dynamic_cast( + *n )->getStrValue(); + rCont.delVariable( sVar ); + } + break; + + case AstNode::typeIf: + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + + Variable v = execExpr( (*b).begin() ); + if( v.getType() != Variable::typeBool ) + { + throw Bu::ExceptionBase( + "If statement evaluated to non-boolean."); + } + b++; + if( v.getBool() ) + { + i++; + sI.push( (*b).begin() ); + continue; + } + else + { + b++; + if( b ) + { + i++; + sI.push( (*b).begin() ); + continue; + } + } + } + break; + + case AstNode::typeFor: + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Bu::FString sVar = dynamic_cast( + (*b).first() )->getStrValue(); + b++; + Variable v = execExpr( (*b).begin() ); + b++; + for( VarList::const_iterator vi = v.getList().begin(); + vi; vi++ ) + { + rCont.addVariable( sVar, *vi ); + run( (*b).begin() ); + } + } + break; + + case AstNode::typeFunction: + { + Variable vIn; + execFunc( pExpr, vIn ); + } + break; + + case AstNode::typeReturn: + vReturn = execExpr( (*pExpr->getBranchBegin()).begin() ); + return vReturn; + break; + + case AstNode::typeFunctionDef: + case AstNode::typeActionDef: + // We ignore these, we already dealt with them + break; + + case AstNode::typeTarget: + // This actually runs exactly like a for loop, if there's + // only one item, then we only go once, if it's a list, go + // more than once :-P + if( pCurTarget == NULL ) + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Variable vLoop = execExpr( (*b).begin() ); + b++; + if( vLoop.getType() == Variable::typeString ) + { + rCont.addTarget( + buildTarget( + vLoop.getString(), (*b).begin() + ) + ); + } + else if( vLoop.getType() == Variable::typeList ) + { + for( VarList::iterator i = vLoop.begin(); i; i++ ) + { + rCont.addTarget( + buildTarget( + (*i).getString(), (*b).begin() + ) + ); + } + } + } + else + { + throw Bu::ExceptionBase( + "You cannot declare a target within " + "a target decleration."); + } + break; + + case AstNode::typeRuleDef: + if( pCurRule == NULL ) + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Bu::FString sName = dynamic_cast( + (*b).first() + )->getStrValue(); + b++; + rCont.addRule( buildRule( sName, (*b).begin() ) ); + } + else + { + throw Bu::ExceptionBase( + "You cannot declare a rule within " + "a rule decleration."); + } + break; + + case AstNode::typeInput: + if( pCurTarget != NULL ) + { + Variable vRet = execExpr( + (*pExpr->getBranchBegin()).begin() + ); + if( vRet.getType() == Variable::typeString ) + { + pCurTarget->addInput( vRet.getString() ); + } + else if( vRet.getType() == Variable::typeList ) + { + for( VarList::iterator i = vRet.begin(); i; i++ ) + { + pCurTarget->addInput( + (*i).getString() + ); + } + } + } + else if( pCurRule != NULL ) + { + pCurRule->setInput( pExpr ); + } + else + { + throw Bu::ExceptionBase( + "input can only occur within a target or rule."); + } + break; + + case AstNode::typeRequires: + if( pCurTarget != NULL ) + { + Variable vRet = execExpr( + (*pExpr->getBranchBegin()).begin() + ); + if( vRet.getType() == Variable::typeString ) + { + pCurTarget->addRequires( vRet.getString() ); + } + else if( vRet.getType() == Variable::typeList ) + { + for( VarList::iterator i = vRet.begin(); i; i++ ) + { + pCurTarget->addRequires( + (*i).getString() + ); + } + } + } + else if( pCurRule != NULL ) + { + pCurRule->addRequires( pExpr ); + } + else + { + throw Bu::ExceptionBase( + "requires can only occur within a target or rule."); + } + break; + + case AstNode::typeRule: + if( pCurTarget ) + { + pCurTarget->setRule( + dynamic_cast( + (*pExpr->getBranchBegin()).first() + )->getStrValue() + ); + } + else + { + throw Bu::ExceptionBase( + "rule can only occur within a target."); + } + break; + + case AstNode::typeProfile: + if( pCurTarget ) + { + pCurTarget->addProfile( pExpr ); + } + else if( pCurRule ) + { + pCurRule->addProfile( pExpr ); + } + else + { + throw Bu::ExceptionBase( + "profile can only occur within a target or rule."); + } + break; + + case AstNode::typeOutput: + if( pCurRule ) + { + pCurRule->addOutput( pExpr ); + } + else + { + throw Bu::ExceptionBase( + "output can only occur within a rule."); + } + break; + + case AstNode::typeProcessTarget: + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Bu::FString sProfile = dynamic_cast( + (*b).first() + )->getStrValue(); + b++; + Variable vTargs = execExpr( (*b).begin() ); + if( vTargs.getType() == Variable::typeString ) + { + rCont.getTarget( vTargs.getString() )->process( + *this, sProfile + ); + } + else if( vTargs.getType() == Variable::typeList ) + { + for( VarList::iterator v = vTargs.begin(); + v; v++ ) { + rCont.getTarget( (*v).getString() )->process( + *this, sProfile + ); + } + } + } + break; + + case AstNode::typeTag: + if( pCurTarget ) + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Variable vTags = execExpr( (*b).begin() ); + if( vTags.getType() == Variable::typeList ) + { + for( VarList::iterator i = vTags.begin(); i; i++ ) + { + rCont.addTargetToTag( pCurTarget, (*i).toString() ); + } + } + else + { + Bu::FString sTag = vTags.toString(); + if( sTag ) + { + rCont.addTargetToTag( pCurTarget, sTag ); + } + else + { + throw Bu::ExceptionBase( + "A tag evaluted to empty string." + ); + } + } + } + else if( pCurRule ) + { + AstBranch::BranchList::const_iterator b = + pExpr->getBranchBegin(); + Variable vTags = execExpr( (*b).begin() ); + if( vTags.getType() == Variable::typeList ) + { + for( VarList::iterator i = vTags.begin(); i; i++ ) + { + pCurRule->addTag( (*i).toString() ); + } + } + else + { + Bu::FString sTag = vTags.toString(); + if( sTag ) + { + pCurRule->addTag( sTag ); + } + else + { + throw Bu::ExceptionBase( + "A tag evaluted to empty string." + ); + } + } + } + else + { + throw Bu::ExceptionBase( + "tag can only occur within a target or rule."); + } + break; + + case AstNode::typeExpr: + execExpr( (*pExpr->getBranchBegin()).begin() ); + break; + + default: + sio << "Branch? " << (*i)->getType() << sio.nl; + break; + } + } + + i++; + } + + return vReturn; +} + +void Runner::execProfile( Target *pTarget, const Bu::FString &sProfile ) +{ + rCont.pushScope( pTarget->getVars() ); + run( (*(pTarget->getProfile( sProfile )->getRoot()-> + getBranchBegin()+1)).begin() ); + rCont.popScope(); +} + +void Runner::execAction( const Bu::FString &sName ) +{ + try + { + Action *pAct = rCont.getAction( sName ); + + pAct->call( this ); + } + catch( Bu::HashException &e ) + { + Bu::FString sError("No such action '" + sName + "' found."); + rCont.getView()->sysError( sError ); + } +} + +Context &Runner::getContext() +{ + return rCont; +} + +Target *Runner::buildTarget( const Bu::FString &sOutput, + AstBranch::NodeList::const_iterator n ) +{ + Target *pTrg = NULL; + try + { + pTrg = rCont.getTarget( sOutput ); + rCont.pushScope( pTrg->getVars() ); + } + catch( Bu::HashException &e ) + { + pTrg = new Target( sOutput, true ); + rCont.pushScope(); + } + + // sio << " (target) \"" << sOutput << "\" explicit." << sio.nl; + + rCont.addVariable("OUTPUT", sOutput ); + pCurTarget = pTrg; + run( n ); + + rCont.addVariable("INPUT", pTrg->getInputList() ); + pCurTarget = NULL; + + pTrg->setVars( rCont.getScope() ); + rCont.popScope(); + + return pTrg; +} + +Rule *Runner::buildRule( const Bu::FString &sName, + AstBranch::NodeList::const_iterator n ) +{ + Rule *pRule = new Rule( sName ); + + rCont.pushScope(); + pCurRule = pRule; + run( n ); + rCont.popScope(); + pCurRule = NULL; + + return pRule; +} + +Variable Runner::doSet( const AstBranch *pRoot ) +{ + AstBranch::NodeList::const_iterator n = + (*pRoot->getBranchBegin()).begin(); + Bu::FString sVar = dynamic_cast( *n )->getStrValue(); + n++; + const AstLeaf *pLeaf = dynamic_cast( *n ); + n++; + Variable v = execExpr( n ); + + switch( pLeaf->getType() ) + { + case AstNode::typeOpEq: + rCont.addVariable( sVar, v ); + break; + + case AstNode::typeOpPlusEq: + try + { + rCont.getVariable( sVar ) += v; + } catch(...) + { + rCont.addVariable( sVar, v ); + } + break; + + case AstNode::typeOpPlusEqRaw: + try + { + rCont.getVariable( sVar ) << v; + } catch(...) + { + rCont.addVariable( sVar, v ); + } + break; + + default: break; + } + + return v; +} + diff --git a/src/runner.h b/src/runner.h new file mode 100644 index 0000000..98894da --- /dev/null +++ b/src/runner.h @@ -0,0 +1,43 @@ +#ifndef RUNNER_H +#define RUNNER_H + +#include "astbranch.h" + +class Runner +{ +public: + Runner( class Ast &rAst, class Context &rCont ); + virtual ~Runner(); + + /** + * Run through and pull out all of the functions. Maybe more later. + */ + void initialize(); + class Variable execExpr( AstBranch::NodeList::const_iterator e ); + class Variable execExpr( AstBranch::NodeList::const_iterator e, + const class Variable &vIn ); + void run(); + Variable run( AstBranch::NodeList::const_iterator n ); + class Variable execFunc( const class AstBranch *pFunc, + class Variable &vIn ); + void execProfile( class Target *pTarget, const Bu::FString &sProfile ); + void execAction( const Bu::FString &sName ); + + Context &getContext(); + +private: + class Target *buildTarget( const Bu::FString &sOutput, + AstBranch::NodeList::const_iterator n ); + class Rule *buildRule( const Bu::FString &sName, + AstBranch::NodeList::const_iterator n ); + void attachDefaults(); + Variable doSet( const AstBranch *pRoot ); + +private: + class Ast &rAst; + class Context &rCont; + Target *pCurTarget; + Rule *pCurRule; +}; + +#endif diff --git a/src/target.cpp b/src/target.cpp new file mode 100644 index 0000000..f3e54b7 --- /dev/null +++ b/src/target.cpp @@ -0,0 +1,402 @@ +#include "target.h" +#include "variable.h" +#include "condition.h" +#include "astleaf.h" +#include "astbranch.h" +#include "runner.h" +#include "context.h" +#include "profile.h" +#include "view.h" + +#include +#include +#include + +#include +using namespace Bu; + +Target::Target( bool bExplicit ) : + bExplicit( bExplicit ), + bRun( false ), + iDepCount( 0 ) +{ +} + +Target::Target( const Bu::FString &sOutput, bool bExplicit ) : + bExplicit( bExplicit ), + lsOutput( sOutput ), + iDepCount( 0 ) +{ +} + +Target::~Target() +{ +} + +void Target::addInput( const Bu::FString &sInput ) +{ + lsInput.append( sInput ); +} + +const StrList &Target::getInputList() const +{ + return lsInput; +} + +void Target::resetInputList( const StrList &lInputs ) +{ + lsInput = lInputs; + if( lsInput.getSize() == 1 ) + { + hVars.insert("INPUT", lsInput.first() ); + } + else + { + Variable vInput( Variable::typeList ); + for( StrList::iterator i = lsInput.begin(); i; i++ ) + { + vInput.append( Variable( *i ) ); + } + hVars.insert("INPUT", vInput ); + } +} + +void Target::addRequires( const Bu::FString &sReq ) +{ + lsRequires.append( sReq ); +} + +void Target::addRequires( const AstBranch *pBr ) +{ + lbRequires.append( pBr ); +} + +const StrList &Target::getRequiresList() const +{ + return lsRequires; +} + +void Target::buildRequires( Runner &r ) +{ + r.getContext().getView()->buildRequires( *this ); + r.getContext().pushScope( hVars ); + for( AstBranchList::iterator i = lbRequires.begin(); i; i++ ) + { + Variable v = r.execExpr( (*(*i)->getBranchBegin()).begin() ); + if( v.getType() == Variable::typeList ) + { + for( VarList::iterator j = v.begin(); j; j++ ) + { + Bu::FString sReq = (*j).toString(); + addRequires( sReq ); + try + { + addDep( r.getContext().getTarget( sReq ) ); + } + catch(...) { } + } + } + else + { + Bu::FString sReq = v.toString(); + addRequires( sReq ); + try + { + addDep( r.getContext().getTarget( sReq ) ); + } + catch(...) { } + } + } + r.getContext().popScope(); +} + +void Target::addOutput( const Bu::FString &sOutput ) +{ + lsOutput.append( sOutput ); +} + +const StrList &Target::getOutputList() const +{ + return lsOutput; +} + +void Target::setPrefix( const Bu::FString &sPrefix ) +{ + this->sPrefix = sPrefix; +} + +const Bu::FString &Target::getPrefix() const +{ + return sPrefix; +} + +void Target::setRule( const Bu::FString &sRule ) +{ + this->sRule = sRule; +} + +const Bu::FString &Target::getRule() const +{ + return sRule; +} + +bool Target::hasRule() const +{ + return !sRule.isEmpty(); +} + +bool Target::isExplicit() const +{ + return bExplicit; +} + +void Target::addDep( Target *pDep ) +{ + lDeps.append( pDep ); +} + +const TargetList &Target::getDepList() const +{ + return lDeps; +} + +void Target::addProfile( const class AstBranch *pProfRoot ) +{ + Profile *pProf = new Profile( pProfRoot ); + hProfiles.insert( pProf->getName(), pProf ); + /* + hProfiles.insert( + dynamic_cast( + (*pProfRoot->getBranchBegin()).first() + )->getStrValue(), + pProfRoot + ); + */ +} + +void Target::addProfile( const class Profile *pSrc ) +{ + hProfiles.insert( pSrc->getName(), new Profile( *pSrc ) ); +} + +bool Target::hasProfile( const Bu::FString &sName ) const +{ + return hProfiles.has( sName ); +} + +const Profile *Target::getProfile( const Bu::FString &sName ) const +{ + return hProfiles.get( sName ); +} + +void Target::setVars( const VarHash &hNewVars ) +{ + hVars = hNewVars; +} + +const VarHash &Target::getVars() const +{ + return hVars; +} + +void Target::setDisplay( const Bu::FString &sNewDisplay ) +{ + if( !sDisplay ) + sDisplay = sNewDisplay; +} + +const Bu::FString &Target::getDisplay() const +{ + return sDisplay; +} + +void Target::process( class Runner &r, const Bu::FString &sProfile ) +{ + r.getContext().getView()->beginTarget( sProfile, *this ); + bRun = true; + bool bShouldExec = false; + + for( TargetList::iterator i = lDeps.begin(); i; i++ ) + { + if( (*i)->bRun ) + continue; + + // TODO: This is important, in the future, it may be possible for a + // target to be triggered by multiple dependant targets, to cover for + // this the below mergeUnder should be *TEMPORARY* and the target + // that was marged to be reset post processing. + (*i)->mergeUnder( hVars ); + (*i)->process( r, sProfile ); + } + try + { + bShouldExec = hProfiles.get( sProfile )->shouldExec( r, *this ); + } + catch( Bu::HashException &e ) + { + } + + if( !bShouldExec ) + { + r.getContext().getView()->skipTarget( sProfile, *this ); + } + else + { + r.getContext().getView()->processTarget( sProfile, *this ); + r.execProfile( this, sProfile ); + } + + r.getContext().getView()->endTarget(); +} + +void Target::mergeUnder( const VarHash &hNewVars ) +{ + for( VarHash::const_iterator i = hNewVars.begin(); i; i++ ) + { + if( !hVars.has( i.getKey() ) ) + { + hVars.insert( i.getKey(), i.getValue() ); + } + } +} + +bool Target::hasRun() +{ + return bRun; +} + +void Target::mergeUnder( const Target *pSrc ) +{ + // If either are explicit, then it's explicit + bExplicit = bExplicit || pSrc->bExplicit; + + merge( lsInput, pSrc->lsInput ); + merge( lsRequires, pSrc->lsRequires ); + merge( lsOutput, pSrc->lsOutput ); + + if( !sPrefix ) + sPrefix = pSrc->sPrefix; + + sRule = pSrc->sRule; + + mergeUnder( pSrc->hVars ); + + // Deps? They should be computed much after merging anyway, peh! + + for( ProfileHash::const_iterator i = pSrc->hProfiles.begin(); i; i++ ) + { + if( !hProfiles.has( i.getKey() ) ) + { + hProfiles.insert( i.getKey(), i.getValue() ); + } + } + + if( !sDisplay ) + sDisplay = pSrc->sDisplay; + + // Now we need to reset our vars. + hVars.insert("INPUT", lsInput ); + hVars.insert("REQUIRES", lsRequires ); + hVars.insert("OUTPUT", lsOutput ); +} + +void Target::merge( StrList &lOut, const StrList &lIn ) +{ + Bu::Heap hStr; + for( StrList::const_iterator i = lOut.begin(); i; i++ ) + { + hStr.enqueue( *i ); + } + for( StrList::const_iterator i = lIn.begin(); i; i++ ) + { + hStr.enqueue( *i ); + } + + lOut.clear(); + + if( hStr.isEmpty() ) + return; + + lOut.append( hStr.dequeue() ); + while( !hStr.isEmpty() ) + { + if( hStr.peek() == lOut.last() ) + { + hStr.dequeue(); + } + else + { + lOut.append( hStr.dequeue() ); + } + } +} + +void Target::resetRun( bool bHasRun ) +{ + bRun = bHasRun; + + for( TargetList::iterator i = lDeps.begin(); i; i++ ) + { + (*i)->resetRun( bHasRun ); + } +} + +void Target::setDepCount() +{ + bRun = true; + iDepCount = 1; + for( TargetList::iterator i = lDeps.begin(); i; i++ ) + { + if( (*i)->bRun ) + { + continue; + } + (*i)->setDepCount(); + iDepCount += (*i)->getDepCount(); + } +} + +int Target::getDepCount() const +{ + return iDepCount; +} + +void Target::collapseDeps() +{ + if( lDeps.getSize() <= 1 ) + return; + Bu::Hash hDep; + for( TargetList::iterator i = lDeps.begin(); i; i++ ) + { + if( hDep.has( (ptrdiff_t)*i ) ) + { + lDeps.erase( i ); + i--; + } + else + { + hDep.insert( (ptrdiff_t)*i, true ); + } + } +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t ) +{ + f.incIndent(); + f << f.nl << "Input = " << t.lsInput << "," << f.nl + << "Requires = " << t.lsRequires << "," << f.nl + << "Output = \"" << t.lsOutput << "\"," << f.nl + << "Prefix = \"" << t.sPrefix << "\"," << f.nl + << "Rule = \"" << t.sRule << "\"," << f.nl + << "Explicit = " << t.bExplicit << "," << f.nl + << "Vars = " << t.hVars + << f.nl; + f.decIndent(); + return f; +} + +template<> Bu::Formatter &Bu::operator<< ( Bu::Formatter &f, const Target *t ) +{ + return f << (*t); +} + diff --git a/src/target.h b/src/target.h new file mode 100644 index 0000000..766366a --- /dev/null +++ b/src/target.h @@ -0,0 +1,89 @@ +#ifndef TARGET_H +#define TARGET_H + +#include "types.h" +#include + +class Target +{ + friend Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t ); +public: + Target( bool bExplicit ); + Target( const Bu::FString &sOutput, bool bExplicit ); + virtual ~Target(); + + void addInput( const Bu::FString &sInput ); + const StrList &getInputList() const; + void resetInputList( const StrList &lInputs ); + + void addRequires( const Bu::FString &sReq ); + void addRequires( const AstBranch *pBr ); + const StrList &getRequiresList() const; + void buildRequires( class Runner &r ); + + void addOutput( const Bu::FString &sOutput ); + const StrList &getOutputList() const; + + void setPrefix( const Bu::FString &sPrefix ); + const Bu::FString &getPrefix() const; + + void setRule( const Bu::FString &sRule ); + const Bu::FString &getRule() const; + bool hasRule() const; + + bool isExplicit() const; + + void addDep( Target *pDep ); + const TargetList &getDepList() const; + + void addProfile( const class AstBranch *pProfRoot ); + void addProfile( const class Profile *pSrc ); + bool hasProfile( const Bu::FString &sName ) const; + const class Profile *getProfile( const Bu::FString &sName ) const; + + void setVars( const VarHash &hNewVars ); + const VarHash &getVars() const; + + void setDisplay( const Bu::FString &sNewDisplay ); + const Bu::FString &getDisplay() const; + + void process( class Runner &r, const Bu::FString &sProfile ); + + void mergeUnder( const Target *pSrc ); + + bool hasRun(); + + void resetRun( bool bHasRun=true ); + void setDepCount(); + int getDepCount() const; + + void collapseDeps(); + +private: + void mergeUnder( const VarHash &hVars ); + void merge( StrList &lOut, const StrList &lIn ); + +private: + bool bExplicit; + StrList lsInput; + StrList lsRequires; + StrList lsOutput; + Bu::FString sPrefix; + Bu::FString sRule; + VarHash hVars; + TargetList lDeps; + ProfileHash hProfiles; + Bu::FString sDisplay; + bool bRun; + AstBranchList lbRequires; + int iDepCount; +}; + +Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t ); + +namespace Bu +{ + template<> Bu::Formatter &operator<< ( Bu::Formatter &f, const Target *t ); +}; + +#endif diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..e405e35 --- /dev/null +++ b/src/types.h @@ -0,0 +1,26 @@ +#ifndef TYPES_H +#define TYPES_H + +#include "bu/fstring.h" +#include "bu/list.h" +#include "bu/hash.h" + +typedef Bu::List StrList; + +class Variable; +typedef Bu::List VarList; +typedef Bu::Hash VarHash; + +class Condition; + +class Target; +typedef Bu::List TargetList; +class Profile; +typedef Bu::Hash ProfileHash; + +class AstNode; +class AstBranch; +class AstLeaf; +typedef Bu::List AstBranchList; + +#endif diff --git a/src/variable.cpp b/src/variable.cpp new file mode 100644 index 0000000..99bac59 --- /dev/null +++ b/src/variable.cpp @@ -0,0 +1,892 @@ +#include "variable.h" +#include "astleaf.h" +#include "bu/sio.h" +using Bu::sio; + +#include + +Variable::Variable() : + eType( typeNone ) +{ + memset( &uVal, 0, sizeof(uVal) ); +} + +Variable::Variable( Type t ) : + eType( t ) +{ + memset( &uVal, 0, sizeof(uVal) ); + if( eType == typeString || eType == typeRef ) + { + uVal.sVal = new Bu::FString; + } + else if( eType == typeList ) + { + uVal.lVal = new VarList; + } +} + +Variable::Variable( int iVal ) : + eType( typeInt ) +{ + memset( &uVal, 0, sizeof(uVal) ); + uVal.iVal = iVal; +} + +Variable::Variable( double fVal ) : + eType( typeFloat ) +{ + memset( &uVal, 0, sizeof(uVal) ); + uVal.fVal = fVal; +} + +Variable::Variable( bool bVal ) : + eType( typeBool ) +{ + memset( &uVal, 0, sizeof(uVal) ); + uVal.bVal = bVal; +} + +Variable::Variable( const Bu::FString &sVal ) : + eType( typeString ) +{ + memset( &uVal, 0, sizeof(uVal) ); + uVal.sVal = new Bu::FString( sVal ); +} + +Variable::Variable( const char *sVal ) : + eType( typeString ) +{ + memset( &uVal, 0, sizeof(uVal) ); + uVal.sVal = new Bu::FString( sVal ); +} + +Variable::Variable( const Variable &v ) : + eType( v.eType ) +{ + memset( &uVal, 0, sizeof(uVal) ); + if( eType == typeString || eType == typeRef ) + { + uVal.sVal = new Bu::FString( *v.uVal.sVal ); + } + else if( eType == typeList ) + { + uVal.lVal = new VarList( *v.uVal.lVal ); + } + else + { + uVal = v.uVal; + } +} + +Variable::Variable( const class AstLeaf &l ) +{ + switch( l.getDataType() ) + { + case AstNode::typeDataInt: + eType = typeInt; + uVal.iVal = l.getIntValue(); + break; + + case AstNode::typeDataFloat: + eType = typeFloat; + uVal.fVal = l.getFloatValue(); + break; + + case AstNode::typeDataBool: + eType = typeBool; + uVal.bVal = l.getBoolValue(); + break; + + case AstNode::typeDataString: + eType = typeString; + uVal.sVal = new Bu::FString( l.getStrValue() ); + break; + + case AstNode::typeDataNone: + eType = typeNone; + memset( &uVal, 0, sizeof(uVal) ); + break; + + default: + sio << "Unhandled type <>" << sio.nl << sio.nl; + break; + } +} + +Variable::Variable( const StrList &lst ) +{ + if( lst.getSize() == 1 ) + { + eType = typeString; + uVal.sVal = new Bu::FString( lst.first() ); + } + else + { + eType = typeList; + uVal.lVal = new VarList(); + for( StrList::const_iterator i = lst.begin(); i; i++ ) + { + uVal.lVal->append( Variable( *i ) ); + } + } +} + +Variable::Variable( const VarList &lst ) +{ + eType = typeList; + uVal.lVal = new VarList( lst ); +} + +Variable::~Variable() +{ + if( eType == typeString || eType == typeRef ) + { + delete uVal.sVal; + } + else if( eType == typeList ) + { + delete uVal.lVal; + } +} + +Variable Variable::mkRef( const Bu::FString &sVal ) +{ + Variable v( typeRef ); + (*v.uVal.sVal) = sVal; + return v; +} + +Variable::Type Variable::getType() const +{ + return eType; +} + +int Variable::getInt() const +{ + if( eType != typeInt ) throw Bu::ExceptionBase("Wrong variable type."); + return uVal.iVal; +} + +double Variable::getFloat() const +{ + if( eType != typeFloat ) throw Bu::ExceptionBase("Wrong variable type."); + return uVal.fVal; +} + +bool Variable::getBool() const +{ + if( eType != typeBool ) throw Bu::ExceptionBase("Wrong variable type."); + return uVal.bVal; +} + +const Bu::FString &Variable::getString() const +{ + if( eType != typeString && eType != typeRef ) throw Bu::ExceptionBase("Wrong variable type."); + return *uVal.sVal; +} + +const VarList &Variable::getList() const +{ + if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type."); + return *uVal.lVal; +} + +int Variable::toInt() const +{ + switch( eType ) + { + case typeInt: + return uVal.iVal; + + case typeFloat: + return (int)uVal.fVal; + + case typeBool: + return (uVal.bVal)?(1):(0); + + case typeString: + case typeRef: + return strtol( uVal.sVal->getStr(), NULL, 0 ); + + default: + return 0; + } + return 0; +} + +double Variable::toFloat() const +{ + switch( eType ) + { + case typeInt: + return (double)uVal.iVal; + + case typeFloat: + return uVal.fVal; + + case typeBool: + return (uVal.bVal)?(1.0):(0.0); + + case typeString: + case typeRef: + return strtod( uVal.sVal->getStr(), NULL ); + + default: + return 0.0; + } + return 0.0; +} + +bool Variable::toBool() const +{ + switch( eType ) + { + case typeInt: + return uVal.iVal != 0; + + case typeFloat: + return uVal.fVal != 0.0; + + case typeBool: + return uVal.bVal; + + case typeString: + case typeRef: + return (*uVal.sVal) == "true"; + + case typeList: + return !(*uVal.lVal).isEmpty(); + + default: + return false; + } + return false; +} + +Bu::FString Variable::toString() const +{ + Bu::FString sRet; + switch( eType ) + { + case typeNone: + // No type, no data, we return empty string + break; + + case typeInt: + sRet.format("%d", uVal.iVal ); + break; + + case typeFloat: + sRet.format("%f", uVal.fVal ); + break; + + case typeBool: + sRet = (uVal.bVal)?("true"):("false"); + break; + + case typeString: + case typeRef: + sRet = *uVal.sVal; + break; + + case typeList: + { + for( VarList::const_iterator i = uVal.lVal->begin(); i; i++ ) + { + if( i != uVal.lVal->begin() ) + sRet += " "; + sRet += (*i).toString(); + } + } + break; + + case typeVersion: + break; + } + + return sRet; +} + +VarList Variable::toList() const +{ + if( eType == typeList ) + return *this; + return VarList( *this ); +} + +Variable Variable::toType( Type eNewType ) const +{ + switch( eNewType ) + { + case typeNone: + return Variable(); + + case typeBool: + return Variable( toBool() ); + + case typeInt: + return Variable( toInt() ); + + case typeFloat: + return Variable( toFloat() ); + + case typeVersion: + return Variable(); + + case typeString: + return Variable( toString() ); + + case typeList: + return Variable( toList() ); + + case typeRef: + return Variable::mkRef( toString() ); + } + throw Bu::ExceptionBase("Unhandled case in Variable toType"); +} + +void Variable::append( const Variable &v ) +{ + if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type."); + + if( v.eType == typeList ) + { + uVal.lVal->append( *v.uVal.lVal ); + } + else + { + uVal.lVal->append( v ); + } +} + +VarList::iterator Variable::begin() +{ + if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type."); + + return uVal.lVal->begin(); +} + +VarList::const_iterator Variable::begin() const +{ + if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type."); + + return const_cast(uVal.lVal)->begin(); +} + +void Variable::doNegate() +{ + switch( eType ) + { + case typeNone: + break; + + case typeBool: + throw Bu::ExceptionBase("You cannot negate boolean values."); + + case typeInt: + uVal.iVal = -uVal.iVal; + break; + + case typeFloat: + uVal.fVal = -uVal.fVal; + break; + + case typeVersion: + throw Bu::ExceptionBase("You cannot negate version values."); + + case typeString: + throw Bu::ExceptionBase("You cannot negate string values."); + + case typeList: + throw Bu::ExceptionBase("You cannot negate list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot negate reference values."); + } +} + +void Variable::doNot() +{ + bool bVal = !toBool(); + reset( typeBool ); + uVal.bVal = bVal; +} + +const Variable &Variable::operator=( const Variable &rhs ) +{ + reset( rhs.eType ); + if( rhs.eType == typeString || rhs.eType == typeRef ) + { + uVal.sVal = new Bu::FString( *rhs.uVal.sVal ); + } + else if( rhs.eType == typeList ) + { + uVal.lVal = new VarList( *rhs.uVal.lVal ); + } + else + { + uVal = rhs.uVal; + } + + return *this; +} + +const Variable &Variable::operator=( const int &rhs ) +{ + reset( typeInt ); + uVal.iVal = rhs; + + return *this; +} + +const Variable &Variable::operator=( const double &rhs ) +{ + reset( typeFloat ); + uVal.fVal = rhs; + + return *this; +} + +const Variable &Variable::operator=( const bool &rhs ) +{ + reset( typeBool ); + uVal.bVal = rhs; + + return *this; +} + +const Variable &Variable::operator=( const Bu::FString &rhs ) +{ + reset( typeString ); + uVal.sVal = new Bu::FString( rhs ); + + return *this; +} + +const Variable &Variable::operator+=( const Variable &rhs ) +{ + switch( eType ) + { + case typeNone: + reset( rhs.eType ); + if( eType == typeString || eType == typeRef ) + { + uVal.sVal = new Bu::FString( *rhs.uVal.sVal ); + } + else if( eType == typeList ) + { + uVal.lVal = new VarList( *rhs.uVal.lVal ); + } + else + { + uVal = rhs.uVal; + } + break; + + case typeInt: + uVal.iVal += rhs.getInt(); + break; + + case typeFloat: + uVal.fVal += rhs.getFloat(); + break; + + case typeBool: + throw Bu::ExceptionBase("Can't += with a boolean..."); + break; + + case typeString: + uVal.sVal->append(" "); + uVal.sVal->append( rhs.getString() ); + break; + + case typeList: + uVal.lVal->append( rhs.getList() ); + break; + + case typeVersion: + break; + + default: + break; + } + return *this; +} + +const Variable &Variable::operator<<( const Variable &rhs ) +{ + switch( eType ) + { + case typeNone: + reset( rhs.eType ); + if( eType == typeString ) + { + uVal.sVal = new Bu::FString( *rhs.uVal.sVal ); + } + else if( eType == typeList ) + { + uVal.lVal = new VarList( *rhs.uVal.lVal ); + } + else + { + uVal = rhs.uVal; + } + break; + + case typeString: + uVal.sVal->append( rhs.getString() ); + break; + + case typeList: + uVal.lVal->append( rhs.getList() ); + break; + + default: + throw Bu::ExceptionBase("Can't << with non-string or non-list."); + break; + } + return *this; +} + +bool Variable::operator==( const Variable &rhs ) const +{ + if( eType != rhs.eType ) + return false; + switch( eType ) + { + case typeNone: + return true; + + case typeInt: + return uVal.iVal == rhs.uVal.iVal; + + case typeFloat: + return uVal.fVal == rhs.uVal.fVal; + + case typeBool: + return uVal.bVal == rhs.uVal.bVal; + + case typeString: + case typeRef: + return *uVal.sVal == *rhs.uVal.sVal; + + case typeList: + return *uVal.lVal == *rhs.uVal.lVal; + + case typeVersion: + return false; + } + + return false; +} + +bool Variable::operator!=( const Variable &rhs ) const +{ + return !(*this == rhs); +} + +bool Variable::operator<( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return false; + + case typeBool: + throw Bu::ExceptionBase("You cannot < compare boolean values."); + + case typeInt: + return toInt() < rhs.toInt(); + + case typeFloat: + return toFloat() < rhs.toFloat(); + + case typeVersion: + return true; + + case typeString: + return toString() < rhs.toString(); + + case typeList: + throw Bu::ExceptionBase("You cannot < compare list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot < compare reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable < compare"); +} + +bool Variable::operator>( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return false; + + case typeBool: + throw Bu::ExceptionBase("You cannot > compare boolean values."); + + case typeInt: + return toInt() > rhs.toInt(); + + case typeFloat: + return toFloat() > rhs.toFloat(); + + case typeVersion: + return true; + + case typeString: + return toString() > rhs.toString(); + + case typeList: + throw Bu::ExceptionBase("You cannot > compare list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot > compare reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable > compare"); +} + +bool Variable::operator<=( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return false; + + case typeBool: + throw Bu::ExceptionBase("You cannot <= compare boolean values."); + + case typeInt: + return toInt() <= rhs.toInt(); + + case typeFloat: + return toFloat() <= rhs.toFloat(); + + case typeVersion: + return true; + + case typeString: + return toString() <= rhs.toString(); + + case typeList: + throw Bu::ExceptionBase("You cannot <= compare list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot <= compare reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable <= compare"); +} + +bool Variable::operator>=( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return false; + + case typeBool: + throw Bu::ExceptionBase("You cannot >= compare boolean values."); + + case typeInt: + return toInt() >= rhs.toInt(); + + case typeFloat: + return toFloat() >= rhs.toFloat(); + + case typeVersion: + return true; + + case typeString: + return toString() >= rhs.toString(); + + case typeList: + throw Bu::ExceptionBase("You cannot >= compare list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot >= compare reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable >= compare"); +} + +Variable Variable::operator+( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return Variable(); + + case typeBool: + throw Bu::ExceptionBase("You cannot add boolean values."); + + case typeInt: + return Variable( toInt() + rhs.toInt() ); + + case typeFloat: + return Variable( toFloat() + rhs.toFloat() ); + + case typeVersion: + throw Bu::ExceptionBase("You cannot add version values."); + + case typeString: + return Variable( toString() + rhs.toString() ); + + case typeList: + return Variable( toList() + rhs.toList() ); + + case typeRef: + throw Bu::ExceptionBase("You cannot add reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable add"); +} + +Variable Variable::operator-( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return Variable(); + + case typeBool: + throw Bu::ExceptionBase("You cannot subtract boolean values."); + + case typeInt: + return Variable( toInt() - rhs.toInt() ); + + case typeFloat: + return Variable( toFloat() - rhs.toFloat() ); + + case typeVersion: + throw Bu::ExceptionBase("You cannot subtract version values."); + + case typeString: + throw Bu::ExceptionBase("You cannot subtract string values."); + + case typeList: + throw Bu::ExceptionBase("You cannot subtract list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot subtract reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable subtract"); +} + +Variable Variable::operator*( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return Variable(); + + case typeBool: + throw Bu::ExceptionBase("You cannot multiply boolean values."); + + case typeInt: + return Variable( toInt() * rhs.toInt() ); + + case typeFloat: + return Variable( toFloat() * rhs.toFloat() ); + + case typeVersion: + throw Bu::ExceptionBase("You cannot multiply version values."); + + case typeString: + throw Bu::ExceptionBase("You cannot multiply string values."); + + case typeList: + throw Bu::ExceptionBase("You cannot multiply list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot multiply reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable multiply"); +} + +Variable Variable::operator/( const Variable &rhs ) const +{ + Type eTop = Bu::max( eType, rhs.eType ); + switch( eTop ) + { + case typeNone: + return Variable(); + + case typeBool: + throw Bu::ExceptionBase("You cannot divide boolean values."); + + case typeInt: + return Variable( toInt() / rhs.toInt() ); + + case typeFloat: + return Variable( toFloat() / rhs.toFloat() ); + + case typeVersion: + throw Bu::ExceptionBase("You cannot divide version values."); + + case typeString: + throw Bu::ExceptionBase("You cannot divide string values."); + + case typeList: + throw Bu::ExceptionBase("You cannot divide list values."); + + case typeRef: + throw Bu::ExceptionBase("You cannot divide reference values."); + } + throw Bu::ExceptionBase("Unhandled case in Variable divide"); +} + +void Variable::reset( Type eNewType ) +{ + if( eType == typeString || eType == typeRef ) + { + delete uVal.sVal; + } + else if( eType == typeList ) + { + delete uVal.lVal; + } + memset( &uVal, 0, sizeof(uVal) ); + + eType = eNewType; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Variable::Type &t ) +{ + switch( t ) + { + case Variable::typeNone: f << "*typeless*"; break; + case Variable::typeInt: f << "int"; break; + case Variable::typeFloat: f << "double"; break; + case Variable::typeBool: f << "bool"; break; + case Variable::typeString: f << "string"; break; + case Variable::typeList: f << "list"; break; + case Variable::typeVersion: f << "version"; break; + case Variable::typeRef: f << "ref"; break; + } + return f; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Variable &v ) +{ + f << "Variable(" << v.getType() << ") = "; + switch( v.getType() ) + { + case Variable::typeNone: break; + case Variable::typeInt: f << v.getInt(); break; + case Variable::typeFloat: f << v.getFloat(); break; + case Variable::typeBool: f << v.getBool(); break; + case Variable::typeString: f << v.getString(); break; + case Variable::typeList: f << v.getList(); break; + case Variable::typeVersion:/*f << v.getVersion();*/ break; + case Variable::typeRef: f << v.getString(); break; + } + + return f; +} + diff --git a/src/variable.h b/src/variable.h new file mode 100644 index 0000000..1b5542e --- /dev/null +++ b/src/variable.h @@ -0,0 +1,115 @@ +#ifndef VARIABLE_H +#define VARIABLE_H + +#include "types.h" + +/** + * A build variable, which is basically a flexible, limited type range variant. + */ +class Variable +{ +public: + enum Type + { + typeNone, + typeBool, + typeInt, + typeFloat, + typeVersion, + typeString, + typeList, + typeRef /**< Reference by name, it's just a string. */ + }; + +public: + Variable(); + Variable( Type t ); + Variable( int iVal ); + Variable( double fVal ); + Variable( bool bVal ); + Variable( const Bu::FString &sVal ); + Variable( const char *sVal ); + Variable( const Variable &v ); + Variable( const class AstLeaf &l ); + /** + * This special case function turns the variable into a string if there is + * only one string in the list, or a list of strings if there is more or + * less than one. + */ + Variable( const StrList &lst ); + Variable( const VarList &lst ); + virtual ~Variable(); + + static Variable mkRef( const Bu::FString &sVal ); + + Type getType() const; + + // Raw aquisition functions, if the type isn't right, + // they throw an exception + int getInt() const; + double getFloat() const; + bool getBool() const; + const Bu::FString &getString() const; + const VarList &getList() const; + + // Conversion functions, they'll return the requested type, maybe an error + // if the source data is really bad + int toInt() const; + double toFloat() const; + bool toBool() const; + Bu::FString toString() const; + VarList toList() const; + + Variable toType( Type eNewType ) const; + + void append( const Variable &v ); + VarList::iterator begin(); + VarList::const_iterator begin() const; + + void doNegate(); + void doNot(); + + const Variable &operator=( const Variable &rhs ); + const Variable &operator=( const int &rhs ); + const Variable &operator=( const double &rhs ); + const Variable &operator=( const bool &rhs ); + const Variable &operator=( const Bu::FString &rhs ); + + const Variable &operator+=( const Variable &rhs ); + const Variable &operator<<( const Variable &rhs ); + + bool operator==( const Variable &rhs ) const; + bool operator!=( const Variable &rhs ) const; + bool operator<( const Variable &rhs ) const; + bool operator>( const Variable &rhs ) const; + bool operator<=( const Variable &rhs ) const; + bool operator>=( const Variable &rhs ) const; + + Variable operator+( const Variable &rhs ) const; + Variable operator-( const Variable &rhs ) const; + Variable operator*( const Variable &rhs ) const; + Variable operator/( const Variable &rhs ) const; + +private: + Type eType; + union + { + int iVal; + double fVal; + bool bVal; + Bu::FString *sVal; + VarList *lVal; + } uVal; + + void reset( Type eType ); +}; + +namespace Bu +{ + class Formatter; +} + +Bu::Formatter &operator<<( Bu::Formatter &f, const Variable::Type &t ); +Bu::Formatter &operator<<( Bu::Formatter &f, const Variable &v ); + +#endif diff --git a/src/view.cpp b/src/view.cpp new file mode 100644 index 0000000..6dcc9bb --- /dev/null +++ b/src/view.cpp @@ -0,0 +1,10 @@ +#include "view.h" + +View::View() +{ +} + +View::~View() +{ +} + diff --git a/src/view.h b/src/view.h new file mode 100644 index 0000000..fb172aa --- /dev/null +++ b/src/view.h @@ -0,0 +1,46 @@ +#ifndef VIEW_H +#define VIEW_H + +#include +#include "types.h" + +/** + * Base class for all views. A view is the only way that build is allowed to + * communicate with the user during the processing of a buildfile, the main + * executable may output some things for command line arguments and whatnot on + * it's own, or debugging info, but all reports of everything happening during + * the process is sent through a view, so it can be made pretty and usable. + */ +class View +{ +public: + View(); + virtual ~View(); + + virtual void beginAction( const Bu::FString &sAction )=0; + virtual void endAction()=0; + + virtual void skipTarget( const Bu::FString &sProfile, + const Target &rTarget )=0; + virtual void beginTarget( const Bu::FString &sProfile, + const Target &rTarget )=0; + virtual void processTarget( const Bu::FString &sProfile, + const Target &rTarget )=0; + virtual void endTarget()=0; + + virtual void buildRequires( const Target &rTarget )=0; + virtual void cmdStarted( const Bu::FString &sCmd )=0; + virtual void cmdFinished( const Bu::FString &sStdOut, + const Bu::FString &sStdErr, long iExit )=0; + + virtual void userError( const Bu::FString &sMsg )=0; + virtual void userWarning( const Bu::FString &sMsg )=0; + virtual void userNotice( const Bu::FString &sMsg )=0; + + virtual void sysError( const Bu::FString &sMsg )=0; + virtual void sysWarning( const Bu::FString &sMsg )=0; + +private: +}; + +#endif diff --git a/src/viewdefault.cpp b/src/viewdefault.cpp new file mode 100644 index 0000000..c38c62f --- /dev/null +++ b/src/viewdefault.cpp @@ -0,0 +1,170 @@ +#include "viewdefault.h" +#include "target.h" + +#include + +#include +using namespace Bu; + +PluginInterface3( pluginViewDefault, default, ViewDefault, View, + "Mike Buland", 0, 1 ); + +#define ESC "\x1b" + +#define C_RESET ESC "[0m" +#define C_RED ESC "[31m" +#define C_GREEN ESC "[32m" +#define C_YELLOW ESC "[33m" +#define C_BLUE ESC "[34m" +#define C_MAGENTA ESC "[35m" +#define C_CYAN ESC "[36m" +#define C_WHITE ESC "[37m" +#define C_DEFAULT ESC "[39m" + +#define C_BR_RED ESC "[1;31m" +#define C_BR_GREEN ESC "[1;32m" +#define C_BR_YELLOW ESC "[1;33m" +#define C_BR_BLUE ESC "[1;34m" +#define C_BR_MAGENTA ESC "[1;35m" +#define C_BR_CYAN ESC "[1;36m" +#define C_BR_WHITE ESC "[1;37m" + +ViewDefault::ViewDefault() : + bFirst( true ), + iDepth( 0 ), + iTotal( 0 ), + iCurrent( 0 ) +{ +} + +ViewDefault::~ViewDefault() +{ +} + +void ViewDefault::beginAction( const Bu::FString &/*sAction*/ ) +{ +} + +void ViewDefault::endAction() +{ +} + +void ViewDefault::skipTarget( const Bu::FString &/*sProfile*/, + const Target &/*rTarget*/ ) +{ + iCurrent++; +} + +void ViewDefault::beginTarget( const Bu::FString &sProfile, + const Target &rTarget ) +{ + if( iDepth == 0 ) + { + iTotal = rTarget.getDepCount(); + iCurrent = 0; + if( bFirst == false ) + { + sio << sio.nl; + } + bFirst = false; + sio << C_BR_WHITE << " --- " << C_BR_CYAN << sProfile << " " + << rTarget.getOutputList().first() << C_BR_WHITE << " --- " + << C_RESET << sio.nl; + } + iDepth++; +} + +void ViewDefault::processTarget( const Bu::FString &/*sProfile*/, + const Target &rTarget ) +{ + iCurrent++; + + int iPct = (iTotal>0)?(iCurrent*100/iTotal):(100); + sio << C_BR_WHITE << "[" << C_BR_GREEN << Fmt(3) << iPct + << "%" << C_BR_WHITE << "] " << C_BR_MAGENTA + << Fmt(10) << rTarget.getDisplay() << C_BR_WHITE + << ": " << rTarget.getOutputList().first() << C_RESET << sio.nl; +} + +void ViewDefault::endTarget() +{ + iDepth--; +} + +void ViewDefault::buildRequires( const Target &rTarget ) +{ + int iPct = (iTotal>0)?(iCurrent*100/iTotal):(100); + sio << C_BR_WHITE << "[" << C_BR_GREEN << Fmt(3) << iPct + << "%" << C_BR_WHITE << "] " << C_BR_MAGENTA + << Fmt(10) << "deps" << C_BR_WHITE + << ": " << rTarget.getOutputList().first() << C_RESET << sio.nl; +} + +void ViewDefault::cmdStarted( const Bu::FString &/*sCmd*/ ) +{ +} + +void ViewDefault::cmdFinished( const Bu::FString &sStdOut, + const Bu::FString &sStdErr, long /*iExit*/ ) +{ + if( sStdOut ) + { + Bu::FString::const_iterator b; + b = sStdOut.begin(); + while( b ) + { + Bu::FString::const_iterator e, max; + max = b + 78; + for( e = b; e != max && *e != '\n'; e++ ) { } + sio << C_BR_GREEN << "| " << C_RESET << FString( b, e ) << sio.nl; + b = e; + if( *b == '\n' ) + b++; + } + sio << C_BR_GREEN << "\\-----" << C_RESET << sio.nl; + } + if( sStdErr ) + { + Bu::FString::const_iterator b; + b = sStdErr.begin(); + while( b ) + { + Bu::FString::const_iterator e, max; + max = b + 78; + for( e = b; e != max && *e != '\n'; e++ ) { } + sio << C_BR_RED << "| " << C_RESET << FString( b, e ) << sio.nl; + b = e; + if( *b == '\n' ) + b++; + } + sio << C_BR_RED << "\\-----" << C_RESET << sio.nl; + } + //sio << C_BR_WHITE << "[" << C_BR_GREEN << sStdOut << C_BR_WHITE << "]" << sio.nl; + //sio << C_BR_WHITE << "[" << C_BR_RED << sStdErr << C_BR_WHITE << "]" << sio.nl; +} + +void ViewDefault::userError( const Bu::FString &sMsg ) +{ + sio << C_BR_RED << "Error: " << sMsg << C_RESET << sio.nl; +} + +void ViewDefault::userWarning( const Bu::FString &sMsg ) +{ + sio << C_BR_YELLOW << "Warning: " << sMsg << C_RESET << sio.nl; +} + +void ViewDefault::userNotice( const Bu::FString &sMsg ) +{ + sio << C_BR_GREEN << "Notice: " << sMsg << C_RESET << sio.nl; +} + +void ViewDefault::sysError( const Bu::FString &sMsg ) +{ + sio << C_BR_RED << sMsg << C_RESET << sio.nl; +} + +void ViewDefault::sysWarning( const Bu::FString &sMsg ) +{ + sio << C_BR_YELLOW << sMsg << C_RESET << sio.nl; +} + diff --git a/src/viewdefault.h b/src/viewdefault.h new file mode 100644 index 0000000..c9a554d --- /dev/null +++ b/src/viewdefault.h @@ -0,0 +1,42 @@ +#ifndef VIEW_DEFAULT_H +#define VIEW_DEFAULT_H + +#include "view.h" + +class ViewDefault : public View +{ +public: + ViewDefault(); + virtual ~ViewDefault(); + + virtual void beginAction( const Bu::FString &sAction ); + virtual void endAction(); + + virtual void skipTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void beginTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void processTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void endTarget(); + + virtual void buildRequires( const Target &rTarget ); + virtual void cmdStarted( const Bu::FString &sCmd ); + virtual void cmdFinished( const Bu::FString &sStdOut, + const Bu::FString &sStdErr, long iExit ); + + virtual void userError( const Bu::FString &sMsg ); + virtual void userWarning( const Bu::FString &sMsg ); + virtual void userNotice( const Bu::FString &sMsg ); + + virtual void sysError( const Bu::FString &sMsg ); + virtual void sysWarning( const Bu::FString &sMsg ); + +private: + bool bFirst; + int iDepth; + int iTotal; + int iCurrent; +}; + +#endif diff --git a/src/viewmake.cpp b/src/viewmake.cpp new file mode 100644 index 0000000..39d1327 --- /dev/null +++ b/src/viewmake.cpp @@ -0,0 +1,86 @@ +#include "viewmake.h" +#include "target.h" + +#include + +#include +using namespace Bu; + +PluginInterface3( pluginViewMake, make, ViewMake, View, + "Mike Buland", 0, 1 ); + +ViewMake::ViewMake() +{ +} + +ViewMake::~ViewMake() +{ +} + +void ViewMake::beginAction( const Bu::FString &/*sAction*/ ) +{ +} + +void ViewMake::endAction() +{ +} + +void ViewMake::skipTarget( const Bu::FString &/*sProfile*/, + const Target &/*rTarget*/ ) +{ +} + +void ViewMake::beginTarget( const Bu::FString &/*sProfile*/, + const Target &/*rTarget*/ ) +{ +} + +void ViewMake::processTarget( const Bu::FString &/*sProfile*/, + const Target &/*rTarget*/ ) +{ +} + +void ViewMake::endTarget() +{ +} + +void ViewMake::buildRequires( const Target &/*rTarget*/ ) +{ +} + +void ViewMake::cmdStarted( const Bu::FString &sCmd ) +{ + sio << sCmd << sio.nl; +} + +void ViewMake::cmdFinished( const Bu::FString &sStdOut, + const Bu::FString &sStdErr, long /*iExit*/ ) +{ + sio << sStdOut << sStdErr; +} + +void ViewMake::userError( const Bu::FString &sMsg ) +{ + sio << "Error: " << sMsg << sio.nl; +} + +void ViewMake::userWarning( const Bu::FString &sMsg ) +{ + sio << "Warning: " << sMsg << sio.nl; +} + +void ViewMake::userNotice( const Bu::FString &sMsg ) +{ + sio << "Notice: " << sMsg << sio.nl; +} + +void ViewMake::sysError( const Bu::FString &sMsg ) +{ + sio << sMsg << sio.nl; +} + +void ViewMake::sysWarning( const Bu::FString &sMsg ) +{ + sio << sMsg << sio.nl; +} + diff --git a/src/viewmake.h b/src/viewmake.h new file mode 100644 index 0000000..9100a86 --- /dev/null +++ b/src/viewmake.h @@ -0,0 +1,36 @@ +#ifndef VIEW_MAKE_H +#define VIEW_MAKE_H + +#include "view.h" + +class ViewMake : public View +{ +public: + ViewMake(); + virtual ~ViewMake(); + + virtual void beginAction( const Bu::FString &sAction ); + virtual void endAction(); + + virtual void skipTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void beginTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void processTarget( const Bu::FString &sProfile, + const Target &rTarget ); + virtual void endTarget(); + + virtual void buildRequires( const Target &rTarget ); + virtual void cmdStarted( const Bu::FString &sCmd ); + virtual void cmdFinished( const Bu::FString &sStdOut, + const Bu::FString &sStdErr, long iExit ); + + virtual void userError( const Bu::FString &sMsg ); + virtual void userWarning( const Bu::FString &sMsg ); + virtual void userNotice( const Bu::FString &sMsg ); + + virtual void sysError( const Bu::FString &sMsg ); + virtual void sysWarning( const Bu::FString &sMsg ); +}; + +#endif diff --git a/src/viewplugger.cpp b/src/viewplugger.cpp new file mode 100644 index 0000000..6046f82 --- /dev/null +++ b/src/viewplugger.cpp @@ -0,0 +1,14 @@ +#include "viewplugger.h" + +extern Bu::PluginInfo pluginViewDefault; +extern Bu::PluginInfo pluginViewMake; +ViewPlugger::ViewPlugger() +{ + registerBuiltinPlugin( &pluginViewDefault ); + registerBuiltinPlugin( &pluginViewMake ); +} + +ViewPlugger::~ViewPlugger() +{ +} + diff --git a/src/viewplugger.h b/src/viewplugger.h new file mode 100644 index 0000000..b58635c --- /dev/null +++ b/src/viewplugger.h @@ -0,0 +1,20 @@ +#ifndef VIEW_PLUGGER_H +#define VIEW_PLUGGER_H + +#include "view.h" +#include +#include + +class ViewPlugger : public Bu::Plugger, public Bu::Singleton +{ + friend class Bu::Singleton; +private: + ViewPlugger(); + +public: + virtual ~ViewPlugger(); + +private: +}; + +#endif -- cgit v1.2.3