openscad-cpp-evaluator 1.4.1__tar.gz → 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/utf8.hpp +6 -0
  3. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/function_builtins.cpp +2 -23
  5. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/utf8.cpp +20 -0
  6. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/value.cpp +84 -7
  7. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_value.cpp +106 -0
  8. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/.github/workflows/ci.yml +0 -0
  9. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/.github/workflows/wheels.yml +0 -0
  10. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/.gitignore +0 -0
  11. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/.gitmodules +0 -0
  12. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/CLAUDE.md +0 -0
  13. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/CMakeLists.txt +0 -0
  14. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/LICENSE +0 -0
  15. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/README.md +0 -0
  16. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/bindings/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/bindings/ast_to_py.cpp +0 -0
  18. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/bindings/module.cpp +0 -0
  19. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/cmake/embed_font.cmake +0 -0
  20. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/examples/CMakeLists.txt +0 -0
  21. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/examples/eval_perf_benchmark.cpp +0 -0
  22. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/examples/manifold_cache_reuse.cpp +0 -0
  23. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/examples/minimal_debugger.cpp +0 -0
  24. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  25. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/.gitignore +0 -0
  26. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  27. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  28. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/README.md +0 -0
  29. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  30. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  31. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  32. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  33. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  34. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  35. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  36. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  37. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  38. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  39. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  40. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  41. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  42. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  43. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  44. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  45. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  46. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  47. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  48. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  49. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  50. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  51. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  52. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  53. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  54. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  55. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  56. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  57. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  58. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  59. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  60. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  61. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  62. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  63. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  64. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  65. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  66. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  67. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  68. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  69. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  70. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  71. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  72. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  73. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  74. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  75. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  76. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  77. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  78. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  79. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  80. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  81. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  82. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  83. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  84. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  85. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  86. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  87. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  88. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  89. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  90. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  91. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  92. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  93. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  94. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  95. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  96. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  97. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  98. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  99. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  100. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  101. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  102. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  103. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  104. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  105. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  106. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  107. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  108. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  109. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  110. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  111. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  112. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  113. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  114. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  115. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  116. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  117. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  118. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  119. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  120. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  121. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  122. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  123. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  124. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  125. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  126. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  127. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/resources/fonts/LICENSE +0 -0
  128. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  129. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/CMakeLists.txt +0 -0
  130. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/booleans.cpp +0 -0
  131. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/builtins.hpp +0 -0
  132. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/call_args.cpp +0 -0
  133. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/color.cpp +0 -0
  134. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/control.cpp +0 -0
  135. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/dxf_dim.cpp +0 -0
  136. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/extrude.cpp +0 -0
  137. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/import.cpp +0 -0
  138. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/modifiers.cpp +0 -0
  139. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/primitives_2d.cpp +0 -0
  140. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/primitives_3d.cpp +0 -0
  141. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/registry.cpp +0 -0
  142. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/roof.cpp +0 -0
  143. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/segments.cpp +0 -0
  144. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/surface.cpp +0 -0
  145. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/text.cpp +0 -0
  146. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/topology.cpp +0 -0
  147. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/builtins/transforms.cpp +0 -0
  148. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/bytecode_compiler.cpp +0 -0
  149. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/bytecode_vm.cpp +0 -0
  150. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/colored_body.cpp +0 -0
  151. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/csg_generate.cpp +0 -0
  152. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/csg_resolve.cpp +0 -0
  153. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/css_colors.cpp +0 -0
  154. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/debug/debug_repl.cpp +0 -0
  155. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/debug_profile.cpp +0 -0
  156. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/eval_context.cpp +0 -0
  157. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/eval_error.cpp +0 -0
  158. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/eval_use.cpp +0 -0
  159. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/export.cpp +0 -0
  160. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/expr_eval.cpp +0 -0
  161. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/format_closure.cpp +0 -0
  162. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/import/dxf_import.cpp +0 -0
  163. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/import/mesh_import.cpp +0 -0
  164. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/import/surface_load.cpp +0 -0
  165. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/import/svg_import.cpp +0 -0
  166. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/manifold_cache.cpp +0 -0
  167. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/measure_geometry.cpp +0 -0
  168. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/mesh_check.cpp +0 -0
  169. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/stmt_eval.cpp +0 -0
  170. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/text/font_match.cpp +0 -0
  171. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/text/freetype_font_provider.cpp +0 -0
  172. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/text/text_metrics.cpp +0 -0
  173. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/user_calls.cpp +0 -0
  174. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/src/zip_stored.cpp +0 -0
  175. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/CMakeLists.txt +0 -0
  176. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_booleans.cpp +0 -0
  177. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_bytecode_compiler.cpp +0 -0
  178. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_children_separate.cpp +0 -0
  179. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_cli.cpp +0 -0
  180. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_control_flow.cpp +0 -0
  181. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_csg_tree.cpp +0 -0
  182. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_debug_hooks.cpp +0 -0
  183. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_dxf_svg_import.cpp +0 -0
  184. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_eval_context.cpp +0 -0
  185. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_export.cpp +0 -0
  186. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_expr_eval.cpp +0 -0
  187. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_extrude_roof.cpp +0 -0
  188. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_feature_detection.cpp +0 -0
  189. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_function_builtins.cpp +0 -0
  190. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_helpers.hpp +0 -0
  191. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_import_export.cpp +0 -0
  192. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_include_cache.cpp +0 -0
  193. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_manifold_cache.cpp +0 -0
  194. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_mesh_check.cpp +0 -0
  195. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_multi_color_merge.cpp +0 -0
  196. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_primitives.cpp +0 -0
  197. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_profiling.cpp +0 -0
  198. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_python_bindings.py +0 -0
  199. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_render_expr.cpp +0 -0
  200. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_scope_trail.cpp +0 -0
  201. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_scoping.cpp +0 -0
  202. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_surface.cpp +0 -0
  203. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_tail_calls.cpp +0 -0
  204. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_text.cpp +0 -0
  205. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_transforms.cpp +0 -0
  206. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_unexpected_args.cpp +0 -0
  207. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_use.cpp +0 -0
  208. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_viewport_params.cpp +0 -0
  209. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tests/test_zip_stored.cpp +0 -0
  210. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tools/cli/CMakeLists.txt +0 -0
  211. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tools/cli/cli_lib.cpp +0 -0
  212. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tools/cli/cli_lib.hpp +0 -0
  213. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/tools/cli/main.cpp +0 -0
  214. {openscad_cpp_evaluator-1.4.1 → openscad_cpp_evaluator-1.5.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.4.1
3
+ Version: 1.5.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -1,5 +1,6 @@
1
1
  #pragma once
2
2
 
3
+ #include <cstdint>
3
4
  #include <string>
4
5
  #include <vector>
5
6
 
@@ -17,6 +18,11 @@ namespace oscadeval {
17
18
  // string that actually holds non-ASCII pays, and those are short in
18
19
  // practice.
19
20
 
21
+ // One code point as UTF-8. The caller decides which code points are
22
+ // legal -- chr() drops an invalid one, a \uXXXX escape substitutes a space
23
+ // -- so this encodes whatever it is given.
24
+ std::string utf8Encode(uint32_t cp);
25
+
20
26
  // True when every byte is < 0x80, so byte offsets and character offsets are
21
27
  // the same thing.
22
28
  bool utf8IsAscii(const std::string& s);
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.4.1"
7
+ version = "1.5.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -193,29 +193,8 @@ double atan2Degrees(double y, double x) {
193
193
  return std::fabs(degs - whole) < 3.0E-14 ? whole : degs;
194
194
  }
195
195
 
196
- // UTF-8 encode/decode for chr()/ord() -- OpenSCAD strings are unicode text,
197
- // not byte arrays, so a codepoint above U+007F needs real multi-byte
198
- // handling, not a raw byte cast.
199
- std::string utf8Encode(uint32_t cp) {
200
- std::string out;
201
- if (cp <= 0x7F) {
202
- out.push_back(static_cast<char>(cp));
203
- } else if (cp <= 0x7FF) {
204
- out.push_back(static_cast<char>(0xC0 | (cp >> 6)));
205
- out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
206
- } else if (cp <= 0xFFFF) {
207
- out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
208
- out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
209
- out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
210
- } else {
211
- out.push_back(static_cast<char>(0xF0 | (cp >> 18)));
212
- out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
213
- out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
214
- out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
215
- }
216
- return out;
217
- }
218
-
196
+ // utf8Encode lives in utf8.cpp -- the string-literal escape decoder
197
+ // (\uXXXX) needs it too.
219
198
  uint32_t utf8DecodeFirst(const std::string& s) {
220
199
  if (s.empty()) return 0;
221
200
  const auto b = [&](size_t i) { return static_cast<unsigned char>(s[i]); };
@@ -17,6 +17,26 @@ size_t nextCharStart(const std::string& s, size_t i) {
17
17
 
18
18
  } // namespace
19
19
 
20
+ std::string utf8Encode(uint32_t cp) {
21
+ std::string out;
22
+ if (cp <= 0x7F) {
23
+ out.push_back(static_cast<char>(cp));
24
+ } else if (cp <= 0x7FF) {
25
+ out.push_back(static_cast<char>(0xC0 | (cp >> 6)));
26
+ out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
27
+ } else if (cp <= 0xFFFF) {
28
+ out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
29
+ out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
30
+ out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
31
+ } else {
32
+ out.push_back(static_cast<char>(0xF0 | (cp >> 18)));
33
+ out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
34
+ out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
35
+ out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
36
+ }
37
+ return out;
38
+ }
39
+
20
40
  bool utf8IsAscii(const std::string& s) {
21
41
  for (char c : s) {
22
42
  if (static_cast<unsigned char>(c) >= 0x80) return false;
@@ -374,17 +374,60 @@ Value matmul(const Value& a, const Value& b, std::string* error) {
374
374
  " and " + oscTypeName(e2));
375
375
  }
376
376
 
377
+ namespace {
378
+
379
+ // `count` hex digits starting at `at`, into `out`. False (leaving `out`
380
+ // untouched) if any of them is not a hex digit.
381
+ bool hexDigits(const std::string& s, size_t at, size_t count, std::uint32_t& out) {
382
+ std::uint32_t v = 0;
383
+ for (size_t k = 0; k < count; ++k) {
384
+ const char c = s[at + k];
385
+ std::uint32_t d;
386
+ if (c >= '0' && c <= '9') d = static_cast<std::uint32_t>(c - '0');
387
+ else if (c >= 'a' && c <= 'f') d = static_cast<std::uint32_t>(c - 'a' + 10);
388
+ else if (c >= 'A' && c <= 'F') d = static_cast<std::uint32_t>(c - 'A' + 10);
389
+ else return false;
390
+ v = (v << 4) | d;
391
+ }
392
+ out = v;
393
+ return true;
394
+ }
395
+
396
+ // What a well-formed \x/\u escape contributes. A code point that cannot
397
+ // be encoded -- zero, or a lone surrogate -- becomes a SPACE rather than
398
+ // nothing or invalid UTF-8. That is the reference's behaviour, checked by
399
+ // equality since the bytes are hard to see: "\u0000" == " ", "\ud83d" ==
400
+ // " ", and "\x00" == "\u0000", all true on 2026.02.01. Note chr() does
401
+ // NOT agree -- chr(55357) is the empty string -- so the two paths differ
402
+ // deliberately.
403
+ std::string encodeEscapedCodePoint(std::uint32_t cp) {
404
+ if (cp == 0 || (cp >= 0xD800u && cp <= 0xDFFFu) || cp > 0x10FFFFu) return " ";
405
+ return utf8Encode(cp);
406
+ }
407
+
408
+ } // namespace
409
+
377
410
  std::string unescapeStringLiteral(const std::string& raw) {
378
- // Most strings carry no escape at all, and this runs on every
379
- // evaluation of a literal on the tree-walking path -- so don't build a
380
- // second copy of the string unless there is something to change.
381
- const size_t first = raw.find('\\');
411
+ // Most strings need no work at all, and this runs on every evaluation
412
+ // of a literal on the tree-walking path -- so don't build a second copy
413
+ // unless there is something to change. A bare newline counts as
414
+ // something to change (see the loop), so it has to open the scan too.
415
+ const size_t first = raw.find_first_of("\\\n");
382
416
  if (first == std::string::npos) return raw;
383
417
 
384
418
  std::string out;
385
419
  out.reserve(raw.size());
386
420
  out.append(raw, 0, first);
387
421
  for (size_t i = first; i < raw.size(); ++i) {
422
+ // A raw LF inside a string literal contributes NOTHING -- writing a
423
+ // string across two source lines joins them, keeping the second
424
+ // line's indentation:
425
+ // s = "abcd
426
+ // efgh"; // -> "abcd efgh", 12 characters
427
+ // A raw CR is not special and stands for itself, so a CRLF file
428
+ // leaves the CR behind (len 3 for "x<CR><LF>y"). Both verified on
429
+ // 2026.02.01.
430
+ if (raw[i] == '\n') continue;
388
431
  if (raw[i] != '\\' || i + 1 >= raw.size()) {
389
432
  out.push_back(raw[i]); // a trailing lone backslash stands for itself
390
433
  continue;
@@ -394,10 +437,44 @@ std::string unescapeStringLiteral(const std::string& raw) {
394
437
  case 'n': out.push_back('\n'); ++i; break;
395
438
  case 't': out.push_back('\t'); ++i; break;
396
439
  case 'r': out.push_back('\r'); ++i; break;
397
- case '\n': ++i; break; // line continuation: contributes nothing
440
+ case 'x': {
441
+ // \xNN, exactly two hex digits, and ASCII ONLY. The
442
+ // reference rejects anything above 0x7F -- a raw high byte
443
+ // would not be valid UTF-8, and the rest of the string is
444
+ // -- leaving the text to stand for itself: "\xff" is the
445
+ // three characters x, f, f. Verified against 2026.02.01,
446
+ // which takes \x00..\x7f and refuses \x80..\xff.
447
+ std::uint32_t cp = 0;
448
+ if (i + 3 < raw.size() && hexDigits(raw, i + 2, 2, cp) && cp <= 0x7Fu) {
449
+ out += encodeEscapedCodePoint(cp);
450
+ i += 3;
451
+ break;
452
+ }
453
+ out.push_back(next);
454
+ ++i;
455
+ break;
456
+ }
457
+ case 'u': {
458
+ // \uXXXX, exactly four hex digits. No \u{...} form: the
459
+ // reference calls that an undefined escape.
460
+ std::uint32_t cp = 0;
461
+ if (i + 5 < raw.size() && hexDigits(raw, i + 2, 4, cp)) {
462
+ out += encodeEscapedCodePoint(cp);
463
+ i += 5;
464
+ break;
465
+ }
466
+ out.push_back(next);
467
+ ++i;
468
+ break;
469
+ }
470
+ case '\n': ++i; break; // backslash + LF: both go
398
471
  case '\r':
399
- // Only a CRLF pair is a line continuation; a lone CR is an
400
- // ordinary escaped character like any other.
472
+ // A backslash before CRLF takes the whole line ending.
473
+ // Deliberately unlike the reference, which treats the
474
+ // backslash as an undefined escape and keeps the CR --
475
+ // leaving a stray control character in any string wrapped
476
+ // in a file written on Windows. See this suite's
477
+ // BackslashNewlineContributesNothing.
401
478
  if (i + 2 < raw.size() && raw[i + 2] == '\n') { i += 2; break; }
402
479
  out.push_back('\r');
403
480
  ++i;
@@ -496,3 +496,109 @@ TEST(StringEscapes, BothEvaluationPathsCookTheLiteral) {
496
496
  ASSERT_EQ(echoed.size(), 1u);
497
497
  EXPECT_EQ(echoed[0], "ECHO: 3") << "line continuation, compiled path";
498
498
  }
499
+
500
+ // Every expectation below was read off OpenSCAD 2026.02.01 rather than a
501
+ // spec, because the rules are narrow and not guessable:
502
+ //
503
+ // \xNN two hex digits, ASCII ONLY -- \x80..\xff are REFUSED, since a
504
+ // raw high byte is not valid UTF-8 and the rest of the string is
505
+ // \uXXXX exactly four hex digits; there is no \u{...} form
506
+ // an unencodable code point (zero, or a lone surrogate) becomes a SPACE
507
+ // anything else after a backslash is an "undefined escape": the
508
+ // backslash is dropped and the rest stands for itself
509
+ //
510
+ // The reference also emits "WARNING: Undefined escape sequence" for that
511
+ // last case, at PARSE time (once per literal, not once per evaluation --
512
+ // checked with a literal inside a for loop). This port has no parse-time
513
+ // warning channel, so it produces the same STRING without the warning.
514
+ TEST(StringEscapes, DecodesFourDigitUnicodeEscapes) {
515
+ EXPECT_EQ(unescapeStringLiteral("\\u03a9"), "Ω"); // len() of this is 1, not 5
516
+ EXPECT_EQ(unescapeStringLiteral("a\\u03a9b"), "aΩb");
517
+ EXPECT_EQ(unescapeStringLiteral("\\u0041"), "A");
518
+ EXPECT_EQ(unescapeStringLiteral("\\u00a9"), "©");
519
+ EXPECT_EQ(unescapeStringLiteral("\\uffff"), "￿");
520
+ EXPECT_EQ(unescapeStringLiteral("\\uFFFF"), "￿"); // hex digits are case-insensitive
521
+ }
522
+
523
+ TEST(StringEscapes, DecodesAsciiHexEscapesOnly) {
524
+ EXPECT_EQ(unescapeStringLiteral("\\x41"), "A");
525
+ EXPECT_EQ(unescapeStringLiteral("\\x7e"), "~");
526
+ EXPECT_EQ(unescapeStringLiteral("\\x7f"), "\x7f");
527
+ // Above 0x7F the reference refuses and the text stands for itself.
528
+ EXPECT_EQ(unescapeStringLiteral("\\x80"), "x80");
529
+ EXPECT_EQ(unescapeStringLiteral("\\xa9"), "xa9");
530
+ EXPECT_EQ(unescapeStringLiteral("\\xff"), "xff");
531
+ }
532
+
533
+ TEST(StringEscapes, AnUnencodableCodePointBecomesASpace) {
534
+ // Confirmed by equality on the reference, since the bytes are hard to
535
+ // read back: "" == " ", "\ud83d" == " " and "\x00" == ""
536
+ // are all true there. chr() does NOT agree -- chr(55357) is the empty
537
+ // string -- so the two paths differ deliberately.
538
+ EXPECT_EQ(unescapeStringLiteral("\\u0000"), " ");
539
+ EXPECT_EQ(unescapeStringLiteral("\\x00"), " ");
540
+ EXPECT_EQ(unescapeStringLiteral("\\ud83d"), " "); // lone high surrogate
541
+ EXPECT_EQ(unescapeStringLiteral("\\udfff"), " "); // lone low surrogate
542
+ }
543
+
544
+ TEST(StringEscapes, AnUndefinedEscapeDropsOnlyItsBackslash) {
545
+ EXPECT_EQ(unescapeStringLiteral("\\q"), "q");
546
+ EXPECT_EQ(unescapeStringLiteral("\\u12"), "u12"); // too few digits
547
+ EXPECT_EQ(unescapeStringLiteral("\\uZZZZ"), "uZZZZ"); // not hex
548
+ EXPECT_EQ(unescapeStringLiteral("\\u{41}"), "u{41}"); // no brace form
549
+ EXPECT_EQ(unescapeStringLiteral("\\x4"), "x4");
550
+ EXPECT_EQ(unescapeStringLiteral("\\xg1"), "xg1");
551
+ }
552
+
553
+ TEST(StringEscapes, LeavesTheAlreadyHandledEscapesAlone) {
554
+ EXPECT_EQ(unescapeStringLiteral("\\n"), "\n");
555
+ EXPECT_EQ(unescapeStringLiteral("\\t"), "\t");
556
+ EXPECT_EQ(unescapeStringLiteral("\\r"), "\r");
557
+ EXPECT_EQ(unescapeStringLiteral("\\\\"), "\\");
558
+ EXPECT_EQ(unescapeStringLiteral("\\\""), "\"");
559
+ EXPECT_EQ(unescapeStringLiteral("plain"), "plain");
560
+ }
561
+
562
+ TEST(StringEscapes, ATruncatedEscapeDoesNotReadPastTheEnd) {
563
+ // \u and \x look ahead four and two characters; one cut short at the
564
+ // end of the literal must stop rather than run off the buffer.
565
+ EXPECT_EQ(unescapeStringLiteral("\\u"), "u");
566
+ EXPECT_EQ(unescapeStringLiteral("\\u0"), "u0");
567
+ EXPECT_EQ(unescapeStringLiteral("\\u03a"), "u03a");
568
+ EXPECT_EQ(unescapeStringLiteral("\\x"), "x");
569
+ EXPECT_EQ(unescapeStringLiteral("\\x4"), "x4");
570
+ EXPECT_EQ(unescapeStringLiteral("\\"), "\\");
571
+ }
572
+
573
+ TEST(StringEscapes, ARawNewlineInsideALiteralContributesNothing) {
574
+ // Writing a string across two source lines joins them, keeping the
575
+ // second line's indentation -- this is how OpenSCAD lets a long string
576
+ // be wrapped, and it is NOT a backslash continuation.
577
+ //
578
+ // s = "abcd
579
+ // efgh"; // -> "abcd efgh", 12 characters
580
+ EXPECT_EQ(unescapeStringLiteral("abcd\n efgh"), "abcd efgh");
581
+ EXPECT_EQ(unescapeStringLiteral("x\ny"), "xy");
582
+ EXPECT_EQ(unescapeStringLiteral("x\n\ny"), "xy");
583
+ EXPECT_EQ(unescapeStringLiteral("x\n \n y"), "x y");
584
+ }
585
+
586
+ TEST(StringEscapes, ARawCarriageReturnStandsForItself) {
587
+ // Only the LF is special. A CRLF file therefore leaves the CR in the
588
+ // string: "x<CR><LF>y" is three characters, not two.
589
+ EXPECT_EQ(unescapeStringLiteral("x\ry"), "x\ry");
590
+ EXPECT_EQ(unescapeStringLiteral("x\r\ny"), "x\ry");
591
+ }
592
+
593
+ TEST(StringEscapes, ABackslashBeforeANewlineTakesTheWholeLineEnding) {
594
+ // The reference has no line-continuation escape at all: it calls the
595
+ // backslash an undefined escape, drops only it, then applies the
596
+ // ordinary newline rules -- so "x\\<CR><LF>y" is 3 characters there,
597
+ // the CR surviving. This port takes the whole line ending instead, on
598
+ // purpose, so a string wrapped in a Windows-line-ending file does not
599
+ // pick up a stray CR. Same reasoning as
600
+ // BackslashNewlineContributesNothing above.
601
+ EXPECT_EQ(unescapeStringLiteral("x\\\ny"), "xy");
602
+ EXPECT_EQ(unescapeStringLiteral("x\\\r\ny"), "xy"); // reference keeps the CR
603
+ EXPECT_EQ(unescapeStringLiteral("x\\\ry"), "x\ry");
604
+ }