/*---------------------------------------------------------------------------- ChucK Concurrent, On-the-fly Audio Programming Language Compiler and Virtual Machine Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. http://chuck.stanford.edu/ http://chuck.cs.princeton.edu/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A. -----------------------------------------------------------------------------*/ //----------------------------------------------------------------------------- // file: ulib_machine.cpp // desc: class library for 'Machine' // // author: Ge Wang (ge@ccrma.stanford.edu | gewang@cs.princeton.edu) // date: Spring 2004 //----------------------------------------------------------------------------- #include "ulib_doc.h" #include "chuck.h" #include "chuck_type.h" #include "chuck_vm.h" #include "chuck_instr.h" #include "chuck_compile.h" #include "chuck_errmsg.h" #include "util_string.h" #include #include #include #include using namespace std; // 1.5.0.0 ((ge, spencer) | added CK_DLL_CTOR( CKDoc_ctor ); CK_DLL_DTOR( CKDoc_dtor ); CK_DLL_MFUN( CKDoc_addGroup_str ); CK_DLL_MFUN( CKDoc_addGroup_type ); CK_DLL_MFUN( CKDoc_numGroups ); CK_DLL_MFUN( CKDoc_clearGroup ); CK_DLL_CTRL( CKDoc_examplesRoot_set ); CK_DLL_CGET( CKDoc_examplesRoot_get ); CK_DLL_CTRL( CKDoc_outputFormat_set ); CK_DLL_CGET( CKDoc_outputFormat_get ); CK_DLL_MFUN( CKDoc_genIndex ); CK_DLL_MFUN( CKDoc_genCSS ); CK_DLL_MFUN( CKDoc_genGroups ); CK_DLL_MFUN( CKDoc_genType_type ); CK_DLL_MFUN( CKDoc_genType_str ); CK_DLL_MFUN( CKDoc_outputToDir ); // offset static t_CKUINT CKDoc_offset_data = 0; // static initialization t_CKINT const CKDoc::FORMAT_NONE = 0; t_CKINT const CKDoc::FORMAT_HTML = 1; t_CKINT const CKDoc::FORMAT_TEXT = 2; t_CKINT const CKDoc::FORMAT_MARKDOWN = 3; t_CKINT const CKDoc::FORMAT_JSON = 4; //----------------------------------------------------------------------------- // name: ckdoc_query() // desc: add class for ckdoc documentation generator | 1.5.0.0 (ge, spencer, terry) //----------------------------------------------------------------------------- DLL_QUERY ckdoc_query( Chuck_DL_Query * QUERY ) { // get environment reference Chuck_Env * env = QUERY->env(); // set module name QUERY->setname( QUERY, "CKDoc" ); // function Chuck_DL_Func * func = NULL; // documentation string std::string doc; //--------------------------------------------------------------------- // init as class // 1.5.0.0 added by spencer and ge //--------------------------------------------------------------------- // doc string doc = "A ChucK documentation generator. Based on Spencer Salazar's ckdoc utility."; // begin class definition if( !type_engine_import_class_begin( env, "CKDoc", "Object", env->global(), CKDoc_ctor, CKDoc_dtor, doc.c_str() ) ) return FALSE; // data offset CKDoc_offset_data = type_engine_import_mvar( env, "int", "@CKDoc_data", FALSE ); if( CKDoc_offset_data == CK_INVALID_OFFSET ) goto error; // const static variables, for format selection, e.g., with CKDoc.HTML or CKDoc.TEXT if( !type_engine_import_svar( env, "int", "NONE", TRUE, (t_CKUINT)&CKDoc::FORMAT_NONE, "no output format." ) ) goto error; if( !type_engine_import_svar( env, "int", "HTML", TRUE, (t_CKUINT)&CKDoc::FORMAT_HTML, "output HTML format." ) ) goto error; if( !type_engine_import_svar( env, "int", "TEXT", TRUE, (t_CKUINT)&CKDoc::FORMAT_TEXT, "output TEXT format (not implemented)." ) ) goto error; if( !type_engine_import_svar( env, "int", "MARKDOWN", TRUE, (t_CKUINT)&CKDoc::FORMAT_MARKDOWN, "output MARKDOWN format (not implemented)." ) ) goto error; if( !type_engine_import_svar( env, "int", "JSON", TRUE, (t_CKUINT)&CKDoc::FORMAT_JSON, "output JSON format (not implemented)." ) ) goto error; // addGroup (by types) func = make_new_mfun( "void", "addGroup", CKDoc_addGroup_type ); func->add_arg( "Type[]", "types" ); func->add_arg( "string", "name" ); func->add_arg( "string", "shortName" ); func->add_arg( "string", "description" ); func->doc = "Add a group of types to be documented, including group 'name', a 'shortName' to be used for any files, and a group 'description'."; if( !type_engine_import_mfun( env, func ) ) goto error; // addGroup (by names) func = make_new_mfun( "void", "addGroup", CKDoc_addGroup_str ); func->add_arg( "string[]", "typeNames" ); func->add_arg( "string", "name" ); func->add_arg( "string", "shortName" ); func->add_arg( "string", "description" ); func->doc = "Add a group of types (by type name) to be documented, including group 'name', a 'shortName' to be used for any files, and a group 'description'."; if( !type_engine_import_mfun( env, func ) ) goto error; // numGroups func = make_new_mfun( "int", "numGroups", CKDoc_numGroups ); func->doc = "Get the number of groups added."; if( !type_engine_import_mfun( env, func ) ) goto error; // clear func = make_new_mfun( "void", "clear", CKDoc_clearGroup ); func->doc = "Clear all added groups."; if( !type_engine_import_mfun( env, func ) ) goto error; // examplesRoot func = make_new_mfun( "string", "examplesRoot", CKDoc_examplesRoot_set ); func->add_arg( "string", "path" ); func->doc = "Set the examples directory root path; returns what was set."; if( !type_engine_import_mfun( env, func ) ) goto error; // examplesRoot func = make_new_mfun( "string", "examplesRoot", CKDoc_examplesRoot_get ); func->doc = "Get the current examples directory root path."; if( !type_engine_import_mfun( env, func ) ) goto error; // outputFormat func = make_new_mfun( "int", "outputFormat", CKDoc_outputFormat_set ); func->add_arg( "int", "which" ); func->doc = "Set which output format to use; see CKDoc.HTML, CKDoc.TEXT, CKDoc.MARKDOWN, CKDoc.JSON."; if( !type_engine_import_mfun( env, func ) ) goto error; // CKDoc_outputFormat_set func = make_new_mfun( "int", "outputFormat", CKDoc_outputFormat_get ); func->doc = "Set which output format is selected; see CKDoc.HTML, CKDoc.TEXT, CKDoc.MARKDOWN, CKDoc.JSON."; if( !type_engine_import_mfun( env, func ) ) goto error; // genIndex func = make_new_mfun( "string", "genIndex", CKDoc_genIndex ); func->add_arg( "string", "indexTitle" ); func->doc = "Generate top-level index; return as string."; if( !type_engine_import_mfun( env, func ) ) goto error; // genCSS func = make_new_mfun( "string", "genCSS", CKDoc_genCSS ); func->doc = "Generate CSS; return as string."; if( !type_engine_import_mfun( env, func ) ) goto error; // genGroups func = make_new_mfun( "void", "genGroups", CKDoc_genGroups ); func->add_arg( "string[]", "results" ); func->doc = "Generate documentation for all added groups, return each in a separate entry"; if( !type_engine_import_mfun( env, func ) ) goto error; // genType func = make_new_mfun( "string", "genType", CKDoc_genType_type ); func->add_arg( "Type", "type" ); func->doc = "Generate documentation for a single Type."; if( !type_engine_import_mfun( env, func ) ) goto error; // genType func = make_new_mfun( "string", "genType", CKDoc_genType_str ); func->add_arg( "string", "typeName" ); func->doc = "Generate documentation for a single Type (by name)."; if( !type_engine_import_mfun( env, func ) ) goto error; // outputToDir func = make_new_mfun( "int", "outputToDir", CKDoc_outputToDir ); func->add_arg( "string", "path" ); func->add_arg( "string", "indexTitle" ); func->doc = "Generate everything as files into the output directory."; if( !type_engine_import_mfun( env, func ) ) goto error; // end the class import type_engine_import_class_end( env ); return true; error: return false; } //----------------------------------------------------------------------------- // name: struct CKDocGroup | 1.5.0.0 (ge, spencer) added // desc: structure for a doc group e.g., "UGens" //----------------------------------------------------------------------------- struct CKDocGroup { // types in the group vector types; // name of the group string name; // short name of the group (used for filenames and such) string shortName; // description of the group string desc; }; //----------------------------------------------------------------------------- // name: type2url() // desc: convert type name to URL, using groups to detemine the file for each //----------------------------------------------------------------------------- string type2url( const string & type, const vector & groups ) { // search for( t_CKINT i = 0; i < groups.size(); i++ ) { for( t_CKINT j = 0; j < groups[i]->types.size(); j++ ) { // check if( groups[i]->types[j] == NULL ) continue; // compare if( type == groups[i]->types[j]->name ) { // file + location within file: basically: 'group.html#type' return groups[i]->shortName + ".html#" + type; } } } // TODO: make a list of default classes on chuck website to link to // return "[type2url NOT IMPLEMENTED]"; return ""; } //----------------------------------------------------------------------------- // name: class CKDocHTMLOutput | 1.5.0.0 (ge, spencer) added // desc: HTML output (based on Spencer's ckdoc) //----------------------------------------------------------------------------- class CKDocHTMLOutput : public CKDocOutput { private: Chuck_Func * m_func; std::string m_title; Chuck_VM * m_vm_ref; Chuck_Env * m_env_ref; public: CKDocHTMLOutput( Chuck_VM * vm ) : m_vm_ref(vm), m_func(NULL) { // get the env m_env_ref = vm != NULL ? vm->env() : NULL; } // file extension virtual string fileExtension() const { return ".html"; } // render HTML index virtual string renderIndex( const string & indexTitle, const vector & groups ); public: void begin( const string & title ) { // trim m_title = trim(title); // check length if( m_title.length() == 0 ) m_title = " "; // string to return m_outputStr += "\n"; m_outputStr += "\n"; m_outputStr += "\n"; m_outputStr += "" + m_title + "\n"; m_outputStr += "\n"; } void heading() { m_outputStr += "\n"; m_outputStr += "
\n"; m_outputStr += "
\n"; m_outputStr += "
"; m_outputStr += ""; m_outputStr += "\n"; m_outputStr += "

  /  " + m_title + "

