initial commit, all seems to work

This commit is contained in:
John Bintz 2008-10-08 19:54:28 -04:00
commit 59a795ee9d
36 changed files with 1129 additions and 0 deletions

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
Harmonious Code is Copyright 2008 John Bintz.
Harmonious Code 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.
Harmonious Code 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 Foobar. If not, see <http://www.gnu.org/licenses/>.

8
README Normal file
View File

@ -0,0 +1,8 @@
To build Harmonious Code:
* Make sure you're running haXe 2
* Download the PHP documentation from CVS
* Copy/symlink the phpdoc/phpbook/phpbook-xsl/version.xml file from the
PHP documentationinto the data directory
* Run test.sh. This will take a while as functions_tokens_cache.hxd is
built in the data directory.

3
build/command_line.hxml Normal file
View File

@ -0,0 +1,3 @@
-main CommandLineInterface
-neko ../neko/codeparser.n
-cp ../src

4
build/command_line.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
./setup.sh
haxe command_line.hxml && neko ../neko/codeparser.n $1 $2 $3 $4 $5

4
build/javascript.hxml Executable file
View File

@ -0,0 +1,4 @@
-js ../htdocs/harmoniouscode.js
-main JavaScriptTarget
-resource ../data/functions_tokens_cache.hxd
-cp ../src

6
build/javascript.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
./setup.sh
haxe -cp ../src -neko ../neko/regenerate.n -main RegenerateDataFiles && neko ../neko/regenerate.n
rm ../htdocs/harmoniouscode.js
haxe javascript.hxml

5
build/setup.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
for dir in neko data; do
if [ ! -e "../${dir}" ]; then mkdir "../${dir}" 2>&1; fi
done

5
build/test.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
./setup.sh
haxe tests.hxml && neko ../neko/my_tests.n

4
build/tests.hxml Normal file
View File

@ -0,0 +1,4 @@
-main MyTests
-neko ../neko/my_tests.n
-resource ../data/functions_tokens_cache.hxd
-cp ../src

43
htdocs/index.php Normal file
View File

@ -0,0 +1,43 @@
<html>
<head>
<title>Harmonious Code: Will my PHP code and Web hosting work together?</title>
<meta name="keywords" content="php, static code analysis, web hosting, functions, constants, language features" />
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<h1>Harmonious Code</h1>
<div id="loading">
Loading...
</div>
<div id="form" style="display: none">
<div id="form-holder">
<form action="" method="post" onsubmit="return false;">
<textarea name="source" id="source" rows="15" cols="80"></textarea><br />
<input id="analyze-code-button" type="button" value="Analyze Code" onclick="JavaScriptTarget.do_analysis(this.form.elements.source)" />
</form>
</div>
<div id="output"></div>
<div id="permanent-ignore" style="display: none">
To permanently ignore tokens, do one of the following:
<ul>
<li>To ignore globally, place at the top of the file on its own line:
<blockquote><code>//harmonious <span class="ignore-code-holder"></span></code></blockquote>
</li>
<li>To ignore within a particular block of code, wrap the code in the following:
<blockquote><code>//harmonious <span class="ignore-code-holder"></span><br />
&nbsp;&nbsp;...your code...<br />
//harmonious_end</code></blockquote>
</li>
</ul>
</div>
</div>
<div id="local-info">
<?php @include('local-info.php') ?>
</div>
<div id="haxe:trace"></div>
<div id="footer">
Harmonious Code is Copyright &copy; 2008 John Bintz &mdash; Licensed under the GPL Version 2
</div>
<script type="text/javascript" src="harmoniouscode.js"> </script>
</body>
</html>

73
htdocs/style.css Normal file
View File

@ -0,0 +1,73 @@
body {
background-color: #def
}
body, td, div, li, p, span {
font-family: tahoma, verdana, arial, sans-serif;
font-size: 12px;
}
div#form-holder {
text-align: center
}
input#analyze-code-button {
width: 200px;
padding: 5px;
}
div#loading {
text-align: center;
font-size: 20px;
font-weight: bold
}
table {
border-left: solid #aaa 1px
}
td, th {
border-right: solid #aaa 1px
}
td {
padding: 0 3px
}
td.token {
text-align: center;
font-weight: bold
}
tr.enabled {
background-color: #bfb
}
th {
padding: 0 15px
}
tr.disabled {
background-color: #fbb
}
th.filter {
text-decoration: underline;
cursor: pointer;
cursor: hand;
}
th.is-filtering {
color: blue;
}
div#footer {
text-align: center;
font-size: 10px;
color: #555;
font-style: oblique
}
span.ignore-code-holder {
font-family: monospace
}

