commit 59a795ee9da3c1866b9170d6b53d259be71e101c Author: John Bintz Date: Wed Oct 8 19:54:28 2008 -0400 initial commit, all seems to work diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..260f14e --- /dev/null +++ b/LICENSE @@ -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 . diff --git a/README b/README new file mode 100644 index 0000000..104c137 --- /dev/null +++ b/README @@ -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. diff --git a/build/command_line.hxml b/build/command_line.hxml new file mode 100644 index 0000000..43fd736 --- /dev/null +++ b/build/command_line.hxml @@ -0,0 +1,3 @@ +-main CommandLineInterface +-neko ../neko/codeparser.n +-cp ../src \ No newline at end of file diff --git a/build/command_line.sh b/build/command_line.sh new file mode 100755 index 0000000..25d0488 --- /dev/null +++ b/build/command_line.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +./setup.sh +haxe command_line.hxml && neko ../neko/codeparser.n $1 $2 $3 $4 $5 diff --git a/build/javascript.hxml b/build/javascript.hxml new file mode 100755 index 0000000..817b075 --- /dev/null +++ b/build/javascript.hxml @@ -0,0 +1,4 @@ +-js ../htdocs/harmoniouscode.js +-main JavaScriptTarget +-resource ../data/functions_tokens_cache.hxd +-cp ../src \ No newline at end of file diff --git a/build/javascript.sh b/build/javascript.sh new file mode 100755 index 0000000..abd6ca2 --- /dev/null +++ b/build/javascript.sh @@ -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 diff --git a/build/setup.sh b/build/setup.sh new file mode 100755 index 0000000..eb76776 --- /dev/null +++ b/build/setup.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +for dir in neko data; do + if [ ! -e "../${dir}" ]; then mkdir "../${dir}" 2>&1; fi +done diff --git a/build/test.sh b/build/test.sh new file mode 100755 index 0000000..0f4f308 --- /dev/null +++ b/build/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +./setup.sh +haxe tests.hxml && neko ../neko/my_tests.n + diff --git a/build/tests.hxml b/build/tests.hxml new file mode 100644 index 0000000..b3a69c4 --- /dev/null +++ b/build/tests.hxml @@ -0,0 +1,4 @@ +-main MyTests +-neko ../neko/my_tests.n +-resource ../data/functions_tokens_cache.hxd +-cp ../src \ No newline at end of file diff --git a/htdocs/index.php b/htdocs/index.php new file mode 100644 index 0000000..3424dde --- /dev/null +++ b/htdocs/index.php @@ -0,0 +1,43 @@ + + + Harmonious Code: Will my PHP code and Web hosting work together? + + + + +

Harmonious Code

