File size: 7,011 Bytes
ffc07fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
! Layer 5 β€” Sovereign IDE Syntax Highlighting
! Query compiler + theme mapping
! Maps to: snapkitty-resonance-bridge token stream structure
MODULE sovereign_syntax
  USE iso_c_binding
  USE sovereign_runtime, ONLY: runtime_panic, str_intern
  USE sovereign_parser,  ONLY: parser_ctx_t, cst_node_t
  IMPLICIT NONE
  PRIVATE

  ! ── Color IDs (theme slots) ───────────────────────────────────────────────
  INTEGER, PARAMETER :: COLOR_DEFAULT    = 0
  INTEGER, PARAMETER :: COLOR_KEYWORD    = 1
  INTEGER, PARAMETER :: COLOR_IDENTIFIER = 2
  INTEGER, PARAMETER :: COLOR_STRING     = 3
  INTEGER, PARAMETER :: COLOR_NUMBER     = 4
  INTEGER, PARAMETER :: COLOR_COMMENT    = 5
  INTEGER, PARAMETER :: COLOR_OPERATOR   = 6
  INTEGER, PARAMETER :: COLOR_TYPE       = 7
  INTEGER, PARAMETER :: COLOR_FUNCTION   = 8
  INTEGER, PARAMETER :: COLOR_ERROR      = 9
  INTEGER, PARAMETER :: N_COLORS         = 10

  ! ── Theme ─────────────────────────────────────────────────────────────────
  ! RGBA packed as 0xRRGGBBAA
  TYPE :: theme_t
    INTEGER(C_INT32_T) :: colors(N_COLORS)
    LOGICAL            :: worm_sealed = .FALSE.
  END TYPE theme_t

  ! ── Highlight span ────────────────────────────────────────────────────────
  INTEGER, PARAMETER :: MAX_SPANS = 131072
  TYPE :: span_t
    INTEGER :: start_byte = 0
    INTEGER :: end_byte   = 0
    INTEGER :: color_id   = COLOR_DEFAULT
  END TYPE span_t

  TYPE :: span_list_t
    TYPE(span_t) :: spans(MAX_SPANS)
    INTEGER      :: count = 0
  END TYPE span_list_t

  ! ── Query ─────────────────────────────────────────────────────────────────
  INTEGER, PARAMETER :: MAX_QUERY_PATTERNS = 256
  TYPE :: query_pattern_t
    INTEGER(C_INT32_T) :: node_kind_id = 0
    INTEGER            :: color_id     = COLOR_DEFAULT
  END TYPE query_pattern_t

  TYPE :: query_t
    TYPE(query_pattern_t) :: patterns(MAX_QUERY_PATTERNS)
    INTEGER               :: pattern_count = 0
  END TYPE query_t

  PUBLIC :: syntax_init
  PUBLIC :: syntax_load_theme
  PUBLIC :: syntax_highlight
  PUBLIC :: syntax_highlight_incremental
  PUBLIC :: theme_default_sovereign_dark

