mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			219 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#! /usr/bin/python3
 | 
						|
# Tests for scripts/glibcpp.py
 | 
						|
# Copyright (C) 2022-2025 Free Software Foundation, Inc.
 | 
						|
# This file is part of the GNU C Library.
 | 
						|
#
 | 
						|
# The GNU C Library is free software; you can redistribute it and/or
 | 
						|
# modify it under the terms of the GNU Lesser General Public
 | 
						|
# License as published by the Free Software Foundation; either
 | 
						|
# version 2.1 of the License, or (at your option) any later version.
 | 
						|
#
 | 
						|
# The GNU C Library is distributed in the hope that it will be useful,
 | 
						|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
# Lesser General Public License for more details.
 | 
						|
#
 | 
						|
# You should have received a copy of the GNU Lesser General Public
 | 
						|
# License along with the GNU C Library; if not, see
 | 
						|
# <https://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
import inspect
 | 
						|
import sys
 | 
						|
 | 
						|
import glibcpp
 | 
						|
 | 
						|
# Error counter.
 | 
						|
errors = 0
 | 
						|
 | 
						|
class TokenizerErrors:
 | 
						|
    """Used as the error reporter during tokenization."""
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        self.errors = []
 | 
						|
 | 
						|
    def error(self, token, message):
 | 
						|
        self.errors.append((token, message))
 | 
						|
 | 
						|
def check_macro_definitions(source, expected):
 | 
						|
    reporter = TokenizerErrors()
 | 
						|
    tokens = glibcpp.tokenize_c(source, reporter)
 | 
						|
 | 
						|
    actual = []
 | 
						|
    for md in glibcpp.macro_definitions(tokens):
 | 
						|
        if md.function:
 | 
						|
            md_name = '{}({})'.format(md.name, ','.join(md.args_lowered))
 | 
						|
        else:
 | 
						|
            md_name = md.name
 | 
						|
        actual.append((md_name, md.body_lowered))
 | 
						|
 | 
						|
    if actual != expected or reporter.errors:
 | 
						|
        global errors
 | 
						|
        errors += 1
 | 
						|
        # Obtain python source line information.
 | 
						|
        frame = inspect.stack(2)[1]
 | 
						|
        print('{}:{}: error: macro definition mismatch, actual definitions:'
 | 
						|
              .format(frame[1], frame[2]))
 | 
						|
        for md in actual:
 | 
						|
            print('note: {} {!r}'.format(md[0], md[1]))
 | 
						|
 | 
						|
        if reporter.errors:
 | 
						|
            for err in reporter.errors:
 | 
						|
                print('note: tokenizer error: {}: {}'.format(
 | 
						|
                    err[0].line, err[1]))
 | 
						|
 | 
						|
def check_macro_eval(source, expected, expected_errors=''):
 | 
						|
    reporter = TokenizerErrors()
 | 
						|
    tokens = list(glibcpp.tokenize_c(source, reporter))
 | 
						|
 | 
						|
    if reporter.errors:
 | 
						|
        # Obtain python source line information.
 | 
						|
        frame = inspect.stack(2)[1]
 | 
						|
        for err in reporter.errors:
 | 
						|
            print('{}:{}: tokenizer error: {}: {}'.format(
 | 
						|
                frame[1], frame[2], err[0].line, err[1]))
 | 
						|
        return
 | 
						|
 | 
						|
    class EvalReporter:
 | 
						|
        """Used as the error reporter during evaluation."""
 | 
						|
 | 
						|
        def __init__(self):
 | 
						|
            self.lines = []
 | 
						|
 | 
						|
        def error(self, line, message):
 | 
						|
            self.lines.append('{}: error: {}\n'.format(line, message))
 | 
						|
 | 
						|
        def note(self, line, message):
 | 
						|
            self.lines.append('{}: note: {}\n'.format(line, message))
 | 
						|
 | 
						|
    reporter = EvalReporter()
 | 
						|
    actual = glibcpp.macro_eval(glibcpp.macro_definitions(tokens), reporter)
 | 
						|
    actual_errors = ''.join(reporter.lines)
 | 
						|
    if actual != expected or actual_errors != expected_errors:
 | 
						|
        global errors
 | 
						|
        errors += 1
 | 
						|
        # Obtain python source line information.
 | 
						|
        frame = inspect.stack(2)[1]
 | 
						|
        print('{}:{}: error: macro evaluation mismatch, actual results:'
 | 
						|
              .format(frame[1], frame[2]))
 | 
						|
        for k, v in actual.items():
 | 
						|
            print('  {}: {!r}'.format(k, v))
 | 
						|
        for msg in reporter.lines:
 | 
						|
            sys.stdout.write('  | ' + msg)
 | 
						|
 | 
						|
