Skip to content

Commit 8546536

Browse files
committed
Merge branch 'feat/c'
2 parents d56b49e + 1f5fd2b commit 8546536

File tree

10 files changed

+595
-13
lines changed

10 files changed

+595
-13
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bin/

.vscode/c_cpp_properties.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "Linux",
5+
"includePath": [
6+
"${workspaceFolder}/**"
7+
],
8+
"defines": [],
9+
"compilerPath": "/usr/bin/clang",
10+
"cStandard": "c17",
11+
"cppStandard": "c++17",
12+
"intelliSenseMode": "linux-clang-x64"
13+
}
14+
],
15+
"version": 4
16+
}

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"files.associations": {
3+
"stdlib.h": "c",
4+
"libquill.h": "c"
5+
}
6+
}

Makefile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.PHONY: all clean build test-c shared-lib
2+
3+
# Default target
4+
all: build shared-lib
5+
6+
# Build the binary
7+
build:
8+
@echo "Building Go binary..."
9+
@mkdir -p bin
10+
go build -o bin/quill cmd/quill/main.go
11+
@echo "Binary built: bin/quill"
12+
13+
# Build the shared library
14+
shared-lib:
15+
@echo "Building shared library..."
16+
@mkdir -p bin
17+
CGO_ENABLED=1 go build -buildmode=c-shared -o bin/libquill.so cmd/c/main.go
18+
@echo "Shared library built: bin/libquill.so"
19+
@echo "Header file: bin/libquill.h"
20+
21+
# Clean build folder
22+
clean:
23+
rm -rf bin
24+
@echo "Cleaned build folder."
25+
26+
# Test the C API
27+
test-c: shared-lib
28+
@echo "Building C test..."
29+
gcc -Wall -Wextra -std=c99 \
30+
-I./bin \
31+
-L./bin \
32+
-o bin/test_quill \
33+
examples/c/example.c \
34+
-lquill \
35+
-Wl,-rpath,$$PWD/bin
36+
@echo "C test built: bin/test_quill"
37+
@echo "Running C test..."
38+
LD_LIBRARY_PATH=./bin ./bin/test_quill

cmd/c/main.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package main
2+
3+
// #include <stdlib.h>
4+
// #include <stdio.h>
5+
import "C"
6+
import (
7+
"quill/internal/jsonapi"
8+
"sync"
9+
"unsafe"
10+
)
11+
12+
var (
13+
interpreters = make(map[int]*jsonapi.QuillInterpreter)
14+
nextID = 1
15+
mu sync.Mutex
16+
)
17+
18+
//export quill_new_interpreter
19+
func quill_new_interpreter(source *C.char) *C.char {
20+
goSource := C.GoString(source)
21+
interp, result := jsonapi.NewQuillInterpreter(goSource)
22+
23+
if interp != nil {
24+
mu.Lock()
25+
interpreters[nextID] = interp
26+
nextID++
27+
mu.Unlock()
28+
}
29+
30+
cResult := C.CString(result)
31+
if cResult == nil {
32+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
33+
}
34+
return cResult
35+
}
36+
37+
//export quill_step
38+
func quill_step(interpID C.int) *C.char {
39+
mu.Lock()
40+
interp, exists := interpreters[int(interpID)]
41+
mu.Unlock()
42+
43+
if !exists {
44+
return C.CString(`{"success":false,"error":"Invalid interpreter ID"}`)
45+
}
46+
47+
result := interp.Step()
48+
cResult := C.CString(result)
49+
if cResult == nil {
50+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
51+
}
52+
return cResult
53+
}
54+
55+
//export quill_handle_choice
56+
func quill_handle_choice(interpID C.int, choiceIndex C.int) *C.char {
57+
mu.Lock()
58+
interp, exists := interpreters[int(interpID)]
59+
mu.Unlock()
60+
61+
if !exists {
62+
return C.CString(`{"success":false,"error":"Invalid interpreter ID"}`)
63+
}
64+
65+
result := interp.HandleChoiceInput(int(choiceIndex))
66+
cResult := C.CString(result)
67+
if cResult == nil {
68+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
69+
}
70+
return cResult
71+
}
72+
73+
//export quill_get_state
74+
func quill_get_state(interpID C.int) *C.char {
75+
mu.Lock()
76+
interp, exists := interpreters[int(interpID)]
77+
mu.Unlock()
78+
79+
if !exists {
80+
return C.CString(`{"success":false,"error":"Invalid interpreter ID"}`)
81+
}
82+
83+
result := interp.GetState()
84+
cResult := C.CString(result)
85+
if cResult == nil {
86+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
87+
}
88+
return cResult
89+
}
90+
91+
//export quill_is_ended
92+
func quill_is_ended(interpID C.int) *C.char {
93+
mu.Lock()
94+
interp, exists := interpreters[int(interpID)]
95+
mu.Unlock()
96+
97+
if !exists {
98+
return C.CString(`{"success":false,"error":"Invalid interpreter ID"}`)
99+
}
100+
101+
result := interp.IsEnded()
102+
cResult := C.CString(result)
103+
if cResult == nil {
104+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
105+
}
106+
return cResult
107+
}
108+
109+
//export quill_is_waiting_for_choice
110+
func quill_is_waiting_for_choice(interpID C.int) *C.char {
111+
mu.Lock()
112+
interp, exists := interpreters[int(interpID)]
113+
mu.Unlock()
114+
115+
if !exists {
116+
return C.CString(`{"success":false,"error":"Invalid interpreter ID"}`)
117+
}
118+
119+
result := interp.IsWaitingForChoice()
120+
cResult := C.CString(result)
121+
if cResult == nil {
122+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
123+
}
124+
return cResult
125+
}
126+
127+
//export quill_parse_only
128+
func quill_parse_only(source *C.char) *C.char {
129+
goSource := C.GoString(source)
130+
result := jsonapi.ParseOnly(goSource)
131+
cResult := C.CString(result)
132+
if cResult == nil {
133+
return C.CString(`{"success":false,"error":"Failed to allocate C string"}`)
134+
}
135+
return cResult
136+
}
137+
138+
//export quill_free_string
139+
func quill_free_string(str *C.char) {
140+
C.free(unsafe.Pointer(str))
141+
}
142+
143+
//export quill_free_interpreter
144+
func quill_free_interpreter(interpID C.int) {
145+
mu.Lock()
146+
delete(interpreters, int(interpID))
147+
mu.Unlock()
148+
}
149+
150+
func main() {}