\n"; m_outputStr += "
\n"; m_outputStr += "
\n"; m_outputStr += "
\n"; // #title } void end() { m_outputStr += "\n"; } void title(const std::string &_title) { } void begin_body() { m_outputStr += "
\n"; } void end_body() { m_outputStr += "
\n"; // #body m_outputStr += "\n"; } void begin_toc() { m_outputStr += "\n"; } void begin_classes( CKDocGroup * group ) { m_outputStr += "
\n"; m_outputStr += "

" + group->desc + "

\n"; } void end_classes() { m_outputStr += "
\n"; } void begin_class(Chuck_Type * type, const vector & groups ) { m_outputStr += "name + "\" />
\n"; m_outputStr += "

name + "\">" + type->name + "

\n"; // type heirarchy Chuck_Type * parent = type->parent; // check if there is a parent class if( parent != NULL ) { m_outputStr += "

"; m_outputStr += "inherits "; // m_outputStr += "

"; } // iterate over parents while( parent != NULL ) { m_outputStr += ": name, groups) + "\" class=\"" + css_class_for_type(m_env_ref, parent) + "\">" + parent->name + " ", // go up the inheritance chain parent = parent->parent; } // end the tag if( type->parent != NULL ) m_outputStr += "

\n"; if(type->doc.size() > 0) { std::string doc = capitalize(type->doc); m_outputStr += "

