diff --git a/src/godotiq/parsers/gd_parser.py b/src/godotiq/parsers/gd_parser.py
index 76a6a0c..b67e0a5 100644
--- a/src/godotiq/parsers/gd_parser.py
+++ b/src/godotiq/parsers/gd_parser.py
@@ -358,7 +358,461 @@ def _parse_enum_body(body: str) -> dict[str, int | None]:
 
 
 # ---------------------------------------------------------------------------
-# Public API (stub — replaced in section 02)
+# State constants
+# ---------------------------------------------------------------------------
+
+_STATE_TOP_LEVEL = "TOP_LEVEL"
+_STATE_IN_FUNCTION = "IN_FUNCTION"
+_STATE_IN_ENUM = "IN_ENUM"
+_STATE_IN_MULTILINE_VALUE = "IN_MULTILINE_VALUE"
+_STATE_IN_INNER_CLASS = "IN_INNER_CLASS"
+
+# Annotations that apply to variables (bufferable)
+_VAR_ANNOTATIONS = frozenset({"export", "export_range", "export_enum",
+                               "export_file", "export_dir", "export_multiline",
+                               "export_placeholder", "export_color_no_alpha",
+                               "export_node_path", "export_flags",
+                               "export_flags_2d_physics", "export_flags_2d_render",
+                               "export_flags_2d_navigation",
+                               "export_flags_3d_physics", "export_flags_3d_render",
+                               "export_flags_3d_navigation",
+                               "onready"})
+
+# Grouping annotations to discard (not buffered)
+_GROUP_ANNOTATIONS = frozenset({"export_group", "export_subgroup",
+                                 "export_category"})
+
+
+def _get_indent(line: str) -> int:
+    """Return count of leading whitespace characters."""
+    return len(line) - len(line.lstrip())
+
+
+def _bracket_depth(text: str) -> int:
+    """Count net bracket depth in *text* (``[{`` +1, ``]}`` -1)."""
+    depth = 0
+    for ch in text:
+        if ch in "[{":
+            depth += 1
+        elif ch in "]}":
+            depth -= 1
+    return depth
+
+
+_PROPERTY_RE = re.compile(r"^(var|const)\s+(\w+)\s*:\s*(\w[\w\[\]]*)\s*:\s*$")
+
+
+def _parse_var_line(var_line: str, lineno: int, *,
+                    is_static: bool = False,
+                    annotation: str = "") -> tuple[GdVariable | None, str | None]:
+    """Parse a var/const line and return (variable, state_to_enter).
+
+    Returns ``(None, None)`` if the line doesn't match.
+    *state_to_enter* is ``"IN_MULTILINE_VALUE"`` or ``"IN_FUNCTION"``
+    (for property getter/setter) or ``None`` for a normal single-line var.
+    """
+    # Property getter/setter: "var health: int:" — trailing colon, no default
+    pm = _PROPERTY_RE.match(var_line)
+    if pm:
+        var = GdVariable(
+            name=pm.group(2),
+            type_hint=pm.group(3),
+            is_const=(pm.group(1) == "const"),
+            is_static=is_static,
+            annotation=annotation,
+            scope="class",
+            line=lineno,
+        )
+        return var, _STATE_IN_FUNCTION
+
+    m = _VAR_RE.match(var_line)
+    if not m:
+        return None, None
+
+    keyword, name, type_hint, default = m.groups()
+    type_hint = (type_hint or "").strip()
+    default = (default or "").strip()
+
+    var = GdVariable(
+        name=name,
+        type_hint=type_hint,
+        default_value=default,
+        is_const=(keyword == "const"),
+        is_static=is_static,
+        annotation=annotation,
+        scope="class",
+        line=lineno,
+    )
+
+    # Multiline value: unclosed brackets
+    if default and _bracket_depth(default) > 0:
+        return var, _STATE_IN_MULTILINE_VALUE
+
+    return var, None
+
+
+# ---------------------------------------------------------------------------
+# Core parser
+# ---------------------------------------------------------------------------
+
+
+def _parse_gd_content(
+    content: str, known_autoloads: list[str] | None = None
+) -> GdScript:
+    """Parse GDScript content string into a GdScript dataclass.
+
+    This is the internal parsing function.  Use ``parse_gd()`` for
+    file-based parsing.
+
+    Args:
+        content: Raw GDScript source text.
+        known_autoloads: Optional list of known autoload names for detection.
+
+    Returns:
+        Populated ``GdScript`` dataclass.  Never raises on malformed content.
+    """
+    result = GdScript()
+    state = _STATE_TOP_LEVEL
+
+    # Function tracking
+    current_func: GdFunction | None = None
+    func_indent = 0
+    last_nonblank_line = 0
+
+    # Enum accumulation
+    enum_buffer: list[str] = []
+    enum_name = ""
+    enum_line = 0
+    enum_depth = 0
+
+    # Multiline value accumulation
+    multiline_var: GdVariable | None = None
+    multiline_depth = 0
+
+    # Inner class tracking
+    inner_class_indent = 0
+
+    # Annotation buffer
+    annotation_buffer = ""
+
+    lines = content.split("\n") if content else []
+
+    i = 0
+    while i < len(lines):
+        lineno = i + 1  # 1-indexed
+        raw_line = lines[i]
+        i += 1
+
+        # -- All-state scanning: preload/load and is_instance_valid --
+        for plm in _PRELOAD_LOAD_RE.finditer(raw_line):
+            path = plm.group(1)
+            is_pre = bool(_PRELOAD_RE.search(raw_line))
+            result.preload_refs.append(
+                GdPreloadRef(path=path, is_preload=is_pre, line=lineno)
+            )
+        if _IS_INSTANCE_VALID_RE.search(raw_line):
+            result.is_instance_valid_lines.append(lineno)
+
+        stripped = raw_line.strip()
+
+        # ---------------------------------------------------------------
+        # IN_ENUM state
+        # ---------------------------------------------------------------
+        if state == _STATE_IN_ENUM:
+            enum_buffer.append(stripped)
+            enum_depth += stripped.count("{") - stripped.count("}")
+            if enum_depth <= 0:
+                body = " ".join(enum_buffer)
+                # Remove everything up to first { and after last }
+                start = body.find("{")
+                end = body.rfind("}")
+                if start != -1 and end != -1:
+                    body = body[start + 1:end]
+                result.enums.append(
+                    GdEnum(name=enum_name, values=_parse_enum_body(body),
+                           line=enum_line)
+                )
+                state = _STATE_TOP_LEVEL
+            continue
+
+        # ---------------------------------------------------------------
+        # IN_MULTILINE_VALUE state
+        # ---------------------------------------------------------------
+        if state == _STATE_IN_MULTILINE_VALUE:
+            multiline_depth += _bracket_depth(stripped)
+            if multiline_var is not None:
+                multiline_var.default_value += "\n" + stripped
+            if multiline_depth <= 0:
+                if multiline_var is not None:
+                    result.variables.append(multiline_var)
+                    multiline_var = None
+                state = _STATE_TOP_LEVEL
+            continue
+
+        # ---------------------------------------------------------------
+        # IN_INNER_CLASS state
+        # ---------------------------------------------------------------
+        if state == _STATE_IN_INNER_CLASS:
+            if not stripped:
+                continue
+            if _get_indent(raw_line) <= inner_class_indent:
+                state = _STATE_TOP_LEVEL
+                i -= 1  # re-process this line in TOP_LEVEL
+            continue
+
+        # ---------------------------------------------------------------
+        # IN_FUNCTION state
+        # ---------------------------------------------------------------
+        if state == _STATE_IN_FUNCTION:
+            if not stripped or stripped.startswith("#"):
+                continue
+            indent = _get_indent(raw_line)
+            if indent <= func_indent:
+                # Exited function — set end_line and re-process
+                if current_func is not None:
+                    current_func.end_line = last_nonblank_line
+                    current_func = None
+                state = _STATE_TOP_LEVEL
+                i -= 1  # re-process current line
+                continue
+            # Body line — track last non-blank for end_line
+            last_nonblank_line = lineno
+            # Section 03 will add body pattern scanning here
+            continue
+
+        # ---------------------------------------------------------------
+        # TOP_LEVEL state
+        # ---------------------------------------------------------------
+        if not stripped or stripped.startswith("#"):
+            continue
+
+        # Strip inline comment before matching
+        cleaned = _strip_comment(stripped)
+
+        # 1. Combined class_name + extends
+        m = _COMBINED_CLASS_EXTENDS_RE.match(cleaned)
+        if m:
+            result.class_name = m.group(1)
+            result.extends = m.group(2).strip()
+            continue
+
+        # 2. class_name
+        m = _CLASS_NAME_RE.match(cleaned)
+        if m:
+            result.class_name = m.group(1)
+            continue
+
+        # 3. extends
+        m = _EXTENDS_RE.match(cleaned)
+        if m:
+            result.extends = m.group(1).strip()
+            continue
+
+        # 4. signal
+        m = _SIGNAL_RE.match(cleaned)
+        if m:
+            sig_name = m.group(1)
+            sig_args_str = m.group(2) or ""
+            result.signals.append(
+                GdSignal(
+                    name=sig_name,
+                    args=_parse_signal_args(sig_args_str),
+                    line=lineno,
+                )
+            )
+            continue
+
+        # 5. Enum (single-line first, then multiline start)
+        m = _ENUM_SINGLE_RE.match(cleaned)
+        if m:
+            result.enums.append(
+                GdEnum(
+                    name=m.group(1) or "",
+                    values=_parse_enum_body(m.group(2)),
+                    line=lineno,
+                )
+            )
+            continue
+        m = _ENUM_START_RE.match(cleaned)
+        if m:
+            enum_name = m.group(1) or ""
+            enum_line = lineno
+            enum_buffer = [cleaned]
+            enum_depth = cleaned.count("{") - cleaned.count("}")
+            state = _STATE_IN_ENUM
+            continue
+
+        # 6. Annotation + var on same line
+        m = _ANNOTATION_VAR_RE.match(cleaned)
+        if m:
+            ann = m.group(1)
+            rest = cleaned[m.end():].strip()
+            # re-parse "var ..." portion
+            var_line_text = m.group(2) + " " + rest
+            var, next_state = _parse_var_line(var_line_text, lineno,
+                                              annotation=ann)
+            if var is not None:
+                if next_state == _STATE_IN_MULTILINE_VALUE:
+                    multiline_var = var
+                    multiline_depth = _bracket_depth(var.default_value)
+                    state = _STATE_IN_MULTILINE_VALUE
+                elif next_state == _STATE_IN_FUNCTION:
+                    result.variables.append(var)
+                    func_indent = 0
+                    current_func = None
+                    last_nonblank_line = lineno
+                    state = _STATE_IN_FUNCTION
+                else:
+                    result.variables.append(var)
+            annotation_buffer = ""
+            continue
+
+        # 7. Standalone annotation
+        m = _STANDALONE_ANNOTATION_RE.match(cleaned)
+        if m:
+            ann_name = m.group(1)
+            if ann_name in _GROUP_ANNOTATIONS:
+                # Discard grouping annotations
+                continue
+            if ann_name.startswith("export") or ann_name in _VAR_ANNOTATIONS:
+                # Buffer for next var
+                annotation_buffer = cleaned  # full "@export_range(0, 100)"
+                continue
+            # Non-var annotations (@rpc, @warning_ignore, etc.) — discard
+            continue
+
+        # 8. Inner class
+        m = _INNER_CLASS_RE.match(cleaned)
+        if m:
+            result.inner_classes.append(m.group(1))
+            inner_class_indent = _get_indent(raw_line)
+            state = _STATE_IN_INNER_CLASS
+            annotation_buffer = ""
+            continue
+
+        # 9. Function (static or regular)
+        m = _FUNC_START_RE.match(cleaned)
+        if m:
+            func_name = m.group(1)
+            is_static = cleaned.startswith("static ")
+
+            # Extract full arg string using depth-aware paren matching
+            paren_start = cleaned.index("(")
+            paren_end = _find_matching_paren(cleaned, paren_start)
+            if paren_end == -1:
+                paren_end = len(cleaned) - 1
+
+            args_str = cleaned[paren_start + 1:paren_end]
+            func_args = _parse_func_args(args_str)
+
+            # Extract return type
+            return_type = ""
+            after_paren = cleaned[paren_end + 1:].strip()
+            if after_paren.startswith("->"):
+                rt = after_paren[2:].strip()
+                # Remove trailing colon
+                if rt.endswith(":"):
+                    rt = rt[:-1].strip()
+                return_type = rt
+
+            func = GdFunction(
+                name=func_name,
+                args=func_args,
+                return_type=return_type,
+                is_static=is_static,
+                is_virtual=func_name.startswith("_"),
+                is_private=func_name.startswith("_"),
+                line=lineno,
+                end_line=lineno,
+            )
+            result.functions.append(func)
+
+            # Check for one-liner: content after the colon
+            colon_idx = cleaned.rfind(":")
+            if colon_idx != -1:
+                after_colon = cleaned[colon_idx + 1:].strip()
+                if after_colon:
+                    # One-liner function — end_line == line, stay TOP_LEVEL
+                    func.end_line = lineno
+                    annotation_buffer = ""
+                    continue
+
+            current_func = func
+            func_indent = _get_indent(raw_line)
+            last_nonblank_line = lineno
+            state = _STATE_IN_FUNCTION
+            annotation_buffer = ""
+            continue
+
+        # 10. Static var
+        if cleaned.startswith("static "):
+            rest = cleaned[7:]  # len("static ") == 7
+            if rest.startswith("var ") or rest.startswith("const "):
+                var, next_state = _parse_var_line(
+                    rest, lineno, is_static=True,
+                    annotation=annotation_buffer,
+                )
+                annotation_buffer = ""
+                if var is not None:
+                    if next_state == _STATE_IN_MULTILINE_VALUE:
+                        multiline_var = var
+                        multiline_depth = _bracket_depth(var.default_value)
+                        state = _STATE_IN_MULTILINE_VALUE
+                    elif next_state == _STATE_IN_FUNCTION:
+                        result.variables.append(var)
+                        func_indent = 0
+                        current_func = None
+                        last_nonblank_line = lineno
+                        state = _STATE_IN_FUNCTION
+                    else:
+                        result.variables.append(var)
+                continue
+
+        # 11. var / const
+        if cleaned.startswith("var ") or cleaned.startswith("const "):
+            var, next_state = _parse_var_line(
+                cleaned, lineno,
+                annotation=annotation_buffer,
+            )
+            annotation_buffer = ""
+            if var is not None:
+                if next_state == _STATE_IN_MULTILINE_VALUE:
+                    multiline_var = var
+                    multiline_depth = _bracket_depth(var.default_value)
+                    state = _STATE_IN_MULTILINE_VALUE
+                elif next_state == _STATE_IN_FUNCTION:
+                    result.variables.append(var)
+                    func_indent = 0
+                    current_func = None
+                    last_nonblank_line = lineno
+                    state = _STATE_IN_FUNCTION
+                else:
+                    result.variables.append(var)
+            continue
+
+    # -- End-of-file cleanup --
+    if state == _STATE_IN_FUNCTION and current_func is not None:
+        current_func.end_line = last_nonblank_line
+    if state == _STATE_IN_MULTILINE_VALUE and multiline_var is not None:
+        result.variables.append(multiline_var)
+    if state == _STATE_IN_ENUM:
+        body = " ".join(enum_buffer)
+        start = body.find("{")
+        end = body.rfind("}")
+        if start != -1 and end != -1:
+            body = body[start + 1:end]
+        elif start != -1:
+            body = body[start + 1:]
+        result.enums.append(
+            GdEnum(name=enum_name, values=_parse_enum_body(body),
+                   line=enum_line)
+        )
+
+    return result
+
+
+# ---------------------------------------------------------------------------
+# Public API
 # ---------------------------------------------------------------------------
 
 
