-
Notifications
You must be signed in to change notification settings - Fork 0
/
base64_sem2.c
179 lines (147 loc) · 5.61 KB
/
base64_sem2.c
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
// Example parser: Base64, with fine-grained semantic actions
//
// Demonstrates how to attach semantic actions to a grammar and transform the
// parse tree into the desired semantic representation, in this case a sequence
// of 8-bit values.
//
// Note how the grammar is defined by using the macros H_RULE and H_ARULE.
// Those rules using ARULE get an attached action which must be declared (as
// a function of type HAction) with a standard name based on the rule name.
//
// This variant of the example uses coarse-grained semantic actions,
// transforming the entire parse tree in one big step. Compare base64_sem1.c
// for an alternative approach using a fine-grained piece-by-piece
// transformation.
#include <hammer/hammer.h>
#include <hammer/glue.h>
#include <assert.h>
#include <inttypes.h>
///
// Semantic actions for the grammar below, each corresponds to an "ARULE".
// They must be named act_<rulename>.
///
// helper: return the numeric value of a parsed base64 digit
uint8_t bsfdig_value(const HParsedToken *p)
{
uint8_t value = 0;
if(p && p->token_type == TT_UINT) {
uint8_t c = p->uint;
if(c >= 0x40 && c <= 0x5A) // A-Z
value = c - 0x41;
else if(c >= 0x60 && c <= 0x7A) // a-z
value = c - 0x61 + 26;
else if(c >= 0x30 && c <= 0x39) // 0-9
value = c - 0x30 + 52;
else if(c == '+')
value = 62;
else if(c == '/')
value = 63;
}
return value;
}
// helper: append a byte value to a sequence
#define seq_append_byte(res, b) h_seq_snoc(res, H_MAKE_UINT(b))
HParsedToken *act_base64(const HParseResult *p, void* user_data)
{
assert(p->ast->token_type == TT_SEQUENCE);
assert(p->ast->seq->used == 2);
assert(p->ast->seq->elements[0]->token_type == TT_SEQUENCE);
// grab b64_3 block sequence
// grab and analyze b64 end block (_2 or _1)
const HParsedToken *b64_3 = p->ast->seq->elements[0];
const HParsedToken *b64_2 = p->ast->seq->elements[1];
const HParsedToken *b64_1 = p->ast->seq->elements[1];
if(b64_2->token_type != TT_SEQUENCE)
b64_1 = b64_2 = NULL;
else if(b64_2->seq->elements[2]->uint == '=')
b64_2 = NULL;
else
b64_1 = NULL;
// allocate result sequence
HParsedToken *res = H_MAKE_SEQ();
// concatenate base64_3 blocks
for(size_t i=0; i<b64_3->seq->used; i++) {
assert(b64_3->seq->elements[i]->token_type == TT_SEQUENCE);
HParsedToken **digits = b64_3->seq->elements[i]->seq->elements;
uint32_t x = bsfdig_value(digits[0]);
x <<= 6; x |= bsfdig_value(digits[1]);
x <<= 6; x |= bsfdig_value(digits[2]);
x <<= 6; x |= bsfdig_value(digits[3]);
seq_append_byte(res, (x >> 16) & 0xFF);
seq_append_byte(res, (x >> 8) & 0xFF);
seq_append_byte(res, x & 0xFF);
}
// append one trailing base64_2 or _1 block
if(b64_2) {
HParsedToken **digits = b64_2->seq->elements;
uint32_t x = bsfdig_value(digits[0]);
x <<= 6; x |= bsfdig_value(digits[1]);
x <<= 6; x |= bsfdig_value(digits[2]);
seq_append_byte(res, (x >> 10) & 0xFF);
seq_append_byte(res, (x >> 2) & 0xFF);
} else if(b64_1) {
HParsedToken **digits = b64_1->seq->elements;
uint32_t x = bsfdig_value(digits[0]);
x <<= 6; x |= bsfdig_value(digits[1]);
seq_append_byte(res, (x >> 4) & 0xFF);
}
return res;
}
H_ACT_APPLY(act_index0, h_act_index, 0);
#define act_ws h_act_ignore
#define act_document act_index0
///
// Set up the parser with the grammar to be recognized.
///
const HParser *init_parser(void)
{
// CORE
H_RULE (digit, h_ch_range(0x30, 0x39));
H_RULE (alpha, h_choice(h_ch_range(0x41, 0x5a), h_ch_range(0x61, 0x7a), NULL));
H_RULE (space, h_in((uint8_t *)" \t\n\r\f\v", 6));
// AUX.
H_RULE (plus, h_ch('+'));
H_RULE (slash, h_ch('/'));
H_RULE (equals, h_ch('='));
H_RULE (bsfdig, h_choice(alpha, digit, plus, slash, NULL));
H_RULE (bsfdig_4bit, h_in((uint8_t *)"AEIMQUYcgkosw048", 16));
H_RULE (bsfdig_2bit, h_in((uint8_t *)"AQgw", 4));
H_RULE (base64_3, h_repeat_n(bsfdig, 4));
H_RULE (base64_2, h_sequence(bsfdig, bsfdig, bsfdig_4bit, equals, NULL));
H_RULE (base64_1, h_sequence(bsfdig, bsfdig_2bit, equals, equals, NULL));
H_ARULE(base64, h_sequence(h_many(base64_3),
h_optional(h_choice(base64_2,
base64_1, NULL)),
NULL));
H_ARULE(ws, h_many(space));
H_ARULE(document, h_sequence(ws, base64, ws, h_end_p(), NULL));
// BUG sometimes inputs that should just don't parse.
// It *seemed* to happen mostly with things like "bbbbaaaaBA==".
// Using less actions seemed to make it less likely.
if(h_compile(document,PB_LALR,NULL))
fprintf(stderr, "Cannot compile to LALR\n");
return document;
}
///
// Main routine: print input, parse, print result, return success/failure.
///
#include <stdio.h>
int main(int argc, char **argv)
{
uint8_t input[102400];
size_t inputsize;
const HParser *parser;
const HParseResult *result;
parser = init_parser();
inputsize = fread(input, 1, sizeof(input), stdin);
fprintf(stderr, "inputsize=%zu\ninput=", inputsize);
fwrite(input, 1, inputsize, stderr);
result = h_parse(parser, input, inputsize);
if(result) {
fprintf(stderr, "parsed=%" PRId64 " bytes\n", result->bit_length/8);
h_pprint(stdout, result->ast, 0, 0);
return 0;
} else {
return 1;
}
}