" + doc + "

\n"; } else m_outputStr += "

No description available

\n"; } void end_class() { m_outputStr += "

[ top ]

\n"; m_outputStr += "
\n
\n"; } void begin_examples() { m_outputStr += "

examples

    \n"; } void end_examples() { m_outputStr += "
\n"; } void begin_static_member_vars() { m_outputStr += "

static member variables

\n
\n"; } void end_static_member_vars() { m_outputStr += "
\n"; } void begin_member_vars() { m_outputStr += "

member variables

\n
\n"; } void end_member_vars() { m_outputStr += "
\n"; } void begin_static_member_funcs() { m_outputStr += "

static member functions

\n
\n"; } void end_static_member_funcs() { m_outputStr += "
\n"; } void begin_member_funcs() { m_outputStr += "

member functions

\n
\n"; } void end_member_funcs() { m_outputStr += "
\n"; } void example(const std::string &name, const std::string &url) { m_outputStr += "
  • " + name + "
  • \n"; } void static_member_var(Chuck_Value * var) { m_outputStr += "
    \n

    type) + "\">" + var->type->name; // check array depth if( var->type->array_depth ) { m_outputStr += ""; m_outputStr += ""; for(int i = 0; i < var->type->array_depth; i++) m_outputStr += "[]"; } m_outputStr += " "; // function name m_outputStr += "" + var->name + "

    "; if(var->doc.size() > 0) m_outputStr += "

    " + capitalize(var->doc) + "

    \n"; else m_outputStr += "

    No description available

    \n"; m_outputStr += "
    \n"; } void member_var(Chuck_Value * var) { m_outputStr += "
    \n

    type) + "\">" + var->type->name; // check array depth if( var->type->array_depth ) { m_outputStr += ""; m_outputStr += ""; for(int i = 0; i < var->type->array_depth; i++) m_outputStr += "[]"; } m_outputStr += " "; // function name m_outputStr += "" + var->name + "

    "; if(var->doc.size() > 0) m_outputStr += "

    " + capitalize(var->doc) + "

    \n"; else m_outputStr += "

    No description available

    \n"; m_outputStr += "
    \n"; } void begin_static_member_func(Chuck_Func * func) { // return type m_outputStr += "
    \n

    def->ret_type) + "\">" + func->def->ret_type->name; // check array if( func->def->ret_type->array_depth ) { m_outputStr += ""; m_outputStr += ""; for(int i = 0; i < func->def->ret_type->array_depth; i++) m_outputStr += "[]"; } m_outputStr += " "; // function name m_outputStr += "" + string(S_name(func->def->name)) + "("; m_func = func; } void end_static_member_func() { m_outputStr += ")

    \n"; if(m_func->doc.size() > 0) m_outputStr += "

    " + capitalize(m_func->doc) + "

    \n"; else m_outputStr += "

    No description available

    \n"; m_outputStr += "
    \n"; m_func = NULL; } void begin_member_func(Chuck_Func * func) { // return type m_outputStr += "
    \n

    def->ret_type) + "\">" + func->def->ret_type->name.c_str(); // check array if( func->def->ret_type->array_depth ) { m_outputStr += ""; m_outputStr += ""; for(int i = 0; i < func->def->ret_type->array_depth; i++) m_outputStr += "[]"; } m_outputStr += " "; // function name m_outputStr += "" + string(S_name(func->def->name)) + "("; m_func = func; } void end_member_func() { m_outputStr += ")

    \n"; if(m_func->doc.size() > 0) m_outputStr += "

    " + capitalize(m_func->doc) + "

    \n"; else m_outputStr += "

    No description available

    \n"; m_outputStr += "
    \n"; m_func = NULL; } void func_arg(a_Arg_List arg) { // argument type m_outputStr += "type) + "\">" + arg->type->name; // check array if( arg->type->array_depth ) { m_outputStr += ""; m_outputStr += ""; for(int i = 0; i < arg->type->array_depth; i++) m_outputStr += "[]"; } m_outputStr += " "; // argument name std::string varname = varnameclean(S_name(arg->var_decl->xid)); m_outputStr += varname; if(arg->next != NULL) m_outputStr += ", "; } public: static bool isugen( Chuck_Type *type ) { return type->ugen_info != NULL; } static std::string varnameclean( std::string vn ) { // strip [] vn.erase(std::remove(vn.begin(), vn.end(), '['), vn.end()); vn.erase(std::remove(vn.begin(), vn.end(), ']'), vn.end()); return vn; } static std::string cssclean( std::string s ) { for(int i = 0; i < s.length(); i++) { if(!isalnum(s[i])) s[i] = '_'; } return s; } static std::string css_class_for_type( Chuck_Env * env, Chuck_Type * type ) { if( isprim(env, type) || isvoid(env, type) || (type->array_depth && isprim(env, type->array_type)) ) return "ckdoc_typename ckdoc_type_primitive"; else if(isugen(type)) return "ckdoc_typename ckdoc_type_ugen"; else return "ckdoc_typename ckdoc_type_object"; } }; //----------------------------------------------------------------------------- // name: renderIndex() // desc: render HTML index //----------------------------------------------------------------------------- string CKDocHTMLOutput::renderIndex( const string & title, const vector & groups ) { // output string stream ostringstream sout; // output index header sout << "\n\n"; sout << "" << title << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << " \n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "\n"; sout << "
    \n"; // loop over groups for( t_CKINT i = 0; i < groups.size(); i++ ) { CKDocGroup * group = groups[i]; sout << "
    \n"; sout << "
    \n"; sout << "

    shortName << ".html\">" << group->name << "

    \n"; sout << "

    " << group->desc << "

    \n"; sout << "
    \n"; sout << "
    \n"; sout << "

    \n"; for( t_CKINT j = 0; j < group->types.size(); j++ ) { Chuck_Type * type = group->types[j]; if( !type ) continue; string cssClass = css_class_for_type( m_env_ref, type ); // TODO: check for array_depth sout << "shortName << ".html#" << type->name << "\" class=\"" << cssClass << "\">" << type->name << "\n"; } sout << "

    \n"; sout << "
    \n"; sout << "
    \n"; sout << "
    \n"; } sout << "
    \n"; sout << "\n\n"; // return the string literal return sout.str(); }; //----------------------------------------------------------------------------- // name: CKDoc() // desc: constructor //----------------------------------------------------------------------------- CKDoc::CKDoc( Chuck_VM * vm ) { // copy VM reference m_vm_ref = vm; // reset m_format = FORMAT_NONE; m_output = NULL; // default setOutputFormat( FORMAT_HTML ); } //----------------------------------------------------------------------------- // name: ~CKDoc() // desc: destructor //----------------------------------------------------------------------------- CKDoc::~CKDoc() { // clear groups clearGroups(); // clear output generator clearOutput(); // zero out m_vm_ref = NULL; } //----------------------------------------------------------------------------- // name: clearGroups() // desc: clear all added groups //----------------------------------------------------------------------------- void CKDoc::clearGroups() { // iterate groups for( t_CKINT i = 0; i < m_groups.size(); i++ ) { // iterate over a group for( t_CKINT j = 0; j < m_groups[i]->types.size(); j++ ) { // release ref count SAFE_RELEASE( m_groups[i]->types[j] ); } // deallocate the group struct SAFE_DELETE( m_groups[i] ); } // clear the arrar m_groups.clear(); } //----------------------------------------------------------------------------- // name: clearOutput() // desc: clear output generator //----------------------------------------------------------------------------- void CKDoc::clearOutput() { // reste m_format = FORMAT_NONE; // deallocate SAFE_DELETE( m_output ); } //----------------------------------------------------------------------------- // name: addGroup() // add a group of types to document //----------------------------------------------------------------------------- t_CKBOOL CKDoc::addGroup( const vector & types, const string & name, const string & shortName, const string & description ) { // trim string shortNameTrim = trim(shortName); // check for empty string; shortName is needed as filename for outputToDir() if( shortNameTrim == "" ) { EM_error3( "[CKDoc.addGroup()]: 'shortName' parameter cannot be empty" ); return FALSE; } // check for conflicts for( t_CKINT i = 0; i < m_groups.size(); i++ ) { if( shortNameTrim == m_groups[i]->shortName ) { EM_error3( "[CKDoc.addGroup()]: duplicate shortName found: '%s'...", shortNameTrim.c_str() ); return FALSE; } } // allocate group CKDocGroup * group = new CKDocGroup(); // check if( !group ) { EM_error3( "[CKDoc.addGroup()]: could not allocate new memory...bailing out" ); return FALSE; } // copy group->name = trim(name); group->shortName = shortNameTrim; group->desc = trim(description); // iterate for( t_CKINT i = 0; i < types.size(); i++ ) { // filter out NULL elements if( types[i] == NULL ) continue; // ref count SAFE_ADD_REF( types[i] ); // append group->types.push_back( types[i] ); } // add to groups m_groups.push_back( group ); return TRUE; } //----------------------------------------------------------------------------- // name: numGroups() // desc: get number of groups //----------------------------------------------------------------------------- t_CKINT CKDoc::numGroups() const { return m_groups.size(); } //----------------------------------------------------------------------------- // name: setOutputFormat() // desc: set output format: CKDoc.HTML, CKDoc.TEXT, CKDoc.MARKDOWN, CKDoc.JSON //----------------------------------------------------------------------------- t_CKBOOL CKDoc::setOutputFormat( t_CKINT which ) { // clear output clearOutput(); // format names const char * formats[] = { "NONE", "HTML", "TEXT", "MARKDOWN", "JSON" }; // copy for now m_format = which; // check switch( which ) { // currently supported case FORMAT_NONE: break; case FORMAT_HTML: m_output = new CKDocHTMLOutput( m_vm_ref ); break; // currently unsupported case FORMAT_TEXT: case FORMAT_MARKDOWN: case FORMAT_JSON: EM_error3( "[CKDoc]: unsupported format '%s'...", formats[which] ); goto error; // unrecognized default: EM_error3( "[CKDoc]: unrecognized format ID '%i'...", which ); goto error; } return TRUE; error: // set to none m_format = FORMAT_NONE; // return false return FALSE; } //----------------------------------------------------------------------------- // name: getOutputFormat() // desc: get output format //----------------------------------------------------------------------------- t_CKINT CKDoc::getOutpuFormat() const { return m_format; } //----------------------------------------------------------------------------- // name: setExamplesRoot() // desc: set base examples root path //----------------------------------------------------------------------------- void CKDoc::setExamplesRoot( const string & path ) { // set m_examplesRoot = path; // trim m_examplesRoot = trim( m_examplesRoot ); // process if( m_examplesRoot.length() == 0 ) { m_examplesRoot = "./"; } // check for trailing / if( m_examplesRoot[m_examplesRoot.length()-1] != '/' ) { m_examplesRoot += "/"; } } //----------------------------------------------------------------------------- // name: getExamplesRoot() // desc: get base examples root path //----------------------------------------------------------------------------- string CKDoc::getExamplesRoot() const { // set return m_examplesRoot; } //----------------------------------------------------------------------------- // name: genIndex() // desc: generate top-level index file; return as string //----------------------------------------------------------------------------- string CKDoc::genIndex( const string & title ) { // check if( !m_output ) return ""; // generate index return m_output->renderIndex( title, m_groups ); } //----------------------------------------------------------------------------- // name: genCSS() // desc: generate CSS; return as string //----------------------------------------------------------------------------- string CKDoc::genCSS() { // return the string literal return m_ckdocCSS; } //----------------------------------------------------------------------------- // compare functions //----------------------------------------------------------------------------- bool comp_func(Chuck_Func *a, Chuck_Func *b) { return a->name < b->name; } bool comp_value(Chuck_Value *a, Chuck_Value *b) { return a->name < b->name; } //----------------------------------------------------------------------------- // name: genGroups() // desc: generate all added groups, return each in a separate entry //----------------------------------------------------------------------------- void CKDoc::genGroups( vector & results ) { // clear results array results.clear(); // set the size results.resize( m_groups.size() ); // iterate over added groups for( t_CKINT i = 0; i < m_groups.size(); i++ ) { // generate each group results[i] = genGroup( m_groups[i], TRUE ); } } //----------------------------------------------------------------------------- // name: genGroup() // desc: generate documentation for a group //----------------------------------------------------------------------------- string CKDoc::genGroup( CKDocGroup * group, t_CKBOOL clearOutput ) { // check if( m_output == NULL ) return "[NO OUTPUT FORMAT SELECTED (genGroup)]"; bool do_toc = true; bool do_heading = true; list type_args; // the output CKDocOutput * output = m_output; // clear output if( clearOutput ) output->clear(); // output group title output->begin( group->name ); // do heading if( do_heading ) output->heading(); // begin output body output->begin_body(); // table of contents if( do_toc ) { // begin TOC output->begin_toc(); // iterate for( vector::const_iterator t = group->types.begin(); t != group->types.end(); t++ ) { // get the current type Chuck_Type * type = *t; // skip // if( skip(type->name) ) continue; // output the type in TOC output->toc_class( type ); } // end TOC output->end_toc(); } // begin type contents output->begin_classes( group ); // iterate for( vector::const_iterator t = group->types.begin(); t != group->types.end(); t++ ) { // skip // if( skip(type->name) ) continue; // gen type, don't clear output genType( *t, FALSE ); } // end types output->end_classes(); // end file output->end_body(); // end output output->end(); // return output string return output->str(); } //----------------------------------------------------------------------------- // name: genType() // desc: generate documentation for a single Type //----------------------------------------------------------------------------- string CKDoc::genType( Chuck_Type * type, t_CKBOOL clearOutput ) { // check if( m_output == NULL ) return "[NO OUTPUT FORMAT SELECTED (getType)]"; // the output CKDocOutput * output = m_output; // clear it if( clearOutput ) output->clear(); // begin the type output->begin_class( type, m_groups ); // output examples if( type->examples.size() ) { // begin examples output->begin_examples(); // iterate over examples for( vector::iterator _ex = type->examples.begin(); _ex != type->examples.end(); _ex++ ) { string ex = *_ex; string name; string url; // test for absolute vs. relative URL if( ex.find("http://") == 0 || ex.find("https://") == 0 ) { // absolute URL url = ex; // find the example name without path std::basic_string::size_type pos = ex.rfind('/'); if( pos != string::npos ) name = string(ex, pos+1); else name = ex; } else { // relative URL url = m_examplesRoot + ex; name = ex; } // output examples output->example(name, url); } // done with examples output->end_examples(); } // check type info if( type->info ) { // get functions vector funcs; type->info->get_funcs( funcs ); // get values vector vars; type->info->get_values( vars ); // function names map func_names; // member and static functions and values vector mfuncs; vector sfuncs; vector mvars; vector svars; // fprintf(stderr, "### %lu vars\n", vars.size()); // iterate through values for( vector::iterator v = vars.begin(); v != vars.end(); v++ ) { // the value Chuck_Value * value = *v; // check if(value == NULL) continue; // zero length name if( value->name.length() == 0 ) continue; // special internal values if(value->name[0] == '@') continue; // value is a function if( value->type->name == "[function]" ) continue; // static or instance? if( value->is_static ) svars.push_back( value ); else mvars.push_back( value ); } // iterate over functions for( vector::iterator f = funcs.begin(); f != funcs.end(); f++ ) { // the function Chuck_Func * func = *f; // check if(func == NULL) continue; // if already seen (overloaded?) if( func_names.count(func->name) ) continue; // first one func_names[func->name] = 1; // static or instance? if(func->def->static_decl == ae_key_static) sfuncs.push_back(func); else mfuncs.push_back(func); } // sort sort(svars.begin(), svars.end(), comp_value); sort(mvars.begin(), mvars.end(), comp_value); sort(sfuncs.begin(), sfuncs.end(), comp_func); sort(mfuncs.begin(), mfuncs.end(), comp_func); // static vars if( svars.size() ) { // start output output->begin_static_member_vars(); // iterate for( vector::iterator v = svars.begin(); v != svars.end(); v++ ) output->static_member_var(*v); // end output output->end_static_member_vars(); } // member vars if( mvars.size() ) { // start output->begin_member_vars(); // iterate for( vector::iterator v = mvars.begin(); v != mvars.end(); v++ ) output->member_var(*v); // end output->end_member_vars(); } // static functions if( sfuncs.size() ) { // begin static functions output->begin_static_member_funcs(); // iterate for( vector::iterator f = sfuncs.begin(); f != sfuncs.end(); f++ ) { // the func Chuck_Func * func = *f; // begin output output->begin_static_member_func(func); // argument list a_Arg_List args = func->def->arg_list; while(args != NULL) { // output argument output->func_arg( args ); args = args->next; } // end output output->end_static_member_func(); } // end static functions output->end_static_member_funcs(); } // member functions if( mfuncs.size() ) { // begin member functions output->begin_member_funcs(); // iterate for( vector::iterator f = mfuncs.begin(); f != mfuncs.end(); f++ ) { // the func Chuck_Func * func = *f; // begin the func output->begin_member_func(func); // argument list a_Arg_List args = func->def->arg_list; while(args != NULL) { // output argument output->func_arg(args); args = args->next; } // end the func output->end_member_func(); } // end member functions output->end_member_funcs(); } } // end the type output->end_class(); // return result return output->str(); } //----------------------------------------------------------------------------- // name: outputToDir() // desc: generate everything as files into the output directory //----------------------------------------------------------------------------- t_CKBOOL CKDoc::outputToDir( const string & outputDir, const string & indexTitle ) { // groups content vector groupOutput; // trim string path = trim( outputDir ); // process if( path.length() == 0 ) { path = "./"; } // check for trailing / if( path[path.length()-1] != '/' ) { path += "/"; } // clear output m_output->clear(); // gen index if( !outputToFile( path + "index" + m_output->fileExtension(), genIndex( indexTitle ) ) ) goto error; // gen CSS if( !outputToFile( path + "ckdoc.css", genCSS() ) ) goto error; // gen groups genGroups( groupOutput ); // for each group for( t_CKINT i = 0; i < m_groups.size(); i++ ) { if( !outputToFile( path + m_groups[i]->shortName + m_output->fileExtension(), groupOutput[i] ) ) goto error; } // done return TRUE; error: // error EM_error3( "CKDoc: error encountered in outputToDir(...); bailing out..." ); return FALSE; } //----------------------------------------------------------------------------- // name: outputToFile() // desc: write string to file //----------------------------------------------------------------------------- t_CKBOOL CKDoc::outputToFile( const std::string & filename, const std::string & content ) { // open file ofstream fout( filename ); // if not good if( !fout.good() ) { EM_error3( "CKDoc: cannot open file '%s' for output...", filename.c_str() ); return FALSE; } // write fout << content; // done fout.close(); // success return TRUE; } //----------------------------------------------------------------------------- // CKDoc implementation //----------------------------------------------------------------------------- CK_DLL_CTOR( CKDoc_ctor ) { CKDoc * ckdoc = new CKDoc(VM); OBJ_MEMBER_UINT( SELF, CKDoc_offset_data ) = (t_CKUINT)ckdoc; } CK_DLL_DTOR( CKDoc_dtor ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT( SELF, CKDoc_offset_data ); SAFE_DELETE( ckdoc ); OBJ_MEMBER_UINT( SELF, CKDoc_offset_data ) = 0; } CK_DLL_MFUN( CKDoc_addGroup_type ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_Array4 * typeArray = (Chuck_Array4 *)GET_NEXT_OBJECT(ARGS); Chuck_String * groupName = GET_NEXT_STRING(ARGS); Chuck_String * shortName = GET_NEXT_STRING(ARGS); Chuck_String * description = GET_NEXT_STRING(ARGS); // check if null if( typeArray == NULL ) { CK_FPRINTF_STDERR( "CKDoc.addGroup(): null 'types' array; nothing added...\n" ); goto error; } else if( shortName == NULL || shortName->str() == "" ) { CK_FPRINTF_STDERR( "CKDoc.addGroup(): 'shortName' must be non-empty and unique; nothing added...\n" ); goto error; } else { vector types; for( int i = 0; i < typeArray->m_vector.size(); i++ ) { // get pointer as chuck string Chuck_Type * t = (Chuck_Type *)typeArray->m_vector[i]; // check if( t != NULL ) { // append types.push_back( t ); } } // check if( types.size() == 0 ) { CK_FPRINTF_STDERR( "CKDoc.addGroup(): empty 'types' array; nothing added...\n" ); goto error; } // add group and return success code RETURN->v_int = ckdoc->addGroup( types, groupName ? groupName->str() : "", shortName ? shortName->str() : "", description ? description->str() : "" ); } // done return; error: RETURN->v_int = FALSE; } CK_DLL_MFUN( CKDoc_addGroup_str ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_Array4 * strArray = (Chuck_Array4 *)GET_NEXT_OBJECT(ARGS); Chuck_String * groupName = GET_NEXT_STRING(ARGS); Chuck_String * shortName = GET_NEXT_STRING(ARGS); Chuck_String * desc = GET_NEXT_STRING(ARGS); // check if null if( strArray == NULL ) { CK_FPRINTF_STDERR( "[chuck]: CKDoc.addGroup() given null 'typeNames' array; nothing added...\n" ); goto error; } else { // type names vector typeNames; // get the names from chuck strings to c++ strings for( int i = 0; i < strArray->m_vector.size(); i++ ) { // get pointer as chuck string Chuck_String * s = (Chuck_String *)strArray->m_vector[i]; // check if( s != NULL ) { // append typeNames.push_back( s->str() ); } } // types vector types; // convert to types type_engine_names2types( VM->env(), typeNames, types ); // check if( types.size() == 0 ) { CK_FPRINTF_STDERR( "[chuck]: CKDoc.addGroup() given 'typeNames' array has no associated Types; nothing added...\n" ); goto error; } // add group ckdoc->addGroup( types, groupName ? groupName->str() : "", shortName ? shortName->str() : "", desc ? desc->str() : "" ); } // set return value RETURN->v_int = TRUE; // done return; error: RETURN->v_int = FALSE; } CK_DLL_MFUN( CKDoc_numGroups ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); // return (could be different than requested) RETURN->v_int = ckdoc->numGroups(); } CK_DLL_MFUN( CKDoc_clearGroup ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); // clear ckdoc->clearGroups(); } CK_DLL_CTRL( CKDoc_examplesRoot_set ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * path = GET_NEXT_STRING(ARGS); // check if null if( path == NULL ) { // default ckdoc->setExamplesRoot( "./" ); } else { // set examples root ckdoc->setExamplesRoot( trim( path->str() ) ); } // set return value RETURN->v_string = path; } CK_DLL_CGET( CKDoc_examplesRoot_get ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); str->set( ckdoc->getExamplesRoot() ); RETURN->v_string = str; } CK_DLL_CTRL( CKDoc_outputFormat_set ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); t_CKINT format = GET_NEXT_INT(ARGS); // attempt to set ckdoc->setOutputFormat( format ); // return (could be different than requested) RETURN->v_int = ckdoc->getOutpuFormat(); } CK_DLL_CGET( CKDoc_outputFormat_get ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); // return RETURN->v_int = ckdoc->getOutpuFormat(); } CK_DLL_MFUN( CKDoc_genIndex ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * indexTitle = GET_NEXT_STRING(ARGS); Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); str->set( ckdoc->genIndex( indexTitle != NULL ? indexTitle->str() : "" ) ); RETURN->v_string = str; } CK_DLL_MFUN( CKDoc_genCSS ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); str->set( ckdoc->genCSS() ); RETURN->v_string = str; } CK_DLL_MFUN( CKDoc_genGroups ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); // the return array Chuck_Array4 * strArray = (Chuck_Array4 *)GET_NEXT_OBJECT(ARGS); // the c++ return vector vector results; // gen the groups ckdoc->genGroups( results ); // clear the return array strArray->clear(); // loop over results for( t_CKINT i = 0; i < results.size(); i++ ) { // instantiate chuck string Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); // set into chuck string str->set( ckdoc->genCSS() ); // push back strArray->push_back( (t_CKINT)str ); } } CK_DLL_MFUN( CKDoc_genType_type ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_Type * type = (Chuck_Type *)GET_NEXT_OBJECT(ARGS); // return something Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); // gen type str->set( type ? ckdoc->genType( type ) : "" ); // set return RETURN->v_string = str; } CK_DLL_MFUN( CKDoc_genType_str ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * typeName = GET_NEXT_STRING(ARGS); // c++ string string name = typeName ? typeName->str() : ""; // chuck type Chuck_Type * type = type_engine_find_type( VM->env(), typeName->str() ); // return something Chuck_String * str = (Chuck_String *)instantiate_and_initialize_object( SHRED->vm_ref->env()->t_string, SHRED ); // gen type str->set( type ? ckdoc->genType( type ) : "" ); // set return RETURN->v_string = str; } CK_DLL_MFUN( CKDoc_outputToDir ) { CKDoc * ckdoc = (CKDoc *)OBJ_MEMBER_UINT(SELF, CKDoc_offset_data); Chuck_String * path = GET_NEXT_STRING(ARGS); Chuck_String * indexTitle = GET_NEXT_STRING(ARGS); // the path as c++ string string thePath; string theTitle; // check theTitle = indexTitle != NULL ? indexTitle->str() : ""; // check if null if( path == NULL ) { thePath = "."; } else { // set output dir thePath = trim( path->str() ); } // output ckdoc->outputToDir( thePath, theTitle ); } // the default CSS (ckdoc.css), literally string CKDoc::m_ckdocCSS = "\ html\n\ {\n\ margin: 0;\n\ padding: 0;\n\ }\n\ \n\ body\n\ {\n\ margin-left: 2em;\n\ margin-right: 2em;\n\ font-family: Helvetica, sans-serif;\n\ font-size: 14px;\n\ }\n\ \n\ a\n\ {\n\ scroll-margin-top: 80px;\n\ }\n\ \n\ \n\ .index_group\n\ {\n\ clear: both;\n\ background-color: #eee;\n\ padding: 1em 1em 1em 1em;\n\ margin-bottom: 2em;\n\ vertical-align: top;\n\ }\n\ \n\ .index_group_title\n\ {\n\ float: left;\n\ margin: 0;\n\ width: 15%;\n\ padding-right: 3em;\n\ border-right: 4px solid #fcfcfc;\n\ }\n\ \n\ .index_group_classes\n\ {\n\ padding-top: 0.15em;\n\ padding-left: 1em;\n\ padding-bottom: 0.15em;\n\ \n\ margin: 0;\n\ margin-left: 20%;\n\ width: 75%;\n\ }\n\ \n\ .index_group_classes a\n\ {\n\ margin-right: 0.5em;\n\ line-height: 1.5em;\n\ }\n\ \n\ .index_group_desc\n\ {\n\ color: #555;\n\ }\n\ \n\ .group_desc\n\ {\n\ color: #000;\n\ }\n\ \n\ .index_group_title a\n\ {\n\ color: black;\n\ }\n\ \n\ #title\n\ {\n\ position: fixed;\n\ top: 0;\n\ left: 0;\n\ right: 0;\n\ border-bottom: 1px solid black;\n\ padding: 0.5em 2em;\n\ margin-bottom: 1.5em;\n\ background-color: white;\n\ float: left;\n\ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n\ }\n\ \n\ .titleL\n\ {\n\ float: left;\n\ width: auto;\n\ /* vertically center */\n\ line-height: 48px;\n\ height: 48px;\n\ }\n\ \n\ .gray\n\ {\n\ color: gray;\n\ }\n\ \n\ .inline\n\ {\n\ display: inline;\n\ }\n\ \n\ \n\ .titleR\n\ {\n\ float: right;\n\ width: initial;\n\ clear: none;\n\ }\n\ \n\ .titleLogo\n\ {\n\ width: 48px;\n\ height: 48px;\n\ padding-right: 8px;\n\ }\n\ \n\ .titleLogo img\n\ {\n\ width: 48px;\n\ height: 48px;\n\ }\n\ \n\ h1\n\ {\n\ font-size: 28px;\n\ margin: 0;\n\ }\n\ \n\ h1 a\n\ {\n\ color: black;\n\ text-decoration: none;\n\ }\n\ \n\ h1 a:hover\n\ {\n\ color: blue;\n\ text-decoration: underline;\n\ }\n\ \n\ #body\n\ {\n\ clear: both;\n\ margin-top: 80px;\n\ }\n\ \n\ .toc\n\ {\n\ float: left;\n\ position: fixed;\n\ top: 80px;\n\ clear: both;\n\ background-color: #eee;\n\ padding: 0.25em 0.5em 1em 0.75em;\n\ margin: 0;\n\ width: 12.5em;\n\ overflow-x: auto;\n\ overflow-y: auto;\n\ max-height: calc(100% - 100px);\n\ margin-bottom: 20px;\n\ }\n\ \n\ .toc_class\n\ {\n\ font-size: 12px;\n\ margin-top: 0.5em;\n\ margin-bottom: 0.25em;\n\ }\n\ \n\ .classes\n\ {\n\ clear: right;\n\ margin-left: 15em;\n\ margin-right: 0em;\n\ margin-top: 80px;\n\ }\n\ \n\ .class\n\ {\n\ background-color: #eee;\n\ padding: 0.025em 0.5em 0.25em 1em;\n\ }\n\ \n\ .class_title\n\ {\n\ font-family: Menlo, Monaco, Courier;\n\ font-size: 18px;\n\ }\n\ \n\ .class_inherits\n\ {\n\ font-weight: normal;\n\ }\n\ \n\ .class_hierarchy\n\ {\n\ font-family: Menlo, Monaco, Courier;\n\ font-size: 14px;\n\ /* margin-right: 4em; */\n\ }\n\ \n\ .class_section_header\n\ {}\n\ \n\ .class_description\n\ {\n\ font-family: Helvetica;\n\ font-size: 14px;\n\ margin-right: 4em;\n\ }\n\ \n\ .empty_class_description\n\ {\n\ font-family: Helvetica;\n\ font-size: 12px;\n\ font-style: italic;\n\ color: grey;\n\ margin-right: 4em;\n\ }\n\ \n\ .members\n\ {\n\ font-family: Menlo, Monaco, Courier;\n\ font-size: 13px;\n\ }\n\ \n\ .member\n\ {\n\ margin-left: 0em;\n\ padding-left: 0.85em;\n\ padding-top: 0.85em;\n\ border-top: 4px solid #fcfcfc;\n\ border-left: 4px solid #fcfcfc;\n\ \n\ padding-bottom: 0.85em;\n\ padding-left: 0.85em;\n\ margin-bottom: 1.5em;\n\ \n\ margin-right: 4em;\n\ padding-right: 0;\n\ }\n\ \n\ .member_declaration\n\ {\n\ margin-top: 0;\n\ margin-bottom: 0.5em;\n\ }\n\ \n\ .member_description\n\ {\n\ font-family: Helvetica;\n\ font-size: 14px;\n\ margin-top: 0;\n\ margin-bottom: 0;\n\ }\n\ \n\ .empty_member_description\n\ {\n\ font-family: Helvetica;\n\ font-size: 12px;\n\ font-style: italic;\n\ color: grey;\n\ margin-top: 0;\n\ margin-bottom: 0;\n\ }\n\ \n\ .ckdoc_typename\n\ {\n\ font-family: Menlo, Monaco, Courier;\n\ font-size: 13px;\n\ }\n\ \n\ .membername\n\ {\n\ font-weight: bold;\n\ }\n\ \n\ .top_link\n\ {\n\ text-align: right;\n\ margin-top: 2em;\n\ margin-right: 1em;\n\ }\n\ \n\ .top_link a\n\ {\n\ color: #44F;\n\ }\n\ \n\ .examples ul\n\ {\n\ margin-top: -1em;\n\ }\n\ \n\ .examples ul li a\n\ {\n\ text-decoration: none;\n\ }\n\ \n\ .clear\n\ {\n\ clear: both;\n\ }\n\ \n\ .ckdoc_type_primitive\n\ {\n\ color: blue;\n\ }\n\ \n\ .ckdoc_type_ugen\n\ {\n\ color: #A200EC;\n\ }\n\ \n\ .ckdoc_type_object\n\ {\n\ color: #800023;\n\ }\n\ ";