summaryrefslogtreecommitdiff
path: root/README.md
blob: 5953056f78723113f2bdacab65e8da2314d0d5a4 (plain)
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
---
layout: base
title: honeysuckle README
---

honeysuckle
===========

A pure C library to make writing Lua bindings simple. honeysuckle provides 
functions to make argument parsing, table operations, error creation and 
handling, string wrangling, and registry operations easy and straightforward 
from within your C code.

Table of contents
-----------------

- [Installation](#installation)
- [Usage](#usage)
- [Development](#development)
- [License](#license)
- [Footer](#footer)

Installation
------------

[(Back to top)](#table-of-contents)

```bash
mkdir build
cd build
cmake ..
make
sudo make install
```

To make the tests as well, do the above and then do `make test`.

You should now be able to include `<honeysuckle.h>` into your C files for use.

Usage
-----

[(Back to top)](#table-of-contents)

- [Type constants](#type-constants)
- [Argument parsing](#argument-parsing)
  - [Overloading](#overloading)
- [Table construction](#table-construction)
  - [Enums](#enums)
- [Table processing](#table-processing)
- [Errors and error handling](#errors-and-error-handling)
- [String wrangling](#string-wrangling)
- [Registry values](#registry-values)

### Type constants

[(Back to top)](#table-of-contents)

These constants are used when honeysuckle needs to know a type to expect.

- `HS_BOOL` - booleans
- `HS_INT` - integers
- `HS_NUM` - numbers (double by default)
- `HS_STR` - strings
- `HS_TBL` - tables
- `HS_FUNC` - any function
- `HS_CFUNC` - C functions specifically
- `HS_USER` - lua full userdata
- `HS_LIGHT` - lua light userdata
- `HS_NIL` - lua's nil
- `HS_ANY` - any type

`hs_type_to_string()` takes as argument a single type constant and returns a 
const string representing the type.

### Argument parsing

[(Back to top)](#table-of-contents)

`hs_parse_args()` is the primary argument-parsing function. It accepts as 
arguments a lua_State pointer, any number of alternating type constant and 
pointer pairs, and finally `HS_END` to close the arguments. This function 
performs typechecking and in case of an error throws with helpful error 
messages.

`HS_TBL`, `HS_FUNC`, `HS_NIL`, and `HS_ANY` take a pointer to an integer 
that will be filled with the stack index of the argument.

```c
int some_lua_binding(lua_State *L)
{
	bool b;
	int i;
	int tbl_index;
	lua_Number n;
	char *str;
	void* user;
	hs_parse_args(L,
		HS_BOOL, &b,
		HS_INT, &i,
		HS_TBL, &tbl_index,
		HS_NUM, &n,
		HS_STR, &str,
		HS_USER, &user,
		HS_END);
	// do something...
	return 1;
}
```

#### Overloading

There is another function, `hs_parse_overloaded()`, that permits overloaded 
argument parsing. Its syntax is exactly as `hs_parse_args()` except that after 
each `HS_END` you may provide another argument list, and after the final 
argument list you must provide another `HS_END`. This function throws an error 
only if none of the argument lists match the provided arguments. 

```c
int overloaded_lua_binding(lua_State *L)
{
	int i;
	char *str;
	int choice = hs_parse_overloaded(L,
		HS_INT, &i, HS_END,
		HS_STR, &str, HS_END,
		HS_END);
	if (choice == 0) {
		// do something with i
		// str remains unassigned!!
	}
	else {
		// choice == 1, do something with str
		// i remains unassigned!!
	}
	return 0;
}
```

### Table construction

[(Back to top)](#table-of-contents)

`hs_create_table()` takes a lua_State pointer, some number of string key/type 
constant/value triplets, and finally `HS_END`. It returns the stack index of 
the newly created table. Most of the type constants expect their associated 
type (e.g. `HS_INT` expects an `int`, `HS_CFUNC` expects `lua_CFunction`, 
etc.) but `HS_FUNC` and `HS_TBL` expect an integer stack index. You cannot 
use `HS_NIL` or `HS_ANY` with this function. `hs_create_table()` 
automatically pops any stack indices provided to it.

```c
int *ptr; // some pointer

hs_create_table(L,
	"intVal", HS_INT, 4,
	"numVal", HS_NUM, 6.0f,
	"tableVal", HS_TBL, hs_create_table(L,
		"subTableCFunc", HS_CFUNC, some_lua_binding,
		"subTableString", HS_STR, "i'm a string!",
		HS_END),
	"lightUserdataVal", HS_LIGHT, (void *)ptr,
	HS_END);
```

#### Enums

`hs_create_enum()` takes a lua_State pointer, some number of string/integer 
pairs, and finally `HS_END`. It creates a table in which, for each string/int 
pair `tbl[string] = int` and `tbl[int] = string` and returns the stack index 
of said table. `hs_string_to_enum()` uses such a table to convert a string 
literal to enum value and `hs_enum_to_string()` converts enums to strings. In 
case the value does not exist, converting to a string results in NULL and 
converting to an enum results in a negative value.

```c
enum { VALUE_1, VALUE_2, VALUE_3 } example_t;

int index = hs_create_enum(L,
	"one", VALUE_1,
	"two", VALUE_2,
	"three", VALUE_3,
	HS_END);
	
example_t e = hs_string_to_enum(L, index, "one"); // VALUE_1
char *str = hs_enum_to_string(L, index, VALUE_3); // "three"
```

### Table processing

[(Back to top)](#table-of-contents)

`hs_process_table()` is intended for creating lua bindings that can be called 
like `set_config{ debug=true, verbosity=2, logfile='run.log' }`. It can 
only operate on stringly-keyed values.

```c
void set_verbosity(int value, void *data) { // set verbosity level }
void set_debug(bool value, void *data) { // set debug }
void set_logfile(char *value, void *data) { // set log filename }
void process_any_key(int stack_index, void *data) { // do something }

// tbl_index comes from somewhere...
	
hs_process_table(L, tbl_index, NULL,
	"verbosity", HS_INT, set_verbosity,
	"debug", HS_BOOL, set_debug,
	"logfile", HS_STR, set_logfile,
	"extraData", HS_ANY, process_any_key,
	HS_END);
```

### Errors and error handling

[(Back to top)](#table-of-contents)

`hs_throw_error()` constructs and throws error messages like printf.

```c
hs_throw_error(L, "ERROR: %d is not within range", 10);
```

`hs_traceback()` can be supplied to `lua_pcall()` to get a helpful stack 
trace error message:

```c
lua_pushcfunction(L, hs_traceback);
int tr_pos = lua_gettop();

// push function
// push n arguments
int result = lua_pcall(L, nargs, nret, tr_pos);
```

`hs_call()` performs the above, accepting simply a lua_State pointer, int 
nargs, and int nret as arguments. Alternatively, you can use `hs_call_args()` 
which provides the same helpful, contained error messages as `hs_call()` but 
takes arguments explicitly in the call for both function arguments and 
returns:

```c
// some_function accepts int, str and returns number, boolean
lua_pushcfunction(L, some_function);

lua_Number n;
bool b;
hs_call_args(L, 
	HS_INT, 4, HS_STR, "hello", HS_END,
	HS_NUM, &n, HS_BOOL, &b, HS_END);
```

### String wrangling

[(Back to top)](#table-of-contents)

`hs_pushstring()` allows you to push string values using printf format 
strings.

```c
hs_pushstring(L, "hello there, %s. I hear the current time is %d:%02d", "dylan", 10, 5);
```

### Registry values

[(Back to top)](#table-of-contents)

honeysuckle provides three macros, `hs_rstore()`, `hs_rload()`, and `hs_
rdel()` to make registry access easier. These are mostly just thin wrappers 
around `luaL_ref()`, `luaL_unref()` and `lua_rawgeti()` but they come up 
frequently enough that I think these are helpful.

```c
lua_pushstring(L, "some string");
int my_string_ref = hs_rstore(L);
// ...
hs_rload(L, my_string_ref); // load the value referenced by my_string_ref
hs_rdel(L, my_string_ref); // done with this reference, remove from the registry table
```

Development
-----------
[(Back to top)](#table-of-contents)

honeysuckle is still very early in development. As the specifics of the build 
process and testing become clearer I'll update this section.

If you're interested in helping out, send me an email! The to-dos at the time of writing are:

1. Finish writing the basic tests
2. Write implementations for the API described above
3. Expand the test cases

License
-------

[(Back to top)](#table-of-contents)


honeysuckle is licensed under the [Anti-Capitalist Software License]. 
Basically: if you're a single person, a co-op, or a nonprofit, feel free to 
use this. Otherwise, send me an email. c:

[Anti-Capitalist Software License]: https://anticapitalist.software