CONTAINS

  ! ---------------------------------------------------------------------------
  SUBROUTINE syntax_init()
    WRITE(*,'(A)') '[syntax] init'
  END SUBROUTINE syntax_init

  ! ---------------------------------------------------------------------------
  ! Load a theme; WORM-seal after load
  SUBROUTINE syntax_load_theme(theme, path)
    TYPE(theme_t),    INTENT(INOUT) :: theme
    CHARACTER(LEN=*), INTENT(IN)    :: path
    ! TODO: parse theme file (JSON/TOML)
    CALL theme_default_sovereign_dark(theme)
    theme%worm_sealed = .TRUE.
    WRITE(*,'(A,A)') '[syntax] theme loaded from ', TRIM(path)
  END SUBROUTINE syntax_load_theme

  ! ---------------------------------------------------------------------------
  ! Sovereign dark theme β€” matches bob-ide color palette
  SUBROUTINE theme_default_sovereign_dark(theme)
    TYPE(theme_t), INTENT(INOUT) :: theme
    theme%colors(COLOR_DEFAULT+1)    = INT(Z'D4D4D4FF', C_INT32_T)  ! #D4D4D4
    theme%colors(COLOR_KEYWORD+1)    = INT(Z'569CD6FF', C_INT32_T)  ! #569CD6
    theme%colors(COLOR_IDENTIFIER+1) = INT(Z'9CDCFEFF', C_INT32_T)  ! #9CDCFE
    theme%colors(COLOR_STRING+1)     = INT(Z'CE9178FF', C_INT32_T)  ! #CE9178
    theme%colors(COLOR_NUMBER+1)     = INT(Z'B5CEA8FF', C_INT32_T)  ! #B5CEA8
    theme%colors(COLOR_COMMENT+1)    = INT(Z'6A9955FF', C_INT32_T)  ! #6A9955
    theme%colors(COLOR_OPERATOR+1)   = INT(Z'D4D4D4FF', C_INT32_T)
    theme%colors(COLOR_TYPE+1)       = INT(Z'4EC9B0FF', C_INT32_T)  ! #4EC9B0
    theme%colors(COLOR_FUNCTION+1)   = INT(Z'DCDCAAFF', C_INT32_T)  ! #DCDCAA
    theme%colors(COLOR_ERROR+1)      = INT(Z'F44747FF', C_INT32_T)  ! #F44747
    theme%worm_sealed = .FALSE.
  END SUBROUTINE theme_default_sovereign_dark

  ! ---------------------------------------------------------------------------
  ! Full highlight pass over entire CST
  SUBROUTINE syntax_highlight(ctx, spans, query)
    TYPE(parser_ctx_t), INTENT(IN)    :: ctx
    TYPE(span_list_t),  INTENT(INOUT) :: spans
    TYPE(query_t),      INTENT(IN)    :: query
    spans%count = 0
    CALL highlight_node(ctx, ctx%root, spans, query)
    WRITE(*,'(A,I0,A)') '[syntax] highlight: ', spans%count, ' spans'
  END SUBROUTINE syntax_highlight

  ! ---------------------------------------------------------------------------
  ! Incremental highlight β€” only reprocess changed CST subtree
  SUBROUTINE syntax_highlight_incremental(ctx, spans, query, from_node)
    TYPE(parser_ctx_t), INTENT(IN)    :: ctx
    TYPE(span_list_t),  INTENT(INOUT) :: spans
    TYPE(query_t),      INTENT(IN)    :: query
    INTEGER,            INTENT(IN)    :: from_node
    ! TODO: invalidate spans overlapping from_node range, re-run from there
    CALL highlight_node(ctx, from_node, spans, query)
  END SUBROUTINE syntax_highlight_incremental

  ! ── Internal ──────────────────────────────────────────────────────────────

  RECURSIVE SUBROUTINE highlight_node(ctx, idx, spans, query)
    TYPE(parser_ctx_t), INTENT(IN)    :: ctx
    INTEGER,            INTENT(IN)    :: idx
    TYPE(span_list_t),  INTENT(INOUT) :: spans
    TYPE(query_t),      INTENT(IN)    :: query
    INTEGER :: i, color, child
    IF (idx == 0) RETURN
    ! Match node kind against query patterns
    color = COLOR_DEFAULT
    IF (ctx%nodes(idx)%is_error) THEN
      color = COLOR_ERROR
    ELSE
      DO i = 1, query%pattern_count
        IF (query%patterns(i)%node_kind_id == ctx%nodes(idx)%kind) THEN
          color = query%patterns(i)%color_id
          EXIT
        END IF
      END DO
    END IF
    ! Emit span if leaf
    IF (ctx%nodes(idx)%n_children == 0) THEN
      IF (spans%count < MAX_SPANS) THEN
        spans%count = spans%count + 1
        spans%spans(spans%count)%start_byte = ctx%nodes(idx)%start_byte
        spans%spans(spans%count)%end_byte   = ctx%nodes(idx)%end_byte
        spans%spans(spans%count)%color_id   = color
      END IF
      RETURN
    END IF
    ! Recurse into children
    DO i = 1, ctx%nodes(idx)%n_children
      child = ctx%nodes(idx)%children(i)
      CALL highlight_node(ctx, child, spans, query)
    END DO
  END SUBROUTINE highlight_node

END MODULE sovereign_syntax