summaryrefslogtreecommitdiffhomepage
path: root/src/tokscan/tbnf_scan_token.c
blob: b5469591181f618174cc7a421b0cc7636cf52412 (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
/**************************************************************/
/*  treebnf: a tree oriented bnf library                      */
/*  Copyright (C) 2024  SysDeer Technologies, LLC             */
/*  Released under GPLv2 and GPLv3; see COPYING.TREEBNF.      */
/**************************************************************/

#include <treebnf/treebnf.h>

#define TBNF_STATE_STACK_SIZE   (512)

/* single token, read-only context */
int tbnf_scan_token(const struct tbnf_scan_ctx * sctx, struct tbnf_token * tok)
{
	int ret  = 0;
	int len  = 0;
	int type = 0;

	int tidx = 0;
	int sidx = sctx->tok_scan_state;

	for (; tidx < sctx->tok_scan_nents; ) {
		if (sctx->tok_scan_tbls[sidx][tidx].tok_scan_fn)
			ret = sctx->tok_scan_tbls[sidx][tidx].tok_scan_fn(sctx);

		if (ret > len) {
			len  = ret;
			type = tidx;
		}

		tidx++;
	}

	tok->tok_type = type;
	tok->tok_len  = len;
	tok->tok_off  = sctx->tok_scan_mark - sctx->tok_scan_base;

	return (len > 0) ? 0 : -1;
}

/* scan up to ntoks tokens, read-write context */
int tbnf_scan_tokens(struct tbnf_scan_ctx * sctx, size_t ntoks, struct tbnf_token * tokv, int any)
{
	int                     ret;
	int *                   state;
	int *                   stcap;
	int                     ststk[TBNF_STATE_STACK_SIZE];
	struct tbnf_scan_tbl *  pentry;

	ret      = 0;
	ntoks    = (ntoks > INT32_MAX) ? INT32_MAX : ntoks;

	state    = ststk;
	state[0] = sctx->tok_scan_state;

	stcap    = &state[TBNF_STATE_STACK_SIZE];
	stcap--;

	/*******************************************************************/
	/* a positive return value that's smaller than the original ntoks, */
	/* in combination with mark < cap, indicates an error while trying */
	/* to obtain the next token.                                       */
	/*******************************************************************/

	for (; ntoks && (sctx->tok_scan_mark < sctx->tok_scan_cap); ) {
		if (tbnf_scan_token(sctx,tokv) < 0)
			return (ret > 0) ? ret : (-1);

		pentry = &sctx->tok_scan_tbls[*state][tokv->tok_type];

		switch (pentry->tok_state_op) {
			case TBNF_STATE_POP:
				if (state == ststk)
					return (-1);

				state--;
				sctx->tok_scan_state = *state;
				break;

			case TBNF_STATE_KEEP:
				break;

			case TBNF_STATE_PUSH:
				if (state == stcap)
					return (-1);

				sctx->tok_scan_state = pentry->tok_state_next;
				*++state             = sctx->tok_scan_state;
				break;
		}

		sctx->tok_scan_type  = tokv->tok_type;
		sctx->tok_scan_mark += tokv->tok_len;

		tokv->tok_any = any;
		tokv++;

		ntoks--;
		ret++;
	}

	return ret;
}