1#include "stdarg.h"
2#include "stdbool.h"
3#include "stdio.h"
4#include "stdlib.h"
5#include "string.h"
6#include <prairie/lexer.h>
7#include <prairie/parser.h>
8#include <prairie/utils.h>
9
10void advance(prairie_parser_ctx_t *ctx) {
11 ctx->current_token = ctx->current_token->next;
12}
13
14/**
15 * Returns true if the assertion passes
16 */
17bool assert(prairie_parser_ctx_t *ctx, prairie_token_type_t type) {
18 if (ctx->current_token->type != type) {
19 printf("unexpected token, expected %s, found %s\n",
20 prairie_token_type_to_string(type),
21 prairie_token_to_string(ctx->current_token));
22 return false;
23 }
24 return true;
25}
26
27bool assert_advance(prairie_parser_ctx_t *ctx, prairie_token_type_t type) {
28 if (assert(ctx, type)) {
29 advance(ctx);
30 return true;
31 }
32 return false;
33}
34
35prairie_method_t string_to_method(char *value) {
36 if (strcmp(value, "GET") == 0)
37 return GET;
38 if (strcmp(value, "POST") == 0)
39 return POST;
40
41 // Might want to do some error handling here but default > crash
42 return GET;
43}
44
45prairie_protocol_t string_to_protocol(char *value) {
46 if (strcmp(value, "HTTP/1.0") == 0)
47 return HTTP_1_0;
48 if (strcmp(value, "HTTP/1.1") == 0)
49 return HTTP_1_1;
50 if (strcmp(value, "HTTP/2") == 0)
51 return HTTP_2;
52
53 // Might want to do some error handling here but default > crash
54 return HTTP_1_1;
55}
56
57#define free_and_return \
58 { \
59 printf("Error occured\n"); \
60 free(ctx); \
61 free(request); \
62 return NULL; \
63 }
64
65void prairie_print_request(prairie_request_t *request) {
66 printf("%d %s %d\n", request->method, request->route, request->protocol);
67 prairie_header_t *header = request->header_start;
68 while (header != NULL) {
69 printf("%s: %s\n", header->key, header->value);
70 header = header->next;
71 }
72 printf("%s\n", request->body);
73}
74
75prairie_request_t *prairie_parse_request(prairie_token_t *token_start) {
76 prairie_request_t *request = malloc(sizeof(prairie_request_t));
77 prairie_parser_ctx_t *ctx = malloc(sizeof(prairie_parser_ctx_t));
78 ctx->current_token = token_start;
79 ctx->request = request;
80
81 if (!assert(ctx, PRAIRIE_TOKEN_IDENTIFIER))
82 free_and_return;
83
84 // printf("method: %s\n", ctx->current_token->lexeme);
85 ctx->request->method = string_to_method(ctx->current_token->lexeme);
86 advance(ctx);
87
88 if (!assert(ctx, PRAIRIE_TOKEN_IDENTIFIER))
89 free_and_return;
90
91 // printf("route: %s\n", ctx->current_token->lexeme);
92 ctx->request->route = ctx->current_token->lexeme;
93 advance(ctx);
94
95 if (!assert(ctx, PRAIRIE_TOKEN_IDENTIFIER))
96 free_and_return;
97
98 // printf("protocol: %s\n", ctx->current_token->lexeme);
99 ctx->request->protocol = string_to_protocol(ctx->current_token->lexeme);
100 advance(ctx);
101
102 while (ctx->current_token != NULL) {
103 if (ctx->current_token->type == PRAIRIE_TOKEN_IDENTIFIER) {
104 prairie_header_t *header = malloc(sizeof(prairie_header_t));
105 header->key = ctx->current_token->lexeme;
106 header->next = NULL;
107 advance(ctx);
108
109 if (!assert_advance(ctx, PRAIRIE_TOKEN_COLON))
110 free_and_return;
111
112 header->value = ctx->current_token->lexeme;
113
114 if (ctx->request->header_start == NULL) {
115 ctx->request->header_start = header;
116 ctx->request->header_end = header;
117 } else {
118 ctx->request->header_end->next = header;
119 ctx->request->header_end = header;
120 }
121 advance(ctx);
122 } else if (ctx->current_token->type == PRAIRIE_TOKEN_BODY) {
123 ctx->request->body = ctx->current_token->lexeme;
124 free(ctx);
125
126 return request;
127 } else {
128 printf("unexpected token. expected identifier or body, found %s\n",
129 prairie_token_to_string(ctx->current_token));
130 free_and_return;
131 }
132 }
133
134 free(ctx);
135 return request;
136}