From d3e85e80fd192d3fb19fa207bd6ce158a012b45a Mon Sep 17 00:00:00 2001
From: seyko <seyko2@gmail.com>
Date: Tue, 5 Apr 2016 10:43:50 +0300
Subject: [PATCH] Identifiers can start and/or contain '.' in *.S

    modified version of the old one which don't allow '.'
    in #define Identifiers. This allow correctly preprocess
    the following code in *.S

        #define SRC(y...)               \
        9999: y;                        \
        .section __ex_table, "a";       \
        .long 9999b, 6001f      ;       \
        // .previous

        SRC(1: movw (%esi), %bx)
        6001:

    A test included.
---
 tcc.h              |  4 ++-
 tccasm.c           | 87 ++++++++++++++++++++++++----------------------
 tccpp.c            | 12 +++++--
 tcctok.h           | 57 ++++++++++++++++--------------
 tests/Makefile     |  6 +++-
 tests/pp/12.S      |  8 +++++
 tests/pp/12.expect |  2 ++
 tests/pp/Makefile  | 12 ++++++-
 8 files changed, 115 insertions(+), 73 deletions(-)
 create mode 100644 tests/pp/12.S
 create mode 100644 tests/pp/12.expect

diff --git a/tcc.h b/tcc.h
index 0200207b..2e1e32fd 100644
--- a/tcc.h
+++ b/tcc.h
@@ -957,7 +957,9 @@ struct TCCState {
 
 #define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
 #define TOK_ASM_int TOK_INT
-#define TOK_ASM_weak TOK_WEAK1
+#define DEF_ASMDIR(x) DEF(TOK_ASMDIR_ ## x, "." #x)
+#define TOK_ASMDIR_FIRST TOK_ASMDIR_byte
+#define TOK_ASMDIR_LAST TOK_ASMDIR_section
 
 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 /* only used for i386 asm opcodes definitions */
diff --git a/tccasm.c b/tccasm.c
index 28e07fda..fade2d10 100644
--- a/tccasm.c
+++ b/tccasm.c
@@ -331,24 +331,23 @@ static void asm_parse_directive(TCCState *s1)
     uint8_t *ptr;
 
     /* assembler directive */
-    next();
     sec = cur_text_section;
     switch(tok) {
-    case TOK_ASM_align:
-    case TOK_ASM_p2align:
-    case TOK_ASM_skip:
-    case TOK_ASM_space:
+    case TOK_ASMDIR_align:
+    case TOK_ASMDIR_p2align:
+    case TOK_ASMDIR_skip:
+    case TOK_ASMDIR_space:
         tok1 = tok;
         next();
         n = asm_int_expr(s1);
-        if (tok1 == TOK_ASM_p2align)
+        if (tok1 == TOK_ASMDIR_p2align)
         {
             if (n < 0 || n > 30)
                 tcc_error("invalid p2align, must be between 0 and 30");
             n = 1 << n;
-            tok1 = TOK_ASM_align;
+            tok1 = TOK_ASMDIR_align;
         }
-        if (tok1 == TOK_ASM_align) {
+        if (tok1 == TOK_ASMDIR_align) {
             if (n < 0 || (n & (n-1)) != 0)
                 tcc_error("alignment must be a positive power of two");
             offset = (ind + n - 1) & -n;
@@ -372,7 +371,7 @@ static void asm_parse_directive(TCCState *s1)
         }
         ind += size;
         break;
-    case TOK_ASM_quad:
+    case TOK_ASMDIR_quad:
         next();
         for(;;) {
             uint64_t vl;
@@ -399,15 +398,15 @@ static void asm_parse_directive(TCCState *s1)
             next();
         }
         break;
-    case TOK_ASM_byte:
+    case TOK_ASMDIR_byte:
         size = 1;
         goto asm_data;
-    case TOK_ASM_word:
-    case TOK_SHORT:
+    case TOK_ASMDIR_word:
+    case TOK_ASMDIR_short:
         size = 2;
         goto asm_data;
-    case TOK_LONG:
-    case TOK_INT:
+    case TOK_ASMDIR_long:
+    case TOK_ASMDIR_int:
         size = 4;
     asm_data:
         next();
@@ -433,7 +432,7 @@ static void asm_parse_directive(TCCState *s1)
             next();
         }
         break;
-    case TOK_ASM_fill:
+    case TOK_ASMDIR_fill:
         {
             int repeat, size, val, i, j;
             uint8_t repeat_buf[8];
@@ -475,7 +474,7 @@ static void asm_parse_directive(TCCState *s1)
             }
         }
         break;
-    case TOK_ASM_org:
+    case TOK_ASMDIR_org:
         {
             unsigned long n;
             next();
@@ -488,10 +487,10 @@ static void asm_parse_directive(TCCState *s1)
             goto zero_pad;
         }
         break;
-    case TOK_ASM_globl:
-    case TOK_ASM_global:
-    case TOK_ASM_weak:
-    case TOK_ASM_hidden:
+    case TOK_ASMDIR_globl:
+    case TOK_ASMDIR_global:
+    case TOK_ASMDIR_weak:
+    case TOK_ASMDIR_hidden:
     tok1 = tok;
 	do { 
             Sym *sym;
@@ -502,18 +501,18 @@ static void asm_parse_directive(TCCState *s1)
                 sym = label_push(&s1->asm_labels, tok, 0);
                 sym->type.t = VT_VOID;
             }
-	    if (tok1 != TOK_ASM_hidden)
+	    if (tok1 != TOK_ASMDIR_hidden)
                 sym->type.t &= ~VT_STATIC;
-            if (tok1 == TOK_ASM_weak)
+            if (tok1 == TOK_ASMDIR_weak)
                 sym->type.t |= VT_WEAK;
-	    else if (tok1 == TOK_ASM_hidden)
+	    else if (tok1 == TOK_ASMDIR_hidden)
 	        sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
             next();
 	} while (tok == ',');
 	break;
