#!/bin/sh set -eu base="${TMPDIR-/tmp}/mcpu-cpp-ucs2-identifiers-$$" mkdir -p "$base" trap 'rm -rf "$base"' EXIT HUP INT TERM # The source file is UTF-8 externally. mcpu-cpp decodes it to strict UCS-2, # and identifier classification is then performed with the locale-independent # Unicode XID_Start/XID_Continue predicates provided by LibMPUIO. cat > "$base/main.c" <<'EOT' #define АНДРЕЙ 11 #define résumé 12 #define Élève 13 #define ΩΜΕΓΑ 14 #define переменная2 15 #define a١ 16 #define é 17 #define СУММА(лево, право) лево + право #define СТРОКА(имя) #имя #define СКЛЕИТЬ(лево, право) лево ## право #define АНДРЕЙ2 18 #define ВРЕМЕННЫЙ 19 #undef ВРЕМЕННЫЙ #define VALUE$OLD 20 #define ДОЛЛАР$ИМЯ 21 #define DOLLAR_PARAM(x$old) x$old #define CAT_DOLLAR(a,b) a ## b #define LEFT$RIGHT 22 АНДРЕЙ résumé Élève ΩΜΕΓΑ переменная2 a١ é СУММА(АНДРЕЙ, résumé) СТРОКА(АНДРЕЙ) СКЛЕИТЬ(АНДР, ЕЙ) СКЛЕИТЬ(АНДРЕЙ, 2) андрей ВРЕМЕННЫЙ VALUE$OLD ДОЛЛАР$ИМЯ DOLLAR_PARAM(23) CAT_DOLLAR(LEFT$, RIGHT) #ifdef АНДРЕЙ ifdef_ok #endif #ifndef ВРЕМЕННЫЙ ifndef_ok #endif #if defined(résumé) && defined(ΩΜΕΓΑ) defined_ok #endif #ifdef VALUE$OLD dollar_ifdef_ok #endif #if defined(ДОЛЛАР$ИМЯ) dollar_defined_ok #endif EOT "$MCPU_CPP" --no-config "$base/main.c" -o "$base/out" grep -Fx '11' "$base/out" >/dev/null grep -Fx '12' "$base/out" >/dev/null grep -Fx '13' "$base/out" >/dev/null grep -Fx '14' "$base/out" >/dev/null grep -Fx '15' "$base/out" >/dev/null grep -Fx '16' "$base/out" >/dev/null grep -Fx '17' "$base/out" >/dev/null grep -Fx '11 + 12' "$base/out" >/dev/null grep -Fx '"АНДРЕЙ"' "$base/out" >/dev/null # Raw operands of ## are concatenated, then the result is rescanned. grep -Fx '11' "$base/out" >/dev/null grep -Fx '18' "$base/out" >/dev/null grep -Fx 'андрей' "$base/out" >/dev/null grep -Fx 'ВРЕМЕННЫЙ' "$base/out" >/dev/null grep -Fx 'ifdef_ok' "$base/out" >/dev/null grep -Fx 'ifndef_ok' "$base/out" >/dev/null grep -Fx 'defined_ok' "$base/out" >/dev/null grep -Fx '20' "$base/out" >/dev/null grep -Fx '21' "$base/out" >/dev/null grep -Fx '23' "$base/out" >/dev/null grep -Fx '22' "$base/out" >/dev/null grep -Fx 'dollar_ifdef_ok' "$base/out" >/dev/null grep -Fx 'dollar_defined_ok' "$base/out" >/dev/null # XID_Continue admits combining marks after an identifier start. The same # combining mark is not XID_Start and therefore cannot begin a macro name. printf '#define \314\201bad 1\n' > "$base/bad-start.c" if "$MCPU_CPP" --no-config "$base/bad-start.c" -o "$base/bad-start.out" 2>"$base/bad-start.err"; then echo "combining mark accepted as identifier start" >&2 exit 1 fi grep 'macro name expected after #define' "$base/bad-start.err" >/dev/null # A decimal digit is XID_Continue but not XID_Start. Non-ASCII digits are # therefore legal after the first character but cannot start an identifier. printf '#define \331\241bad 1\n' > "$base/bad-digit-start.c" if "$MCPU_CPP" --no-config "$base/bad-digit-start.c" -o "$base/bad-digit-start.out" 2>"$base/bad-digit-start.err"; then echo "non-ASCII digit accepted as identifier start" >&2 exit 1 fi grep 'macro name expected after #define' "$base/bad-digit-start.err" >/dev/null # Dollar is an identifier-continuation extension, never an identifier start. printf '#define $BAD 1\n' > "$base/bad-dollar-start.c" if "$MCPU_CPP" --no-config "$base/bad-dollar-start.c" -o "$base/bad-dollar-start.out" 2>"$base/bad-dollar-start.err"; then echo "dollar accepted as identifier start" >&2 exit 1 fi grep 'macro name expected after #define' "$base/bad-dollar-start.err" >/dev/null