synalog 2.1.0__tar.gz → 2.2.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 (188) hide show
  1. {synalog-2.1.0 → synalog-2.2.0}/Cargo.lock +1 -1
  2. {synalog-2.1.0 → synalog-2.2.0}/Cargo.toml +1 -1
  3. {synalog-2.1.0 → synalog-2.2.0}/PKG-INFO +29 -29
  4. {synalog-2.1.0 → synalog-2.2.0}/README.md +28 -28
  5. {synalog-2.1.0 → synalog-2.2.0}/docs/assertions.md +5 -4
  6. synalog-2.2.0/docs/assets/synalog-dark.svg +11 -0
  7. synalog-2.2.0/docs/assets/synalog-light.svg +11 -0
  8. {synalog-2.1.0 → synalog-2.2.0}/docs/cli.md +7 -7
  9. {synalog-2.1.0 → synalog-2.2.0}/docs/differences-datalog.md +8 -8
  10. {synalog-2.1.0 → synalog-2.2.0}/docs/differences.md +2 -2
  11. {synalog-2.1.0 → synalog-2.2.0}/docs/engines.md +1 -1
  12. synalog-2.1.0/docs/examples/shop/concepts/Customer.l → synalog-2.2.0/docs/examples/shop/concepts/customer.l +1 -1
  13. synalog-2.1.0/docs/examples/shop/concepts/OrderStatus.l → synalog-2.2.0/docs/examples/shop/concepts/order_status.l +1 -1
  14. synalog-2.1.0/docs/examples/shop/rules/CustomerRevenue.l → synalog-2.2.0/docs/examples/shop/rules/customer_revenue.l +2 -2
  15. synalog-2.1.0/docs/examples/shop/rules/OrdersByStatus.l → synalog-2.2.0/docs/examples/shop/rules/orders_by_status.l +2 -2
  16. synalog-2.1.0/docs/examples/shop/rules/TopCustomers.l → synalog-2.2.0/docs/examples/shop/rules/top_customers.l +1 -1
  17. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/shop.log +6 -6
  18. synalog-2.1.0/docs/examples/smoke/concepts/Device.l → synalog-2.2.0/docs/examples/smoke/concepts/device.l +1 -1
  19. synalog-2.1.0/docs/examples/smoke/concepts/TestStatus.l → synalog-2.2.0/docs/examples/smoke/concepts/test_status.l +1 -1
  20. synalog-2.1.0/docs/examples/smoke/rules/FailuresByDevice.l → synalog-2.2.0/docs/examples/smoke/rules/failures_by_device.l +2 -2
  21. synalog-2.1.0/docs/examples/smoke/rules/RunsPerDay.l → synalog-2.2.0/docs/examples/smoke/rules/runs_per_day.l +1 -1
  22. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/smoke.log +5 -5
  23. {synalog-2.1.0 → synalog-2.2.0}/docs/getting-started.md +15 -15
  24. {synalog-2.1.0 → synalog-2.2.0}/docs/javascripts/playground.js +2 -2
  25. synalog-2.2.0/docs/javascripts/synalog-highlight.js +302 -0
  26. {synalog-2.1.0 → synalog-2.2.0}/docs/knowledge-graphs.md +24 -24
  27. {synalog-2.1.0 → synalog-2.2.0}/docs/language/aggregation.md +7 -7
  28. {synalog-2.1.0 → synalog-2.2.0}/docs/language/directives.md +8 -8
  29. {synalog-2.1.0 → synalog-2.2.0}/docs/language/functions.md +5 -5
  30. {synalog-2.1.0 → synalog-2.2.0}/docs/language/functors.md +4 -4
  31. {synalog-2.1.0 → synalog-2.2.0}/docs/language/index.md +31 -31
  32. {synalog-2.1.0 → synalog-2.2.0}/docs/language/recursion.md +8 -6
  33. {synalog-2.1.0 → synalog-2.2.0}/docs/language/syntax.md +15 -15
  34. {synalog-2.1.0 → synalog-2.2.0}/docs/language/temporal.md +11 -11
  35. {synalog-2.1.0 → synalog-2.2.0}/docs/python-api.md +35 -4
  36. {synalog-2.1.0 → synalog-2.2.0}/docs/stylesheets/extra.css +49 -0
  37. {synalog-2.1.0 → synalog-2.2.0}/docs/verification.md +5 -4
  38. synalog-2.2.0/img/synalog-dark.svg +11 -0
  39. synalog-2.2.0/img/synalog-light.svg +11 -0
  40. {synalog-2.1.0 → synalog-2.2.0}/pyproject.toml +1 -1
  41. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/__init__.py +4 -0
  42. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/cli.py +2 -2
  43. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/introspect.py +23 -8
  44. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/runners.py +12 -0
  45. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/SKILL.md +34 -33
  46. synalog-2.1.0/skills/synalog/examples/company/concepts/Department.l → synalog-2.2.0/skills/synalog/examples/company/concepts/department.l +1 -1
  47. synalog-2.1.0/skills/synalog/examples/company/concepts/Person.l → synalog-2.2.0/skills/synalog/examples/company/concepts/person.l +1 -1
  48. synalog-2.1.0/skills/synalog/examples/company/concepts/WorksIn.l → synalog-2.2.0/skills/synalog/examples/company/concepts/works_in.l +3 -3
  49. synalog-2.1.0/skills/synalog/examples/company/rules/TeamMate.l → synalog-2.2.0/skills/synalog/examples/company/rules/team_mate.l +3 -3
  50. synalog-2.1.0/skills/synalog/examples/org/concepts/AllManagers.l → synalog-2.2.0/skills/synalog/examples/org/concepts/all_managers.l +1 -1
  51. synalog-2.1.0/skills/synalog/examples/sales/concepts/Region.l → synalog-2.2.0/skills/synalog/examples/sales/concepts/region.l +1 -1
  52. synalog-2.1.0/skills/synalog/examples/sales/rules/MonthlySales.l → synalog-2.2.0/skills/synalog/examples/sales/rules/monthly_sales.l +1 -1
  53. synalog-2.1.0/skills/synalog/examples/sales/rules/TopRegion.l → synalog-2.2.0/skills/synalog/examples/sales/rules/top_region.l +1 -1
  54. synalog-2.1.0/skills/synalog/examples/sales/rules/TotalByRegion.l → synalog-2.2.0/skills/synalog/examples/sales/rules/total_by_region.l +2 -2
  55. {synalog-2.1.0 → synalog-2.2.0}/src/assertion/mod.rs +1 -1
  56. {synalog-2.1.0 → synalog-2.2.0}/src/assertion/parse.rs +10 -0
  57. {synalog-2.1.0 → synalog-2.2.0}/src/assertion/translate.rs +22 -18
  58. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/dialects.rs +8 -4
  59. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/expr_translate_test.rs +12 -0
  60. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/functors.rs +231 -2
  61. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/functors_test.rs +12 -0
  62. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/universe.rs +144 -8
  63. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/universe_test.rs +138 -12
  64. {synalog-2.1.0 → synalog-2.2.0}/src/errors.rs +7 -0
  65. {synalog-2.1.0 → synalog-2.2.0}/src/python.rs +95 -12
  66. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/assertions.rs +5 -0
  67. synalog-2.2.0/src/verifier/dependencies.rs +63 -0
  68. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/directives.rs +3 -2
  69. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/functors.rs +1 -1
  70. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/mod.rs +32 -1
  71. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/undefined.rs +120 -4
  72. {synalog-2.1.0 → synalog-2.2.0}/src/wasm.rs +13 -2
  73. {synalog-2.1.0 → synalog-2.2.0}/uv.lock +1 -1
  74. {synalog-2.1.0 → synalog-2.2.0}/zensical.toml +4 -2
  75. synalog-2.1.0/docs/assets/synalog-dark.svg +0 -11
  76. synalog-2.1.0/docs/assets/synalog-light.svg +0 -11
  77. synalog-2.1.0/docs/examples/axioms.l +0 -122
  78. synalog-2.1.0/docs/examples/axioms.log +0 -12968
  79. synalog-2.1.0/docs/examples/axioms.ttl +0 -68
  80. synalog-2.1.0/img/synalog-dark.svg +0 -11
  81. synalog-2.1.0/img/synalog-light.svg +0 -11
  82. {synalog-2.1.0 → synalog-2.2.0}/.gitignore +0 -0
  83. {synalog-2.1.0 → synalog-2.2.0}/LICENSE +0 -0
  84. {synalog-2.1.0 → synalog-2.2.0}/NOTICE +0 -0
  85. {synalog-2.1.0 → synalog-2.2.0}/docs/assets/synalog_favicon.png +0 -0
  86. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/results.json +0 -0
  87. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/speedup_by_engine.png +0 -0
  88. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/speedup_distribution.png +0 -0
  89. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/summary.md +0 -0
  90. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/summary.png +0 -0
  91. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/time_comparison.png +0 -0
  92. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark/verify_by_engine.png +0 -0
  93. {synalog-2.1.0 → synalog-2.2.0}/docs/benchmark.md +0 -0
  94. {synalog-2.1.0 → synalog-2.2.0}/docs/development.md +0 -0
  95. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/aggregation.l +0 -0
  96. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/aggregation.log +0 -0
  97. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/directives.l +0 -0
  98. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/directives.log +0 -0
  99. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/functions.l +0 -0
  100. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/functions.log +0 -0
  101. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/functors.l +0 -0
  102. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/functors.log +0 -0
  103. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/getting_started.l +0 -0
  104. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/getting_started.log +0 -0
  105. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/knowledge_graphs.l +0 -0
  106. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/knowledge_graphs.log +0 -0
  107. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/recursion.l +0 -0
  108. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/recursion.log +0 -0
  109. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/run.py +0 -0
  110. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/shop/data/orders.csv +0 -0
  111. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/shop/layer.toml +0 -0
  112. /synalog-2.1.0/docs/examples/shop/tables/Orders.l → /synalog-2.2.0/docs/examples/shop/tables/orders.l +0 -0
  113. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/smoke/data/smoke_tests.csv +0 -0
  114. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/smoke/layer.toml +0 -0
  115. /synalog-2.1.0/docs/examples/smoke/tables/SmokeTests.l → /synalog-2.2.0/docs/examples/smoke/tables/smoke_tests.l +0 -0
  116. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/syntax.l +0 -0
  117. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/syntax.log +0 -0
  118. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/temporal.l +0 -0
  119. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/temporal.log +0 -0
  120. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/temporal_graph.l +0 -0
  121. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/temporal_graph.log +0 -0
  122. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/verification.l +0 -0
  123. {synalog-2.1.0 → synalog-2.2.0}/docs/examples/verification.log +0 -0
  124. {synalog-2.1.0 → synalog-2.2.0}/docs/index.md +0 -0
  125. {synalog-2.1.0 → synalog-2.2.0}/docs/javascripts/summarize.js +0 -0
  126. {synalog-2.1.0 → synalog-2.2.0}/docs/playground/vendor/codemirror.js +0 -0
  127. {synalog-2.1.0 → synalog-2.2.0}/docs/playground.md +0 -0
  128. {synalog-2.1.0 → synalog-2.2.0}/docs/why.md +0 -0
  129. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/__main__.py +0 -0
  130. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/checking.py +0 -0
  131. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/config.py +0 -0
  132. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/execution.py +0 -0
  133. {synalog-2.1.0 → synalog-2.2.0}/python/synalog/project.py +0 -0
  134. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/company/data/departments.csv +0 -0
  135. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/company/data/employees.csv +0 -0
  136. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/company/layer.toml +0 -0
  137. /synalog-2.1.0/skills/synalog/examples/company/tables/Departments.l → /synalog-2.2.0/skills/synalog/examples/company/tables/departments.l +0 -0
  138. /synalog-2.1.0/skills/synalog/examples/company/tables/Employees.l → /synalog-2.2.0/skills/synalog/examples/company/tables/employees.l +0 -0
  139. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/org/data/employees.csv +0 -0
  140. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/org/layer.toml +0 -0
  141. /synalog-2.1.0/skills/synalog/examples/org/tables/Employees.l → /synalog-2.2.0/skills/synalog/examples/org/tables/employees.l +0 -0
  142. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/sales/data/sales.csv +0 -0
  143. {synalog-2.1.0 → synalog-2.2.0}/skills/synalog/examples/sales/layer.toml +0 -0
  144. /synalog-2.1.0/skills/synalog/examples/sales/tables/Sales.l → /synalog-2.2.0/skills/synalog/examples/sales/tables/sales.l +0 -0
  145. {synalog-2.1.0 → synalog-2.2.0}/src/assertion/report.rs +0 -0
  146. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/annotations.rs +0 -0
  147. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/annotations_test.rs +0 -0
  148. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/concertina.rs +0 -0
  149. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/dialects_test.rs +0 -0
  150. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/expr_translate.rs +0 -0
  151. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/mod.rs +0 -0
  152. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/program.rs +0 -0
  153. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/program_test.rs +0 -0
  154. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/rule_translate.rs +0 -0
  155. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/rule_translate_test.rs +0 -0
  156. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/builder.rs +0 -0
  157. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/built_in.rs +0 -0
  158. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/edge.rs +0 -0
  159. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/expression.rs +0 -0
  160. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/graph.rs +0 -0
  161. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/inference.rs +0 -0
  162. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/intersection.rs +0 -0
  163. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/mod.rs +0 -0
  164. {synalog-2.1.0 → synalog-2.2.0}/src/compiler/type_inference/types.rs +0 -0
  165. {synalog-2.1.0 → synalog-2.2.0}/src/lib.rs +0 -0
  166. {synalog-2.1.0 → synalog-2.2.0}/src/parser/json.rs +0 -0
  167. {synalog-2.1.0 → synalog-2.2.0}/src/parser/json_test.rs +0 -0
  168. {synalog-2.1.0 → synalog-2.2.0}/src/parser/mod.rs +0 -0
  169. {synalog-2.1.0 → synalog-2.2.0}/src/parser/names.rs +0 -0
  170. {synalog-2.1.0 → synalog-2.2.0}/src/parser/parse.rs +0 -0
  171. {synalog-2.1.0 → synalog-2.2.0}/src/parser/parse_test.rs +0 -0
  172. {synalog-2.1.0 → synalog-2.2.0}/src/parser/rewrite.rs +0 -0
  173. {synalog-2.1.0 → synalog-2.2.0}/src/parser/rewrite_test.rs +0 -0
  174. {synalog-2.1.0 → synalog-2.2.0}/src/parser/span.rs +0 -0
  175. {synalog-2.1.0 → synalog-2.2.0}/src/parser/span_test.rs +0 -0
  176. {synalog-2.1.0 → synalog-2.2.0}/src/parser/traverse.rs +0 -0
  177. {synalog-2.1.0 → synalog-2.2.0}/src/parser/traverse_test.rs +0 -0
  178. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/arity.rs +0 -0
  179. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/contradiction.rs +0 -0
  180. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/front_matter.rs +0 -0
  181. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/orderby.rs +0 -0
  182. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/positional.rs +0 -0
  183. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/recursion.rs +0 -0
  184. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/reserved.rs +0 -0
  185. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/safety.rs +0 -0
  186. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/sqlexpr.rs +0 -0
  187. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/stratification.rs +0 -0
  188. {synalog-2.1.0 → synalog-2.2.0}/src/verifier/vars.rs +0 -0
