return true;
}
+static void
+macro_tokenize (struct macro *m, struct lexer *lexer)
+{
+ struct state
+ {
+ struct segmenter segmenter;
+ struct substring body;
+ };
+
+ struct state state = {
+ .segmenter = SEGMENTER_INIT (lex_get_syntax_mode (lexer)),
+ .body = m->body,
+ };
+ struct state saved = state;
+
+ struct token token = { .type = T_STOP };
+
+ while (state.body.length > 0)
+ {
+ struct scanner scanner;
+ scanner_init (&scanner, &token);
+
+ for (;;)
+ {
+ enum segment_type type;
+ int seg_len = segmenter_push (&state.segmenter, state.body.string,
+ state.body.length, true, &type);
+ assert (seg_len >= 0);
+
+ struct substring segment = ss_head (state.body, seg_len);
+ ss_advance (&state.body, seg_len);
+
+ enum scan_result result = scanner_push (&scanner, type, segment, &token);
+ if (result == SCAN_SAVE)
+ saved = state;
+ else if (result == SCAN_BACK)
+ {
+ state = saved;
+ break;
+ }
+ else if (result == SCAN_DONE)
+ break;
+ }
+
+ /* We have a token in 'token'. */
+ if (is_scan_type (token.type))
+ {
+ if (token.type != SCAN_SKIP)
+ {
+ /* XXX report error */
+ }
+ }
+ else
+ tokens_add (&m->body_tokens, &token);
+ token_destroy (&token);
+ }
+}
+
int
cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
{
goto error;
size_t allocated_params = 0;
- size_t n_keywords = 0;
while (!lex_match (lexer, T_RPAREN))
{
if (m->n_params >= allocated_params)
m->params = x2nrealloc (m->params, &allocated_params,
sizeof *m->params);
- struct macro_param *p = &m->params[m->n_params++];
+ size_t param_index = m->n_params++;
+ struct macro_param *p = &m->params[param_index];
*p = (struct macro_param) { .expand_arg = true };
/* Parse parameter name. */
if (match_macro_id (lexer, "!POSITIONAL"))
{
- if (n_keywords)
+ if (param_index > 0 && !m->params[param_index - 1].positional)
{
lex_error (lexer, _("Positional parameters must precede "
"keyword parameters."));
goto error;
}
- p->name = NULL;
+
+ p->positional = true;
+ p->name = xasprintf ("!%zu", param_index + 1);
}
else
{
- n_keywords++;
-
if (!lex_force_id (lexer))
goto error;
- p->name = ss_xstrdup (lex_tokss (lexer));
+ p->positional = false;
+ p->name = xasprintf ("!%s", lex_tokcstr (lexer));
lex_get (lexer);
if (!lex_force_match (lexer, T_EQUALS))
}
m->body = ds_ss (&body);
+ macro_tokenize (m, lexer);
+
lex_define_macro (lexer, m);
return CMD_SUCCESS;
#include <stdlib.h>
+#include "data/settings.h"
#include "language/lexer/segment.h"
#include "language/lexer/scan.h"
#include "libpspp/assertion.h"
}
free (m->params);
ss_dealloc (&m->body);
+ tokens_uninit (&m->body_tokens);
free (m);
}
\f
\f
enum me_state
{
- ME_START,
+ /* Error state. */
+ ME_ERROR,
/* Accumulating tokens in me->params toward the end of any type of
argument. */
const struct macro *macro;
struct tokens **args;
- size_t arg_index;
+ const struct macro_param *param;
};
static int
static int
me_next_arg (struct macro_expander *me)
{
- if (me->arg_index >= me->macro->n_params)
+ if (!me->param)
{
assert (!me->macro->n_params);
return me_finished (me);
}
- else if (!me->macro->params[me->arg_index].name)
+ else if (me->param->positional)
{
- me->arg_index++;
- if (me->arg_index >= me->macro->n_params)
+ me->param++;
+ if (me->param >= &me->macro->params[me->macro->n_params])
return me_finished (me);
else
{
- if (!me->macro->params[me->arg_index].name)
- me->state = ME_ARG;
- else
- me->state = ME_KEYWORD;
+ me->state = me->param->positional ? ME_ARG : ME_KEYWORD;
return 0;
}
}
}
}
-static int
-me_add_start (struct macro_expander *me, const struct token *token)
-{
- if (token->type != T_ID && token->type != T_MACRO_ID)
- return -1;
-
- me->macro = macro_set_find (me->macros, token->string.string);
- if (!me->macro)
- return -1;
-
- me->n_tokens = 1;
- me->args = xcalloc (me->macro->n_params, sizeof *me->args);
- me->arg_index = 0;
- return me_next_arg (me);
-}
-
static int
me_error (struct macro_expander *me)
{
- me->state = ME_START;
+ me->state = ME_ERROR;
return -1;
}
static int
me_add_arg (struct macro_expander *me, const struct token *token)
{
- const struct macro_param *p = &me->macro->params[me->arg_index];
if (token->type == T_STOP)
{
- char *param_name = (p->name
- ? xstrdup (p->name)
- : xasprintf ("%zu", me->arg_index));
msg (SE, _("Unexpected end of file reading argument %s "
- "to macro %s."), param_name, me->macro->name);
- free (param_name);
+ "to macro %s."), me->param->name, me->macro->name);
return me_error (me);
}
me->n_tokens++;
- struct tokens **argp = &me->args[me->arg_index];
+ const struct macro_param *p = me->param;
+ struct tokens **argp = &me->args[p - me->macro->params];
if (!*argp)
*argp = xzalloc (sizeof **argp);
struct tokens *arg = *argp;
me_expected (struct macro_expander *me, const struct token *token,
const struct token *wanted)
{
- const struct macro_param *p = &me->macro->params[me->arg_index];
- char *param_name = (p->name
- ? xstrdup (p->name)
- : xasprintf ("%zu", me->arg_index));
char *actual = token_to_string (token);
if (!actual)
actual = xstrdup ("<eof>");
char *expected = token_to_string (wanted);
msg (SE, _("Found `%s' while expecting `%s' reading argument %s "
"to macro %s."),
- actual, expected, param_name, me->macro->name);
+ actual, expected, me->param->name, me->macro->name);
free (expected);
free (actual);
- free (param_name);
return me_error (me);
}
{
me->n_tokens++;
- const struct macro_param *p = &me->macro->params[me->arg_index];
- if (token_equal (&p->enclose[0], token))
+ if (token_equal (&me->param->enclose[0], token))
{
me->state = ME_ARG;
return 0;
}
- return me_expected (me, token, &p->enclose[0]);
+ return me_expected (me, token, &me->param->enclose[0]);
+}
+
+static const struct macro_param *
+macro_find_parameter_by_name (const struct macro *m, struct substring name)
+{
+ for (size_t i = 0; i < m->n_params; i++)
+ {
+ const struct macro_param *p = &m->params[i];
+ struct substring p_name = ss_cstr (p->name);
+ if (!utf8_strncasecmp (p_name.string, p_name.length,
+ name.string, name.length))
+ return p;
+ }
+ return NULL;
}
static int
if (token->type != T_ID)
return me_finished (me);
- for (size_t i = 0; i < me->macro->n_params; i++)
+ const struct macro_param *p = macro_find_parameter_by_name (me->macro,
+ token->string);
+ if (p)
{
- const struct macro_param *p = &me->macro->params[i];
- if (p->name && ss_equals_case (ss_cstr (p->name), token->string))
+ size_t arg_index = p - me->macro->params;
+ me->param = p;
+ if (me->args[arg_index])
{
- me->arg_index = i;
- if (me->args[i])
- {
- msg (SE,
- _("Argument %s multiply specified in call to macro %s."),
- p->name, me->macro->name);
- return me_error (me);
- }
-
- me->n_tokens++;
- me->state = ME_EQUALS;
- return 0;
+ msg (SE,
+ _("Argument %s multiply specified in call to macro %s."),
+ p->name, me->macro->name);
+ return me_error (me);
}
+
+ me->n_tokens++;
+ me->state = ME_EQUALS;
+ return 0;
}
return me_finished (me);
struct macro_expander *me = xmalloc (sizeof *me);
*me = (struct macro_expander) {
.macros = macros,
-
- .state = ME_START,
.n_tokens = 1,
-
.macro = macro,
- .args = xcalloc (macro->n_params, sizeof *me->args),
- .arg_index = 0,
};
*mep = me;
- return me_next_arg (me);
+
+ if (!macro->n_params)
+ return 1;
+ else
+ {
+ me->state = macro->params[0].positional ? ME_ARG : ME_KEYWORD;
+ me->args = xcalloc (macro->n_params, sizeof *me->args);
+ me->param = macro->params;
+ return 0;
+ }
}
void
{
switch (me->state)
{
- case ME_START:
- return me_add_start (me, token);
+ case ME_ERROR:
+ return -1;
case ME_ARG:
return me_add_arg (me, token);
}
}
-void
-macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
+static void
+macro_expand (const struct tokens *tokens, int nesting_countdown,
+ const struct macro_set *macros, const struct macro_expander *me,
+ bool *expand, struct tokens *exp)
{
- struct state
+ if (nesting_countdown <= 0)
{
- struct segmenter segmenter;
- struct substring body;
- };
-
- struct state state;
- segmenter_init (&state.segmenter, SEG_MODE_INTERACTIVE /*XXX*/);
- state.body = me->macro->body;
-
- struct state saved = state;
-
- struct token token = { .type = T_STOP };
+ printf ("maximum nesting level exceeded\n");
+ for (size_t i = 0; i < tokens->n; i++)
+ tokens_add (exp, &tokens->tokens[i]);
+ return;
+ }
- while (state.body.length > 0)
+ for (size_t i = 0; i < tokens->n; i++)
{
- struct scanner scanner;
- scanner_init (&scanner, &token);
+ const struct token *token = &tokens->tokens[i];
+ if (token->type == T_MACRO_ID && me)
+ {
+ const struct macro_param *param = macro_find_parameter_by_name (
+ me->macro, token->string);
+ if (param)
+ {
+ printf ("expand %s to:\n", param->name);
+ const struct tokens *arg = me->args[param - me->macro->params];
+ tokens_print (arg, stdout);
+ if (*expand && param->expand_arg)
+ macro_expand (arg, nesting_countdown, macros, NULL, expand, exp);
+ else
+ for (size_t i = 0; i < arg->n; i++)
+ tokens_add (exp, &arg->tokens[i]);
+ continue;
+ }
+ }
- for (;;)
+ if (*expand)
{
- enum segment_type type;
- int seg_len = segmenter_push (&state.segmenter, state.body.string,
- state.body.length, true, &type);
- assert (seg_len >= 0);
-
- struct substring segment = ss_head (state.body, seg_len);
- ss_advance (&state.body, seg_len);
- printf ("segment \"%.*s\" %s token.type=%d\n", (int) segment.length, segment.string, segment_type_to_string (type), token.type);
-
- enum scan_result result = scanner_push (&scanner, type, segment, &token);
- if (result == SCAN_SAVE)
- saved = state;
- else if (result == SCAN_BACK)
+ struct macro_expander *subme;
+ int retval = macro_expander_create (macros, token, &subme);
+ for (size_t j = 1; !retval; j++)
{
- printf ("back\n");
- state = saved;
- break;
+ static const struct token stop = { .type = T_STOP };
+ retval = macro_expander_add (
+ subme, i + j < tokens->n ? &tokens->tokens[i + j] : &stop);
}
- else if (result == SCAN_DONE)
+ if (retval > 0)
{
- printf ("done\n");
- break;
+ i += retval - 1;
+ macro_expand (&subme->macro->body_tokens, nesting_countdown - 1,
+ macros, subme, expand, exp);
+ macro_expander_destroy (subme);
+ continue;
}
+
+ macro_expander_destroy (subme);
}
- /* We have a token in 'token'. */
- printf ("add token %d %s\n", token.type, token_type_to_name (token.type));
- if (is_scan_type (token.type))
+ if (token->type != T_MACRO_ID)
{
- /* XXX report error if it's not SCAN_SKIP */
+ tokens_add (exp, token);
+ continue;
+ }
+
+ if (ss_equals_case (token->string, ss_cstr ("!onexpand")))
+ *expand = true;
+ else if (ss_equals_case (token->string, ss_cstr ("!offexpand")))
+ *expand = false;
+ else if (ss_equals_case (token->string, ss_cstr ("!length")))
+ {
+ if (i + 1 >= tokens->n || tokens->tokens[i + 1].type != T_LPAREN)
+ {
+ printf ("`(' expected following !LENGTH'\n");
+ continue;
+ }
+
+ int n_parens = 1;
+ size_t j;
+ for (j = i + 2; n_parens && j < tokens->n; j++)
+ if (tokens->tokens[j].type == T_LPAREN)
+ n_parens++;
+ else if (tokens->tokens[j].type == T_RPAREN)
+ n_parens--;
+ if (n_parens)
+ {
+ printf ("Unbalanced parentheses in !LENGTH argument.\n");
+ continue;
+ }
+
+ size_t lparen_idx = i + 1;
+ size_t rparen_idx = j - 1;
+ const struct tokens unexpanded_args = {
+ .tokens = &tokens->tokens[lparen_idx + 1],
+ .n = rparen_idx - (lparen_idx + 1),
+ };
+ struct tokens args = { .n = 0 };
+ macro_expand (&unexpanded_args, nesting_countdown, macros,
+ me, expand, &args);
+
+ if (args.n != 1)
+ {
+ tokens_uninit (&args);
+ printf ("!LENGTH argument must be a single token (not %zu)\n", args.n);
+ continue;
+ }
+
+ char *s = token_to_string (&args.tokens[0]);
+ struct token t = { .type = T_POS_NUM, .number = strlen (s) };
+ tokens_add (exp, &t);
+ free (s);
+
+ tokens_uninit (&args);
+
+ i = rparen_idx;
}
else
- tokens_add (exp, &token);
- token_destroy (&token);
+ tokens_add (exp, token);
}
}
+
+
+void
+macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
+{
+ for (size_t i = 0; i < me->macro->n_params; i++)
+ {
+ printf ("%s:\n", me->macro->params[i].name);
+ tokens_print (me->args[i], stdout);
+ }
+
+ bool expand = true;
+ macro_expand (&me->macro->body_tokens, settings_get_mnest (),
+ me->macros, me, &expand, exp);
+
+ printf ("expansion:\n");
+ tokens_print (exp, stdout);
+}
+