59407d83913ebc775f4608cbf3dbdcdb0525861f — Simon Ser 5 hours ago fc2631d master
Restrict PSn to parameter expansion

Closes: https://github.com/emersion/mrsh/issues/88
4 files changed, 69 insertions(+), 8 deletions(-)

M include/mrsh/parser.h
M include/parser.h
M parser/word.c
M shell/entry.c
M include/mrsh/parser.h => include/mrsh/parser.h +0 -4
@@ 36,10 36,6 @@ struct mrsh_program *mrsh_parse_program(struct mrsh_parser *parser);
  * Parse a program line. Continuation lines are consumed.
  */
 struct mrsh_program *mrsh_parse_line(struct mrsh_parser *parser);
-/**
- * Parse a single word. Continuation lines are consumed.
- */
-struct mrsh_word *mrsh_parse_word(struct mrsh_parser *parser);
 
 /**
  * Parse an arithmetic expression.

M include/parser.h => include/parser.h +1 -0
@@ 93,5 93,6 @@ struct mrsh_word *expect_dollar(struct mrsh_parser *parser);
 struct mrsh_word *back_quotes(struct mrsh_parser *parser);
 struct mrsh_word *word(struct mrsh_parser *parser, char end);
 struct mrsh_word *arithmetic_word(struct mrsh_parser *parser, char end);
+struct mrsh_word *parameter_expansion_word(struct mrsh_parser *parser);
 
 #endif

M parser/word.c => parser/word.c +66 -3
@@ 834,7 834,70 @@ struct mrsh_word *arithmetic_word(struct mrsh_parser *parser, char end) {
 	}
 }
 
-struct mrsh_word *mrsh_parse_word(struct mrsh_parser *parser) {
-	parser_begin(parser);
-	return word_list(parser, 0, word);
+/**
+ * Parses a word, only recognizing parameter expansion. Quoting and operators
+ * are ignored. */
+struct mrsh_word *parameter_expansion_word(struct mrsh_parser *parser) {
+	struct mrsh_array children = {0};
+	struct mrsh_buffer buf = {0};
+	struct mrsh_position child_begin = {0};
+
+	while (true) {
+		if (!mrsh_position_valid(&child_begin)) {
+			child_begin = parser->pos;
+		}
+
+		char c = parser_peek_char(parser);
+		if (c == '\0') {
+			break;
+		}
+
+		if (c == '$') {
+			push_buffer_word_string(parser, &children, &buf, &child_begin);
+			struct mrsh_word *t = expect_dollar(parser);
+			if (t == NULL) {
+				return NULL;
+			}
+			mrsh_array_add(&children, t);
+			continue;
+		}
+
+		if (c == '`') {
+			push_buffer_word_string(parser, &children, &buf, &child_begin);
+			struct mrsh_word *t = back_quotes(parser);
+			if (t == NULL) {
+				return NULL;
+			}
+			mrsh_array_add(&children, t);
+			continue;
+		}
+
+		if (c == '\\') {
+			// Unquoted backslash
+			parser_read_char(parser);
+			c = parser_peek_char(parser);
+			if (c == '\n') {
+				// Continuation line
+				read_continuation_line(parser);
+				continue;
+			}
+		}
+
+		parser_read_char(parser);
+		mrsh_buffer_append_char(&buf, c);
+	}
+
+	push_buffer_word_string(parser, &children, &buf, &child_begin);
+	mrsh_buffer_finish(&buf);
+
+	consume_symbol(parser);
+
+	if (children.len == 1) {
+		struct mrsh_word *word = children.data[0];
+		mrsh_array_finish(&children); // TODO: don't allocate this array
+		return word;
+	} else {
+		struct mrsh_word_list *wl = mrsh_word_list_create(&children, false);
+		return &wl->word;
+	}
 }

M shell/entry.c => shell/entry.c +2 -1
@@ 9,13 9,14 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "builtin.h"
+#include "parser.h"
 
 static char *expand_str(struct mrsh_state *state, const char *src) {
 	struct mrsh_parser *parser = mrsh_parser_with_data(src, strlen(src));
 	if (parser == NULL) {
 		return NULL;
 	}
-	struct mrsh_word *word = mrsh_parse_word(parser);
+	struct mrsh_word *word = parameter_expansion_word(parser);
 	if (word == NULL) {
 		struct mrsh_position err_pos;
 		const char *err_msg = mrsh_parser_error(parser, &err_pos);