# Individual test cases follow.
 | 
						|
 | 
						|
check_macro_definitions('', [])
 | 
						|
check_macro_definitions('int main()\n{\n{\n', [])
 | 
						|
check_macro_definitions("""
 | 
						|
#define A 1
 | 
						|
#define B 2 /* ignored */
 | 
						|
#define C 3 // also ignored
 | 
						|
#define D \
 | 
						|
 4
 | 
						|
#define STRING "string"
 | 
						|
#define FUNCLIKE(a, b) (a + b)
 | 
						|
#define FUNCLIKE2(a, b) (a + \
 | 
						|
 b)
 | 
						|
""", [('A', ['1']),
 | 
						|
      ('B', ['2']),
 | 
						|
      ('C', ['3']),
 | 
						|
      ('D', ['4']),
 | 
						|
      ('STRING', ['"string"']),
 | 
						|
      ('FUNCLIKE(a,b)', list('(a+b)')),
 | 
						|
      ('FUNCLIKE2(a,b)', list('(a+b)')),
 | 
						|
      ])
 | 
						|
check_macro_definitions('#define MACRO', [('MACRO', [])])
 | 
						|
check_macro_definitions('#define MACRO\n', [('MACRO', [])])
 | 
						|
check_macro_definitions('#define MACRO()', [('MACRO()', [])])
 | 
						|
check_macro_definitions('#define MACRO()\n', [('MACRO()', [])])
 | 
						|
 | 
						|
check_macro_eval('#define A 1', {'A': 1})
 | 
						|
check_macro_eval('#define A (1)', {'A': 1})
 | 
						|
check_macro_eval('#define A (1 + 1)', {'A': 2})
 | 
						|
check_macro_eval('#define A (1U << 31)', {'A': 1 << 31})
 | 
						|
check_macro_eval('#define A (1 | 2)', {'A': 1 | 2})
 | 
						|
check_macro_eval('''\
 | 
						|
#define A (B + 1)
 | 
						|
#define B 10
 | 
						|
#define F(x) ignored
 | 
						|
#define C "not ignored"
 | 
						|
''', {
 | 
						|
    'A': 11,
 | 
						|
    'B': 10,
 | 
						|
    'C': '"not ignored"',
 | 
						|
})
 | 
						|
 | 
						|
# Checking for evaluation errors.
 | 
						|
check_macro_eval('''\
 | 
						|
#define A 1
 | 
						|
#define A 2
 | 
						|
''', {
 | 
						|
    'A': 1,
 | 
						|
}, '''\
 | 
						|
2: error: macro A redefined
 | 
						|
1: note: location of previous definition
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A A
 | 
						|
#define B 1
 | 
						|
''', {
 | 
						|
    'A': None,
 | 
						|
    'B': 1,
 | 
						|
}, '''\
 | 
						|
1: error: macro definition A refers to itself
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A B
 | 
						|
#define B A
 | 
						|
''', {
 | 
						|
    'A': None,
 | 
						|
    'B': None,
 | 
						|
}, '''\
 | 
						|
1: error: macro definition A refers to itself
 | 
						|
2: note: evaluated from B
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A B
 | 
						|
#define B C
 | 
						|
#define C A
 | 
						|
''', {
 | 
						|
    'A': None,
 | 
						|
    'B': None,
 | 
						|
    'C': None,
 | 
						|
}, '''\
 | 
						|
1: error: macro definition A refers to itself
 | 
						|
3: note: evaluated from C
 | 
						|
2: note: evaluated from B
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A 1 +
 | 
						|
''', {
 | 
						|
    'A': None,
 | 
						|
}, '''\
 | 
						|
1: error: uninterpretable macro token sequence: 1 +
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A 3*5
 | 
						|
''', {
 | 
						|
    'A': None,
 | 
						|
}, '''\
 | 
						|
1: error: uninterpretable macro token sequence: 3 * 5
 | 
						|
''')
 | 
						|
 | 
						|
check_macro_eval('''\
 | 
						|
#define A 3 + 5
 | 
						|
''', {
 | 
						|
    'A': 8,
 | 
						|
}, '''\
 | 
						|
1: error: missing parentheses around + expression
 | 
						|
1: note: in definition of macro A
 | 
						|
''')
 | 
						|
 | 
						|
if errors:
 | 
						|
    sys.exit(1)
 |