-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.go
More file actions
205 lines (170 loc) · 5 KB
/
parser.go
File metadata and controls
205 lines (170 loc) · 5 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package md4c
/*
#cgo LDFLAGS: -lmd4c
#include <stdlib.h>
#include <md4c.h>
// コールバック関数のGo側ラッパー
extern int goEnterBlockCallback(MD_BLOCKTYPE, void*, void*);
extern int goLeaveBlockCallback(MD_BLOCKTYPE, void*, void*);
extern int goEnterSpanCallback(MD_SPANTYPE, void*, void*);
extern int goLeaveSpanCallback(MD_SPANTYPE, void*, void*);
extern int goTextCallback(MD_TEXTTYPE, MD_CHAR*, MD_SIZE, void*); // constを削除
// C側のコールバック関数(Goの関数を呼び出す)
static int c_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
return goEnterBlockCallback(type, detail, userdata);
}
static int c_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
return goLeaveBlockCallback(type, detail, userdata);
}
static int c_enter_span(MD_SPANTYPE type, void* detail, void* userdata) {
return goEnterSpanCallback(type, detail, userdata);
}
static int c_leave_span(MD_SPANTYPE type, void* detail, void* userdata) {
return goLeaveSpanCallback(type, detail, userdata);
}
static int c_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
// constキャストを行う
return goTextCallback(type, (MD_CHAR*)text, size, userdata);
}
// パーサー初期化用のヘルパー
static MD_PARSER create_parser(unsigned int flags) {
MD_PARSER parser = {
.abi_version = 0,
.flags = flags,
.enter_block = c_enter_block,
.leave_block = c_leave_block,
.enter_span = c_enter_span,
.leave_span = c_leave_span,
.text = c_text,
.debug_log = NULL,
.syntax = NULL
};
return parser;
}
*/
import "C"
import (
"sync"
"unsafe"
)
// Parser はMD4Cのパーサーラッパー
type Parser struct {
callbacks Callbacks
flags uint32
}
var (
parserRegistry = make(map[uintptr]*Parser)
parserRegistryMu sync.RWMutex
)
// NewParser は新しいパーサーを作成
func NewParser(callbacks Callbacks, flags uint32) *Parser {
return &Parser{
callbacks: callbacks,
flags: flags,
}
}
// Parse はマークダウンテキストをパース
func (p *Parser) Parse(markdown string) error {
// レジストリにパーサーを登録
parserID := uintptr(unsafe.Pointer(p))
parserRegistryMu.Lock()
parserRegistry[parserID] = p
parserRegistryMu.Unlock()
defer func() {
parserRegistryMu.Lock()
delete(parserRegistry, parserID)
parserRegistryMu.Unlock()
}()
cMarkdown := C.CString(markdown)
defer C.free(unsafe.Pointer(cMarkdown))
cParser := C.create_parser(C.uint(p.flags))
//nolint:unsafeptr
result := C.md_parse(
(*C.MD_CHAR)(unsafe.Pointer(cMarkdown)),
C.MD_SIZE(len(markdown)),
&cParser,
unsafe.Pointer(parserID),
)
if result != 0 {
return &ParseError{Code: int(result)}
}
return nil
}
// コールバック関数のGo実装(C側から呼ばれる)
//export goEnterBlockCallback
func goEnterBlockCallback(blockType C.MD_BLOCKTYPE, detail unsafe.Pointer, userdata unsafe.Pointer) C.int {
parserID := uintptr(userdata)
parserRegistryMu.RLock()
parser, ok := parserRegistry[parserID]
parserRegistryMu.RUnlock()
if !ok || parser.callbacks.EnterBlock == nil {
return 0
}
err := parser.callbacks.EnterBlock(int(blockType), detail)
if err != nil {
return -1
}
return 0
}
//export goLeaveBlockCallback
func goLeaveBlockCallback(blockType C.MD_BLOCKTYPE, detail unsafe.Pointer, userdata unsafe.Pointer) C.int {
parserID := uintptr(userdata)
parserRegistryMu.RLock()
parser, ok := parserRegistry[parserID]
parserRegistryMu.RUnlock()
if !ok || parser.callbacks.LeaveBlock == nil {
return 0
}
err := parser.callbacks.LeaveBlock(int(blockType), detail)
if err != nil {
return -1
}
return 0
}
//export goEnterSpanCallback
func goEnterSpanCallback(spanType C.MD_SPANTYPE, detail unsafe.Pointer, userdata unsafe.Pointer) C.int {
parserID := uintptr(userdata)
parserRegistryMu.RLock()
parser, ok := parserRegistry[parserID]
parserRegistryMu.RUnlock()
if !ok || parser.callbacks.EnterSpan == nil {
return 0
}
err := parser.callbacks.EnterSpan(int(spanType), detail)
if err != nil {
return -1
}
return 0
}
//export goLeaveSpanCallback
func goLeaveSpanCallback(spanType C.MD_SPANTYPE, detail unsafe.Pointer, userdata unsafe.Pointer) C.int {
parserID := uintptr(userdata)
parserRegistryMu.RLock()
parser, ok := parserRegistry[parserID]
parserRegistryMu.RUnlock()
if !ok || parser.callbacks.LeaveSpan == nil {
return 0
}
err := parser.callbacks.LeaveSpan(int(spanType), detail)
if err != nil {
return -1
}
return 0
}
//export goTextCallback
func goTextCallback(textType C.MD_TEXTTYPE, text *C.MD_CHAR, size C.MD_SIZE, userdata unsafe.Pointer) C.int {
parserID := uintptr(userdata)
parserRegistryMu.RLock()
parser, ok := parserRegistry[parserID]
parserRegistryMu.RUnlock()
if !ok || parser.callbacks.Text == nil {
return 0
}
// C文字列をGoの文字列に変換
goText := C.GoStringN((*C.char)(unsafe.Pointer(text)), C.int(size))
err := parser.callbacks.Text(int(textType), goText)
if err != nil {
return -1
}
return 0
}