Skip to content

Commit f09db18

Browse files
committed
Allow block exits in loop predicates
1 parent 8e7d126 commit f09db18

File tree

4 files changed

+167
-232
lines changed

4 files changed

+167
-232
lines changed

include/prism/parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ typedef enum {
364364
/** a rescue statement within a lambda expression */
365365
PM_CONTEXT_LAMBDA_RESCUE,
366366

367+
/** the predicate clause of a loop statement */
368+
PM_CONTEXT_LOOP_PREDICATE,
369+
367370
/** the top level context */
368371
PM_CONTEXT_MAIN,
369372

src/prism.c

Lines changed: 59 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -14,180 +14,6 @@ pm_version(void) {
1414
*/
1515
#define PM_TAB_WHITESPACE_SIZE 8
1616

17-
#ifndef PM_DEBUG_LOGGING
18-
/**
19-
* Debugging logging will provide you with additional debugging functions as
20-
* well as automatically replace some functions with their debugging
21-
* counterparts.
22-
*/
23-
#define PM_DEBUG_LOGGING 0
24-
#endif
25-
26-
#if PM_DEBUG_LOGGING
27-
28-
/******************************************************************************/
29-
/* Debugging */
30-
/******************************************************************************/
31-
32-
PRISM_ATTRIBUTE_UNUSED static const char *
33-
debug_context(pm_context_t context) {
34-
switch (context) {
35-
case PM_CONTEXT_BEGIN: return "BEGIN";
36-
case PM_CONTEXT_BEGIN_ENSURE: return "BEGIN_ENSURE";
37-
case PM_CONTEXT_BEGIN_ELSE: return "BEGIN_ELSE";
38-
case PM_CONTEXT_BEGIN_RESCUE: return "BEGIN_RESCUE";
39-
case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES";
40-
case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS";
41-
case PM_CONTEXT_BLOCK_ENSURE: return "BLOCK_ENSURE";
42-
case PM_CONTEXT_BLOCK_ELSE: return "BLOCK_ELSE";
43-
case PM_CONTEXT_BLOCK_RESCUE: return "BLOCK_RESCUE";
44-
case PM_CONTEXT_CASE_IN: return "CASE_IN";
45-
case PM_CONTEXT_CASE_WHEN: return "CASE_WHEN";
46-
case PM_CONTEXT_CLASS: return "CLASS";
47-
case PM_CONTEXT_CLASS_ELSE: return "CLASS_ELSE";
48-
case PM_CONTEXT_CLASS_ENSURE: return "CLASS_ENSURE";
49-
case PM_CONTEXT_CLASS_RESCUE: return "CLASS_RESCUE";
50-
case PM_CONTEXT_DEF: return "DEF";
51-
case PM_CONTEXT_DEF_PARAMS: return "DEF_PARAMS";
52-
case PM_CONTEXT_DEF_ENSURE: return "DEF_ENSURE";
53-
case PM_CONTEXT_DEF_ELSE: return "DEF_ELSE";
54-
case PM_CONTEXT_DEF_RESCUE: return "DEF_RESCUE";
55-
case PM_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS";
56-
case PM_CONTEXT_DEFINED: return "DEFINED";
57-
case PM_CONTEXT_ELSE: return "ELSE";
58-
case PM_CONTEXT_ELSIF: return "ELSIF";
59-
case PM_CONTEXT_EMBEXPR: return "EMBEXPR";
60-
case PM_CONTEXT_FOR_INDEX: return "FOR_INDEX";
61-
case PM_CONTEXT_FOR: return "FOR";
62-
case PM_CONTEXT_IF: return "IF";
63-
case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES";
64-
case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END";
65-
case PM_CONTEXT_LAMBDA_ENSURE: return "LAMBDA_ENSURE";
66-
case PM_CONTEXT_LAMBDA_ELSE: return "LAMBDA_ELSE";
67-
case PM_CONTEXT_LAMBDA_RESCUE: return "LAMBDA_RESCUE";
68-
case PM_CONTEXT_MAIN: return "MAIN";
69-
case PM_CONTEXT_MODULE: return "MODULE";
70-
case PM_CONTEXT_MODULE_ELSE: return "MODULE_ELSE";
71-
case PM_CONTEXT_MODULE_ENSURE: return "MODULE_ENSURE";
72-
case PM_CONTEXT_MODULE_RESCUE: return "MODULE_RESCUE";
73-
case PM_CONTEXT_NONE: return "NONE";
74-
case PM_CONTEXT_PARENS: return "PARENS";
75-
case PM_CONTEXT_POSTEXE: return "POSTEXE";
76-
case PM_CONTEXT_PREDICATE: return "PREDICATE";
77-
case PM_CONTEXT_PREEXE: return "PREEXE";
78-
case PM_CONTEXT_RESCUE_MODIFIER: return "RESCUE_MODIFIER";
79-
case PM_CONTEXT_SCLASS: return "SCLASS";
80-
case PM_CONTEXT_SCLASS_ENSURE: return "SCLASS_ENSURE";
81-
case PM_CONTEXT_SCLASS_ELSE: return "SCLASS_ELSE";
82-
case PM_CONTEXT_SCLASS_RESCUE: return "SCLASS_RESCUE";
83-
case PM_CONTEXT_TERNARY: return "TERNARY";
84-
case PM_CONTEXT_UNLESS: return "UNLESS";
85-
case PM_CONTEXT_UNTIL: return "UNTIL";
86-
case PM_CONTEXT_WHILE: return "WHILE";
87-
}
88-
return NULL;
89-
}
90-
91-
PRISM_ATTRIBUTE_UNUSED static void
92-
debug_contexts(pm_parser_t *parser) {
93-
pm_context_node_t *context_node = parser->current_context;
94-
fprintf(stderr, "CONTEXTS: ");
95-
96-
if (context_node != NULL) {
97-
while (context_node != NULL) {
98-
fprintf(stderr, "%s", debug_context(context_node->context));
99-
context_node = context_node->prev;
100-
if (context_node != NULL) {
101-
fprintf(stderr, " <- ");
102-
}
103-
}
104-
} else {
105-
fprintf(stderr, "NONE");
106-
}
107-
108-
fprintf(stderr, "\n");
109-
}
110-
111-
PRISM_ATTRIBUTE_UNUSED static void
112-
debug_node(const pm_parser_t *parser, const pm_node_t *node) {
113-
pm_buffer_t output_buffer = { 0 };
114-
pm_prettyprint(&output_buffer, parser, node);
115-
116-
fprintf(stderr, "%.*s", (int) output_buffer.length, output_buffer.value);
117-
pm_buffer_free(&output_buffer);
118-
}
119-
120-
PRISM_ATTRIBUTE_UNUSED static void
121-
debug_lex_mode(pm_parser_t *parser) {
122-
pm_lex_mode_t *lex_mode = parser->lex_modes.current;
123-
bool first = true;
124-
125-
while (lex_mode != NULL) {
126-
if (first) {
127-
first = false;
128-
} else {
129-
fprintf(stderr, " <- ");
130-
}
131-
132-
switch (lex_mode->mode) {
133-
case PM_LEX_DEFAULT: fprintf(stderr, "DEFAULT"); break;
134-
case PM_LEX_EMBEXPR: fprintf(stderr, "EMBEXPR"); break;
135-
case PM_LEX_EMBVAR: fprintf(stderr, "EMBVAR"); break;
136-
case PM_LEX_HEREDOC: fprintf(stderr, "HEREDOC"); break;
137-
case PM_LEX_LIST: fprintf(stderr, "LIST (terminator=%c, interpolation=%d)", lex_mode->as.list.terminator, lex_mode->as.list.interpolation); break;
138-
case PM_LEX_REGEXP: fprintf(stderr, "REGEXP (terminator=%c)", lex_mode->as.regexp.terminator); break;
139-
case PM_LEX_STRING: fprintf(stderr, "STRING (terminator=%c, interpolation=%d)", lex_mode->as.string.terminator, lex_mode->as.string.interpolation); break;
140-
}
141-
142-
lex_mode = lex_mode->prev;
143-
}
144-
145-
fprintf(stderr, "\n");
146-
}
147-
148-
PRISM_ATTRIBUTE_UNUSED static void
149-
debug_state(pm_parser_t *parser) {
150-
fprintf(stderr, "STATE: ");
151-
bool first = true;
152-
153-
if (parser->lex_state == PM_LEX_STATE_NONE) {
154-
fprintf(stderr, "NONE\n");
155-
return;
156-
}
157-
158-
#define CHECK_STATE(state) \
159-
if (parser->lex_state & state) { \
160-
if (!first) fprintf(stderr, "|"); \
161-
fprintf(stderr, "%s", #state); \
162-
first = false; \
163-
}
164-
165-
CHECK_STATE(PM_LEX_STATE_BEG)
166-
CHECK_STATE(PM_LEX_STATE_END)
167-
CHECK_STATE(PM_LEX_STATE_ENDARG)
168-
CHECK_STATE(PM_LEX_STATE_ENDFN)
169-
CHECK_STATE(PM_LEX_STATE_ARG)
170-
CHECK_STATE(PM_LEX_STATE_CMDARG)
171-
CHECK_STATE(PM_LEX_STATE_MID)
172-
CHECK_STATE(PM_LEX_STATE_FNAME)
173-
CHECK_STATE(PM_LEX_STATE_DOT)
174-
CHECK_STATE(PM_LEX_STATE_CLASS)
175-
CHECK_STATE(PM_LEX_STATE_LABEL)
176-
CHECK_STATE(PM_LEX_STATE_LABELED)
177-
CHECK_STATE(PM_LEX_STATE_FITEM)
178-
179-
#undef CHECK_STATE
180-
181-
fprintf(stderr, "\n");
182-
}
183-
184-
PRISM_ATTRIBUTE_UNUSED static void
185-
debug_token(pm_token_t * token) {
186-
fprintf(stderr, "%s: \"%.*s\"\n", pm_token_type_human(token->type), (int) (token->end - token->start), token->start);
187-
}
188-
189-
#endif
190-
19117
// Macros for min/max.
19218
#define MIN(a,b) (((a)<(b))?(a):(b))
19319
#define MAX(a,b) (((a)>(b))?(a):(b))
@@ -491,8 +317,52 @@ lex_state_set(pm_parser_t *parser, pm_lex_state_t state) {
491317
parser->lex_state = state;
492318
}
493319

320+
#ifndef PM_DEBUG_LOGGING
321+
/**
322+
* Debugging logging will print additional information to stdout whenever the
323+
* lexer state changes.
324+
*/
325+
#define PM_DEBUG_LOGGING 0
326+
#endif
327+
494328
#if PM_DEBUG_LOGGING
495-
static inline void
329+
PRISM_ATTRIBUTE_UNUSED static void
330+
debug_state(pm_parser_t *parser) {
331+
fprintf(stderr, "STATE: ");
332+
bool first = true;
333+
334+
if (parser->lex_state == PM_LEX_STATE_NONE) {
335+
fprintf(stderr, "NONE\n");
336+
return;
337+
}
338+
339+
#define CHECK_STATE(state) \
340+
if (parser->lex_state & state) { \
341+
if (!first) fprintf(stderr, "|"); \
342+
fprintf(stderr, "%s", #state); \
343+
first = false; \
344+
}
345+
346+
CHECK_STATE(PM_LEX_STATE_BEG)
347+
CHECK_STATE(PM_LEX_STATE_END)
348+
CHECK_STATE(PM_LEX_STATE_ENDARG)
349+
CHECK_STATE(PM_LEX_STATE_ENDFN)
350+
CHECK_STATE(PM_LEX_STATE_ARG)
351+
CHECK_STATE(PM_LEX_STATE_CMDARG)
352+
CHECK_STATE(PM_LEX_STATE_MID)
353+
CHECK_STATE(PM_LEX_STATE_FNAME)
354+
CHECK_STATE(PM_LEX_STATE_DOT)
355+
CHECK_STATE(PM_LEX_STATE_CLASS)
356+
CHECK_STATE(PM_LEX_STATE_LABEL)
357+
CHECK_STATE(PM_LEX_STATE_LABELED)
358+
CHECK_STATE(PM_LEX_STATE_FITEM)
359+
360+
#undef CHECK_STATE
361+
362+
fprintf(stderr, "\n");
363+
}
364+
365+
static void
496366
debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) {
497367
fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number);
498368
debug_state(parser);
@@ -8492,6 +8362,8 @@ context_terminator(pm_context_t context, pm_token_t *token) {
84928362
case PM_CONTEXT_MODULE_ENSURE:
84938363
case PM_CONTEXT_SCLASS_ENSURE:
84948364
return token->type == PM_TOKEN_KEYWORD_END;
8365+
case PM_CONTEXT_LOOP_PREDICATE:
8366+
return token->type == PM_TOKEN_KEYWORD_DO || token->type == PM_TOKEN_KEYWORD_THEN;
84958367
case PM_CONTEXT_FOR_INDEX:
84968368
return token->type == PM_TOKEN_KEYWORD_IN;
84978369
case PM_CONTEXT_CASE_WHEN:
@@ -8664,6 +8536,7 @@ context_human(pm_context_t context) {
86648536
case PM_CONTEXT_IF: return "if statement";
86658537
case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
86668538
case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8539+
case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
86678540
case PM_CONTEXT_MAIN: return "top level context";
86688541
case PM_CONTEXT_MODULE: return "module definition";
86698542
case PM_CONTEXT_PARENS: return "parentheses";
@@ -15170,6 +15043,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
1517015043
case PM_CONTEXT_LAMBDA_ELSE:
1517115044
case PM_CONTEXT_LAMBDA_ENSURE:
1517215045
case PM_CONTEXT_LAMBDA_RESCUE:
15046+
case PM_CONTEXT_LOOP_PREDICATE:
1517315047
case PM_CONTEXT_POSTEXE:
1517415048
case PM_CONTEXT_UNTIL:
1517515049
case PM_CONTEXT_WHILE:
@@ -17318,6 +17192,7 @@ parse_retry(pm_parser_t *parser, const pm_node_t *node) {
1731817192
case PM_CONTEXT_IF:
1731917193
case PM_CONTEXT_LAMBDA_BRACES:
1732017194
case PM_CONTEXT_LAMBDA_DO_END:
17195+
case PM_CONTEXT_LOOP_PREDICATE:
1732117196
case PM_CONTEXT_PARENS:
1732217197
case PM_CONTEXT_POSTEXE:
1732317198
case PM_CONTEXT_PREDICATE:
@@ -17396,6 +17271,7 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) {
1739617271
case PM_CONTEXT_LAMBDA_ELSE:
1739717272
case PM_CONTEXT_LAMBDA_ENSURE:
1739817273
case PM_CONTEXT_LAMBDA_RESCUE:
17274+
case PM_CONTEXT_LOOP_PREDICATE:
1739917275
case PM_CONTEXT_PARENS:
1740017276
case PM_CONTEXT_POSTEXE:
1740117277
case PM_CONTEXT_PREDICATE:
@@ -19146,12 +19022,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1914619022
parser_lex(parser);
1914719023
return (pm_node_t *) pm_true_node_create(parser, &parser->previous);
1914819024
case PM_TOKEN_KEYWORD_UNTIL: {
19025+
context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
1914919026
pm_do_loop_stack_push(parser, true);
19027+
1915019028
parser_lex(parser);
1915119029
pm_token_t keyword = parser->previous;
19152-
1915319030
pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
19031+
1915419032
pm_do_loop_stack_pop(parser);
19033+
context_pop(parser);
1915519034

1915619035
expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
1915719036
pm_statements_node_t *statements = NULL;
@@ -19167,12 +19046,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1916719046
return (pm_node_t *) pm_until_node_create(parser, &keyword, &parser->previous, predicate, statements, 0);
1916819047
}
1916919048
case PM_TOKEN_KEYWORD_WHILE: {
19049+
context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
1917019050
pm_do_loop_stack_push(parser, true);
19051+
1917119052
parser_lex(parser);
1917219053
pm_token_t keyword = parser->previous;
19173-
1917419054
pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
19055+
1917519056
pm_do_loop_stack_pop(parser);
19057+
context_pop(parser);
1917619058

1917719059
expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
1917819060
pm_statements_node_t *statements = NULL;

test/prism/fixtures/break.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@ tap { break(1) }
2323
foo { break 42 } == 42
2424

2525
foo { |a| break } == 42
26+
27+
while _ && break; end
28+
29+
until _ && break; end

0 commit comments

Comments
 (0)