Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Install bison/flex (Linux)
if: runner.os == 'Linux'
Expand All @@ -22,11 +22,26 @@ jobs:
if: runner.os == 'macOS'
run: brew install bison flex

- name: Install bison/flex (Windows)
if: runner.os == 'Windows'
# windows-latest ships MSYS2 pre-installed at C:\msys64 (not on
# PATH). Its bison (3.8.2) satisfies parser.y's %require "3.8";
# winflexbison3's Chocolatey package is stuck on bison 3.7.4 (2021
# release) and fails that check. CMakeLists.txt's own WIN32 block
# points BISON_EXECUTABLE/FLEX_EXECUTABLE straight at the MSYS2
# binaries.
run: C:\msys64\usr\bin\bash.exe -lc "pacman -Sy --noconfirm bison flex"

- name: Configure
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

# --config/-C Release: a no-op on the single-config generators
# Linux/macOS use (CMAKE_BUILD_TYPE already picked the config), but
# required on Windows's default multi-config Visual Studio
# generator, which ignores CMAKE_BUILD_TYPE entirely -- passing it
# unconditionally on all three platforms is simpler than branching.
- name: Build
run: cmake --build build -j
run: cmake --build build --config Release -j

- name: Test
run: ctest --test-dir build --output-on-failure
run: ctest --test-dir build --output-on-failure -C Release
41 changes: 40 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ponytail: MSVC always reports __cplusplus as 199711L regardless of the
# actual /std: level, for legacy compatibility -- a long-standing MSVC-only
# quirk, unless this flag opts into reporting the real value. Bison's own
# generated variant.hh (parser.tab.hpp) branches its whole emplace/copy/move
# API on `__cplusplus >= 201103L`; without this flag MSVC silently takes the
# pre-C++11 copy-based branch, which then fails to compile for any move-only
# %type (this grammar's NodePtr = unique_ptr<ASTNode>) with a "deleted copy
# constructor" error that has nothing to do with our own code.
if(MSVC)
add_compile_options(/Zc:__cplusplus)
endif()

# ponytail: macOS ships an ancient (GPLv2, 2.3) bison; point CMake at a
# homebrew bison (3.5+) if one is present, since our grammar needs
# %skeleton "lalr1.cc" + api.value.type variant support.
Expand All @@ -16,7 +28,34 @@ elseif(APPLE AND EXISTS "/usr/local/opt/bison/bin/bison")
set(BISON_EXECUTABLE "/usr/local/opt/bison/bin/bison" CACHE FILEPATH "Bison executable" FORCE)
endif()

find_package(BISON 3.5 REQUIRED)
# ponytail: Windows has no bison/flex at all by default. Prefer MSYS2's
# (kept current -- our grammar needs 3.8+, see parser.y's %require) over the
# winflexbison3 Chocolatey package, whose last release (2021) bundles bison
# 3.7.4 -- confirmed too old by an actual CI failure ("require bison 3.8,
# but have 3.7.4"), not assumed. MSYS2 ships pre-installed on GitHub's
# windows-latest runner image at C:/msys64 (just not on PATH); falls back to
# win_bison/win_flex (e.g. a local dev machine with winflexbison instead) if
# that path doesn't exist.
if(WIN32)
if(EXISTS "C:/msys64/usr/bin/bison.exe")
set(BISON_EXECUTABLE "C:/msys64/usr/bin/bison.exe" CACHE FILEPATH "Bison executable" FORCE)
else()
find_program(_win_bison_exe NAMES win_bison)
if(_win_bison_exe)
set(BISON_EXECUTABLE "${_win_bison_exe}" CACHE FILEPATH "Bison executable" FORCE)
endif()
endif()
if(EXISTS "C:/msys64/usr/bin/flex.exe")
set(FLEX_EXECUTABLE "C:/msys64/usr/bin/flex.exe" CACHE FILEPATH "Flex executable" FORCE)
else()
find_program(_win_flex_exe NAMES win_flex)
if(_win_flex_exe)
set(FLEX_EXECUTABLE "${_win_flex_exe}" CACHE FILEPATH "Flex executable" FORCE)
endif()
endif()
endif()

find_package(BISON 3.8 REQUIRED)
find_package(FLEX REQUIRED)

include(FetchContent)
Expand Down
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ target_include_directories(openscad_cpp_parser PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(openscad_cpp_parser PUBLIC nlohmann_json::nlohmann_json)
target_compile_options(openscad_cpp_parser PRIVATE -Wall -Wextra)
target_compile_options(openscad_cpp_parser PRIVATE
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra>
)
5 changes: 4 additions & 1 deletion src/grammar/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ const std::unordered_map<std::string, KeywordFactory>& keywordTable() {
#define YY_USER_ACTION updateLocation(driver, yytext, yyleng);
%}

%option noyywrap nounput noinput never-interactive
/* nounistd: MSVC/Windows has no <unistd.h>; flex only includes it for
* isatty()/POSIX I/O we never use (never-interactive already disables the
* interactive-terminal codepath), so this is a no-op everywhere else. */
%option noyywrap nounput noinput never-interactive nounistd
%x STR
%x INCFILE

Expand Down
7 changes: 7 additions & 0 deletions src/grammar/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

%define api.token.constructor
%define api.value.type variant
// NodePtr (unique_ptr<ASTNode>) is move-only. Every action already moves it
// explicitly (std::move($1) etc.), which is enough for GCC/Clang, but
// MSVC's more eager template instantiation of value_type::copy<T> (used
// internally by emplace<T>, referenced whether or not it's ever actually
// called at runtime) fails to compile for a non-copyable T without this --
// the Bison manual's own documented fix for move-only semantic types.
%define api.value.automove
%define parse.error verbose
%locations

Expand Down
5 changes: 4 additions & 1 deletion tests/test_comments_and_files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ class TempDir {
};

void writeFile(const fs::path& p, const std::string& content) {
std::ofstream out(p);
// Binary mode: see the identical comment in test_source_map.cpp's own
// writeFile -- avoids Windows text-mode \r\n injection that the
// production reader (binary mode) would then faithfully preserve.
std::ofstream out(p, std::ios::binary);
out << content;
}

Expand Down
6 changes: 5 additions & 1 deletion tests/test_source_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ class TempDir {
};

void writeFile(const fs::path& p, const std::string& content) {
std::ofstream out(p);
// Binary mode: the production code (source_map.cpp) reads included
// files back in binary mode too, so a text-mode write here would
// silently inject \r\n on Windows that the reader (correctly) never
// strips, corrupting every fixture's expected content on that platform.
std::ofstream out(p, std::ios::binary);
out << content;
}

Expand Down
Loading