mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
570 lines
16 KiB
JavaScript
570 lines
16 KiB
JavaScript
//------------------------------------------------------------------------------
|
|
// <copyright file="codetohtml.js" company="Microsoft">
|
|
//
|
|
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// The use and distribution terms for this software are contained in the file
|
|
// named license.txt, which can be found in the root of this distribution.
|
|
// By using this software in any fashion, you are agreeing to be bound by the
|
|
// terms of this license.
|
|
//
|
|
// You must not remove this notice, or any other, from this software.
|
|
//
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/* *******************************************************************************
|
|
Derived from Peter Torr's formatCodeToHtml JScript sample.
|
|
|
|
This JScript file can (theoretically) be used to format source code for any
|
|
C-like language. It was written for JScript (and can format itself very nicely!)
|
|
but should work with C# and C++.
|
|
|
|
The default behavior is that extensions .cs and .js are recognized and the
|
|
correct .ini files loaded for that language. To support another language,
|
|
copy the js_* files and rename them to <lang_extention>_*. Then modify
|
|
the files to match the correct values for the desired language.
|
|
<lang>_codetohtml.ini Contains the name of the language, the other INI files,
|
|
and the CSS file to use.
|
|
<lang>_keywords.ini Should contain language keywords.
|
|
<lang>_userdefined.ini Should contain user-defined types to special case.
|
|
<lang>_futurereserved.ini Should contain future reserved words in the language.
|
|
|
|
You can also change the files header.txt and footer.txt files to contain whatever you
|
|
want for initial and final HTML blocks.
|
|
|
|
There are two .CSS stylesheets: codeblue and codewhite. Each simply has a different
|
|
background color. Change the .CSS file name in the <lang_extention>_codetohtml.ini
|
|
file to use whichever .CSS you prefer or use the /stylesheet: command-line option.
|
|
********************************************************************************* */
|
|
|
|
import System;
|
|
import System.IO;
|
|
|
|
// Our own special characters to be replaced by <, >, and &
|
|
var sLt = "\u1000";
|
|
var sGt = "\u1001";
|
|
var sAmp = "\u1002";
|
|
var sSpanStart = sLt + "span";
|
|
var sSpanEnd = sLt + "/span" + sGt;
|
|
|
|
// List of punctuation recognised by the language.
|
|
var INI_PUNCTUATION = "punctuation";
|
|
|
|
// To-end-of-line comment character[s]
|
|
var INI_LINE_COMMENT = "line_comment";
|
|
|
|
// Multi-line comments
|
|
var INI_MULTILINE_COMMENT_START = "multiline_comment_start";
|
|
var INI_MULTILINE_COMMENT_END = "multiline_comment_end";
|
|
|
|
// The default INI file -- only useful on the command line
|
|
var INI_INI_FILE = "ini";
|
|
|
|
// file containing keywords
|
|
var INI_KEYWORD_FILE = "keywords";
|
|
|
|
// file containing user-defined keywords
|
|
var INI_USERDEFINED_FILE = "userdefined";
|
|
|
|
// file containing future reserved words
|
|
var INI_FUTURERESERVED_FILE = "futurereserved";
|
|
|
|
// file containing user-specified replacements in the output file
|
|
var INI_USERREPLACEMENTS_FILE = "replace";
|
|
|
|
// file to insert at the start of the output
|
|
var INI_HEADER_FILE = "header";
|
|
|
|
// file to append to the end of the output
|
|
var INI_FOOTER_FILE = "footer";
|
|
|
|
// Language used for the transformation
|
|
var INI_LANGUAGE = "language";
|
|
|
|
// Default extension for the output
|
|
var INI_EXTENSION = "extension";
|
|
|
|
// Is the language case-sensitive?
|
|
var INI_CASE_SENSITIVE = "case_sensitive";
|
|
|
|
// People can access the command line in their custom replacements
|
|
var INI_COMMANDLINE_ARGS = "commandline";
|
|
|
|
var g_objOptions = null;
|
|
var g_objKeywords = null;
|
|
var g_objUserDefined = null;
|
|
var g_objFutureReserved = null;
|
|
var g_objUserReplacements = null;
|
|
var g_reCodeMatcher = null;
|
|
var g_sInputFile = "";
|
|
var g_sOutputFile = "";
|
|
|
|
g_objOptions = createDefaultOptions();
|
|
if (processCommandLine())
|
|
{
|
|
processIniFile(g_objOptions[INI_INI_FILE], g_objOptions);
|
|
loadSupportFiles();
|
|
g_reCodeMatcher = createRegExp();
|
|
|
|
print("\nRunning with the following options:\n");
|
|
dumpObj(g_objOptions, " ");
|
|
print("\n");
|
|
|
|
formatCode();
|
|
}//if
|
|
|
|
function dumpObj(obj, indent)
|
|
{
|
|
for (var p in obj)
|
|
{
|
|
if (typeof obj[p] == "object")
|
|
{
|
|
print(indent + p + ": Object:")
|
|
dumpObj(obj[p], indent + " ");
|
|
} //if
|
|
else
|
|
{
|
|
print(indent + p + ": " + obj[p]);
|
|
} //else
|
|
} //for
|
|
} //dumpObj()
|
|
|
|
function processCommandLine()
|
|
{
|
|
var filesFound = 0;
|
|
var args = getCommandLineArguments();
|
|
|
|
// Start at 1 since we don't want the EXE name
|
|
for (var i = 1; i < args.length; i++)
|
|
{
|
|
var sArg = args[i];
|
|
|
|
// check for a switch
|
|
// slash ('/') character is a valid filename character on UNIX, so we can't use it as a switch
|
|
if ((sArg.charAt(0) == "-") || ((sArg.charAt(0) == "/") && Path.DirectorySeparatorChar != "/"))
|
|
{
|
|
// Option is of the form <name>:<value>
|
|
// Add it to the global options.
|
|
var iIndex = sArg.indexOf(":");
|
|
g_objOptions[sArg.substring(1, iIndex)] = sArg.substring(iIndex + 1);
|
|
} //if
|
|
// Probably a file name
|
|
else
|
|
{
|
|
switch(filesFound)
|
|
{
|
|
case 0:
|
|
// input file name
|
|
g_sInputFile = sArg;
|
|
if (System.IO.File.Exists(g_sInputFile))
|
|
{
|
|
var ext = "js"; //default
|
|
ext = System.IO.Path.GetExtension(g_sInputFile);
|
|
ext = ext.substring(1, ext.Length); //Trim the "." off the extension
|
|
g_objOptions[INI_INI_FILE] = ext + "_codetohtml.ini";
|
|
} //if
|
|
else
|
|
{
|
|
print("\n Error: invalid file name or missing file: ", g_sInputFile);
|
|
return false;
|
|
} //else
|
|
break;
|
|
case 1:
|
|
// output file name
|
|
g_sOutputFile = sArg;
|
|
break;
|
|
default:
|
|
// nothing
|
|
break;
|
|
} //switch
|
|
filesFound++;
|
|
} //else
|
|
} //for
|
|
|
|
if (filesFound == 0)
|
|
{
|
|
showUsage();
|
|
return false;
|
|
} //if
|
|
|
|
// Save the command line, if people want it
|
|
g_objOptions[INI_COMMANDLINE_ARGS] = args;
|
|
|
|
return true;
|
|
} //processCommandLine()
|
|
|
|
function formatCode()
|
|
{
|
|
var sHeader = guardedReadFile(g_objOptions[INI_HEADER_FILE]);
|
|
var sFooter = guardedReadFile(g_objOptions[INI_FOOTER_FILE]);
|
|
var sSource = guardedReadFile(g_sInputFile);
|
|
|
|
// Replace all the code with the HTML
|
|
sSource = sSource.replace(g_reCodeMatcher, ReplaceSource);
|
|
|
|
// Replace all the tabs with
|
|
sSource = sSource.replace(/ {4}|\t/g, sAmp + "nbsp;" + sAmp + "nbsp;"
|
|
+ sAmp + "nbsp;" + sAmp + "nbsp;")
|
|
|
|
// Replace all the newlines with <br>
|
|
sSource = sSource.replace(/\n/g, sLt + "br" + sGt + "\n");
|
|
|
|
// Patch up <, >, and &
|
|
sSource = sSource.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
sSource = sSource.replace(/\u1000/g, "<").replace(/\u1001/g, ">").replace(/\u1002/g, "&");
|
|
|
|
// Add the header and footer text
|
|
var sOutputText = sHeader + sSource + sFooter;
|
|
|
|
// Do user-specified replacements
|
|
for (var sCustom in g_objUserReplacements)
|
|
{
|
|
debugPrint("Replacing %" + sCustom + "% with eval(" + g_objUserReplacements[sCustom] + ")");
|
|
var reCustom = new RegExp("%" + sCustom + "%", "gm");
|
|
try
|
|
{
|
|
sOutputText = sOutputText.replace(reCustom, eval(g_objUserReplacements[sCustom]));
|
|
} //try
|
|
catch(e)
|
|
{
|
|
logError("Couldn't replace %" + sCustom + "% due to a run-time error");
|
|
} //catch
|
|
} //for
|
|
|
|
if (g_sOutputFile == "")
|
|
g_sOutputFile = g_sInputFile + "." + g_objOptions[INI_EXTENSION];
|
|
|
|
writeFile(g_sOutputFile, sOutputText);
|
|
} //formatCode()
|
|
|
|
// Default options for the INI file (if they aren't over-ridden in the codetohtml.ini file)
|
|
function createDefaultOptions()
|
|
{
|
|
var o = new Object();
|
|
|
|
o[INI_INI_FILE] = "codetohtml.ini";
|
|
o[INI_KEYWORD_FILE] = "keywords.ini";
|
|
o[INI_USERDEFINED_FILE] = "userdefined.ini";
|
|
o[INI_FUTURERESERVED_FILE] = "futurereserved.ini";
|
|
o[INI_USERREPLACEMENTS_FILE] = "replace.ini";
|
|
o[INI_HEADER_FILE] = "header.txt";
|
|
o[INI_FOOTER_FILE] = "footer.txt";
|
|
o[INI_PUNCTUATION] = "~!%^&*()-=+{}[\\]|<>,/?;:";
|
|
o[INI_LINE_COMMENT] = "\\/\\/";
|
|
o[INI_MULTILINE_COMMENT_START] = "\\/\\*";
|
|
o[INI_MULTILINE_COMMENT_END] = "\\*\\/";
|
|
o[INI_LANGUAGE] = "JScript";
|
|
o[INI_EXTENSION] = "html";
|
|
o[INI_CASE_SENSITIVE] = "true";
|
|
|
|
return o;
|
|
} //createDefaultOptions()
|
|
|
|
// Processes the INI file, overriding the defaults with any
|
|
// options found in the INI file
|
|
function processIniFile(sFileName, obj)
|
|
{
|
|
try
|
|
{
|
|
var sData = readFile(sFileName);
|
|
var iniEntries = sData.split(Environment.NewLine);
|
|
|
|
for (var i = 0; i < iniEntries.length; i++)
|
|
{
|
|
var sEntry = iniEntries[i];
|
|
// debugPrint("Got entry '" + sEntry + "'");
|
|
|
|
// Look for comment lines and empty lines
|
|
if ((sEntry.charAt(0) == ";") || (sEntry == ""))
|
|
{
|
|
// print("It was blank or a comment");
|
|
continue;
|
|
} //if
|
|
|
|
// add the <name>=<value> pair to the object
|
|
var iIndex = sEntry.indexOf("=");
|
|
obj[sEntry.substring(0, iIndex)] = sEntry.substring(iIndex + 1);
|
|
} //for
|
|
} //try
|
|
catch(e)
|
|
{
|
|
logError("Couldn't process the file " + sFileName);
|
|
// logError(getErrorInfo(e));
|
|
} //catch
|
|
} //processIniFile()
|
|
|
|
function loadListOfWords(sFileName)
|
|
{
|
|
try
|
|
{
|
|
var arrTmp = readFile(sFileName).split(Environment.NewLine);
|
|
var objTmp = new Object();
|
|
var bCaseSensitive = g_objOptions[INI_CASE_SENSITIVE];
|
|
|
|
// Populate the object with properties named after
|
|
// the elements of the array
|
|
for (var i in arrTmp)
|
|
{
|
|
var s = arrTmp[i];
|
|
|
|
// case-insensitive?
|
|
if (bCaseSensitive != "true")
|
|
s = s.toLowerCase();
|
|
|
|
if (s != "")
|
|
objTmp[s] = true;
|
|
} //for
|
|
|
|
return objTmp;
|
|
} //try
|
|
// Probably the file was not found
|
|
catch(e)
|
|
{
|
|
logError("Couldn't load words from the file " + sFileName);
|
|
// logError(getErrorInfo(e));
|
|
return new Object();
|
|
} //catch
|
|
} //loadListOfWords
|
|
|
|
|
|
// Load and process the support files
|
|
function loadSupportFiles()
|
|
{
|
|
g_objKeywords = loadListOfWords(g_objOptions[INI_KEYWORD_FILE]);
|
|
g_objUserDefined = loadListOfWords(g_objOptions[INI_USERDEFINED_FILE]);
|
|
g_objFutureReserved = loadListOfWords(g_objOptions[INI_FUTURERESERVED_FILE]);
|
|
|
|
g_objUserReplacements = new Object();
|
|
processIniFile(g_objOptions[INI_USERREPLACEMENTS_FILE], g_objUserReplacements);
|
|
} //loadSupportFiles()
|
|
|
|
|
|
function createRegExp()
|
|
{
|
|
// This one works, but colours the entire conditional block,
|
|
// which isn't very useful if it is large. Put it back in if you want.
|
|
// var sConditionalPattern = "\\/\\*@(?:.|\\n)*?@\\*\\/";
|
|
|
|
var sConditionalPattern = "(?:\\/\\*@\\w+|@\\w+|@\\*\\/)";
|
|
|
|
// Single-line and multi-line comments
|
|
var sCommentPattern = "(?:" + g_objOptions[INI_LINE_COMMENT] + ".*?$|" +
|
|
g_objOptions[INI_MULTILINE_COMMENT_START] + "(?:.|\\n)*?" +
|
|
g_objOptions[INI_MULTILINE_COMMENT_END] + ")";
|
|
|
|
// Regular expressions
|
|
var sRegExpPattern = "\\/.*?(?!\\\\).\\/\\w+";
|
|
|
|
// Various types of numbers
|
|
var sExponentialPattern = "(?:(?:\\d+)?\\.)?\\d+e[+-]?\\d+";
|
|
var sFloatPattern = "(?:\\d+)?\\.\\d+";
|
|
var sOctalPattern = "0\\d+";
|
|
var sHexPattern = "0x(?:\\d|[a-f])+";
|
|
var sIntPattern = "\\d+";
|
|
|
|
// Identifiers
|
|
var sIdentifierPattern = "\\b[a-z_$]\\w+?\\b";
|
|
|
|
// Strings, including those with embedded newlines and embedded quotes
|
|
var sStringPattern = "(?:\"\"|\'\'|\"(?:\\n|.)*?(?!\\\\).\"|'.*?(?!\\\\).')";
|
|
|
|
// pre-processor stuff (for our C++ friends)
|
|
var sPreProcessor = "^\\s*#.*$";
|
|
|
|
// Punctuation
|
|
var sPunctuationPattern = "[" + g_objOptions[INI_PUNCTUATION] + "]";
|
|
|
|
// All sorts of code!
|
|
var sCodePattern =
|
|
"(" + sConditionalPattern + ")|" +
|
|
"(" + sCommentPattern + ")|" +
|
|
"(" + sRegExpPattern + ")|" +
|
|
"(" + sExponentialPattern + ")|" +
|
|
"(" + sFloatPattern + ")|" +
|
|
"(" + sOctalPattern + ")|" +
|
|
"(" + sHexPattern + ")|" +
|
|
"(" + sIntPattern + ")|" +
|
|
"(" + sIdentifierPattern + ")|" +
|
|
"(" + sStringPattern + ")|" +
|
|
"(" + sPreProcessor + ")|" +
|
|
"(" + sPunctuationPattern + ")";
|
|
|
|
return new RegExp(sCodePattern, "gim");
|
|
} //createRegExp()
|
|
|
|
function ReplaceSource(match, conditional, comment, re, exp, dec, oct, hex, num, id, str, pre, punct)
|
|
{
|
|
// debugPrint("Found text '" + match + "'");
|
|
|
|
if (conditional != undefined)
|
|
{
|
|
// debugPrint("...it's a conditional comment!");
|
|
return (sSpanStart + " class=\"conditional\"" + sGt + conditional + sSpanEnd);
|
|
}
|
|
|
|
if (comment != undefined)
|
|
{
|
|
// debugPrint("...it's a comment!");
|
|
return (sSpanStart + " class=\"comment\"" + sGt + comment + sSpanEnd);
|
|
}
|
|
|
|
if (re != undefined)
|
|
{
|
|
// debugPrint("...it's a regular expression!");
|
|
return (sSpanStart + " class=\"regexp\"" + sGt + re + sSpanEnd);
|
|
}
|
|
|
|
if (id != undefined)
|
|
{
|
|
// do we need to lower-case the string for matching?
|
|
var tmpId = id;
|
|
if (g_objOptions[INI_CASE_SENSITIVE] != "true")
|
|
tmpId = tmpId.toLowerCase();
|
|
|
|
// debugPrint("...it's an identifier!");
|
|
|
|
if (tmpId in g_objKeywords)
|
|
{
|
|
// debugPrint("...it's a keyword!");
|
|
return (sSpanStart + " class=\"keyword\"" + sGt + id + sSpanEnd);
|
|
}
|
|
else if (tmpId in g_objUserDefined)
|
|
{
|
|
// debugPrint("...it's a user-defined keyword!");
|
|
return (sSpanStart + " class=\"userdefined\"" + sGt + id + sSpanEnd);
|
|
}
|
|
else if (tmpId in g_objFutureReserved)
|
|
{
|
|
// debugPrint("...it's a future reserved keyword!");
|
|
return (sSpanStart + " class=\"futurereserved\"" + sGt + id + sSpanEnd);
|
|
}
|
|
else
|
|
{
|
|
return (sSpanStart + " class=\"identifier\"" + sGt + id + sSpanEnd);
|
|
}
|
|
} //if
|
|
|
|
if (exp != undefined)
|
|
{
|
|
// debugPrint("...it's an exponential number!");
|
|
return (sSpanStart + " class=\"exponential\"" + sGt + exp + sSpanEnd);
|
|
}
|
|
|
|
if (dec != undefined)
|
|
{
|
|
// debugPrint("...it's a decimal number!");
|
|
return (sSpanStart + " class=\"decimal\"" + sGt + dec + sSpanEnd);
|
|
}
|
|
|
|
if (oct != undefined)
|
|
{
|
|
// debugPrint("...it's an octal number (bad)!");
|
|
return (sSpanStart + " class=\"octal\"" + sGt + oct + sSpanEnd);
|
|
}
|
|
|
|
if (hex != undefined)
|
|
{
|
|
// debugPrint("...it's a hex number!");
|
|
return (sSpanStart + " class=\"hexadecimal\"" + sGt + hex + sSpanEnd);
|
|
}
|
|
|
|
if (num != undefined)
|
|
{
|
|
// debugPrint("...it's a normal number!");
|
|
return (sSpanStart + " class=\"number\"" + sGt + num + sSpanEnd);
|
|
}
|
|
|
|
if (str != undefined)
|
|
{
|
|
// debugPrint("...it's a string!");
|
|
return (sSpanStart + " class=\"string\"" + sGt + str + sSpanEnd);
|
|
}
|
|
|
|
if (pre != undefined)
|
|
{
|
|
// debugPrint("...it's a pre-processor directive!");
|
|
return (sSpanStart + " class=\"preprocessor\"" + sGt + pre + sSpanEnd);
|
|
}
|
|
|
|
if (punct != undefined)
|
|
{
|
|
// debugPrint("...it's a punctuator!");
|
|
return (sSpanStart + " class=\"punctuation\"" + sGt + punct + sSpanEnd);
|
|
}
|
|
|
|
logError("Found something but don't know what is is! " + match);
|
|
return sSpanStart + " class=\"error\"" + sGt + match + sSpanEnd;
|
|
} //ReplaceSource()
|
|
|
|
function guardedReadFile(sFileName)
|
|
{
|
|
try
|
|
{
|
|
return readFile(sFileName);
|
|
} //try
|
|
catch(e)
|
|
{
|
|
logError("Couldn't open file " + sFileName);
|
|
return "";
|
|
} //catch
|
|
} //guardedReadFile()
|
|
|
|
function showUsage()
|
|
{
|
|
logError("\nUsage: codetohtml <infile> [<outfile>]\n\n\
|
|
Default for outfile is <infile>.html\n\
|
|
Requires the files 'codetohtml.ini', 'keywords.ini', 'userdefined.ini',\n\
|
|
and 'futurereserved.ini' to be present in the directory.\n");
|
|
} //showUsage()
|
|
|
|
function debugPrint(s)
|
|
{
|
|
print(s);
|
|
} //debugPrint()
|
|
|
|
function readFile(sFileName : String) : String
|
|
{
|
|
var stream : StreamReader = File.OpenText(sFileName);
|
|
var sData : String = stream.ReadToEnd();
|
|
stream.Close();
|
|
return sData;
|
|
} //readFile()
|
|
|
|
function writeFile(sFileName : String, sContents : String)
|
|
{
|
|
var stream : StreamWriter = File.CreateText(sFileName);
|
|
stream.Write(sContents);
|
|
stream.Close();
|
|
} //writeFile()
|
|
|
|
function getCommandLineArguments() : Array
|
|
{
|
|
// BUG - compiler doesn't treat COM+ arrays as JScript arrays yet.
|
|
var arrArgs : Array = new Array();
|
|
var tmp : String[] = Environment.GetCommandLineArgs();
|
|
|
|
for (var i : int = 0; i < tmp.Length; i++)
|
|
arrArgs.push(tmp[i]);
|
|
|
|
return arrArgs;
|
|
} //getCommandLineArguments()
|
|
|
|
function logError(s : String)
|
|
{
|
|
Console.Error.WriteLine(s);
|
|
} //logError
|
|
|
|
function getErrorInfo(e) : String
|
|
{
|
|
if (e instanceof Error)
|
|
return e.number + ": " + e.description;
|
|
else if (e instanceof Exception)
|
|
return e.ToString() + "\n\n" + e.Message + "\n" + e.StackTrace;
|
|
else
|
|
return String(e);
|
|
} //getErrorInfo()
|
|
|