summaryrefslogtreecommitdiff
path: root/README.md
blob: 57a2f84e81dd8aae3c373e5e8d1e510f6f1fbf13 (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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
---
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` - lua_Integer (DO NOT use int, long int, or other types!)
- `HS_NUM` - lua_Number (DO NOT use float, double, or other types!)
- `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.

For the numeric types (`HS_INT` and `HS_NUM`, using variables other 
than `lua_Integer` and `lua_Number` may result in undefined behavior.

### 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 
`struct hs_arg`s. These can either be constructed by hand or with 
function-like macros identified by the lowercase version of the type 
names from the section above. Use of the macros is recommended as 
they will likely reduce type mismatch errors (e.g. giving HS_BOOL as 
type but providing a pointer to a string).

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.

Please note that, due to technical limitations, this function can 
only parse up to 255 arguments. This function may mutate any 
preexisting contents of the variables passed to contain the 
arguments.

```c
int some_lua_binding(lua_State *L)
{
	bool b;
	lua_Integer i;
	int tbl_index;
	lua_Number n;
	char *str;
	void* user;
	hs_parse_args(L,
		// explicit initializer for struct
		{ HS_BOOL, .ptr.bool = &b }, 
		// macro initialization (recommended)
		hs_int(i), 
		hs_tbl(tbl_index),
		hs_num(n),
		hs_str(str),
		hs_user(user)
	);
	// 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 each overload option must be wrapped 
in the macro `hs_overload()`. This function returns an integer 
containing the zero-indexed index of the overload option that was 
actually used.

Please note that because of Lua's type system, overloads that differ 
only in an integer/number pair, function/c function pair, or 
userdata/light userdata pair in the same position may not work as 
expected.

If none of the argument lists match the provided arguments, this 
function throws an error.

This function may mutate preexisting data in the variables passed to 
store arguments.

```c
int overloaded_lua_binding(lua_State *L)
{
	lua_Integer i;
	lua_Number n;
	char *str;
	int choice = hs_parse_overloaded(L,
		hs_overload( hs_int(i) ),
		hs_overload( hs_str(str), hs_num(n) )
	);
	if (choice == 0) {
		// do something with i
		// str and n remain unassigned!!
	}
	else {
		// choice == 1, do something with str and n
		// i remains unassigned!!
	}
	return 0;
}
```

### Table construction

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

`hs_create_table()` takes a lua_State pointer and then up to 255 `
struct hs_tbl_entry`s. These can either be constructed by hand, or 
via function-like macros identified by lowercase versions of the 
typenames shown above (e.g. `hs_str_int` would create a string key 
with integer value). Most of the type constants expect their 
associated type (e.g. `HS_INT` expects a `lua_Integer`, `HS_CFUNC` expects `
lua_CFunction`, etc.) but `HS_FUNC`, `HS_USER`, and `HS_TBL` expect 
an integer stack index. You cannot use `HS_NIL` or `HS_ANY` with 
this function.

This function returns the stack index of the newly created table, 
and automatically pops any stack indices provided to it.

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

hs_create_table(L,
	// manual struct initialization
	{ HS_STR, .key.string="intVal", HS_INT, .value.integer=4 },
	// macro initialization (recommended)
	hs_str_num("numVal", 6.0f),
	hs_int_tbl(12, hs_create_table(L,
		hs_str_cfunc("subTableCFunc", some_lua_binding),
		hs_int_bool(15, true),
	)
);
```

### 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 operates only on values with string keys 
and boolean, number, or string types. `hs_process_table()` accepts a 
lua_State pointer, a stack index for the table to operate on, and 
then a series of `struct hs_table_processor`s. These can either be 
constructed manually, or via function-like macros. This function 
does not pop the table from the stack.

honeysuckle provides the functions `hs_pt_set_bool`, 
`hs_pt_set_int`, `hs_pt_set_num`, and `hs_pt_set_string` for the 
very common operations of "set a variable to the provided value". 
These can also be set via the function-like macros `hs_set_bool()`, 
`hs_set_int()`, `hs_set_num()`, and `hs_set_str()`.

User-defined functions should have signature 
`void (function)([type], void *)`, where [type] is either 
`lua_Boolean`, `lua_Integer`, `lua_Number`, or `const char *`, 
depending on the expected value type.


```c
void open_logfile(const char *logfile, void *settings) {
	// do something to open and configure the logfile
}

// tbl_index comes from somewhere...

struct settings {
	bool debug;
	lua_Integer verbosity;
	float epsilon;
};

struct settings s;

hs_process_table(L, tbl_index,
	// manual struct initialization
	{ "verbosity", HS_INT, .func.integer=hs_pt_set_int, &(s.verbosity) },
	// macro initialization (recommended)
	hs_process_bool("debug", hs_pt_set_bool, s.debug),
	hs_process_str("logfile", set_logfile, s),
	// alternative to explicitly specifying hs_pt_set_bool
	hs_set_num("epsilon", s.epsilon)
);
```

### 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.


### 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:

* Write initial tests for errors, tracebacks, and function calling
* Implement everything
* 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