+
+ Loading... +
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/htdocs/style.css b/htdocs/style.css new file mode 100644 index 0000000..69e5010 --- /dev/null +++ b/htdocs/style.css @@ -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 +} \ No newline at end of file diff --git a/src/CodeParser.hx b/src/CodeParser.hx new file mode 100644 index 0000000..43a7f0a --- /dev/null +++ b/src/CodeParser.hx @@ -0,0 +1,141 @@ +class CodeParser { + public var tokenProcessors(getTokenProcessors, null) : Hash; + + public function new() { + this.tokenProcessors = new Hash(); + } + + #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 { + var flattened_tokens = new Hash(); + 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 { + var results = new Array(); + + var function_token_processor = this.tokenProcessors.get("FunctionTokenProcessor"); + + var function_tokens_found = new Hash(); + var tokens_to_ignore = new Array>(); + var flattened_tokens = new Hash(); + + 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(); + 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; + } +} \ No newline at end of file diff --git a/src/CodeToken.hx b/src/CodeToken.hx new file mode 100644 index 0000000..31647d1 --- /dev/null +++ b/src/CodeToken.hx @@ -0,0 +1,3 @@ +class CodeToken { + +} \ No newline at end of file diff --git a/src/CodeVersionInformation.hx b/src/CodeVersionInformation.hx new file mode 100644 index 0000000..364ed3a --- /dev/null +++ b/src/CodeVersionInformation.hx @@ -0,0 +1,204 @@ +class CodeVersionInformation { + public var final_versions : Hash>; + public var minimum_versions : Hash; + public var maximum_versions : Hash; + public var all_modules : Array; + + public static function breakdown_version_number(version : String) { + return version.split("."); + } + + public static function get_version_lower_than(s : String) : String { + var greater_than = ~/\<= (.*)$/; + + 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(); + + for (regexp in [ ~/^([^\ ]*) (.*)$/ ]) { + if (regexp.match(s)) { + var version = regexp.matched(2).split("-").shift(); + + var greater_than = ~/\>= (.*)$/; + 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 { + return get_terminal_version(versions, true); + } + + public static function get_lowest_version(versions : Array) : 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, ?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>, + source_hash : Hash>, + is_lower : Bool) : Hash> { + for (source in source_hash.keys()) { + if (!target_hash.exists(source)) { target_hash.set(source, new Array()); } + 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 { + var version_lists = new Hash>(); + 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()); + } + var tmp = version_lists.get(source); + tmp.push(version_match.matched(2)); + version_lists.set(source, tmp); + } + } + + var final_versions = new Hash(); + 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) { + var start_minimum_versions = new Hash>(); + var start_maximum_versions = new Hash>(); + + for (result in results) { + if (result.is_enabled) { + var internal_minimum_version = new Hash>(); + var internal_maximum_version = new Hash>(); + + 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()); + } + 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()); + } + + 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(); + this.maximum_versions = new Hash(); + this.all_modules = new Array(); + + 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>(); + + 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; + } +} \ No newline at end of file diff --git a/src/CommandLineInterface.hx b/src/CommandLineInterface.hx new file mode 100644 index 0000000..8cae61f --- /dev/null +++ b/src/CommandLineInterface.hx @@ -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"); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ConstantToken.hx b/src/ConstantToken.hx new file mode 100644 index 0000000..1d65bd1 --- /dev/null +++ b/src/ConstantToken.hx @@ -0,0 +1,5 @@ +class ConstantToken extends Token { + override public function getTokenType() { + return ResultType.Constant; + } +} \ No newline at end of file diff --git a/src/ConstantTokenProcessor.hx b/src/ConstantTokenProcessor.hx new file mode 100644 index 0000000..57d2453 --- /dev/null +++ b/src/ConstantTokenProcessor.hx @@ -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(); + 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 +} \ No newline at end of file diff --git a/src/FunctionToken.hx b/src/FunctionToken.hx new file mode 100644 index 0000000..54a73b7 --- /dev/null +++ b/src/FunctionToken.hx @@ -0,0 +1,5 @@ +class FunctionToken extends Token { + override public function getTokenType() { + return ResultType.Function; + } +} \ No newline at end of file diff --git a/src/FunctionTokenProcessor.hx b/src/FunctionTokenProcessor.hx new file mode 100644 index 0000000..37c288f --- /dev/null +++ b/src/FunctionTokenProcessor.hx @@ -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(); + 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 +} \ No newline at end of file diff --git a/src/JavaScriptCallback.hx b/src/JavaScriptCallback.hx new file mode 100644 index 0000000..b092c82 --- /dev/null +++ b/src/JavaScriptCallback.hx @@ -0,0 +1,5 @@ +class JavaScriptCallback { + public function parse(t : Token) { + js.Lib.document.getElementById("Current token: " + t.token); + } +} \ No newline at end of file diff --git a/src/JavaScriptTarget.hx b/src/JavaScriptTarget.hx new file mode 100644 index 0000000..18a3161 --- /dev/null +++ b/src/JavaScriptTarget.hx @@ -0,0 +1,178 @@ +class JavaScriptTarget { + static public var code_parser : CodeParser; + static public var current_results : Array; + static public var show_only_modules : Hash; + + static public function main() { + var function_token = new FunctionToken("a","a"); + + code_parser = new CodeParser(); + code_parser.loadProcessorsFromResources(); + + show_only_modules = new Hash(); + + #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:
    "; + + var minimum = version_info.final_versions.get("minimum"); + + for (module in minimum.keys()) { + output += "
  • " + module + ": " + minimum.get(module) + "
  • "; + } + + output += "
"; + + 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:
    "; + printed_message = true; + } + output += ("
  • " + module + ": " + maximum.get(module) + "
  • "); + } + + if (printed_message) { output += "
"; } + + if (!version_info.is_valid()) { + output += "

This code may not run!

"; + } + + output += "
"; + + output += ""; + + output += ""; + + 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 += ""; + } + + output += ""; + + var ignored_tokens = new Array(); + + 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 += ""; + + output += ""; + + output += ""; + + for (module in version_info.all_modules) { + output += ""; + } + + output += ""; + id_index++; + } + } + output += "
TokenIgnore?" + module + "
" + result.token + ""; + if (max_versions.exists(module)) { + output += max_versions.get(module); + } else { + output += " "; + } + output += "
"; + + output += "
"; + + 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(); + + 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 +} \ No newline at end of file diff --git a/src/MyTests.hx b/src/MyTests.hx new file mode 100644 index 0000000..cf229f7 --- /dev/null +++ b/src/MyTests.hx @@ -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(); + } +} \ No newline at end of file diff --git a/src/RegenerateDataFiles.hx b/src/RegenerateDataFiles.hx new file mode 100644 index 0000000..92262c3 --- /dev/null +++ b/src/RegenerateDataFiles.hx @@ -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"); + } + } +} \ No newline at end of file diff --git a/src/Result.hx b/src/Result.hx new file mode 100644 index 0000000..36110b0 --- /dev/null +++ b/src/Result.hx @@ -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, 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; + } +} \ No newline at end of file diff --git a/src/ResultType.hx b/src/ResultType.hx new file mode 100644 index 0000000..cf213a4 --- /dev/null +++ b/src/ResultType.hx @@ -0,0 +1,5 @@ +enum ResultType { + Generic; + Function; + Constant; +} diff --git a/src/TestCodeParser.hx b/src/TestCodeParser.hx new file mode 100644 index 0000000..9823100 --- /dev/null +++ b/src/TestCodeParser.hx @@ -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 +} \ No newline at end of file diff --git a/src/TestCodeVersionInformation.hx b/src/TestCodeVersionInformation.hx new file mode 100644 index 0000000..f04dd38 --- /dev/null +++ b/src/TestCodeVersionInformation.hx @@ -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 <= 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 >= 5.2.0"), + new Result(ResultType.Function, "two", "PHP 4 <= 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 >= 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 >= 5.2.0")); + assertEquals("4.0.4", CodeVersionInformation.get_version_lower_than("PHP 4 <= 4.0.4")); + } + + function testCreate() { + var valid_results = [ + new Result(ResultType.Function, "one", "PHP 4, PHP 5"), + new Result(ResultType.Function, "two", "PHP 4 >= 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 >= 4.0.3}", CodeVersionInformation.split_version_string("PHP 4 >= 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 >= 4.0.6, xmod 5"), + ]; + var v = new CodeVersionInformation(valid_results); + + assertEquals("[PHP, xmod, zmod]", v.all_modules.toString()); + } +} \ No newline at end of file diff --git a/src/TestConstantToken.hx b/src/TestConstantToken.hx new file mode 100644 index 0000000..5d2020a --- /dev/null +++ b/src/TestConstantToken.hx @@ -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); + } +} \ No newline at end of file diff --git a/src/TestFunctionToken.hx b/src/TestFunctionToken.hx new file mode 100644 index 0000000..564cb40 --- /dev/null +++ b/src/TestFunctionToken.hx @@ -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); + } +} \ No newline at end of file diff --git a/src/TestFunctionTokenProcessor.hx b/src/TestFunctionTokenProcessor.hx new file mode 100644 index 0000000..ffd08ff --- /dev/null +++ b/src/TestFunctionTokenProcessor.hx @@ -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 = ""; + tokenProcessor = new FunctionTokenProcessor(); + tokenProcessor.populate_from_string(testXml); + } + + public function testGenerateSampleToken() { + var testTokenArray = [ new FunctionToken(functionName, functionFrom) ]; + assertTrue(tokenProcessor.tokenHash.exists(functionName)); + } +} \ No newline at end of file diff --git a/src/TestJavaScriptTarget.hx b/src/TestJavaScriptTarget.hx new file mode 100644 index 0000000..59aeb1d --- /dev/null +++ b/src/TestJavaScriptTarget.hx @@ -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()); + } +} \ No newline at end of file diff --git a/src/TestResult.hx b/src/TestResult.hx new file mode 100644 index 0000000..46414a9 --- /dev/null +++ b/src/TestResult.hx @@ -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); + } +} \ No newline at end of file diff --git a/src/TestToken.hx b/src/TestToken.hx new file mode 100644 index 0000000..9a26ef5 --- /dev/null +++ b/src/TestToken.hx @@ -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); + } +} \ No newline at end of file diff --git a/src/Token.hx b/src/Token.hx new file mode 100644 index 0000000..9f8c74e --- /dev/null +++ b/src/Token.hx @@ -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); + } +} \ No newline at end of file diff --git a/src/TokenProcessor.hx b/src/TokenProcessor.hx new file mode 100644 index 0000000..0a61894 --- /dev/null +++ b/src/TokenProcessor.hx @@ -0,0 +1,28 @@ +class TokenProcessor { + public var tokenHash : Hash; + public static var cachePath : String = null; + + public function new() { this.tokenHash = new Hash(); } + 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())); + } +} \ No newline at end of file diff --git a/src/test_code.php b/src/test_code.php new file mode 100644 index 0000000..7392648 --- /dev/null +++ b/src/test_code.php @@ -0,0 +1,7 @@ + \ No newline at end of file