Skip to content

Commit 4e5d2d9

Browse files
authored
Merge pull request #7 from garazdawi/master
Align C lexer with C23 standards and update CI configuration
1 parent d5028d5 commit 4e5d2d9

14 files changed

Lines changed: 1385 additions & 478 deletions

File tree

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-22.04
12+
env:
13+
MIX_ENV: test
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
include:
18+
- pair:
19+
elixir: "1.14"
20+
otp: "24"
21+
- pair:
22+
elixir: "1.19"
23+
otp: "28"
24+
lint: lint
25+
steps:
26+
- uses: actions/checkout@v6
27+
28+
- uses: erlef/setup-beam@v1
29+
with:
30+
otp-version: ${{matrix.pair.otp}}
31+
elixir-version: ${{matrix.pair.elixir}}
32+
33+
- name: Install Dependencies
34+
run: mix deps.get --only test
35+
36+
- run: mix format --check-formatted
37+
if: ${{ matrix.lint }}
38+
39+
- run: mix deps.get && mix deps.unlock --check-unused
40+
if: ${{ matrix.lint }}
41+
42+
- run: mix deps.compile
43+
44+
- run: mix compile --warnings-as-errors
45+
if: ${{ matrix.lint }}
46+
47+
- run: mix test

.travis.yml

