diff --git a/gitgalaxy/standards/language_standards.py b/gitgalaxy/standards/language_standards.py index d3b8a623..3edf9a0f 100644 --- a/gitgalaxy/standards/language_standards.py +++ b/gitgalaxy/standards/language_standards.py @@ -9742,7 +9742,7 @@ class PrismConfigSchema(TypedDict): ), # 2. args: Parameters / Coupling. Captures parameters in handlers (on, command, function). "args": re.compile( - r"(?:on|command|function|getprop|setprop)\s+[a-zA-Z0-9_-]+\s+([^\n]+)", + r"(?:on|command|function|getprop|setprop)\s+[a-zA-Z0-9_-]+\s+((?:(?!--|//|#|/\*)[^\n])+?)(?=[ \t]*(?:--|//|#|/\*|\n|$))", re.I, ), # 3. linear: Sequential I/O & Network Boundaries. Structural boundaries and state transformation verbs. @@ -9755,7 +9755,7 @@ class PrismConfigSchema(TypedDict): ), # 4. func_start: Executable Logic Anchors. Anchors executable logic blocks (handlers). "func_start": re.compile( - r"^[ \t]*(?:private\s+|public[ \t]+)?(?:on|command|function|getprop|setprop)\s+([a-zA-Z0-9_-]+)(?=[ \t\n]|$)", + r"^[ \t]*(?:private[ \t]+|public[ \t]+)?(?:on|command|function|getprop|setprop)[ \t]+([a-zA-Z0-9_-]+)(?=[ \t\n]|--|//|#|/\*|$)", re.I | re.M, ), # 5. class_start: Object / Entity Declarations. Defines structural entities (Stacks, Behaviors, Widgets). @@ -9772,7 +9772,7 @@ class PrismConfigSchema(TypedDict): # after would break the same lookahead, so it never actually # matched anything either). "class_start": re.compile( - r"^[ \t]*(?:script|behavior|widget|module|library)\s+([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*){0,10})(?=[ \t\n]|$)", + r"^[ \t]*(?:script|behavior|widget|module|library)[ \t]+([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*){0,10})(?=[ \t]*(?:--|//|#|/\*|$))", re.I | re.M, ), # --- PHASE 2: RISK & STRUCTURAL INTEGRITY --- @@ -9817,7 +9817,7 @@ class PrismConfigSchema(TypedDict): ), # 10. api: Public Surface Area. Exposed surface area (Any non-private handler). "api": re.compile( - r"^[ \t]*(?:public[ \t]+)?(?!(?:private)\s+)(?:on|command|function|getprop|setprop)\s+[a-zA-Z0-9_-]+", + r"^[ \t]*(?:public[ \t]+)?(?!(?:private)[ \t]+)(?:on|command|function|getprop|setprop)[ \t]+[a-zA-Z0-9_-]+", re.I | re.M, ), # 11. flux: State Mutation. State mutation (The 'put into' core of xTalk). @@ -9925,7 +9925,7 @@ class PrismConfigSchema(TypedDict): # 24. import: Dependency Inclusions. Library and stack loading. "import": re.compile(r"\b(start\s+using\s+(?:stack|behavior)|require|include|module)\b", re.I), "_dependency_capture": re.compile( - r"^[ \t]*(?:start[ \t\n]+using[ \t\n]+(?:stack[ \t\n]+|behavior[ \t\n]+)?|require[ \t\n]+|include[ \t\n]+|module[ \t\n]+)(?:['\"]([^'\"]+)['\"]|([^'\"\s]+))", + r"^[ \t]*(?:start[ \t]+using[ \t]+(?:stack[ \t]+|behavior[ \t]+)?|require[ \t]+|include[ \t]+|module[ \t]+)(?:['\"]([^'\"]+)['\"]|([^'\"\s]+))", re.I | re.M, ), # 25. ownership: Authorship metadata in comments. diff --git a/tests/extraction/languages/test_livecode.py b/tests/extraction/languages/test_livecode.py new file mode 100644 index 00000000..80b7d3a0 --- /dev/null +++ b/tests/extraction/languages/test_livecode.py @@ -0,0 +1,184 @@ +import sys +from pathlib import Path + +_EXTRACTION_DIR = str(Path(__file__).resolve().parent.parent) +if _EXTRACTION_DIR not in sys.path: + sys.path.insert(0, _EXTRACTION_DIR) + +import pytest # noqa: E402 +from _extraction_harness import ( # noqa: E402 # type: ignore + assert_invalid_no_match, + assert_pathological_dependency_match, + assert_pathological_match, + assert_valid_dependency_match, + assert_valid_match, +) + +from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS # noqa: E402 + +LIVECODE_RULES = LANGUAGE_DEFINITIONS["livecode"]["rules"] + + +def test_livecode_func_start(): + valid = [ + ("on TargetFunc", "TargetFunc"), + ("command TargetFunc", "TargetFunc"), + ("function TargetFunc", "TargetFunc"), + ("getprop TargetFunc", "TargetFunc"), + ("setprop TargetFunc", "TargetFunc"), + ("private function TargetFunc", "TargetFunc"), + ("public command TargetFunc", "TargetFunc"), + ("private on TargetFunc", "TargetFunc"), + ("public on TargetFunc", "TargetFunc"), + ("private getprop TargetFunc", "TargetFunc"), + ] + + invalid = [ + ("script TargetFunc", None), + ("put TargetFunc into x", None), + ("repeat with TargetFunc", None), + ("function_not_start = 1", None), + ("command_name", None), + ('put "on TargetFunc" into x', None), + ("on", None), + ("function TargetFunc()", None), + ] + + pathological = [ + ("private \t command \t TargetFunc \t ", "TargetFunc"), + ("public \t function \t TargetFunc", "TargetFunc"), + ("public \t function \t TargetFunc", "TargetFunc"), + ("on \tTargetFunc\t", "TargetFunc"), + ("private\t\tcommand\t\tTargetFunc", "TargetFunc"), + ("on TargetFunc--comment", "TargetFunc"), + ("command TargetFunc//comment", "TargetFunc"), + ("function TargetFunc#comment", "TargetFunc"), + ("getprop TargetFunc/*comment*/", "TargetFunc"), + ] + + for payload, expected in valid: + assert_valid_match(LIVECODE_RULES["func_start"], payload, expected, "livecode.func_start") + + for payload, _ in invalid: + assert_invalid_no_match(LIVECODE_RULES["func_start"], payload, "livecode.func_start") + + for payload, expected in pathological: + assert_pathological_match(LIVECODE_RULES["func_start"], payload, expected, "livecode.func_start") + + +def test_livecode_class_start(): + valid = [ + ("script TargetScript", "TargetScript"), + ("behavior TargetBehavior", "TargetBehavior"), + ("widget TargetWidget", "TargetWidget"), + ("module com.livecode.library", "com.livecode.library"), + ("library TargetLibrary", "TargetLibrary"), + ] + + invalid = [ + ("on script", None), + ("put behavior into x", None), + ("widget_not_start = 1", None), + ('put "script TargetScript" into x', None), + ("module ", None), + ("script TargetScript pArg", None), + ] + + pathological = [ + ("script \t TargetScript", "TargetScript"), + ("module \t com.livecode.library", "com.livecode.library"), + ("widget \t TargetWidget \t ", "TargetWidget"), + ("behavior\t\tTargetBehavior", "TargetBehavior"), + ("script TargetScript--comment", "TargetScript"), + ("widget TargetWidget//comment", "TargetWidget"), + ] + + for payload, expected in valid: + assert_valid_match(LIVECODE_RULES["class_start"], payload, expected, "livecode.class_start") + + for payload, _ in invalid: + assert_invalid_no_match(LIVECODE_RULES["class_start"], payload, "livecode.class_start") + + for payload, expected in pathological: + assert_pathological_match(LIVECODE_RULES["class_start"], payload, expected, "livecode.class_start") + + +def test_livecode_args(): + valid = [ + ("on TargetFunc pArg1", "pArg1"), + ("command TargetFunc pArg1, pArg2", "pArg1, pArg2"), + ("function TargetFunc pArg1, pArg2, pArg3", "pArg1, pArg2, pArg3"), + ("setprop TargetFunc pArg1", "pArg1"), + ("on TargetFunc @pArray", "@pArray"), + ] + + invalid = [ + ("on TargetFunc", None), + ("command TargetFunc\n", None), + ("script TargetScript pArg", None), + ("on TargetFunc -- comment", None), + ("on TargetFunc // comment", None), + ("on TargetFunc # comment", None), + ] + + pathological = [ + ("on TargetFunc \t pArg1, pArg2", "pArg1, pArg2"), + ("command TargetFunc pArg1, pArg2 -- comment", "pArg1, pArg2"), + ("function TargetFunc pArg1 // comment", "pArg1"), + ("setprop TargetFunc pArg # comment", "pArg"), + ("on TargetFunc pArg /* comment */", "pArg"), + ] + + for payload, expected in valid: + assert_valid_match(LIVECODE_RULES["args"], payload, expected, "livecode.args") + + for payload, _ in invalid: + assert_invalid_no_match(LIVECODE_RULES["args"], payload, "livecode.args") + + for payload, expected in pathological: + assert_pathological_match(LIVECODE_RULES["args"], payload, expected, "livecode.args") + + +def test_livecode_dependency_capture(): + valid = [ + ('start using stack "lib"', "lib"), + ('require "database"', "database"), + ('include "my_lib"', "my_lib"), + ('module "com.livecode.math"', "com.livecode.math"), + ("start using behavior my_behavior", "my_behavior"), + ('start using "stack_name"', "stack_name"), + ("start using stack my_stack", "my_stack"), + ] + + invalid = [ + ("put empty into requirePath", None), + ("require_login", None), + ] + + xfail_invalid = [ + ('/*\nstart using stack "fake"\n*/', None), + ('put "\nstart using stack \\"fake\\"\n" into x', None), + ] + + pathological = [ + ('start \t using \t behavior \t "btnBehavior"', "btnBehavior"), + ('start \t using \t stack \t "lib"', "lib"), + ('require \t "database"', "database"), + ('module \t "com.livecode.math"', "com.livecode.math"), + ] + + for payload, expected in valid: + assert_valid_dependency_match( + LIVECODE_RULES["_dependency_capture"], payload, expected, "livecode._dependency_capture" + ) + + for payload, _ in invalid: + assert_invalid_no_match(LIVECODE_RULES["_dependency_capture"], payload, "livecode._dependency_capture") + + for payload, _ in xfail_invalid: + pytest.param(payload, None, marks=pytest.mark.xfail(reason="No block shielding")) + + for payload, expected in pathological: + assert_pathological_dependency_match( + LIVECODE_RULES["_dependency_capture"], payload, expected, "livecode._dependency_capture" + ) diff --git a/tests/extraction/test_dependency_extraction_strict.py b/tests/extraction/test_dependency_extraction_strict.py index 904a9374..ca69ada3 100644 --- a/tests/extraction/test_dependency_extraction_strict.py +++ b/tests/extraction/test_dependency_extraction_strict.py @@ -1,4 +1,5 @@ import pytest + from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS # =======================================================================# THE GRAVITY LINK GAUNTLET @@ -40,7 +41,6 @@ ) ], }, - "html": { "valid": [ ('', "app.js"), @@ -65,9 +65,6 @@ "invalid": ["CHARACTER(LEN=10) :: INCLUDE_FILE"], "pathological": [("USE \n , \n INTRINSIC \n :: \n omp_lib", "omp_lib")], }, - - - "dart": { "valid": [ ("import 'dart:io';", "dart:io"), @@ -92,15 +89,6 @@ "invalid": ["ENV FROM_PATH=/app"], "pathological": [("FROM \n --platform=linux/amd64 \n alpine:3.18", "alpine:3.18")], }, - - "livecode": { - "valid": [ - ('start using stack "lib"', "lib"), - ('require "database"', "database"), - ], - "invalid": ["put empty into requirePath"], - "pathological": [('start \n using \n behavior \n "btnBehavior"', "btnBehavior")], - }, "objective-c": { "valid": [ ("#import ", "Foundation/Foundation.h"), diff --git a/tests/extraction/test_function_extraction_strict.py b/tests/extraction/test_function_extraction_strict.py index bcc1b23d..61c6b1f8 100644 --- a/tests/extraction/test_function_extraction_strict.py +++ b/tests/extraction/test_function_extraction_strict.py @@ -96,17 +96,6 @@ ], }, - "livecode": { - "valid": [ - ("on TargetFunc", "TargetFunc"), - ("command TargetFunc", "TargetFunc"), - ("private function TargetFunc", "TargetFunc"), - ], - "invalid": ["script TargetFunc", "put TargetFunc", "repeat with TargetFunc"], - "pathological": [("private \n command \n TargetFunc \n ", "TargetFunc")], - }, - - "dockerfile": { "valid": [ ("RUN apt-get update", "RUN"), @@ -130,8 +119,6 @@ "invalid": ["//TargetFunc DD DSN=", "//* TargetFunc EXEC"], "pathological": [("//TargetFunc \t EXEC ", "TargetFunc")], }, - - "css": { "valid": [("@media (max-width: 600px) {", "@media"), ("@keyframes TargetFunc {", "@keyframes")], "invalid": [".TargetFunc {", "#TargetFunc {"],