-    case TOK_ASM_string:
-    case TOK_ASM_ascii:
-    case TOK_ASM_asciz:
+    case TOK_ASMDIR_string:
+    case TOK_ASMDIR_ascii:
+    case TOK_ASMDIR_asciz:
         {
             const uint8_t *p;
             int i, size, t;
@@ -525,7 +524,7 @@ static void asm_parse_directive(TCCState *s1)
                     expect("string constant");
                 p = tokc.str.data;
                 size = tokc.str.size;
-                if (t == TOK_ASM_ascii && size > 0)
+                if (t == TOK_ASMDIR_ascii && size > 0)
                     size--;
                 for(i = 0; i < size; i++)
                     g(p[i]);
@@ -538,9 +537,9 @@ static void asm_parse_directive(TCCState *s1)
             }
 	}
 	break;
-    case TOK_ASM_text:
-    case TOK_ASM_data:
-    case TOK_ASM_bss:
+    case TOK_ASMDIR_text:
+    case TOK_ASMDIR_data:
+    case TOK_ASMDIR_bss:
 	{ 
             char sname[64];
             tok1 = tok;
@@ -557,7 +556,7 @@ static void asm_parse_directive(TCCState *s1)
             use_section(s1, sname);
 	}
 	break;
-    case TOK_ASM_file:
+    case TOK_ASMDIR_file:
         {
             char filename[512];
 
@@ -575,7 +574,7 @@ static void asm_parse_directive(TCCState *s1)
             next();
         }
         break;
-    case TOK_ASM_ident:
+    case TOK_ASMDIR_ident:
         {
             char ident[256];
 
@@ -593,7 +592,7 @@ static void asm_parse_directive(TCCState *s1)
             next();
         }
         break;
-    case TOK_ASM_size:
+    case TOK_ASMDIR_size:
         { 
             Sym *sym;
 
@@ -614,7 +613,7 @@ static void asm_parse_directive(TCCState *s1)
             }
         }
         break;
-    case TOK_ASM_type:
+    case TOK_ASMDIR_type:
         { 
             Sym *sym;
             const char *newtype;
@@ -632,7 +631,7 @@ static void asm_parse_directive(TCCState *s1)
                 newtype = tokc.str.data;
             } else {
                 if (tok == '@' || tok == '%')
-                    skip(tok);
+                    next();
                 newtype = get_tok_str(tok, NULL);
             }
 
@@ -646,7 +645,7 @@ static void asm_parse_directive(TCCState *s1)
             next();
         }
         break;
-    case TOK_SECTION1:
+    case TOK_ASMDIR_section:
         {
             char sname[256];
 
@@ -666,12 +665,18 @@ static void asm_parse_directive(TCCState *s1)
                 if (tok != TOK_STR)
                     expect("string constant");
                 next();
+                if (tok == ',') {
+                    next();
+                    if (tok == '@' || tok == '%')
+                        next();
+                    next();
+                }
             }
             last_text_section = cur_text_section;
             use_section(s1, sname);
         }
         break;