Lines changed: 0 additions & 25 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Changelog
2+
3+
All notable changes to `makeup_c` are documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [1.0.0] - 2026-05-08
9+
10+
A near-complete rewrite of the lexer's behaviour to follow C23 (ISO/IEC 9899:2024)
11+
and shed the Elixir-isms inherited from the original lexer template. The token
12+
shape (Makeup token tuples) and public API (`Makeup.Lexers.CLexer.lex/2`) are
13+
unchanged; classifications of individual tokens are not.
14+
15+
### Added
16+
17+
- **C23 keywords**: `constexpr`, `nullptr`, `static_assert`, `thread_local`,
18+
`typeof`, `typeof_unqual`, `restrict`, plus the underscore-capital
19+
alternative spellings (`_Atomic`, `_BitInt`, `_Generic`, `_Bool`,
20+
`_Complex`, `_Imaginary`, `_Noreturn`, `_Static_assert`, `_Thread_local`,
21+
`_Alignas`, `_Alignof`, `_Decimal32`/`_Decimal64`/`_Decimal128`).
22+
- **Standard library typedefs** highlighted as types: stdint exact-,
23+
least-, and fast-width forms (`int8_t`, `int_least16_t`, `int_fast32_t`,
24+
…), `intmax_t`/`uintmax_t`, `intptr_t`/`uintptr_t`, `size_t`/`ssize_t`/
25+
`ptrdiff_t`/`max_align_t`/`errno_t`, the C23 `char8_t` and `nullptr_t`,
26+
and the ubiquitous `FILE` and `va_list`.
27+
- **Standard predefined macros** as `:name_builtin_pseudo`: `__func__`
28+
(C99), `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__`, `__STDC__`,
29+
`__STDC_VERSION__`, `__STDC_HOSTED__`, `__STDC_UTF_16__`,
30+
`__STDC_UTF_32__`, `__VA_ARGS__`, the C23 additions `__VA_OPT__` /
31+
`__has_include` / `__has_c_attribute` / `__has_embed`, and the GCC/
32+
Clang extensions `__FUNCTION__` / `__PRETTY_FUNCTION__`.
33+
- **Integer suffixes**: `u`/`U`, `l`/`L`, `ll`/`LL`, and the C23
34+
`wb`/`WB` (`_BitInt`) suffix in any valid combination.
35+
- **Float suffixes**: `f`/`F`, `l`/`L`, the C23 fixed-width forms
36+
`f16`/`f32`/`f64`/`f128` (and uppercase), `bf16`/`BF16`, and the
37+
decimal forms `df`/`dd`/`dl` (and uppercase).
38+
- **Float literal forms** previously unrecognised: `5.`, `.5`, `1e10`
39+
(no decimal point but exponent), and full **hex floats**
40+
(`0x1.fp10`, `0x1p-3`, `0x.fp+5`).
41+
- **C23 digit separator** `'` between any two digits in a numeric
42+
constant (`1'000`, `0xFFFF'FFFF`, `1'234.567'89`).
43+
- **Character literals** as `:string_char`, including the encoding
44+
prefixes `L`, `u`, `U`, `u8` (C23). Octal (`\NNN`), hex (`\xNN`),
45+
and Unicode (`\u`, `\U`) escapes inside char and string literals.
46+
- **String literal prefixes**: `L"…"`, `u"…"`, `U"…"`, `u8"…"`.
47+
- **All C23 preprocessor directives**, including `#elifdef`,
48+
`#elifndef`, `#embed`, `#warning`. Whitespace is now allowed between
49+
`#` and the directive name (`# include`, `#\tdefine`).
50+
- **`#include <header.h>`** is collapsed into a single `:string` token
51+
by a postprocess pass; `<` and `>` outside an `#include` line stay
52+
ordinary operators.
53+
- **C23 `[[…]]` attribute sequences** as `:name_decorator`
54+
(`[[nodiscard]]`, `[[gnu::aligned(8)]]`, …).
55+
- **CHANGELOG.md** (this file) and a comprehensive test suite (~80
56+
tests covering whitespace, comments, all numeric forms, all
57+
literal forms, every keyword class, attributes, and the
58+
preprocessor) modelled on `makeup_erlang`.
59+
- **Basic C++ keyword coverage** retained from the original lexer so
60+
the same tokenizer works on `.cpp` / `.h` files: `class`,
61+
`namespace`, `template`, `typename`, `try`/`catch`/`throw`, the
62+
`co_*` coroutine keywords, `*_cast` operators, `public`/`private`/
63+
`protected`, etc. Highlighted purely as `:keyword`; no template-,
64+
namespace-, or type-aware logic.
65+
66+
### Changed
67+
68+
- **Elixir requirement**: bumped to `~> 1.14` from `~> 1.4`.
69+
- **CI**: Travis replaced with GitHub Actions, matrixed across Elixir
70+
1.14 / OTP 24 (oldest supported) and Elixir 1.19 / OTP 28 (newest,
71+
with lint stage running `mix format --check-formatted`,
72+
`mix deps.unlock --check-unused`, and `mix compile --warnings-as-errors`).
73+
- **Source layout** moved to the standard makeup arrangement:
74+
`lib/makeup_c.ex``lib/makeup/lexers/c_lexer.ex`, plus the
75+
supporting modules under `lib/makeup/lexers/c_lexer/`. Tests
76+
moved to `test/makeup/c_lexer/`.
77+
- **Identifier rule** is now C-correct: `[A-Za-z_][A-Za-z0-9_]*`. The
78+
uppercase-vs-lowercase split (which routed names like `NULL` to
79+
`:name_constant` and so prevented postprocessor classification) is
80+
gone; both cases produce `:name` and the postprocessor decides.
81+
- **Octal numbers** use the traditional C `0` prefix (`0755`); the
82+
C23 alternative `0o`/`0O` form is also accepted. Plain `0` stays
83+
a decimal integer.
84+
- **Whitespace** now includes `\t` and `\v` per C23 6.4 (was missing
85+
both, so tabs in source produced `:error` tokens).
86+
- **`nullptr`** is classified as `:keyword_constant` (alongside
87+
`true`/`false`/`NULL`) rather than `:keyword`, matching its
88+
semantic role as a predefined constant.
89+
- **`void`** is classified as `:keyword_type` (was incorrectly listed
90+
under constants).
91+
92+
### Removed
93+
94+
- **Elixir-only combinators**: the `?x`/`?\x` "char literal" forms,
95+
the `key:` / `"key":` Elixir keyword-list combinators, and the
96+
trailing-`?`/`!` allowance in identifiers.
97+
- **Leading-underscore-as-comment rule**: identifiers like `_foo` are
98+
no longer rendered as `:comment`. In C a leading underscore is
99+
reserved to the implementation but still forms an ordinary
100+
identifier.
101+
- **`@operator_word`** (the `<iso646.h>` macros `and`, `or`, `not`,
102+
`xor`, `and_eq`, `or_eq`, `xor_eq`, `not_eq`, `bitand`, `bitor`).
103+
Those are *macros* expanding to C operators, not C operators
104+
themselves; they now lex as plain `:name`. (`compl` keeps its
105+
`:keyword` classification because it's also a C++ alternative-
106+
token keyword.)
107+
- **`alignoif`** (typo for `alignof`) and **`byte`** (not a C type)
108+
removed from the keyword/type lists.
109+
- **`_` digit separator**: was Elixir; replaced by the C23 `'`
110+
separator above.
111+
- **`0o`-only octal prefix**: replaced by the standard `0`-prefix
112+
form (with `0o`/`0O` accepted as a C23-compatible alternative).
113+
- **Dead `Makeup.Lexers.CLexer.Helper` module**: all of its functions
114+
(`with_optional_separator`, `escape_delim`, `sigil`, `escaped`,
115+
`keyword_matcher`) were either Elixir-only or unused.
116+
117+
### Fixed
118+
119+
- Tabs and form feeds in source no longer produce `:error` tokens.
120+
- The deprecated uppercase-sigil escape (`~W( ( \) [ ] { })`) that
121+
caused warnings on Elixir 1.19 is replaced by a list literal.
122+
123+
## [0.1.1] - 2020-10-03
124+
125+
- Mix file updated to point to the new repository location.
126+
127+
## [0.1.0] - 2020-09-26
128+
129+
- Initial release. Basic C lexer covering keywords, types,
130+
numbers (decimal/hex/binary/float), strings, single- and
131+
multi-line comments, operators, and preprocessor directives.