@@ -372,8 +826,12 @@ def parse_gd(path: str, known_autoloads: list[str] | None = None) -> GdScript:
 
     Returns:
         Parsed GdScript dataclass with all extracted structural information.
+        Never raises on malformed content — uses best-effort parsing.
 
     Raises:
-        NotImplementedError: Parser not yet implemented (replaced in section 02).
+        FileNotFoundError: If the file does not exist.
     """
-    raise NotImplementedError("gd parser not yet implemented")
+    from pathlib import Path
+
+    content = Path(path).read_text(encoding="utf-8")
+    return _parse_gd_content(content, known_autoloads)
diff --git a/tests/test_parsers/test_gd_parser.py b/tests/test_parsers/test_gd_parser.py
new file mode 100644
index 0000000..e396471
--- /dev/null
+++ b/tests/test_parsers/test_gd_parser.py
@@ -0,0 +1,385 @@
+"""Tests for the GDScript parser.
+
+Section 02: State machine core tests using _parse_gd_content() with inline strings.
+"""
+
+from __future__ import annotations
+
+import pytest
+
+from godotiq.parsers.gd_parser import GdScript, _parse_gd_content, parse_gd
+
+
+# ---------------------------------------------------------------------------
+# TestClassDeclarations
+# ---------------------------------------------------------------------------
+
+
+class TestClassDeclarations:
+    """Test class_name and extends parsing at TOP_LEVEL."""
+
+    def test_parse_class_name(self):
+        result = _parse_gd_content("class_name EconomyManagerClass\n")
+        assert result.class_name == "EconomyManagerClass"
+
+    def test_parse_extends(self):
+        result = _parse_gd_content("extends Node\n")
+        assert result.extends == "Node"
+
+    def test_parse_combined_class_name_extends(self):
+        result = _parse_gd_content("class_name Item extends Node\n")
+        assert result.class_name == "Item"
+        assert result.extends == "Node"
+
+    def test_parse_extends_file_path(self):
+        result = _parse_gd_content('extends "res://path.gd"\n')
+        assert result.extends == '"res://path.gd"'
+
+    def test_minimal_file(self):
+        result = _parse_gd_content("extends Node3D\n")
+        assert result.extends == "Node3D"
+        assert result.class_name == ""
+        assert result.signals == []
+        assert result.functions == []
+        assert result.variables == []
+
+    def test_empty_file(self):
+        result = _parse_gd_content("")
+        assert result.class_name == ""
+        assert result.extends == ""
+
+    def test_missing_file_raises(self):
+        with pytest.raises(FileNotFoundError):
+            parse_gd("/nonexistent/path/script.gd")
+
+
+# ---------------------------------------------------------------------------
+# TestSignalParsing
+# ---------------------------------------------------------------------------
+
+
+class TestSignalParsing:
+    """Test signal declaration parsing."""
+
+    def test_signal_no_args(self):
+        result = _parse_gd_content("signal health_depleted\n")
+        assert len(result.signals) == 1
+        assert result.signals[0].name == "health_depleted"
+        assert result.signals[0].args == []
+
+    def test_signal_untyped_args(self):
+        result = _parse_gd_content("signal foo(a, b)\n")
+        assert result.signals[0].args == [("a", ""), ("b", "")]
+
+    def test_signal_typed_args(self):
+        result = _parse_gd_content(
+            "signal cash_changed(amount: float, delta: float)\n"
+        )
+        sig = result.signals[0]
+        assert sig.args == [("amount", "float"), ("delta", "float")]
+
+    def test_signal_custom_type(self):
+        content = "signal any_state_changed(id: String, old: Printer.State, new: Printer.State)\n"
+        result = _parse_gd_content(content)
+        sig = result.signals[0]
+        assert sig.args[1] == ("old", "Printer.State")
+        assert sig.args[2] == ("new", "Printer.State")
+
+    def test_multiple_signals(self):
+        content = "signal a\nsignal b\nsignal c\n"
+        result = _parse_gd_content(content)
+        assert len(result.signals) == 3
+
+    def test_signal_line_number(self):
+        content = "\n\nsignal foo\n"
+        result = _parse_gd_content(content)
+        assert result.signals[0].line == 3
+
+
+# ---------------------------------------------------------------------------
+# TestFunctionParsing
+# ---------------------------------------------------------------------------
+
+
+class TestFunctionParsing:
+    """Test function declaration and end_line tracking."""
+
+    def test_basic_func(self):
+        content = "func _ready() -> void:\n\tpass\n"
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert f.name == "_ready"
+        assert f.return_type == "void"
+        assert f.args == []
+
+    def test_func_with_typed_params_and_default(self):
+        content = 'func add_cash(amount: float, description: String = "") -> void:\n\tpass\n'
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert f.args == [("amount", "float", ""), ("description", "String", '""')]
+        assert f.return_type == "void"
+
+    def test_func_return_bool(self):
+        content = "func can_afford(amount: float) -> bool:\n\treturn true\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].return_type == "bool"
+
+    def test_static_func(self):
+        content = "static func sum2(a, b):\n\treturn a + b\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].is_static is True
+
+    def test_virtual_func(self):
+        content = "func _ready():\n\tpass\n"
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert f.is_virtual is True
+        assert f.is_private is True
+
+    def test_non_virtual_func(self):
+        content = "func add_cash():\n\tpass\n"
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert f.is_virtual is False
+        assert f.is_private is False
+
+    def test_func_no_return_type(self):
+        content = "func foo():\n\tpass\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].return_type == ""
+
+    def test_func_complex_return_type(self):
+        content = "func get_idle_printers() -> Array[Printer]:\n\tpass\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].return_type == "Array[Printer]"
+
+    def test_func_arg_with_parens_in_default(self):
+        content = "func f(v: Vector2 = Vector2(1, 2)):\n\tpass\n"
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert len(f.args) == 1
+        assert f.args[0] == ("v", "Vector2", "Vector2(1, 2)")
+
+    def test_end_line_for_function(self):
+        content = "func foo():\n\tvar x = 1\n\treturn x\n\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].end_line == 3
+
+    def test_end_line_last_function_in_file(self):
+        content = "func foo():\n\tpass\n"
+        result = _parse_gd_content(content)
+        assert result.functions[0].end_line == 2
+
+    def test_one_liner_func(self):
+        content = "func square(a): return a * a\n"
+        result = _parse_gd_content(content)
+        f = result.functions[0]
+        assert f.end_line == f.line
+
+
+# ---------------------------------------------------------------------------
+# TestVariableParsing
+# ---------------------------------------------------------------------------
+
+
+class TestVariableParsing:
+    """Test variable and constant declaration parsing."""
+
+    def test_typed_var(self):
+        content = "var cash: float = 100.0\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.name == "cash"
+        assert v.type_hint == "float"
+        assert v.default_value == "100.0"
+
+    def test_const(self):
+        content = "const ANSWER = 42\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.is_const is True
+        assert v.default_value == "42"
+
+    def test_typed_const(self):
+        content = "const LEVEL_XP: Array[int] = [0, 40, 100]\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.is_const is True
+        assert v.type_hint == "Array[int]"
+
+    def test_export_same_line(self):
+        content = "@export var speed: float = 5.0\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.annotation == "@export"
+        assert v.name == "speed"
+
+    def test_export_range_same_line(self):
+        content = "@export_range(0, 100) var hp: int\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.annotation == "@export_range(0, 100)"
+
+    def test_onready_same_line(self):
+        content = "@onready var sprite := $Sprite2D\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.annotation == "@onready"
+
+    def test_static_var(self):
+        content = "static var balance: int = 0\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.is_static is True
+
+    def test_class_scope(self):
+        content = "var x: int = 0\n"
+        result = _parse_gd_content(content)
+        assert result.variables[0].scope == "class"
+
+    def test_local_scope_not_captured(self):
+        content = "func foo():\n\tvar local = 1\n"
+        result = _parse_gd_content(content)
+        assert len(result.variables) == 0
+
+    def test_multiline_const_array(self):
+        content = "const XP: Array[int] = [\n\t0,\n\t40,\n\t100,\n]\n"
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert v.is_const is True
+        assert "0" in v.default_value
+        assert "100" in v.default_value
+
+    def test_multiline_var_dict(self):
+        content = 'var stats: Dictionary = {\n\t"wins": 0,\n\t"losses": 0,\n}\n'
+        result = _parse_gd_content(content)
+        v = result.variables[0]
+        assert "wins" in v.default_value
+
+    def test_property_getter_setter(self):
+        content = "var health: int:\n\tget:\n\t\treturn _health\n\tset(value):\n\t\t_health = value\n"
+        result = _parse_gd_content(content)
+        assert len(result.variables) == 1
+        assert result.variables[0].name == "health"
+        assert len(result.functions) == 0
+
+    def test_inline_comment_stripped(self):
+        content = "var speed: float = 5.0  # movement speed\n"
+        result = _parse_gd_content(content)
+        assert result.variables[0].default_value == "5.0"
+
+    def test_export_group_discarded(self):
+        content = '@export_group("Movement")\n@export var speed: float = 5.0\n'
+        result = _parse_gd_content(content)
+        assert len(result.variables) == 1
+        assert result.variables[0].annotation == "@export"
+
+
+# ---------------------------------------------------------------------------
+# TestEnumParsing
+# ---------------------------------------------------------------------------
+
+
+class TestEnumParsing:
+    """Test enum declaration parsing."""
+
+    def test_named_enum(self):
+        content = "enum GameState { PLAYING, PAUSED, DAILY_SUMMARY }\n"
+        result = _parse_gd_content(content)
+        e = result.enums[0]
+        assert e.name == "GameState"
+        assert e.values == {"PLAYING": None, "PAUSED": None, "DAILY_SUMMARY": None}
+
+    def test_enum_explicit_values(self):
+        content = "enum { A, B = 5, C }\n"
+        result = _parse_gd_content(content)
+        assert result.enums[0].values["B"] == 5
+        assert result.enums[0].values["C"] is None
+
+    def test_enum_negative_values(self):
+        content = "enum { X = -1 }\n"
+        result = _parse_gd_content(content)
+        assert result.enums[0].values["X"] == -1
+
+    def test_anonymous_enum(self):
+        content = "enum { A, B }\n"
+        result = _parse_gd_content(content)
+        assert result.enums[0].name == ""
+
+    def test_multiline_enum(self):
+        content = "enum State {\n\tIDLE,\n\tRUNNING,\n\tSTOPPED,\n}\n"
+        result = _parse_gd_content(content)
+        e = result.enums[0]
+        assert e.name == "State"
+        assert "IDLE" in e.values
+        assert "STOPPED" in e.values
+
+    def test_enum_line_number(self):
+        content = "\nenum Foo { A }\n"
+        result = _parse_gd_content(content)
+        assert result.enums[0].line == 2
+
+
+# ---------------------------------------------------------------------------
+# TestInnerClasses
+# ---------------------------------------------------------------------------
+
+
+class TestInnerClasses:
+    """Test inner class detection and body skipping."""
+
+    def test_inner_class_detected(self):
+        content = "class Circle extends Shape:\n\tvar radius: float = 1.0\n\tfunc area():\n\t\treturn PI * radius * radius\n"
+        result = _parse_gd_content(content)
+        assert "Circle" in result.inner_classes
+
+    def test_inner_class_body_skipped(self):
+        content = "class Circle extends Shape:\n\tvar radius: float = 1.0\n\tfunc area():\n\t\tpass\n"
+        result = _parse_gd_content(content)
+        assert len(result.variables) == 0
+        assert len(result.functions) == 0
+
+    def test_inner_class_without_extends(self):
+        content = "class Something:\n\tpass\n"
+        result = _parse_gd_content(content)
+        assert "Something" in result.inner_classes
+
+
+# ---------------------------------------------------------------------------
+# TestEdgeCases
+# ---------------------------------------------------------------------------
+
+
+class TestEdgeCases:
+    """Test edge cases and error handling."""
+
+    def test_empty_string(self):
+        result = _parse_gd_content("")
+        assert result.class_name == ""
+        assert result.extends == ""
+        assert result.signals == []
+
+    def test_only_comments(self):
+        result = _parse_gd_content("# This is a comment\n## Doc comment\n")
+        assert result.class_name == ""
+
+    def test_malformed_line_no_raise(self):
+        content = "extends Node\n!@#$%^&*()\nvar x: int = 1\n"
+        result = _parse_gd_content(content)
+        assert result.extends == "Node"
+        assert len(result.variables) == 1
+
+    def test_unclosed_multiline_at_eof(self):
+        content = "const XP: Array[int] = [\n\t0,\n\t40,\n"
+        result = _parse_gd_content(content)
+        assert len(result.variables) == 1
+
+    def test_annotation_buffer_not_corrupted_by_rpc(self):
+        content = "@rpc\nfunc remote_action():\n\tpass\n@export var x: int = 1\n"
+        result = _parse_gd_content(content)
+        assert result.variables[0].annotation == "@export"
+
+    def test_export_group_no_buffer_corruption(self):
+        content = '@export_group("Group")\nvar plain: int = 1\n'
+        result = _parse_gd_content(content)
+        assert result.variables[0].annotation == ""
diff --git a/tests/test_server.py b/tests/test_server.py
index ff36079..afc2863 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -44,9 +44,9 @@ class TestParserStubs:
         with pytest.raises(FileNotFoundError):
             parse_tscn("/nonexistent/path/scene.tscn")
 
-    def test_parse_gd_stub_raises(self) -> None:
-        with pytest.raises(NotImplementedError):
-            parse_gd("")
+    def test_parse_gd_missing_file_raises(self) -> None:
+        with pytest.raises(FileNotFoundError):
+            parse_gd("/nonexistent/path.gd")
 
     def test_parse_tres_stub_raises(self) -> None:
         with pytest.raises(NotImplementedError):