141
src/CodeParser.hx Normal file
View File

@ -0,0 +1,141 @@
class CodeParser {
public var tokenProcessors(getTokenProcessors, null) : Hash<TokenProcessor>;
public function new() {
this.tokenProcessors = new Hash<TokenProcessor>();
}
#if neko
public function loadProcessorsFromDisk() {
var functionProcessor = new FunctionTokenProcessor();
if (!functionProcessor.load_from_cache()) {
functionProcessor.populate_from_file();
functionProcessor.save_to_cache();
}
this.tokenProcessors.set(Type.getClassName(Type.getClass(functionProcessor)), functionProcessor);
}
#end
public function loadProcessorsFromResources() {
var functionProcessor = new FunctionTokenProcessor();
functionProcessor.load_from_resource();
this.tokenProcessors.set(Type.getClassName(Type.getClass(functionProcessor)), functionProcessor);
}
public function getTokenProcessors() { return this.tokenProcessors; }
private function flatten_tokens_to_ignore(tokens_to_ignore : Array<Hash<Bool>>) : Hash<Bool> {
var flattened_tokens = new Hash<Bool>();
for (token_hash in tokens_to_ignore) {
for (token in token_hash.keys()) {
flattened_tokens.set(token, true);
}
}
return flattened_tokens;
}
public function parse(s : String) : Array<Result> {
var results = new Array<Result>();
var function_token_processor = this.tokenProcessors.get("FunctionTokenProcessor");
var function_tokens_found = new Hash<Bool>();
var tokens_to_ignore = new Array<Hash<Bool>>();
var flattened_tokens = new Hash<Bool>();
var index = 0;
var capture_index = null;
var s_length = s.length;
var is_capturing = false;
var capturable_search = ~/[a-zA-Z0-9\_]/;
var stoppable_search = ~/[a-zA-Z0-9\_\/]/;
while (index < s_length) {
var current = s.charAt(index);
var is_capturable = capturable_search.match(current);
if (is_capturable) {
if (!is_capturing) {
is_capturing = true;
capture_index = index;
}
} else {
if (is_capturing) {
var token = s.substr(capture_index, index - capture_index);
var is_function = false;
var is_function_searching = true;
var paren_search_index = index;
do {
var function_character = s.charAt(paren_search_index);
if (function_character == "(") {
is_function = true;
is_function_searching = false;
index = paren_search_index;
} else {
if (stoppable_search.match(function_character)) {
is_function_searching = false;
index = paren_search_index - 1;
}
}
if (is_function_searching) {
paren_search_index++;
if (paren_search_index >= s_length) {
is_function_searching = false;
}
}
} while (is_function_searching);
is_capturing = false;
if (!flattened_tokens.exists(token)) {
if (is_function) {
if (!function_tokens_found.exists(token)) {
if (function_token_processor.tokenHash.exists(token)) {
results.push(function_token_processor.tokenHash.get(token).toResult());
}
function_tokens_found.set(token, true);
}
}
}
} else {
if (current == "/") {
if (s.indexOf("//harmonious", index) == index) {
var end_of_line = s.indexOf("\n", index);
var ok_to_capture = false;
if (end_of_line > index) { ok_to_capture = true; }
if (end_of_line == -1) {
ok_to_capture = true;
end_of_line = s_length - 1;
}
if (ok_to_capture) {
if (s.indexOf("//harmonious_end", index) == index) {
tokens_to_ignore.pop();
} else {
var new_tokens_to_ignore = s.substr(index, end_of_line - index).split(" ");
new_tokens_to_ignore.shift();
var tokens_to_ignore_hash = new Hash<Bool>();
for (token in new_tokens_to_ignore) {
tokens_to_ignore_hash.set(token, true);
}
tokens_to_ignore.push(tokens_to_ignore_hash);
}
flattened_tokens = flatten_tokens_to_ignore(tokens_to_ignore);
index = end_of_line;
}
}
}
}
}
index++;
}
results.sort(Result.compare);
return results;
}
}

3
src/CodeToken.hx Normal file
View File

@ -0,0 +1,3 @@
class CodeToken {
}

View File