README.md

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MakeupC
2-
A [Makeup](https://github.com/tmbb/makeup/) lexer for the C language.
2+
A [Makeup](https://github.com/elixir-makeup/makeup) lexer for the C language.
33

44
## Installation
55

@@ -13,16 +13,37 @@ def deps do
1313
end
1414
```
1515

16-
The lexer will be automatically registered in Makeup for
17-
the languages "c" as well as the extensions ".c" and ".h".
16+
The lexer registers itself with Makeup for the language name `"c"` and
17+
the file extensions `c` and `h`.
1818

19-
## Status
19+
## Coverage
2020

21-
This lexer was built to highlight C code in a blog. It works for my purposes
22-
so I'm happy. It is fairly naive as it doesn't take into account any of the C language's
23-
type checking. It also has the C++ keywords, but doesn't attempt to do anything fancy
24-
regarding templates and other fancy type definitions. I needed it just for C, and that
25-
is what it does.
21+
The lexer targets C23 (ISO/IEC 9899:2024). It recognises:
2622

27-
If anybody wants to take a crack at making it more type aware or providing better
28-
support for C++, please have a go at it.
23+
- All C23 keywords, including the underscore-capital alternative
24+
spellings (`_Atomic`, `_BitInt`, `_Generic`, ...).
25+
- The standard primitive types plus the typedefs from `<stdint.h>`
26+
(`int8_t` ... `uint64_t`, `int_least*_t`, `int_fast*_t`,
27+
`intmax_t`, `uintmax_t`, `intptr_t`, `uintptr_t`), `<stddef.h>`
28+
(`size_t`, `ptrdiff_t`, `nullptr_t`, `max_align_t`), and the
29+
ubiquitous `FILE` / `va_list`.
30+
- Numeric constants in all bases (decimal, `0x`/`0X`, `0b`/`0B`,
31+
traditional `0`-prefix octal and the C23 `0o`/`0O` form), with
32+
full integer (`u`, `l`, `ll`, `wb`) and floating
33+
(`f`, `l`, `f16`/`f32`/`f64`/`f128`, `bf16`, `df`/`dd`/`dl`)
34+
suffixes, hex floats (`0x1.fp10`), and the C23 `'` digit separator.
35+
- Character and string literals, including the `L`, `u`, `U`, `u8`
36+
encoding prefixes and `\NNN` / `\xNN` / `\u` / `\U` escape forms.
37+
- All C23 preprocessor directives, including `#elifdef`, `#elifndef`,
38+
`#embed`, and `#warning`. `#include <header.h>` is collapsed into
39+
a single string token.
40+
- C23 `[[...]]` attribute sequences as decorators.
41+
- The standard predefined macros (`__FILE__`, `__LINE__`, `__func__`,
42+
`__VA_ARGS__`, `__VA_OPT__`, `__has_include`, ...).
43+
44+
Basic C++ coverage is included so the same lexer can be used for
45+
`.cpp` / `.h` files: keywords like `class`, `namespace`, `template`,
46+
`try`/`catch`/`throw`, the `co_*` coroutine keywords, and the
47+
`*_cast` operators all highlight as keywords. There is no
48+
template-, namespace-, or type-aware logic — these are recognised
49+
purely as tokens.

lib/c_lexer/helper.ex

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)