-    case TOK_ASM_previous:
+    case TOK_ASMDIR_previous:
         { 
             Section *sec;
             next();
@@ -683,13 +688,13 @@ static void asm_parse_directive(TCCState *s1)
         }
         break;
 #ifdef TCC_TARGET_I386
-    case TOK_ASM_code16:
+    case TOK_ASMDIR_code16:
         {
             next();
             s1->seg_size = 16;
         }
         break;
-    case TOK_ASM_code32:
+    case TOK_ASMDIR_code32:
         {
             next();
             s1->seg_size = 32;
@@ -698,7 +703,7 @@ static void asm_parse_directive(TCCState *s1)
 #endif
 #ifdef TCC_TARGET_X86_64
     /* added for compatibility with GAS */
-    case TOK_ASM_code64:
+    case TOK_ASMDIR_code64:
         next();
         break;
 #endif
@@ -763,7 +768,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
             /* horrible gas comment */
             while (tok != TOK_LINEFEED)
                 next();
-        } else if (tok == '.') {
+        } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
             asm_parse_directive(s1);
         } else if (tok == TOK_PPNUM) {
             const char *p;
diff --git a/tccpp.c b/tccpp.c
index 64c49195..cbc37072 100644
--- a/tccpp.c
+++ b/tccpp.c
@@ -1328,6 +1328,8 @@ ST_FUNC void parse_define(void)
     parse_flags |= PARSE_FLAG_SPACES;
     next_nomacro_spc();
     if (tok == '(') {
+        /* must be able to parse TOK_DOTS (in asm mode '.' can be part of identifier) */
+        parse_flags &= ~PARSE_FLAG_ASM_FILE;
         next_nomacro();
         ps = &first;
         if (tok != ')') for (;;) {
@@ -1355,6 +1357,7 @@ ST_FUNC void parse_define(void)
         }
         next_nomacro_spc();
         t = MACRO_FUNC;
+        parse_flags |= (saved_parse_flags & PARSE_FLAG_ASM_FILE);
     }
     tok_str_new(&str);
     spc = 2;
@@ -2460,7 +2463,8 @@ maybe_newline:
         p1 = p;
         h = TOK_HASH_INIT;
         h = TOK_HASH_FUNC(h, c);
-        while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
+        while (c = *++p, (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
+                         || (c == '.' && (parse_flags & PARSE_FLAG_ASM_FILE)))
             h = TOK_HASH_FUNC(h, c);
         if (c != '\\') {
             TokenSym **pts;
@@ -2492,7 +2496,9 @@ maybe_newline:
             p--;
             PEEKC(c, p);
         parse_ident_slow:
-            while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) {
+            while ((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
+                   || (c == '.' && (parse_flags & PARSE_FLAG_ASM_FILE)))
+            {
                 cstr_ccat(&tokcstr, c);
                 PEEKC(c, p);
             }
@@ -2551,7 +2557,7 @@ maybe_newline:
             cstr_reset(&tokcstr);
             cstr_ccat(&tokcstr, '.');
             goto parse_num;
-        } else if ((isidnum_table['.' - CH_EOF] & IS_ID) != 0) { /* asm mode */
+        } else if (parse_flags & PARSE_FLAG_ASM_FILE) {
             *--p = c = '.';
             goto parse_ident_fast;
         } else if (c == '.') {
diff --git a/tcctok.h b/tcctok.h
index 31b6dae2..b2fbebf6 100644
--- a/tcctok.h
+++ b/tcctok.h
@@ -301,35 +301,40 @@
 #endif
 
 /* Tiny Assembler */
- DEF_ASM(byte)
- DEF_ASM(word)
- DEF_ASM(align)
- DEF_ASM(p2align)
- DEF_ASM(skip)
- DEF_ASM(space)
- DEF_ASM(string)
- DEF_ASM(asciz)
- DEF_ASM(ascii)
- DEF_ASM(file)
- DEF_ASM(globl)
- DEF_ASM(global)
- DEF_ASM(hidden)
- DEF_ASM(ident)
- DEF_ASM(size)
- DEF_ASM(type)
- DEF_ASM(text)
- DEF_ASM(data)
- DEF_ASM(bss)
- DEF_ASM(previous)
- DEF_ASM(fill)
- DEF_ASM(org)
- DEF_ASM(quad)
+ DEF_ASMDIR(byte)              /* must be first directive */
+ DEF_ASMDIR(word)
+ DEF_ASMDIR(align)
+ DEF_ASMDIR(p2align)
+ DEF_ASMDIR(skip)
+ DEF_ASMDIR(space)
+ DEF_ASMDIR(string)
+ DEF_ASMDIR(asciz)
+ DEF_ASMDIR(ascii)
+ DEF_ASMDIR(file)
+ DEF_ASMDIR(globl)
+ DEF_ASMDIR(global)
+ DEF_ASMDIR(weak)
+ DEF_ASMDIR(hidden)
+ DEF_ASMDIR(ident)
+ DEF_ASMDIR(size)
+ DEF_ASMDIR(type)
+ DEF_ASMDIR(text)
+ DEF_ASMDIR(data)
+ DEF_ASMDIR(bss)
+ DEF_ASMDIR(previous)
+ DEF_ASMDIR(fill)
+ DEF_ASMDIR(org)
+ DEF_ASMDIR(quad)
 #if defined(TCC_TARGET_I386)
- DEF_ASM(code16)
- DEF_ASM(code32)
+ DEF_ASMDIR(code16)
+ DEF_ASMDIR(code32)
 #elif defined(TCC_TARGET_X86_64)
- DEF_ASM(code64)
+ DEF_ASMDIR(code64)
 #endif
+ DEF_ASMDIR(short)
+ DEF_ASMDIR(long)
+ DEF_ASMDIR(int)
+ DEF_ASMDIR(section)            /* must be last directive */
 
 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 #include "i386-tok.h"
diff --git a/tests/Makefile b/tests/Makefile
index 04390c70..f556cc50 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -73,7 +73,11 @@ DISAS = objdump -d
 
 # libtcc test
 ifdef LIBTCC1
- LIBTCC1:=$(TOP)/$(LIBTCC1)
+ ifdef CONFIG_WIN32
+    LIBTCC1:=$(TOP)/win32/libtcc/libtcc.a
+ else
+    LIBTCC1:=$(TOP)/$(LIBTCC1)
+ endif
 endif
 
 all test : $(TESTS)
diff --git a/tests/pp/12.S b/tests/pp/12.S
new file mode 100644
index 00000000..597ccb4c
--- /dev/null
+++ b/tests/pp/12.S
@@ -0,0 +1,8 @@
+#define SRC(y...)                       \
+        9999: y;                        \
+        .section __ex_table, "a";       \
+        .long 9999b, 6001f      ;       \
+        // .previous
+
+    SRC(1: movw (%esi), %bx)
+6001:
diff --git a/tests/pp/12.expect b/tests/pp/12.expect
new file mode 100644
index 00000000..17a861c2
--- /dev/null
+++ b/tests/pp/12.expect
@@ -0,0 +1,2 @@
+    9999: 1: movw (%esi), %bx; .section __ex_table, "a"; .long 9999b, 6001f ;
+6001:
diff --git a/tests/pp/Makefile b/tests/pp/Makefile
index cb8bfea2..d55699c7 100644
--- a/tests/pp/Makefile
+++ b/tests/pp/Makefile
@@ -7,6 +7,7 @@ include $(TOP)/Makefile
 
 TCC = $(TOP)/tcc
 TESTS = $(patsubst %.c,%.test,$(wildcard *.c))
+TESTS += $(patsubst %.S,%.test,$(wildcard *.S))
 
 ifdef CONFIG_WIN32
  TCC = $(top_srcdir)/win32/tcc
@@ -20,10 +21,19 @@ all test : $(TESTS)
 	    diff -Nu -b -B -I "^#" $(EXTRA_DIFF_OPTS) $*.expect $*.output \
 	    && rm -f $*.output
 
+%.test: %.S %.expect
+	@echo PPTest $* ...
+	@$(TCC) -E -P $< >$*.output 2>&1 ; \
+	    diff -Nu -b -B -I "^#" $(EXTRA_DIFF_OPTS) $*.expect $*.output \
+	    && rm -f $*.output
+
 # automatically generate .expect files with gcc:
-%.expect :
+%.expect: %.c
 	gcc -E -P $*.c >$*.expect 2>&1
 
+%.expect: %.S
+	gcc -E -P $*.S >$*.expect 2>&1
+
 # tell make not to delete
 .PRECIOUS: %.expect