vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

c.lua

(9795B)


      1 -- Copyright 2006-2025 Mitchell. See LICENSE.
      2 -- C LPeg lexer.
      3 
      4 local lexer = lexer
      5 local P, S, B = lpeg.P, lpeg.S, lpeg.B
      6 
      7 local lex = lexer.new(...)
      8 
      9 -- Keywords.
     10 lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)))
     11 
     12 -- Types.
     13 lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE)))
     14 
     15 -- Functions.
     16 local builtin_func = -(B('.') + B('->')) *
     17 	lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))
     18 local func = lex:tag(lexer.FUNCTION, lexer.word)
     19 local method = (B('.') + B('->')) * lex:tag(lexer.FUNCTION_METHOD, lexer.word)
     20 lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '('))
     21 
     22 -- Constants.
     23 lex:add_rule('constants', lex:tag(lexer.CONSTANT_BUILTIN,
     24 	-(B('.') + B('->')) * lex:word_match(lexer.CONSTANT_BUILTIN)))
     25 
     26 -- Labels.
     27 lex:add_rule('label', lex:tag(lexer.LABEL, lexer.starts_line(lexer.word * ':')))
     28 
     29 -- Strings.
     30 local sq_str = lexer.range("'", true)
     31 local dq_str = lexer.range('"', true)
     32 lex:add_rule('string', lex:tag(lexer.STRING, P('L')^-1 * (sq_str + dq_str)))
     33 
     34 -- Identifiers.
     35 lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word))
     36 
     37 -- Comments.
     38 local line_comment = lexer.to_eol('//', true)
     39 local ws = S(' \t')^0
     40 local block_comment = lexer.range('/*', '*/') +
     41 	lexer.range('#if' * ws * '0' * lexer.space, '#endif')
     42 lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment))
     43 
     44 -- Numbers.
     45 local integer = lexer.integer * lexer.word_match('u l ll ul ull lu llu', true)^-1
     46 local float = lexer.float * lexer.word_match('f l df dd dl i j', true)^-1
     47 lex:add_rule('number', lex:tag(lexer.NUMBER, float + integer))
     48 
     49 -- Preprocessor.
     50 local include = lex:tag(lexer.PREPROCESSOR, '#' * ws * 'include') *
     51 	(lex:get_rule('whitespace') * lex:tag(lexer.STRING, lexer.range('<', '>', true)))^-1
     52 local preproc = lex:tag(lexer.PREPROCESSOR, '#' * ws * lex:word_match(lexer.PREPROCESSOR))
     53 lex:add_rule('preprocessor', include + preproc)
     54 
     55 -- Attributes.
     56 local standard_attr = lex:word_match(lexer.ATTRIBUTE)
     57 local non_standard_attr = lexer.word * '::' * lexer.word
     58 local attr_args = lexer.range('(', ')')
     59 local attr = (non_standard_attr + standard_attr) * attr_args^-1
     60 lex:add_rule('attribute', lex:tag(lexer.ATTRIBUTE, '[[' * attr * (ws * ',' * ws * attr)^0 * ']]'))
     61 
     62 -- Operators.
     63 lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}')))
     64 
     65 -- Fold points.
     66 lex:add_fold_point(lexer.PREPROCESSOR, '#if', '#endif')
     67 lex:add_fold_point(lexer.PREPROCESSOR, '#ifdef', '#endif')
     68 lex:add_fold_point(lexer.PREPROCESSOR, '#ifndef', '#endif')
     69 lex:add_fold_point(lexer.OPERATOR, '{', '}')
     70 lex:add_fold_point(lexer.COMMENT, '/*', '*/')
     71 
     72 -- Word lists.
     73 lex:set_word_list(lexer.KEYWORD, {
     74 	'auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum', 'extern', 'for',
     75 	'goto', 'if', 'inline', 'register', 'restrict', 'return', 'sizeof', 'static', 'switch', 'typedef',
     76 	'volatile', 'while', --
     77 	'false', 'true', -- C99
     78 	'alignas', 'alignof', '_Atomic', '_Generic', 'noreturn', '_Static_assert', 'thread_local', -- C11
     79 	-- Compiler.
     80 	'asm', '__asm', '__asm__', '__restrict__', '__inline', '__inline__', '__attribute__', '__declspec'
     81 })
     82 
     83 lex:set_word_list(lexer.TYPE, {
     84 	'bool', 'char', 'double', 'float', 'int', 'long', 'short', 'signed', 'struct', 'union',
     85 	'unsigned', 'void', --
     86 	'complex', 'imaginary', '_Complex', '_Imaginary', -- complex.h C99
     87 	'lconv', -- locale.h
     88 	'div_t', -- math.h
     89 	'va_list', -- stdarg.h
     90 	'bool', '_Bool', -- stdbool.h C99
     91 	-- stddef.h.
     92 	'size_t', 'ptrdiff_t', --
     93 	'max_align_t', -- C11
     94 	-- stdint.h.
     95 	'int8_t', 'int16_t', 'int32_t', 'int64_t', 'int_fast8_t', 'int_fast16_t', 'int_fast32_t',
     96 	'int_fast64_t', 'int_least8_t', 'int_least16_t', 'int_least32_t', 'int_least64_t', 'intmax_t',
     97 	'intptr_t', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'uint_fast8_t', 'uint_fast16_t',
     98 	'uint_fast32_t', 'uint_fast64_t', 'uint_least8_t', 'uint_least16_t', 'uint_least32_t',
     99 	'uint_least64_t', 'uintmax_t', 'uintptr_t', --
    100 	'FILE', 'fpos_t', -- stdio.h
    101 	'div_t', 'ldiv_t', -- stdlib.h
    102 	-- time.h.
    103 	'tm', 'time_t', 'clock_t', --
    104 	'timespec' -- C11
    105 })
    106 
    107 lex:set_word_list(lexer.FUNCTION_BUILTIN, {
    108 	'assert', -- assert.h
    109 	-- complex.h.
    110 	'CMPLX', 'creal', 'cimag', 'cabs', 'carg', 'conj', 'cproj',
    111 	-- C99
    112 	'cexp', 'cpow', 'csin', 'ccos', 'ctan', 'casin', 'cacos', 'catan', 'csinh', 'ccosh', 'ctanh',
    113 	'casinh', 'cacosh', 'catanh',
    114 	-- ctype.h.
    115 	'isalnum', 'isalpha', 'islower', 'isupper', 'isdigit', 'isxdigit', 'iscntrl', 'isgraph',
    116 	'isspace', 'isprint', 'ispunct', 'tolower', 'toupper', --
    117 	'isblank', -- C99
    118 	-- inttypes.h.
    119 	'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C', 'INTMAX_C', 'UINT8_C', 'UINT16_C', 'UINT32_C',
    120 	'UINT64_C', 'UINTMAX_C', --
    121 	'setlocale', 'localeconv', -- locale.h
    122 	-- math.h.
    123 	'abs', 'div', 'fabs', 'fmod', 'exp', 'log', 'log10', 'pow', 'sqrt', 'sin', 'cos', 'tan', 'asin',
    124 	'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'ceil', 'floor', 'frexp', 'ldexp', 'modf',
    125 	-- C99.
    126 	'remainder', 'remquo', 'fma', 'fmax', 'fmin', 'fdim', 'nan', 'exp2', 'expm1', 'log2', 'log1p',
    127 	'cbrt', 'hypot', 'asinh', 'acosh', 'atanh', 'erf', 'erfc', 'tgamma', 'lgamma', 'trunc', 'round',
    128 	'nearbyint', 'rint', 'scalbn', 'ilogb', 'logb', 'nextafter', 'nexttoward', 'copysign', 'isfinite',
    129 	'isinf', 'isnan', 'isnormal', 'signbit', 'isgreater', 'isgreaterequal', 'isless', 'islessequal',
    130 	'islessgreater', 'isunordered', --
    131 	'strtoimax', 'strtoumax', -- inttypes.h C99
    132 	'signal', 'raise', -- signal.h
    133 	'setjmp', 'longjmp', -- setjmp.h
    134 	'va_start', 'va_arg', 'va_end', -- stdarg.h
    135 	-- stdio.h.
    136 	'fopen', 'freopen', 'fclose', 'fflush', 'setbuf', 'setvbuf', 'fwide', 'fread', 'fwrite', 'fgetc',
    137 	'getc', 'fgets', 'fputc', 'putc', 'getchar', 'gets', 'putchar', 'puts', 'ungetc', 'scanf',
    138 	'fscanf', 'sscanf', 'printf', 'fprintf', 'sprintf', 'vprintf', 'vfprintf', 'vsprintf', 'ftell',
    139 	'fgetpos', 'fseek', 'fsetpos', 'rewind', 'clearerr', 'feof', 'ferror', 'perror', 'remove',
    140 	'rename', 'tmpfile', 'tmpnam',
    141 	-- stdlib.h.
    142 	'abort', 'exit', 'atexit', 'system', 'getenv', 'malloc', 'calloc', 'realloc', 'free', 'atof',
    143 	'atoi', 'atol', 'strtol', 'strtoul', 'strtod', 'mblen', 'mbsinit', 'mbrlen', 'qsort', 'bsearch',
    144 	'rand', 'srand', --
    145 	'quick_exit', '_Exit', 'at_quick_exit', 'aligned_alloc', -- C11
    146 	-- string.h.
    147 	'strcpy', 'strncpy', 'strcat', 'strncat', 'strxfrm', 'strlen', 'strcmp', 'strncmp', 'strcoll',
    148 	'strchr', 'strrchr', 'strspn', 'strcspn', 'strpbrk', 'strstr', 'strtok', 'memchr', 'memcmp',
    149 	'memset', 'memcpy', 'memmove', 'strerror',
    150 	-- time.h.
    151 	'difftime', 'time', 'clock', 'asctime', 'ctime', 'gmtime', 'localtime', 'mktime', --
    152 	'timespec_get' -- C11
    153 })
    154 
    155 lex:set_word_list(lexer.CONSTANT_BUILTIN, {
    156 	'NULL', --
    157 	'__DATE__', '__FILE__', '__LINE__', '__TIME__', '__func__', -- preprocessor
    158 	-- errno.h.
    159 	'errno', --
    160 	'E2BIG', 'EACCES', 'EADDRINUSE', 'EADDRNOTAVAIL', 'EAFNOSUPPORT', 'EAGAIN', 'EALREADY', 'EBADF',
    161 	'EBADMSG', 'EBUSY', 'ECANCELED', 'ECHILD', 'ECONNABORTED', 'ECONNREFUSED', 'ECONNRESET',
    162 	'EDEADLK', 'EDESTADDRREQ', 'EDOM', 'EDQUOT', 'EEXIST', 'EFAULT', 'EFBIG', 'EHOSTUNREACH', 'EIDRM',
    163 	'EILSEQ', 'EINPROGRESS', 'EINTR', 'EINVAL', 'EIO', 'EISCONN', 'EISDIR', 'ELOOP', 'EMFILE',
    164 	'EMLINK', 'EMSGSIZE', 'EMULTIHOP', 'ENAMETOOLONG', 'ENETDOWN', 'ENETRESET', 'ENETUNREACH',
    165 	'ENFILE', 'ENOBUFS', 'ENODATA', 'ENODEV', 'ENOENT', 'ENOEXEC', 'ENOLCK', 'ENOLINK', 'ENOMEM',
    166 	'ENOMSG', 'ENOPROTOOPT', 'ENOSPC', 'ENOSR', 'ENOSTR', 'ENOSYS', 'ENOTCONN', 'ENOTDIR',
    167 	'ENOTEMPTY', 'ENOTRECOVERABLE', 'ENOTSOCK', 'ENOTSUP', 'ENOTTY', 'ENXIO', 'EOPNOTSUPP',
    168 	'EOVERFLOW', 'EOWNERDEAD', 'EPERM', 'EPIPE', 'EPROTO', 'EPROTONOSUPPORT', 'EPROTOTYPE', 'ERANGE',
    169 	'EROFS', 'ESPIPE', 'ESRCH', 'ESTALE', 'ETIME', 'ETIMEDOUT', 'ETXTBSY', 'EWOULDBLOCK', 'EXDEV',
    170 	-- float.h.
    171 	'FLT_MIN', 'DBL_MIN', 'LDBL_MIN', 'FLT_MAX', 'DBL_MAX', 'LDBL_MAX',
    172 	-- limits.h.
    173 	'CHAR_BIT', 'MB_LEN_MAX', 'CHAR_MIN', 'CHAR_MAX', 'SCHAR_MIN', 'SHRT_MIN', 'INT_MIN', 'LONG_MIN',
    174 	'SCHAR_MAX', 'SHRT_MAX', 'INT_MAX', 'LONG_MAX', 'UCHAR_MAX', 'USHRT_MAX', 'UINT_MAX', 'ULONG_MAX',
    175 	-- C99.
    176 	'LLONG_MIN', 'ULLONG_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIZE_MAX', 'SIG_ATOMIC_MIN',
    177 	'SIG_ATOMIC_MAX', 'WINT_MIN', 'WINT_MAX', 'WCHAR_MIN', 'WCHAR_MAX', --
    178 	'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', -- locale.h
    179 	-- math.h.
    180 	'HUGE_VAL', --
    181 	'INFINITY', 'NAN', -- C99
    182 	-- stdint.h.
    183 	'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN', 'INT_FAST8_MIN', 'INT_FAST16_MIN',
    184 	'INT_FAST32_MIN', 'INT_FAST64_MIN', 'INT_LEAST8_MIN', 'INT_LEAST16_MIN', 'INT_LEAST32_MIN',
    185 	'INT_LEAST64_MIN', 'INTPTR_MIN', 'INTMAX_MIN', 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
    186 	'INT_FAST8_MAX', 'INT_FAST16_MAX', 'INT_FAST32_MAX', 'INT_FAST64_MAX', 'INT_LEAST8_MAX',
    187 	'INT_LEAST16_MAX', 'INT_LEAST32_MAX', 'INT_LEAST64_MAX', 'INTPTR_MAX', 'INTMAX_MAX', 'UINT8_MAX',
    188 	'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX', 'UINT_FAST8_MAX', 'UINT_FAST16_MAX', 'UINT_FAST32_MAX',
    189 	'UINT_FAST64_MAX', 'UINT_LEAST8_MAX', 'UINT_LEAST16_MAX', 'UINT_LEAST32_MAX', 'UINT_LEAST64_MAX',
    190 	'UINTPTR_MAX', 'UINTMAX_MAX',
    191 	-- stdio.h
    192 	'stdin', 'stdout', 'stderr', 'EOF', 'FOPEN_MAX', 'FILENAME_MAX', 'BUFSIZ', '_IOFBF', '_IOLBF',
    193 	'_IONBF', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'TMP_MAX', --
    194 	'EXIT_SUCCESS', 'EXIT_FAILURE', 'RAND_MAX', -- stdlib.h
    195 	-- signal.h.
    196 	'SIG_DFL', 'SIG_IGN', 'SIG_ERR', 'SIGABRT', 'SIGFPE', 'SIGILL', 'SIGINT', 'SIGSEGV', 'SIGTERM', --
    197 	'CLOCKS_PER_SEC' -- time.h.
    198 })
    199 
    200 lex:set_word_list(lexer.PREPROCESSOR, {
    201 	'define', 'defined', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'line', 'pragma',
    202 	'undef'
    203 })
    204 
    205 lex:set_word_list(lexer.ATTRIBUTE, {
    206 	-- C23
    207 	'deprecated', 'fallthrough', 'nodiscard', 'maybe_unused', 'noreturn', '_Noreturn', 'unsequenced',
    208 	'reproducible'
    209 })
    210 
    211 lexer.property['scintillua.comment'] = '//'
    212 
    213 return lex