examples/c/example.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include "libquill.h"
5+
6+
// Simple function to extract interpreter ID from JSON response
7+
// In a real application, you'd use a proper JSON parser
8+
int extract_interpreter_id(const char* json_response) {
9+
// Look for "success":true first
10+
if (strstr(json_response, "\"success\":true") == NULL) {
11+
return -1; // Error case
12+
}
13+
14+
// This is a very basic extraction - in practice use a JSON library
15+
// The response should contain the interpreter ID
16+
// For now, we'll return a hardcoded value since the Go code doesn't
17+
// actually include the ID in the response
18+
return 1;
19+
}
20+
21+
int main() {
22+
const char* source =
23+
"ALEX: \"Hello there!\"\n"
24+
"BELLA: \"Hi Alex!\"\n"
25+
"\n"
26+
"CHOICE {\n"
27+
" \"How are you?\" {\n"
28+
" ALEX: \"I'm doing great, thanks!\"\n"
29+
" },\n"
30+
" \"What's new?\" {\n"
31+
" ALEX: \"Not much, just working on some projects.\"\n"
32+
" }\n"
33+
"}\n"
34+
"\n"
35+
"ALEX: \"Thanks for asking!\"\n"
36+
"END\n";
37+
38+
printf("=== Quill C API Test ===\n\n");
39+
40+
// Test parse only
41+
printf("1. Testing parse only:\n");
42+
char* parse_result = quill_parse_only(source);
43+
printf("Parse result: %s\n\n", parse_result);
44+
quill_free_string(parse_result);
45+
46+
// Create interpreter
47+
printf("2. Creating interpreter:\n");
48+
char* init_result = quill_new_interpreter(source);
49+
printf("Init result: %s\n\n", init_result);
50+
51+
// Extract interpreter ID from response
52+
int interp_id = extract_interpreter_id(init_result);
53+
if (interp_id == -1) {
54+
printf("Failed to create interpreter\n");
55+
quill_free_string(init_result);
56+
return 1;
57+
}
58+
59+
quill_free_string(init_result);
60+
61+
// Test interpreter methods
62+
printf("3. Testing interpreter methods:\n");
63+
64+
// Get initial state
65+
char* state = quill_get_state(interp_id);
66+
printf("Initial state: %s\n", state);
67+
quill_free_string(state);
68+
69+
// Step through execution
70+
for (int i = 0; i < 5; i++) {
71+
printf("\n--- Step %d ---\n", i + 1);
72+
73+
char* step_result = quill_step(interp_id);
74+
printf("Step result: %s\n", step_result);
75+
quill_free_string(step_result);
76+
77+
// Check if waiting for choice
78+
char* waiting = quill_is_waiting_for_choice(interp_id);
79+
printf("Waiting for choice: %s\n", waiting);
80+
81+
// For demo purposes, always choose option 0 if waiting
82+
// In real usage, you'd parse the JSON to check the boolean value
83+
char* choice_result = quill_handle_choice(interp_id, 0);
84+
printf("Choice result: %s\n", choice_result);
85+
quill_free_string(choice_result);
86+
quill_free_string(waiting);
87+
88+
// Check if ended
89+
char* ended = quill_is_ended(interp_id);
90+
printf("Is ended: %s\n", ended);
91+
quill_free_string(ended);
92+
}
93+
94+
// Clean up
95+
quill_free_interpreter(interp_id);
96+
97+
printf("\n=== Test Complete ===\n");
98+
return 0;
99+
}

internal/interpreter/interpreter.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ type InterpreterResult struct {
2323
}
2424

2525
type DialogData struct {
26-
Character string
27-
Text string
28-
Tags []string
26+
Character string `json:"character"`
27+
Text string `json:"text"`
28+
Tags []string `json:"tags"`
2929
}
3030

3131
type ChoiceOption struct {
32-
Index int
33-
Text string
34-
Tags []string
32+
Index int `json:"index"`
33+
Text string `json:"text"`
34+
Tags []string `json:"tags"`
3535
}
3636

3737
type ChoiceData struct {
38-
Options []ChoiceOption
38+
Options []ChoiceOption `json:"options"`
3939
}
4040

4141
type ExecutionState int
@@ -69,8 +69,8 @@ type InterpreterError struct {
6969
}
7070

7171
type ErrorData struct {
72-
Message string
73-
Line int
72+
Message string `json:"message"`
73+
Line int `json:"line"`
7474
}
7575

7676
func New(program *ast.Program) *Interpreter {

0 commit comments

Comments
 (0)