diff --git a/docs/book/cn/expr.md b/docs/book/cn/expr.md index d73cfc4..68fef2c 100644 --- a/docs/book/cn/expr.md +++ b/docs/book/cn/expr.md @@ -37,6 +37,17 @@ else else fi fi + +-- loop +loop condition do + aaa + func(func2()) + if a then + bbb + else + ccc + fi +end ``` diff --git a/docs/book/en/expr.md b/docs/book/en/expr.md index 5f26648..fb34be4 100644 --- a/docs/book/en/expr.md +++ b/docs/book/en/expr.md @@ -37,6 +37,17 @@ else else fi fi + +-- loop +loop condition do + aaa + func(func2()) + if a then + bbb + else + ccc + fi +end ``` diff --git a/src/mln_expr.c b/src/mln_expr.c index b760551..ccb3a80 100644 --- a/src/mln_expr.c +++ b/src/mln_expr.c @@ -14,6 +14,9 @@ static mln_string_t keywords[] = { mln_string("then"), mln_string("else"), mln_string("fi"), + mln_string("loop"), + mln_string("do"), + mln_string("end"), mln_string(NULL) }; @@ -27,6 +30,9 @@ MLN_DEFINE_TOKEN_TYPE_AND_STRUCT(static, \ EXPR_TK_THEN, \ EXPR_TK_ELSE, \ EXPR_TK_FI, \ + EXPR_TK_LOOP, \ + EXPR_TK_DO, \ + EXPR_TK_END, \ EXPR_TK_STRING); MLN_DEFINE_TOKEN(static, \ mln_expr, \ @@ -38,10 +44,15 @@ MLN_DEFINE_TOKEN(static, \ {EXPR_TK_THEN, "EXPR_TK_THEN"}, \ {EXPR_TK_ELSE, "EXPR_TK_ELSE"}, \ {EXPR_TK_FI, "EXPR_TK_FI"}, \ + {EXPR_TK_LOOP, "EXPR_TK_LOOP"}, \ + {EXPR_TK_DO, "EXPR_TK_DO"}, \ + {EXPR_TK_END, "EXPR_TK_END"}, \ {EXPR_TK_STRING, "EXPR_TK_STRING"}); static inline int mln_expr_parse_if(mln_lex_t *lex, mln_expr_cb_t cb, void *data, mln_expr_val_t *ret, int *eof, mln_expr_struct_t **next); +static inline int +mln_expr_parse_loop(mln_lex_t *lex, mln_expr_cb_t cb, void *data, mln_expr_val_t *ret, int *eof, mln_expr_struct_t **next); MLN_FUNC(static inline, int, mln_get_char, (mln_lex_t *lex, char c), (lex, c), { if (c == '\\') { @@ -338,6 +349,10 @@ MLN_FUNC(static inline, int, mln_expr_parse, \ mln_expr_free(name); return mln_expr_parse_if(lex, cb, data, ret, eof, next); } + if (type == EXPR_TK_LOOP) { + mln_expr_free(name); + return mln_expr_parse_loop(lex, cb, data, ret, eof, next); + } if (type != EXPR_TK_ID) { rc = mln_expr_val_init(ret, name); mln_expr_free(name); @@ -572,6 +587,126 @@ MLN_FUNC(static inline, int, mln_expr_parse_if, \ return MLN_EXPR_RET_OK; }) +MLN_FUNC(static inline, int, mln_expr_parse_loop, \ + (mln_lex_t *lex, mln_expr_cb_t cb, void *data, mln_expr_val_t *ret, int *eof, mln_expr_struct_t **next), \ + (lex, cb, data, ret, eof, next), \ +{ + enum mln_expr_enum type; + int rc, is_true = 1, count = 0; + mln_expr_struct_t *tk; + mln_expr_val_t v; + mln_lex_off_t off = mln_lex_snapshot_record(lex); + +begin: + v.type = mln_expr_type_null; + if ((rc = mln_expr_parse(lex, cb, data, &v, eof, next)) != MLN_EXPR_RET_OK) { + mln_expr_val_destroy(&v); + return rc; + } + if (*eof) { + mln_expr_val_destroy(&v); + return MLN_EXPR_RET_ERR; + } + + switch (v.type) { + case mln_expr_type_null: + is_true = 0; + break; + case mln_expr_type_bool: + is_true = v.data.b; + break; + case mln_expr_type_int: + is_true = v.data.i? 1: 0; + break; + case mln_expr_type_real: + is_true = v.data.r == 0.0? 0: 1; + break; + case mln_expr_type_string: + is_true = (v.data.s != NULL && v.data.s->len)? 1: 0; + break; + default: /* mln_expr_type_udata */ + is_true = v.data.u != NULL? 1: 0; + break; + } + mln_expr_val_destroy(&v); + + /* do */ +again: + if (*next != NULL) { + tk = *next; + *next = NULL; + } else { + if ((tk = mln_expr_token(lex)) == NULL) { + return MLN_EXPR_RET_ERR; + } + } + type = tk->type; + mln_expr_free(tk); + if (type == EXPR_TK_SPACE) { + goto again; + } + if (type != EXPR_TK_DO) { + return MLN_EXPR_RET_ERR; + } + + if (is_true) { + while (1) { + if (*next != NULL) { + tk = *next; + *next = NULL; + } else { + if ((tk = mln_expr_token(lex)) == NULL) { + return MLN_EXPR_RET_ERR; + } + } + if (tk->type == EXPR_TK_END) { + break; + } + *next = tk; + v.type = mln_expr_type_null; + if ((rc = mln_expr_parse(lex, cb, data, &v, eof, next)) != MLN_EXPR_RET_OK) { + mln_expr_val_destroy(&v); + return rc; + } + if (*eof) { + mln_expr_val_destroy(&v); + return MLN_EXPR_RET_ERR; + } + + if (ret != NULL) mln_expr_val_destroy(ret); + mln_expr_val_copy(ret, &v); + mln_expr_val_destroy(&v); + } + mln_lex_snapshot_apply(lex, off); + goto begin; + } else { + while (1) { + if (*next != NULL) { + tk = *next; + *next = NULL; + } else { +lp: + if ((tk = mln_expr_token(lex)) == NULL) { + return MLN_EXPR_RET_ERR; + } + } + + type = tk->type; + mln_expr_free(tk); + if (type == EXPR_TK_LOOP) { + ++count; + } else if (type == EXPR_TK_EOF) { + return MLN_EXPR_RET_ERR; + } else if (type == EXPR_TK_END) { + if (count-- == 0) break; + } + goto lp; + } + } + + return MLN_EXPR_RET_OK; +}) + MLN_FUNC(, mln_expr_val_t *, mln_expr_run, (mln_string_t *exp, mln_expr_cb_t cb, void *data), (exp, cb, data), { mln_lex_t *lex = NULL; struct mln_lex_attr lattr;