@ -0,0 +1,204 @@
class CodeVersionInformation {
public var final_versions : Hash<Hash<String>>;
public var minimum_versions : Hash<String>;
public var maximum_versions : Hash<String>;
public var all_modules : Array<String>;
public static function breakdown_version_number(version : String) {
return version.split(".");
}
public static function get_version_lower_than(s : String) : String {
var greater_than = ~/\&lt;= (.*)$/;
if (greater_than.match(s)) {
return greater_than.matched(1);
}
return null;
}
public static function breakdown_php_version_string(s : String) {
var parts = new Array<String>();
for (regexp in [ ~/^([^\ ]*) (.*)$/ ]) {
if (regexp.match(s)) {
var version = regexp.matched(2).split("-").shift();
var greater_than = ~/\&gt;= (.*)$/;
if (greater_than.match(version)) {
version = greater_than.matched(1);
}
parts.push(regexp.matched(1));
parts.push(version);
break;
}
}
return parts;
}
public static function get_highest_version(versions : Array<String>) : String {
return get_terminal_version(versions, true);
}
public static function get_lowest_version(versions : Array<String>) : String {
return get_terminal_version(versions, false);
}
public static function version_compare(one : String, two : String) {
var one_parts = one.split(".");
var two_parts = two.split(".");
var shortest = Math.floor(Math.min(one_parts.length, two_parts.length));
for (i in 0...shortest) {
var one_int = Std.parseInt(one_parts[i]);
var two_int = Std.parseInt(two_parts[i]);
if (one_int != two_int) {
return (one_int < two_int) ? -1 : 1;
}
}
if (one_parts.length < two_parts.length) { return -1; }
if (one_parts.length > two_parts.length) { return 1; }
return 0;
}
private static function get_terminal_version(versions : Array<String>, ?find_highest : Bool = true) : String {
var terminal_version = null;
for (version in versions) {
if (terminal_version == null) {
terminal_version = version;
} else {
switch (version_compare(terminal_version, version)) {
case -1:
terminal_version = (find_highest) ? version : terminal_version;
case 1:
terminal_version = (find_highest) ? terminal_version : version;
}
}
}
if (terminal_version != null) {
return terminal_version;
} else {
return null;
}
}
private static function merge_versions(target_hash : Hash<Array<String>>,
source_hash : Hash<Array<String>>,
is_lower : Bool) : Hash<Array<String>> {
for (source in source_hash.keys()) {
if (!target_hash.exists(source)) { target_hash.set(source, new Array<String>()); }
var version_info = target_hash.get(source);
if (is_lower) {
version_info.push(get_lowest_version(source_hash.get(source)));
} else {
version_info.push(get_highest_version(source_hash.get(source)));
}
target_hash.set(source, version_info);
}
return target_hash;
}
public static function split_version_string(s : String) : Hash<String> {
var version_lists = new Hash<Array<String>>();
var version_match = ~/^([^\ ]+) (.*)$/;
for (part in s.split(", ")) {
if (version_match.match(part)) {
var source = version_match.matched(1);
if (!version_lists.exists(source)) {
version_lists.set(source, new Array<String>());
}
var tmp = version_lists.get(source);
tmp.push(version_match.matched(2));
version_lists.set(source, tmp);
}
}
var final_versions = new Hash<String>();
for (source in version_lists.keys()) {
var tmp = version_lists.get(source);
tmp.sort(CodeVersionInformation.version_compare);
final_versions.set(source, tmp.join(", "));
}
return final_versions;
}
public function new(results : Array<Result>) {
var start_minimum_versions = new Hash<Array<String>>();
var start_maximum_versions = new Hash<Array<String>>();
for (result in results) {
if (result.is_enabled) {
var internal_minimum_version = new Hash<Array<String>>();
var internal_maximum_version = new Hash<Array<String>>();
for (part in result.version.split(", ")) {
var version_string_info = breakdown_php_version_string(part);
if (version_string_info.length > 0) {
var source = version_string_info[0];
if (!internal_minimum_version.exists(source)) {
internal_minimum_version.set(source, new Array<String>());
}
var version_info = internal_minimum_version.get(source);
version_info.push(version_string_info[1]);
internal_minimum_version.set(source, version_info);
var is_lower_than = get_version_lower_than(part);
if (is_lower_than != null) {
if (!internal_maximum_version.exists(source)) {
internal_maximum_version.set(source, new Array<String>());
}
var versions = internal_maximum_version.get(source);
versions.push(is_lower_than);
internal_maximum_version.set(source, versions);
}
}
}
merge_versions(start_minimum_versions, internal_minimum_version, true);
merge_versions(start_maximum_versions, internal_maximum_version, false);
}
}
this.minimum_versions = new Hash<String>();
this.maximum_versions = new Hash<String>();
this.all_modules = new Array<String>();
for (source in start_minimum_versions.keys()) {
this.minimum_versions.set(source, get_highest_version(start_minimum_versions.get(source)));
this.all_modules.push(source);
}
this.all_modules.sort(function(a, b) {
if (a == "PHP") { return -1; }
if (b == "PHP") { return -1; }
if (a == b) { return 0; }
return (a < b) ? -1 : 1;
});
for (source in start_maximum_versions.keys()) {
this.maximum_versions.set(source, get_lowest_version(start_maximum_versions.get(source)));
}
this.final_versions = new Hash<Hash<String>>();
this.final_versions.set("minimum", minimum_versions);
this.final_versions.set("maximum", maximum_versions);
}
public function is_valid() {
for (source in this.maximum_versions.keys()) {
var versions = [ this.maximum_versions.get(source), this.minimum_versions.get(source) ];
if (get_highest_version(versions) == this.minimum_versions.get(source)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,41 @@
class CommandLineInterface {
static public function main() {
var arguments = neko.Sys.args();
if (arguments.length > 0) {
if (neko.FileSystem.exists(arguments[0])) {
var code = neko.io.File.getContent(arguments[0]);
var parser = new CodeParser();
parser.loadProcessorsFromDisk();
var results = parser.parse(code);
var version_info = new CodeVersionInformation(results);
neko.Lib.print("Your code in " + arguments[0] + " requires the following minimum PHP & PECL module versions:\n");
var minimum = version_info.final_versions.get("minimum");
for (module in minimum.keys()) {
neko.Lib.print("* " + module + ": " + minimum.get(module) + "\n");
}
var maximum = version_info.final_versions.get("maximum");
var printed_message = false;
for (module in maximum.keys()) {
if (!printed_message) {
neko.Lib.print("Your code also can't use PHP or PECL modules newer than:\n");
printed_message = true;
}
neko.Lib.print("* " + module + ": " + maximum.get(module) + "\n");
if (!version_info.is_valid()) {
neko.Lib.print("This code may not run!\n");
}
}
}
}
}
}

5
src/ConstantToken.hx Normal file
View File

@ -0,0 +1,5 @@
class ConstantToken extends Token {
override public function getTokenType() {
return ResultType.Constant;
}
}

View File

@ -0,0 +1,25 @@
class ConstantTokenProcessor extends TokenProcessor {
public static var cachePath : String = "constant_tokens_cache.hxd";
override public function get_cache_path() { return ConstantTokenProcessor.cachePath; }
#if neko
public function populate_from_file(path : String) {
this.populate_from_string(neko.io.File.getContent(path));
}
public function populate_from_string(s : String) {
this.tokenHash = new Hash<Token>();
for (child in Xml.parse(s).firstElement()) {
if (child.nodeType == Xml.Element) {
if (child.nodeName == "function") {
var version = child.get("from");
version = ~/PECL /.replace(version, "");
version = ~/\:/.replace(version, " ");
var token = child.get("name");
this.tokenHash.set(token, new FunctionToken(child.get("name"), version));
}
}
}
}
#end
}

5
src/FunctionToken.hx Normal file
View File

@ -0,0 +1,5 @@
class FunctionToken extends Token {
override public function getTokenType() {
return ResultType.Function;
}
}

View File

@ -0,0 +1,26 @@
class FunctionTokenProcessor extends TokenProcessor {
public static var cachePath : String = "../data/functions_tokens_cache.hxd";
override public function get_cache_path() { return FunctionTokenProcessor.cachePath; }
public static var sourcePath : String = "../data/versions.xml";
#if neko
public function populate_from_file() {
this.populate_from_string(neko.io.File.getContent(sourcePath));
}
public function populate_from_string(s : String) {
this.tokenHash = new Hash<Token>();
for (child in Xml.parse(s).firstElement()) {
if (child.nodeType == Xml.Element) {
if (child.nodeName == "function") {
var version = child.get("from");
version = ~/PECL /.replace(version, "");
version = ~/\:/.replace(version, " ");
var token = child.get("name");
this.tokenHash.set(token, new FunctionToken(child.get("name"), version));
}
}
}
}
#end
}

View File

@ -0,0 +1,5 @@
class JavaScriptCallback {
public function parse(t : Token) {
js.Lib.document.getElementById("Current token: " + t.token);
}
}

178
src/JavaScriptTarget.hx Normal file
View File

@ -0,0 +1,178 @@
class JavaScriptTarget {
static public var code_parser : CodeParser;
static public var current_results : Array<Result>;
static public var show_only_modules : Hash<Bool>;
static public function main() {
var function_token = new FunctionToken("a","a");
code_parser = new CodeParser();
code_parser.loadProcessorsFromResources();
show_only_modules = new Hash<Bool>();
#if js
var loading_div = js.Lib.document.getElementById("loading");
var form_div = js.Lib.document.getElementById("form");
loading_div.style.display = "none";
form_div.style.display = "";
#end
}
static public function get_results(s : String) {
current_results = code_parser.parse(s);
}
static public function change_result(index_id : Int, state : Bool) : Bool {
if (index_id < current_results.length) {
current_results[index_id].is_enabled = state;
return true;
}
return false;
}
static public function toggle_module(module : String) {
if (!show_only_modules.exists(module)) {
show_only_modules.set(module, false);
}
show_only_modules.set(module, !show_only_modules.get(module));
}
#if js
static public function change_result_and_redraw(result_checkbox : Dynamic) {
var index_id_search = ~/^result-enabled-([0-9]+)$/;
if (index_id_search.match(result_checkbox.id)) {
var index_id = Std.parseInt(index_id_search.matched(1));
if (change_result(index_id, !result_checkbox.checked)) {
display_version_information();
}
}
}
static public function display_version_information() {
var version_info = new CodeVersionInformation(current_results);
var output = "Your code in requires the following minimum PHP & PECL module versions:<ul>";
var minimum = version_info.final_versions.get("minimum");
for (module in minimum.keys()) {
output += "<li>" + module + ": " + minimum.get(module) + "</li>";
}
output += "</ul>";
var maximum = version_info.final_versions.get("maximum");
var printed_message = false;
for (module in maximum.keys()) {
if (!printed_message) {
output += "Your code also can't use PHP or PECL modules newer than:<ul>";
printed_message = true;
}
output += ("<li>" + module + ": " + maximum.get(module) + "</li>");
}
if (printed_message) { output += "</ul>"; }
if (!version_info.is_valid()) {
output += "<p><strong>This code may not run!</strong></p>";
}
output += "<form action=\"\" onsubmit=\"return false\">";
output += "<table cellspacing=\"0\" id=\"results-list\">";
output += "<tr><th>Token</th><th>Ignore?</th>";
for (module in version_info.all_modules) {
var classes = ["filter"];
if (show_only_modules.exists(module)) {
if (show_only_modules.get(module)) {
classes.push("is-filtering");
}
}
output += "<th title=\"click to toggle filter\" class=\"" + classes.join(" ") + "\" onclick=\"JavaScriptTarget.toggle_module_and_redraw('" + module + "')\">" + module + "</th>";
}
output += "</tr>";
var ignored_tokens = new Array<String>();
var id_index = 0;
for (result in current_results) {
var ok_to_show = true;
var modules_check_out = true;
if (!result.is_enabled) { ignored_tokens.push(result.token); }
var max_versions = CodeVersionInformation.split_version_string(result.version);
for (module in show_only_modules.keys()) {
if (show_only_modules.get(module)) {
ok_to_show = false;
if (!max_versions.exists(module)) { modules_check_out = false; }
}
}
if (modules_check_out) { ok_to_show = true; }
if (ok_to_show) {
var result_class = (result.is_enabled ? "enabled" : "disabled");
var result_id = "result-" + id_index;
var enabled_id = "result-enabled-" + id_index;
output += "<tr id=\"" + result_id + "\" class=\"" + result_class + "\">";
output += "<td class=\"token\">" + result.token + "</td>";
output += "<td align=\"center\"><input onclick=\"JavaScriptTarget.change_result_and_redraw(this)\" type=\"checkbox\" name=\"" + enabled_id + "\" id=\"" + enabled_id + "\""
+ ((!result.is_enabled) ? " checked" : "") + " /></td>";
for (module in version_info.all_modules) {
output += "<td>";
if (max_versions.exists(module)) {
output += max_versions.get(module);
} else {
output += "&nbsp;";
}
output += "</td>";
}
output += "</tr>";
id_index++;
}
}
output += "</table>";
output += "</form>";
var permanent_ignore_div = js.Lib.document.getElementById('permanent-ignore');
if (ignored_tokens.length > 0) {
var spans = js.Lib.document.getElementsByTagName("span");
for (span_index in 0...spans.length) {
if (spans[span_index].className == "ignore-code-holder") {
spans[span_index].innerHTML = ignored_tokens.join(" ");
}
}
permanent_ignore_div.style.display = "";
} else {
permanent_ignore_div.style.display = "none";
}
js.Lib.document.getElementById('output').innerHTML = output;
}
static public function do_analysis(textarea) {
show_only_modules = new Hash<Bool>();
JavaScriptTarget.get_results(textarea.value);
JavaScriptTarget.display_version_information();
}
static public function toggle_module_and_redraw(module : String) {
JavaScriptTarget.toggle_module(module);
JavaScriptTarget.display_version_information();
}
#end
}

14
src/MyTests.hx Normal file
View File

@ -0,0 +1,14 @@
class MyTests {
static function main() {
var r = new haxe.unit.TestRunner();
r.add(new TestToken());
r.add(new TestFunctionToken());
r.add(new TestFunctionTokenProcessor());
r.add(new TestConstantToken());
r.add(new TestCodeParser());
r.add(new TestCodeVersionInformation());
r.add(new TestResult());
r.add(new TestJavaScriptTarget());
r.run();
}
}

View File

@ -0,0 +1,15 @@
class RegenerateDataFiles {
public static function main() {
var functionProcessor = new FunctionTokenProcessor();
if (!functionProcessor.load_from_cache()) {
neko.Lib.print("Regenerating functions cache...\n");
functionProcessor.populate_from_file();
functionProcessor.save_to_cache();
}
var constantProcessor = new ConstantTokenProcessor();
if (!constantProcessor.load_from_cache()) {
neko.Lib.print("Regenerating constants cache...\n");
}
}
}

30
src/Result.hx Normal file
View File

@ -0,0 +1,30 @@
class Result {
public var type(getType, null) : ResultType;
public var token(getToken, null) : String;
public var version(getVersion, null) : String;
public var is_enabled : Bool;
public function new(type : ResultType, token : String, version : String) {
this.type = type;
this.token = token;
this.version = version;
this.is_enabled = true;
}
public function getType() { return this.type; }
public function getToken() { return this.token; }
public function getVersion() { return this.version; }
public static function change_enabled(results : Array<Result>, token : String, set_is_enabled : Bool) {
for (result in results) {
if (result.token == token) {
result.is_enabled = set_is_enabled;
}
}
}
public static function compare(a : Result, b : Result) : Int{
if (a.token == b.token) { return 0; }
return (a.token < b.token) ? -1 : 1;
}
}

5
src/ResultType.hx Normal file
View File

@ -0,0 +1,5 @@
enum ResultType {
Generic;
Function;
Constant;
}

30
src/TestCodeParser.hx Normal file
View File

@ -0,0 +1,30 @@
class TestCodeParser extends haxe.unit.TestCase {
static var test_code = [
[ "this is my array_shift() method", "1", "{minimum => {PHP => 4}, maximum => {}}" ],
[ "this is my array_shift() json_encode() method", "2", "{minimum => {PHP => 5.2.0, json => 1.2.0}, maximum => {}}" ],
[ "this is my array_shift() json_encode() cpdf_arc()", "3", "{minimum => {PHP => 5.2.0, json => 1.2.0}, maximum => {PHP => 5.0.5}}" ],
[ "array_shift()", "1", "{minimum => {PHP => 4}, maximum => {}}" ],
[ "//harmonious json_encode\narray_shift() json_encode()\n//harmonious_end", "1", "{minimum => {PHP => 4}, maximum => {}}" ],
[ "//harmonious json_encode\narray_shift() json_encode()\n//harmonious_end\njson_encode()", "2", "{minimum => {PHP => 5.2.0, json => 1.2.0}, maximum => {}}" ]
];
#if neko
function testCodeParserLoadTokens() {
var p = new CodeParser();
p.loadProcessorsFromDisk();
assertTrue(p.tokenProcessors.exists("FunctionTokenProcessor"));
}
function testProcessCode() {
var p = new CodeParser();
p.loadProcessorsFromDisk();
for (code in test_code) {
var result = p.parse(code[0]);
assertEquals(Std.parseInt(code[1]), result.length);
var code_version_info = new CodeVersionInformation(result);
assertEquals(code[2], code_version_info.final_versions.toString());
}
}
#end
}

View File

@ -0,0 +1,86 @@
class TestCodeVersionInformation extends haxe.unit.TestCase {
function testIsInvalid() {
var valid_results = [
new Result(ResultType.Function, "one", "PHP 4, PHP 5"),
new Result(ResultType.Function, "two", "PHP 4 &lt;= 4.2.0")
];
var code_version_info = new CodeVersionInformation(valid_results);
assertTrue(code_version_info.is_valid());
var invalid_results = [
new Result(ResultType.Function, "one", "PHP 5 &gt;= 5.2.0"),
new Result(ResultType.Function, "two", "PHP 4 &lt;= 4.2.0")
];
var code_version_info = new CodeVersionInformation(invalid_results);
assertFalse(code_version_info.is_valid());
}
function testBreakdownVersionString() {
assertEquals("[PHP, 4]", CodeVersionInformation.breakdown_php_version_string("PHP 4").toString());
assertEquals("[PHP, 5.2.0]", CodeVersionInformation.breakdown_php_version_string("PHP 5 &gt;= 5.2.0").toString());
assertEquals("[xmlwriter, 2.0.4]", CodeVersionInformation.breakdown_php_version_string("xmlwriter 2.0.4").toString());
assertEquals("[xmlwriter, 0.1]", CodeVersionInformation.breakdown_php_version_string("xmlwriter 0.1-2.0.4").toString());
}
function testVersionCompare() {
assertEquals(-1, CodeVersionInformation.version_compare("4", "5"));
assertEquals(1, CodeVersionInformation.version_compare("5", "4"));
assertEquals(-1, CodeVersionInformation.version_compare("4", "4.5"));
assertEquals(1, CodeVersionInformation.version_compare("4.5", "4"));
assertEquals(1, CodeVersionInformation.version_compare("4.10", "4.5"));
assertEquals(-1, CodeVersionInformation.version_compare("4.5", "4.10"));
}
function testGetHighestVersion() {
assertEquals("5.2.0", CodeVersionInformation.get_highest_version(["5.2.0"]));
assertEquals("5.2.0", CodeVersionInformation.get_highest_version(["5.1.0", "5.2.0"]));
assertEquals("5", CodeVersionInformation.get_highest_version(["4", "4.1", "5"]));
}
function testGetLowestVersion() {
assertEquals("5.2.0", CodeVersionInformation.get_lowest_version(["5.2.0"]));
assertEquals("5.1.0", CodeVersionInformation.get_lowest_version(["5.1.0", "5.2.0"]));
assertEquals("4", CodeVersionInformation.get_lowest_version(["4", "4.1", "5"]));
assertEquals("4.0.6", CodeVersionInformation.get_lowest_version(["4.0.6", "5"]));
}
function testGetVersionLowerThan() {
assertEquals(null, CodeVersionInformation.get_version_lower_than("PHP 5"));
assertEquals(null, CodeVersionInformation.get_version_lower_than("PHP 5 &gt;= 5.2.0"));
assertEquals("4.0.4", CodeVersionInformation.get_version_lower_than("PHP 4 &lt;= 4.0.4"));
}
function testCreate() {
var valid_results = [
new Result(ResultType.Function, "one", "PHP 4, PHP 5"),
new Result(ResultType.Function, "two", "PHP 4 &gt;= 4.0.6, PHP 5"),
];
var v = new CodeVersionInformation(valid_results);
assertEquals("4.0.6", v.minimum_versions.get("PHP"));
Result.change_enabled(valid_results, "two", false);
v = new CodeVersionInformation(valid_results);
assertEquals("4", v.minimum_versions.get("PHP"));
}
function testGetVersionStringSplit() {
assertEquals("{xmlwriter => 2.0.4, PHP => 4}", CodeVersionInformation.split_version_string("PHP 4, xmlwriter 2.0.4").toString());
assertEquals("{xmlwriter => 2.0.4, PHP => 4 &gt;= 4.0.3}", CodeVersionInformation.split_version_string("PHP 4 &gt;= 4.0.3, xmlwriter 2.0.4").toString());
assertEquals("{PHP => 4, 5}", CodeVersionInformation.split_version_string("PHP 5, PHP 4").toString());
}
function testGetModuleInformation() {
var valid_results = [
new Result(ResultType.Function, "one", "PHP 4, zmod 5"),
new Result(ResultType.Function, "two", "PHP 4 &gt;= 4.0.6, xmod 5"),
];
var v = new CodeVersionInformation(valid_results);
assertEquals("[PHP, xmod, zmod]", v.all_modules.toString());
}
}

8
src/TestConstantToken.hx Normal file
View File

@ -0,0 +1,8 @@
class TestConstantToken extends haxe.unit.TestCase {
function testCreateConstantToken() {
var t = new ConstantToken("meow", "hiss");
assertEquals("meow", t.token);
assertEquals("hiss", t.version);
assertEquals(ResultType.Constant, t.token_type);
}
}

8
src/TestFunctionToken.hx Normal file
View File

@ -0,0 +1,8 @@
class TestFunctionToken extends haxe.unit.TestCase {
function testCreateFunctionToken() {
var t = new FunctionToken("meow", "hiss");
assertEquals("meow", t.token);
assertEquals("hiss", t.version);
assertEquals(ResultType.Function, t.token_type);
}
}

View File

@ -0,0 +1,17 @@
class TestFunctionTokenProcessor extends haxe.unit.TestCase {
static var functionName : String = "test";
static var functionFrom : String = "5.2";
var testXml : String;
var tokenProcessor : FunctionTokenProcessor;
public override function setup() {
testXml = "<versions><function name='" + functionName + "' from='" + functionFrom + "'/></versions>";
tokenProcessor = new FunctionTokenProcessor();
tokenProcessor.populate_from_string(testXml);
}
public function testGenerateSampleToken() {
var testTokenArray = [ new FunctionToken(functionName, functionFrom) ];
assertTrue(tokenProcessor.tokenHash.exists(functionName));
}
}

View File

@ -0,0 +1,18 @@
class TestJavaScriptTarget extends haxe.unit.TestCase {
function testGetResults() {
JavaScriptTarget.main();
JavaScriptTarget.get_results("this is my array_shift()");
assertEquals(1, JavaScriptTarget.current_results.length);
assertTrue(JavaScriptTarget.current_results[0].is_enabled);
JavaScriptTarget.change_result(0, false);
assertFalse(JavaScriptTarget.current_results[0].is_enabled);
JavaScriptTarget.get_results("this is my array_shift() zip_close()");
assertEquals(2, JavaScriptTarget.current_results.length);
assertEquals("{}", JavaScriptTarget.show_only_modules.toString());
JavaScriptTarget.toggle_module("zip");
assertEquals("{zip => true}", JavaScriptTarget.show_only_modules.toString());
}
}

22
src/TestResult.hx Normal file
View File

@ -0,0 +1,22 @@
class TestResult extends haxe.unit.TestCase {
function testInstantiateResult() {
var result = new Result(ResultType.Function, "test", "5.2");
assertEquals(ResultType.Function, result.type);
assertEquals("test", result.token);
assertEquals("5.2", result.version);
assertEquals(true, result.is_enabled);
}
function testResultArraySort() {
var result_array = [
new Result(ResultType.Function, "dog", "4"),
new Result(ResultType.Function, "cat", "4"),
];
assertEquals("dog", result_array[0].token);
result_array.sort(Result.compare);
assertEquals("cat", result_array[0].token);
}
}

21
src/TestToken.hx Normal file
View File

@ -0,0 +1,21 @@
class TestToken extends haxe.unit.TestCase {
static var tokenName : String = "test";
static var tokenVersion : String = "5.2";
var t : Token;
public override function setup() {
t = new Token(tokenName, tokenVersion);
}
public function testInstantiateToken() {
assertEquals(tokenName, t.token);
assertEquals(tokenVersion, t.version);
}
public function testToResult() {
var result = t.toResult();
assertEquals(ResultType.Generic, result.type);
assertEquals(tokenName, result.token);
assertEquals(tokenVersion, result.version);
}
}

18
src/Token.hx Normal file
View File

@ -0,0 +1,18 @@
class Token {
public var token(getToken, null) : String;
public var version(getVersion, null) : String;
public var token_type(getTokenType, null) : ResultType;
public function new(t : String, ?m : String) {
this.token = t;
this.version = m;
}
public function getToken() { return this.token; }
public function getVersion() { return this.version; }
public function getTokenType() { return ResultType.Generic; }
public function toResult() {
return new Result(this.token_type, this.token, this.version);
}
}

28
src/TokenProcessor.hx Normal file
View File

@ -0,0 +1,28 @@
class TokenProcessor {
public var tokenHash : Hash<Token>;
public static var cachePath : String = null;
public function new() { this.tokenHash = new Hash<Token>(); }
public function get_cache_path() { return TokenProcessor.cachePath; }
#if neko
public function load_from_cache() : Bool {
if (neko.FileSystem.exists(this.get_cache_path())) {
this.tokenHash = haxe.Unserializer.run(neko.io.File.getContent(this.get_cache_path()));
return true;
} else {
return false;
}
}
public function save_to_cache() {
var fh = neko.io.File.write(this.get_cache_path(), true);
fh.writeString(haxe.Serializer.run(this.tokenHash));
fh.close();
}
#end
public function load_from_resource() {
this.tokenHash = haxe.Unserializer.run(haxe.Resource.getString(this.get_cache_path()));
}
}

7
src/test_code.php Normal file
View File

@ -0,0 +1,7 @@
<?php
//harmonious cpdf_arc
this is my array_shift() json_decode() cpdf_arc() test code
//harmonious_end
?>