@@ -2348,7 +2348,7 @@ dependencies = [
2348
2348
 
2349
2349
  [[package]]
2350
2350
  name = "synalog"
2351
- version = "2.1.0"
2351
+ version = "2.2.0"
2352
2352
  dependencies = [
2353
2353
  "anyhow",
2354
2354
  "duckdb",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "synalog"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  edition = "2024"
5
5
  description = "Logic programming for AI agents: Datalog-family language compiling to optimized SQL"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synalog
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: 3
@@ -289,18 +289,18 @@ A Synalog program is a **project**: a folder with a `layer.toml`, holding one pr
289
289
 
290
290
  ```
291
291
  shop/
292
- ├── tables/Orders.l # the data: one file per database table
293
- ├── concepts/Customer.l # entities and relationships extracted from tables
294
- ├── rules/CustomerRevenue.l # insights derived from concepts
295
- ├── rules/TopCustomers.l
292
+ ├── tables/orders.l # the data: one file per database table
293
+ ├── concepts/customer.l # entities and relationships extracted from tables
294
+ ├── rules/customer_revenue.l # insights derived from concepts
295
+ ├── rules/top_customers.l
296
296
  └── layer.toml # the project's name, description and database
297
297
  ```
298
298
 
299
299
  Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
300
300
 
301
- `tables/Orders.l`
301
+ `tables/orders.l`
302
302
 
303
- ```logica
303
+ ```synalog
304
304
  ---
305
305
  name: Orders
306
306
  description: One row per order, with its customer, amount and status.
@@ -310,41 +310,41 @@ Orders(order_id:, customer_id:, amount:, status:) :-
310
310
  orders(order_id:, customer_id:, amount:, status:);
311
311
  ```
312
312
 
313
- `concepts/Customer.l`
313
+ `concepts/customer.l`
314
314
 
315
- ```logica
315
+ ```synalog
316
316
  ---
317
317
  name: Customer
318
318
  description: Every customer who placed at least one order.
319
319
  ---
320
- import tables.Orders.Orders;
320
+ import tables.orders.Orders;
321
321
 
322
322
  @OrderBy(Customer, "customer_id");
323
323
  Customer(customer_id:) distinct :- Orders(customer_id:);
324
324
  ```
325
325
 
326
- `rules/CustomerRevenue.l`
326
+ `rules/customer_revenue.l`
327
327
 
328
- ```logica
328
+ ```synalog
329
329
  ---
330
330
  name: CustomerRevenue
331
331
  description: Total amount ordered by each customer, all statuses included.
332
332
  ---
333
- import concepts.Customer.Customer;
334
- import tables.Orders.Orders;
333
+ import concepts.customer.Customer;
334
+ import tables.orders.Orders;
335
335
 
336
336
  @OrderBy(CustomerRevenue, "customer_id");
337
337
  CustomerRevenue(customer_id:, total? += amount) distinct :-
338
338
  Customer(customer_id:), Orders(customer_id:, amount:);
339
339
  ```
340
340
 
341
- Imports resolve from the project's folder, so every file runs on its own: `synalog rules/CustomerRevenue.l run CustomerRevenue`. The snippets below show the language itself, one rule at a time.
341
+ Imports resolve from the project's folder, so every file runs on its own: `synalog rules/customer_revenue.l run CustomerRevenue`. The snippets below show the language itself, one rule at a time.
342
342
 
343
343
  ### Named arguments
344
344
 
345
345
  Synalog uses **named arguments only** (no positional arguments). The left side of `:` is the column name, the right side is the variable:
346
346
 
347
- ```logica
347
+ ```synalog
348
348
  # column "amount" bound to variable "total"
349
349
  Orders(amount: total)
350
350
 
@@ -356,7 +356,7 @@ Orders(amount:)
356
356
 
357
357
  Variables are defined with `==`. Arithmetic, string and comparison operators are supported:
358
358
 
359
- ```logica
359
+ ```synalog
360
360
  OrderWithTax(order_id:, total:) :-
361
361
  Orders(order_id:, amount:),
362
362
  total == amount * 1.10;
@@ -368,7 +368,7 @@ OrderWithTax(order_id:, total:) :-
368
368
 
369
369
  Aggregation uses the `distinct` keyword and special operators in the rule head:
370
370
 
371
- ```logica
371
+ ```synalog
372
372
  # Sum
373
373
  Revenue(total? += amount) distinct :- Orders(amount:);
374
374
 
@@ -392,7 +392,7 @@ TopSeller(name? ArgMax= name -> revenue) distinct :- Sales(name:, revenue:);
392
392
 
393
393
  ### Logical operators
394
394
 
395
- ```logica
395
+ ```synalog
396
396
  # Conjunction (AND), comma
397
397
  Result(x:, y:) :- TableA(x:), TableB(x:, y:);
398
398
 
@@ -405,14 +405,14 @@ Inactive(user_id:) :- Users(user_id:), ~Logins(user_id:);
405
405
 
406
406
  Multiple rule definitions for the same predicate combine results (union):
407
407
 
408
- ```logica
408
+ ```synalog
409
409
  HighValue(user_id:) :- Orders(user_id:, amount:), amount > 10000;
410
410
  HighValue(user_id:) :- Referrals(user_id:, tier: "vip");
411
411
  ```
412
412
 
413
413
  ### Conditionals
414
414
 
415
- ```logica
415
+ ```synalog
416
416
  OrderSize(order_id:, size:) :-
417
417
  Orders(order_id:, amount:),
418
418
  size == (if amount > 1000 then "large"
@@ -424,7 +424,7 @@ OrderSize(order_id:, size:) :-
424
424
 
425
425
  Directives control predicate behavior and **must be placed before** the rule definition:
426
426
 
427
- ```logica
427
+ ```synalog
428
428
  @OrderBy(TopCustomers, "total", "DESC");
429
429
  @Limit(TopCustomers, 10);
430
430
  TopCustomers(customer_id:, total? += amount) distinct :- Orders(customer_id:, amount:);
@@ -442,7 +442,7 @@ TopCustomers(customer_id:, total? += amount) distinct :- Orders(customer_id:, am
442
442
 
443
443
  Functors let you reuse predicate logic by parameterizing input predicates:
444
444
 
445
- ```logica
445
+ ```synalog
446
446
  # Define a reusable pattern
447
447
  @OrderBy(SegmentRevenue, "segment_id");
448
448
  SegmentRevenue(segment_id:, total? += amount) distinct :-
@@ -458,7 +458,7 @@ SMBRevenue := SegmentRevenue(Segment: SMBCustomers);
458
458
 
459
459
  Recursive predicates compute transitive closures, for example finding all managers above an employee:
460
460
 
461
- ```logica
461
+ ```synalog
462
462
  @Recursive(AllManagers, 20);
463
463
 
464
464
  # Base case: direct manager
@@ -476,7 +476,7 @@ Useful for: referral chains, org charts, product taxonomies, bill of materials.
476
476
 
477
477
  Find shortest paths in weighted graphs by enumerating route costs recursively, then keeping the minimum per destination with a `Min=` aggregation:
478
478
 
479
- ```logica
479
+ ```synalog
480
480
  # Enumerate route costs from the origin, hop by hop.
481
481
  @Recursive(RouteCost, 10);
482
482
  RouteCost(destination:, cost:) :-
@@ -495,7 +495,7 @@ ShippingCost(destination:, total? Min= cost) distinct :- RouteCost(destination:,
495
495
 
496
496
  When working with timestamps or dates, always convert to string first:
497
497
 
498
- ```logica
498
+ ```synalog
499
499
  @OrderBy(MonthlyOrders, "month");
500
500
  MonthlyOrders(month:, count? += 1) distinct :-
501
501
  Orders(created_at:),
@@ -509,7 +509,7 @@ RecentOrders(order_id:) :-
509
509
 
510
510
  `Today(date:)` (today's date as `"YYYY-MM-DD"`) and `Now(timestamp:)` (the current instant as the engine's native timestamp) are built-in concepts. They are inlined per dialect by the compiler, with no runtime table needed, so they work on every engine. `Now` is the most precise value; derive coarser parts (date, time, hour) from it through the `ToString` → `Substr` pipeline. Join against `Today` whenever a rule needs "today":
511
511
 
512
- ```logica
512
+ ```synalog
513
513
  @OrderBy(ThisMonthOrders, "order_id");
514
514
  ThisMonthOrders(order_id:, created_at:) :-
515
515
  Orders(order_id:, created_at:),
@@ -523,7 +523,7 @@ They are reserved names: you cannot redefine, extend, or update them.
523
523
 
524
524
  Entities and relationships become node and edge concepts, and rules traverse them. Edges join **through** the node concepts rather than the raw table, so a filter on a node applies to every edge built on it:
525
525
 
526
- ```logica
526
+ ```synalog
527
527
  @OrderBy(Person, "person_id");
528
528
  Person(person_id:, name:, profile_url:) distinct :- Employees(person_id:, name:, profile_url:);
529
529
 
@@ -536,7 +536,7 @@ WorksIn(person_id:, department_id:) distinct :-
536
536
 
537
537
  Relationships with a lifetime carry it as a half-open interval `[valid_from, valid_to)`, using `"9999-12-31"` as the open end so ISO strings compare correctly, and `Today` supplies the clock for point-in-time questions:
538
538
 
539
- ```logica
539
+ ```synalog
540
540
  @OrderBy(CurrentEmployment, "person_id");
541
541
  CurrentEmployment(person_id:, company_id:, role:) distinct :-
542
542
  EmployedAt(person_id:, company_id:, role:, valid_from:, valid_to:),
@@ -247,18 +247,18 @@ A Synalog program is a **project**: a folder with a `layer.toml`, holding one pr
247
247
 
248
248
  ```
249
249
  shop/
250
- ├── tables/Orders.l # the data: one file per database table
251
- ├── concepts/Customer.l # entities and relationships extracted from tables
252
- ├── rules/CustomerRevenue.l # insights derived from concepts
253
- ├── rules/TopCustomers.l
250
+ ├── tables/orders.l # the data: one file per database table
251
+ ├── concepts/customer.l # entities and relationships extracted from tables
252
+ ├── rules/customer_revenue.l # insights derived from concepts
253
+ ├── rules/top_customers.l
254
254
  └── layer.toml # the project's name, description and database
255
255
  ```
256
256
 
257
257
  Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
258
258
 
259
- `tables/Orders.l`
259
+ `tables/orders.l`
260
260
 
261
- ```logica
261
+ ```synalog
262
262
  ---
263
263
  name: Orders
264
264
  description: One row per order, with its customer, amount and status.
@@ -268,41 +268,41 @@ Orders(order_id:, customer_id:, amount:, status:) :-
268
268
  orders(order_id:, customer_id:, amount:, status:);
269
269
  ```
270
270
 
271
- `concepts/Customer.l`
271
+ `concepts/customer.l`
272
272
 
273
- ```logica
273
+ ```synalog
274
274
  ---
275
275
  name: Customer
276
276
  description: Every customer who placed at least one order.
277
277
  ---
278
- import tables.Orders.Orders;
278
+ import tables.orders.Orders;
279
279
 
280
280
  @OrderBy(Customer, "customer_id");
281
281
  Customer(customer_id:) distinct :- Orders(customer_id:);
282
282
  ```
283
283
 
284
- `rules/CustomerRevenue.l`
284
+ `rules/customer_revenue.l`
285
285
 
286
- ```logica
286
+ ```synalog
287
287
  ---
288
288
  name: CustomerRevenue
289
289
  description: Total amount ordered by each customer, all statuses included.
290
290
  ---
291
- import concepts.Customer.Customer;
292
- import tables.Orders.Orders;
291
+ import concepts.customer.Customer;
292
+ import tables.orders.Orders;
293
293
 
294
294
  @OrderBy(CustomerRevenue, "customer_id");
295
295
  CustomerRevenue(customer_id:, total? += amount) distinct :-
296
296
  Customer(customer_id:), Orders(customer_id:, amount:);
297
297
  ```
298
298
 
299
- Imports resolve from the project's folder, so every file runs on its own: `synalog rules/CustomerRevenue.l run CustomerRevenue`. The snippets below show the language itself, one rule at a time.
299
+ Imports resolve from the project's folder, so every file runs on its own: `synalog rules/customer_revenue.l run CustomerRevenue`. The snippets below show the language itself, one rule at a time.
300
300
 
301
301
  ### Named arguments
302
302
 
303
303
  Synalog uses **named arguments only** (no positional arguments). The left side of `:` is the column name, the right side is the variable:
304
304
 
305
- ```logica
305
+ ```synalog
306
306
  # column "amount" bound to variable "total"
307
307
  Orders(amount: total)
308
308
 
@@ -314,7 +314,7 @@ Orders(amount:)
314
314
 
315
315
  Variables are defined with `==`. Arithmetic, string and comparison operators are supported:
316
316
 
317
- ```logica
317
+ ```synalog
318
318
  OrderWithTax(order_id:, total:) :-
319
319
  Orders(order_id:, amount:),
320
320
  total == amount * 1.10;
@@ -326,7 +326,7 @@ OrderWithTax(order_id:, total:) :-
326
326
 
327
327
  Aggregation uses the `distinct` keyword and special operators in the rule head:
328
328
 
329
- ```logica
329
+ ```synalog
330
330
  # Sum
331
331
  Revenue(total? += amount) distinct :- Orders(amount:);
332
332
 
@@ -350,7 +350,7 @@ TopSeller(name? ArgMax= name -> revenue) distinct :- Sales(name:, revenue:);
350
350
 
351
351
  ### Logical operators
352
352
 
353
- ```logica
353
+ ```synalog
354
354
  # Conjunction (AND), comma
355
355
  Result(x:, y:) :- TableA(x:), TableB(x:, y:);
356
356
 
@@ -363,14 +363,14 @@ Inactive(user_id:) :- Users(user_id:), ~Logins(user_id:);
363
363
 
364
364
  Multiple rule definitions for the same predicate combine results (union):
365
365
 
366
- ```logica
366
+ ```synalog
367
367
  HighValue(user_id:) :- Orders(user_id:, amount:), amount > 10000;
368
368
  HighValue(user_id:) :- Referrals(user_id:, tier: "vip");
369
369
  ```
370
370
 
371
371
  ### Conditionals
372
372
 
373
- ```logica
373
+ ```synalog
374
374
  OrderSize(order_id:, size:) :-
375
375
  Orders(order_id:, amount:),
376
376
  size == (if amount > 1000 then "large"
@@ -382,7 +382,7 @@ OrderSize(order_id:, size:) :-
382
382
 
383
383
  Directives control predicate behavior and **must be placed before** the rule definition:
384
384
 
385
- ```logica
385
+ ```synalog
386
386
  @OrderBy(TopCustomers, "total", "DESC");
387
387
  @Limit(TopCustomers, 10);
388
388
  TopCustomers(customer_id:, total? += amount) distinct :- Orders(customer_id:, amount:);
@@ -400,7 +400,7 @@ TopCustomers(customer_id:, total? += amount) distinct :- Orders(customer_id:, am
400
400
 
401
401
  Functors let you reuse predicate logic by parameterizing input predicates:
402
402
 
403
- ```logica
403
+ ```synalog
404
404
  # Define a reusable pattern
405
405
  @OrderBy(SegmentRevenue, "segment_id");
406
406
  SegmentRevenue(segment_id:, total? += amount) distinct :-
@@ -416,7 +416,7 @@ SMBRevenue := SegmentRevenue(Segment: SMBCustomers);
416
416
 
417
417
  Recursive predicates compute transitive closures, for example finding all managers above an employee:
418
418
 
419
- ```logica
419
+ ```synalog
420
420
  @Recursive(AllManagers, 20);
421
421
 
422
422
  # Base case: direct manager
@@ -434,7 +434,7 @@ Useful for: referral chains, org charts, product taxonomies, bill of materials.
434
434
 
435
435
  Find shortest paths in weighted graphs by enumerating route costs recursively, then keeping the minimum per destination with a `Min=` aggregation:
436
436
 
437
- ```logica
437
+ ```synalog
438
438
  # Enumerate route costs from the origin, hop by hop.
439
439
  @Recursive(RouteCost, 10);
440
440
  RouteCost(destination:, cost:) :-
@@ -453,7 +453,7 @@ ShippingCost(destination:, total? Min= cost) distinct :- RouteCost(destination:,
453
453
 
454
454
  When working with timestamps or dates, always convert to string first:
455
455
 
456
- ```logica
456
+ ```synalog
457
457
  @OrderBy(MonthlyOrders, "month");
458
458
  MonthlyOrders(month:, count? += 1) distinct :-
459
459
  Orders(created_at:),
@@ -467,7 +467,7 @@ RecentOrders(order_id:) :-
467
467
 
468
468
  `Today(date:)` (today's date as `"YYYY-MM-DD"`) and `Now(timestamp:)` (the current instant as the engine's native timestamp) are built-in concepts. They are inlined per dialect by the compiler, with no runtime table needed, so they work on every engine. `Now` is the most precise value; derive coarser parts (date, time, hour) from it through the `ToString` → `Substr` pipeline. Join against `Today` whenever a rule needs "today":
469
469
 
470
- ```logica
470
+ ```synalog
471
471
  @OrderBy(ThisMonthOrders, "order_id");
472
472
  ThisMonthOrders(order_id:, created_at:) :-
473
473
  Orders(order_id:, created_at:),
@@ -481,7 +481,7 @@ They are reserved names: you cannot redefine, extend, or update them.
481
481
 
482
482
  Entities and relationships become node and edge concepts, and rules traverse them. Edges join **through** the node concepts rather than the raw table, so a filter on a node applies to every edge built on it:
483
483
 
484
- ```logica
484
+ ```synalog
485
485
  @OrderBy(Person, "person_id");
486
486
  Person(person_id:, name:, profile_url:) distinct :- Employees(person_id:, name:, profile_url:);
487
487
 
@@ -494,7 +494,7 @@ WorksIn(person_id:, department_id:) distinct :-
494
494
 
495
495
  Relationships with a lifetime carry it as a half-open interval `[valid_from, valid_to)`, using `"9999-12-31"` as the open end so ISO strings compare correctly, and `Today` supplies the clock for point-in-time questions:
496
496
 
497
- ```logica
497
+ ```synalog
498
498
  @OrderBy(CurrentEmployment, "person_id");
499
499
  CurrentEmployment(person_id:, company_id:, role:) distinct :-
500
500
  EmployedAt(person_id:, company_id:, role:, valid_from:, valid_to:),
@@ -9,7 +9,7 @@ Assertions are written in first-order logic, not in Synalog. A rule and its asse
9
9
 
10
10
  ## A first assertion
11
11
 
12
- ```logica
12
+ ```synalog
13
13
  Parent(x:, y:) :- parents(parent: x, child: y);
14
14
 
15
15
  @Assert(Ancestor,
@@ -48,7 +48,7 @@ Carol is Alice's grandchild, Erin is Carol's, but Erin is Alice's great-grandchi
48
48
 
49
49
  `@Assert` takes the predicate the properties are about, then one named argument per property: the name identifies the property in reports, the string is the statement.
50
50
 
51
- ```logica
51
+ ```synalog
52
52
  @Assert(Revenue,
53
53
  one_row_per_customer: "∀ c r s, Revenue c r → Revenue c s → r = s",
54
54
  positive: "∀ c, Revenue c > 0");
@@ -57,6 +57,7 @@ Carol is Alice's grandchild, Erin is Carol's, but Erin is Alice's great-grandchi
57
57
  - A predicate may have several `@Assert` annotations; a name may appear only once per predicate.
58
58
  - A statement that spans several lines goes in a triple-quoted string (`"""..."""`).
59
59
  - An assertion can be written before its predicate exists: it waits, as `pending`, until the predicate is defined. Write the contract first.
60
+ - Once its predicate exists, an assertion naming a predicate that nothing defines or imports is a warning: a misspelled name, or a missing import, would leave it pending, never checked.
60
61
  - Assertions do not change the generated SQL.
61
62
 
62
63
  ## The statement language
@@ -74,7 +75,7 @@ Statements are written as [Lean](https://lean-lang.org/) propositions, with the
74
75
  | `+`, `-`, `*`, `/` | | arithmetic |
75
76
  | `∑ x, t` | `sum x, t` | sum of `t` over `x` |
76
77
 
77
- ```logica
78
+ ```synalog
78
79
  @Assert(Revenue, consistent: """forall c r s,
79
80
  Revenue c r -> Revenue c s -> r = s""");
80
81
  ```
@@ -92,7 +93,7 @@ A name bound by a quantifier is a variable; an unbound lowercase name is univers
92
93
 
93
94
  The properties worth stating are the ones a rule can silently get wrong. All of these run as written against the tables `orders(order_id, customer_id, amount, status)` and `customers(customer_id, country)`:
94
95
 
95
- ```logica
96
+ ```synalog
96
97
  Order(order_id:, customer_id:, amount:, status:) :- orders(order_id:, customer_id:, amount:, status:);
97
98
  Customer(customer_id:, country:) :- customers(customer_id:, country:);
98
99
 
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 100 100" role="img" aria-label="Synalog">
2
+ <title>Synalog</title>
3
+ <!-- A Hilbert curve of order 3: one line made of smaller copies of itself. -->
4
+ <defs>
5
+ <linearGradient id="synalog" gradientUnits="userSpaceOnUse" x1="14.86" y1="14.86" x2="85.14" y2="85.14">
6
+ <stop offset="0" stop-color="#f0e3fb"/>
7
+ <stop offset="1" stop-color="#a271d6"/>
8
+ </linearGradient>
9
+ </defs>
10
+ <path d="M14.86 14.86 L14.86 24.90 L24.90 24.90 L24.90 14.86 L34.94 14.86 L44.98 14.86 L44.98 24.90 L34.94 24.90 L34.94 34.94 L44.98 34.94 L44.98 44.98 L34.94 44.98 L24.90 44.98 L24.90 34.94 L14.86 34.94 L14.86 44.98 L14.86 55.02 L24.90 55.02 L24.90 65.06 L14.86 65.06 L14.86 75.10 L14.86 85.14 L24.90 85.14 L24.90 75.10 L34.94 75.10 L34.94 85.14 L44.98 85.14 L44.98 75.10 L44.98 65.06 L34.94 65.06 L34.94 55.02 L44.98 55.02 L55.02 55.02 L65.06 55.02 L65.06 65.06 L55.02 65.06 L55.02 75.10 L55.02 85.14 L65.06 85.14 L65.06 75.10 L75.10 75.10 L75.10 85.14 L85.14 85.14 L85.14 75.10 L85.14 65.06 L75.10 65.06 L75.10 55.02 L85.14 55.02 L85.14 44.98 L85.14 34.94 L75.10 34.94 L75.10 44.98 L65.06 44.98 L55.02 44.98 L55.02 34.94 L65.06 34.94 L65.06 24.90 L55.02 24.90 L55.02 14.86 L65.06 14.86 L75.10 14.86 L75.10 24.90 L85.14 24.90 L85.14 14.86" fill="none" stroke="url(#synalog)" stroke-width="5.71" stroke-linecap="round" stroke-linejoin="round"/>
11
+ </svg>
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 100 100" role="img" aria-label="Synalog">
2
+ <title>Synalog</title>
3
+ <!-- A Hilbert curve of order 3: one line made of smaller copies of itself. -->
4
+ <defs>
5
+ <linearGradient id="synalog" gradientUnits="userSpaceOnUse" x1="14.86" y1="14.86" x2="85.14" y2="85.14">
6
+ <stop offset="0" stop-color="#4e2275"/>
7
+ <stop offset="1" stop-color="#b07ddb"/>
8
+ </linearGradient>
9
+ </defs>
10
+ <path d="M14.86 14.86 L14.86 24.90 L24.90 24.90 L24.90 14.86 L34.94 14.86 L44.98 14.86 L44.98 24.90 L34.94 24.90 L34.94 34.94 L44.98 34.94 L44.98 44.98 L34.94 44.98 L24.90 44.98 L24.90 34.94 L14.86 34.94 L14.86 44.98 L14.86 55.02 L24.90 55.02 L24.90 65.06 L14.86 65.06 L14.86 75.10 L14.86 85.14 L24.90 85.14 L24.90 75.10 L34.94 75.10 L34.94 85.14 L44.98 85.14 L44.98 75.10 L44.98 65.06 L34.94 65.06 L34.94 55.02 L44.98 55.02 L55.02 55.02 L65.06 55.02 L65.06 65.06 L55.02 65.06 L55.02 75.10 L55.02 85.14 L65.06 85.14 L65.06 75.10 L75.10 75.10 L75.10 85.14 L85.14 85.14 L85.14 75.10 L85.14 65.06 L75.10 65.06 L75.10 55.02 L85.14 55.02 L85.14 44.98 L85.14 34.94 L75.10 34.94 L75.10 44.98 L65.06 44.98 L55.02 44.98 L55.02 34.94 L65.06 34.94 L65.06 24.90 L55.02 24.90 L55.02 14.86 L65.06 14.86 L75.10 14.86 L75.10 24.90 L85.14 24.90 L85.14 14.86" fill="none" stroke="url(#synalog)" stroke-width="5.71" stroke-linecap="round" stroke-linejoin="round"/>
11
+ </svg>
@@ -99,16 +99,16 @@ duckdb and the PostgreSQL driver ship with synalog. For the other engines (`bigq
99
99
 
100
100
  ### Imports
101
101
 
102
- In a project, `import <folder>.<Name>.<Name>;` statements resolve from the project's folder (the one holding `layer.toml`), whichever file runs and wherever from: `import tables.Orders.Orders;` reads the project's `tables/Orders.l`. The program file's directory and the current directory come next. Pass `--import-root DIR` (repeatable) to search elsewhere; explicit roots replace the defaults.
102
+ In a project, `import <folder>.<name>.<Name>;` statements resolve from the project's folder (the one holding `layer.toml`), whichever file runs and wherever from: `import tables.orders.Orders;` reads the project's `tables/orders.l`. The program file's directory and the current directory come next. Pass `--import-root DIR` (repeatable) to search elsewhere; explicit roots replace the defaults.
103
103
 
104
- In the [`shop` project](language/index.md), `rules/TopCustomers.l` builds on `rules/CustomerRevenue.l`:
104
+ In the [`shop` project](language/index.md), `rules/top_customers.l` builds on `rules/customer_revenue.l`:
105
105
 
106
- ```logica
107
- --8<-- "docs/examples/shop/rules/TopCustomers.l"
106
+ ```synalog
107
+ --8<-- "docs/examples/shop/rules/top_customers.l"
108
108
  ```
109
109
 
110
110
  ```console
111
- $ synalog rules/TopCustomers.l run TopCustomers --load orders=data/orders.csv
111
+ $ synalog rules/top_customers.l run TopCustomers --load orders=data/orders.csv
112
112
  +-------------+-------+
113
113
  | customer_id | total |
114
114
  +-------------+-------+
@@ -118,7 +118,7 @@ $ synalog rules/TopCustomers.l run TopCustomers --load orders=data/orders.csv
118
118
  2 rows
119
119
  ```
120
120
 
121
- `import rules.CustomerRevenue.CustomerRevenue as Revenue;` imports the same predicate under another name. Directives attached to an imported predicate (its `@OrderBy` here) travel with it.
121
+ `import rules.customer_revenue.CustomerRevenue as Revenue;` imports the same predicate under another name. Directives attached to an imported predicate (its `@OrderBy` here) travel with it.
122
122
 
123
123
  ### Errors
124
124
 
@@ -198,7 +198,7 @@ $ synalog introspect
198
198
  Wrote 2 table file(s) to /path/to/sales/tables
199
199
  ```
200
200
 
201
- ```logica
201
+ ```synalog
202
202
  ---
203
203
  name: PublicOrders
204
204
  description: Orders.
@@ -19,7 +19,7 @@ Synalog belongs to the [Datalog](https://en.wikipedia.org/wiki/Datalog) family:
19
19
 
20
20
  Datalog identifies arguments by position: `edge(X, Y)` means whatever the first and second columns happen to be. Synalog uses [named arguments](language/syntax.md#named-arguments) exclusively:
21
21
 
22
- ```logica
22
+ ```synalog
23
23
  # Datalog: positional, not supported
24
24
  # edge(X, Y) :- node(X), node(Y), link(X, Y).
25
25
 
@@ -39,7 +39,7 @@ The trade is deliberate: you give up an incremental in-memory engine and gain th
39
39
 
40
40
  A Datalog relation is a *set*: duplicates cannot exist. Synalog inherits SQL's *multiset* semantics: a rule body that matches a row twice produces it twice, and the union operator `|` is a `UNION ALL`. Deduplication is opt-in with the `distinct` keyword:
41
41
 
42
- ```logica
42
+ ```synalog
43
43
  # Set semantics, as in Datalog: duplicates removed
44
44
  Customer(customer_id:) distinct :- Orders(customer_id:);
45
45
  ```
@@ -50,7 +50,7 @@ This is why the documentation marks concepts and aggregating rules `distinct` th
50
50
 
51
51
  Pure Datalog restricts terms to constants and variables, with no function symbols, which is exactly what guarantees its termination. Synalog drops that restriction: rule bodies can compute with [arithmetic, string and comparison operators](language/syntax.md#operators), [conditionals](language/syntax.md#conditionals), [records](language/syntax.md#records), arrays, [built-in functions](language/functions.md) and [user-defined functions](language/functions.md#user-defined-functions):
52
52
 
53
- ```logica
53
+ ```synalog
54
54
  OrderSize(order_id:, size:) :-
55
55
  Orders(order_id:, amount:),
56
56
  size == (if amount > 1000 then "large" else "small");
@@ -60,7 +60,7 @@ OrderSize(order_id:, size:) :-
60
60
 
61
61
  Core Datalog has no aggregation; systems that add it bolt it on with varying syntax and semantics. In Synalog [aggregation](language/aggregation.md) is part of the rule head: `+=`, `Min=`, `Max=`, `Avg=`, `List=`, `Set=`, `ArgMax=` and more:
62
62
 
63
- ```logica
63
+ ```synalog
64
64
  @OrderBy(CustomerSpend, "total", "DESC");
65
65
  CustomerSpend(customer_id:, total? += amount) distinct :- Orders(customer_id:, amount:);
66
66
  ```
@@ -71,7 +71,7 @@ The non-aggregated head columns act as the grouping key, like SQL's `GROUP BY`.
71
71
 
72
72
  Datalog recursion always terminates because the Herbrand universe is finite: no function symbols means no new values can ever be created. Synalog's expressions break that guarantee (a recursive rule can compute `cost + hop` forever), so [recursion](language/recursion.md) requires an explicit iteration bound via the [`@Recursive` directive](language/directives.md#recursive):
73
73
 
74
- ```logica
74
+ ```synalog
75
75
  @Recursive(AllManagers, 20);
76
76
  AllManagers(employee_id:, manager_id:) :- Employees(employee_id:, manager_id:);
77
77
  AllManagers(employee_id:, manager_id:) :-
@@ -85,7 +85,7 @@ The limit bounds the number of hops, so even cyclic graphs terminate. For a clos
85
85
 
86
86
  Datalog answers are unordered sets, with no notion of "the first ten results". Synalog adds [`@OrderBy` and `@Limit`](language/directives.md) so results are deterministic and pageable, which is what lets an agent with a limited context window walk a large result set page by page:
87
87
 
88
- ```logica
88
+ ```synalog
89
89
  @OrderBy(TopCustomers, "total DESC");
90
90
  @Limit(TopCustomers, 10);
91
91
  TopCustomers(customer_id:, total:) :- CustomerSpend(customer_id:, total:);
@@ -97,7 +97,7 @@ This is also why `@OrderBy` is mandatory on every concept and rule: without it,
97
97
 
98
98
  Datalog predicates are first-order: a rule cannot take another predicate as a parameter. Synalog's [functors](language/functors.md) allow exactly that, instantiating a generic rule with different predicates:
99
99
 
100
- ```logica
100
+ ```synalog
101
101
  EnterpriseRevenue := SegmentRevenue(Segment: EnterpriseCustomer);
102
102
  SMBRevenue := SegmentRevenue(Segment: SMBCustomer);
103
103
  ```
@@ -112,7 +112,7 @@ The classic Datalog safety conditions, range restriction (every head variable bo
112
112
 
113
113
  Datalog has no way to state, next to a predicate, what it should satisfy; some engines accept integrity constraints, written as Datalog rules that must derive nothing. New in Synalog 2.0, and found in neither Datalog nor Logica, `@Assert` states a property in first-order logic, with quantifiers, implication and sums, in a notation distinct from the rules:
114
114
 
115
- ```logica
115
+ ```synalog
116
116
  @Assert(Ancestor,
117
117
  transitive: "∀ x y z, Ancestor x y → Ancestor y z → Ancestor x z",
118
118
  irreflexive: "∀ x, ¬ Ancestor x x");
@@ -6,7 +6,7 @@ Synalog is a fork of [Logica](https://logica.dev/) with a fast Rust core. It kee
6
6
 
7
7
  Synalog doesn't support positional attributes like Logica or Datalog: it only uses **named attributes**, which reduce agent mistakes. This feature is optional in Logica; Synalog makes it mandatory.
8
8
 
9
- ```logica
9
+ ```synalog
10
10
  # Synalog: always named
11
11
  Employee(name:, salary:)
12
12
 
@@ -30,7 +30,7 @@ Synalog embeds a [formal verifier](verification.md) that catches structural erro
30
30
 
31
31
  New in Synalog 2.0, and not in Logica: `@Assert` states what a predicate must satisfy, in first-order logic written as [Lean](https://lean-lang.org/) propositions, and Synalog checks it against the data by searching for counterexamples. `synalog program.l verify` reports the assertions that are violated, and `run` refuses a program that violates one.
32
32
 
33
- ```logica
33
+ ```synalog
34
34
  @Assert(Ancestor, transitive: "∀ x y z, Ancestor x y → Ancestor y z → Ancestor x z");
35
35
  ```
36
36
 
@@ -12,7 +12,7 @@ Synalog compiles to seven SQL dialects. Select the target with the `@Engine` ann
12
12
  | Presto | `presto` | |
13
13
  | Databricks | `databricks` | Double-quoted string literals |
14
14
 
15
- ```logica
15
+ ```synalog
16
16
  @Engine("psql");
17
17
  ```
18
18
 
@@ -3,7 +3,7 @@ name: Customer
3
3
  description: Every customer who placed at least one order.
4
4
  keywords: [customer, buyer, client]
5
5
  ---
6
- import tables.Orders.Orders;
6
+ import tables.orders.Orders;
7
7
 
8
8
  @OrderBy(Customer, "customer_id");
9
9
  Customer(customer_id:) distinct :- Orders(customer_id:);
@@ -2,7 +2,7 @@
2
2
  name: OrderStatus
3
3
  description: The statuses an order can have (shipped, pending, cancelled).
4
4
  ---
5
- import tables.Orders.Orders;
5
+ import tables.orders.Orders;
6
6
 
7
7
  @OrderBy(OrderStatus, "status");
8
8
  OrderStatus(status:) distinct :- Orders(status:);
@@ -3,8 +3,8 @@ name: CustomerRevenue
3
3
  description: Total amount ordered by each customer, all statuses included.
4
4
  keywords: [revenue, spend, sales]
5
5
  ---
6
- import concepts.Customer.Customer;
7
- import tables.Orders.Orders;
6
+ import concepts.customer.Customer;
7
+ import tables.orders.Orders;
8
8
 
9
9
  @OrderBy(CustomerRevenue, "customer_id");
10
10
  CustomerRevenue(customer_id:, total? += amount) distinct :-
@@ -2,8 +2,8 @@
2
2
  name: OrdersByStatus
3
3
  description: Number of orders per status.
4
4
  ---
5
- import concepts.OrderStatus.OrderStatus;
6
- import tables.Orders.Orders;
5
+ import concepts.order_status.OrderStatus;
6
+ import tables.orders.Orders;
7
7
 
8
8
  @OrderBy(OrdersByStatus, "status");
9
9
  OrdersByStatus(status:, count? += 1) distinct :- OrderStatus(status:), Orders(status:);