HLL (Halachic Logic Language) Reference¶
DSL-First Architecture: Write
.hllfiles tomistaber/dsl/, never directly toontology/. After editing, runpython -m mistaber.dsl.buildto compile. Theontology/directory is 100% generated output. The predicate registrybase.yamlis auto-generated from@declare/@sort/@enumdirectives — never edit it manually.
Overview¶
HLL is a domain-specific language for expressing halachic rules in a formal, machine-readable format. It compiles to Answer Set Programming (ASP) for execution by the Clingo solver within the Mistaber reasoning engine.
HLL extends standard ASP with 15 directives for vocabulary registration, world definitions, source provenance, and output control.
Grammar Structure¶
The HLL grammar is defined in mistaber/dsl/grammar.lark:
program := (directive | rule | fact | constraint)*
directive := world_directive | rule_directive | makor_directive | madrega_directive
| declare_directive | sort_directive | subsort_directive | enum_directive
| world_def_directive | endorses_directive | interprets_directive
| interpretation_directive | show_directive | encoding_note_directive
| constraint_directive
rule := head ":-" body "."
fact := head "."
constraint := ":-" body "."
Directives¶
Directives are metadata annotations that provide context for rules, register vocabulary, define worlds, and control output.
Rule Metadata Directives¶
@world(identifier)¶
Specifies the modal world/context for subsequent rules.
Usage: Enables multi-world semantics for representing machloket (disputes). File-level (one per file). When @world and @rule are both set, the emitter auto-generates rule(id)., scope(id, world)., makor(id, ...), madrega(id, level). facts and adds xclingo trace annotations.
Valid Worlds: base, mechaber, rema, gra, sefardi_yo, ashk_mb, ashk_ah
Warning: Multiple @world in one file produces a parser warning; last one wins.
@rule(identifier)¶
Opens a new block-scoped metadata context. Subsequent @makor and @madrega attach to this block. Multiple @rule blocks per file are fully supported.
Block scoping: Each @rule opens a new block that extends until the next @rule or end of file.
Best Practice: Use descriptive names with r_ prefix, following topic-based naming: r_{topic}_{specific}.
Common prefixes:
- r_bb_* — basar bechalav (meat and milk)
- r_dag_* — fish-related
- r_rema_* — Rema-specific (authority prefix for disputes)
Warnings: Duplicate @rule IDs produce a parser warning. @makor/@madrega without a preceding @rule produce orphan warnings.
@makor([source_list])¶
MANDATORY for normative rules. Cites halachic sources. Must appear after a @rule directive (attaches to the current block).
@makor([sa("Yoreh Deah 87:1")])
@makor([sa("YD:87:1"), rambam("Maachalot Asurot 9:1")])
@makor([mishnah("Chullin 8:1"), gemara("Chullin 103b")])
Source Types:
| Type | Full Name | Example |
|------|-----------|---------|
| sa | Shulchan Aruch | sa("YD:87:1") |
| rema | Rema gloss | rema("yd:87:3") |
| rambam | Mishneh Torah | rambam("Maachalot Asurot 9:1") |
| tur | Arba'ah Turim | tur("YD:87") |
| mishnah | Mishnah | mishnah("Chullin 8:1") |
| gemara | Talmud Bavli | gemara("Chullin 103b") |
| tosefta | Tosefta | tosefta("Chullin 8:3") |
| torah | Chumash | torah("shemot:23:19") |
| beit_yosef | Beit Yosef | beit_yosef("yd:87") |
| shach | Siftei Kohen | shach("yd:87:5") |
| taz | Turei Zahav | taz("yd:87:3") |
| yalkut_yosef | Yalkut Yosef | yalkut_yosef("yd:87:3") |
@madrega(level)¶
Specifies the obligation level of the current @rule block. Must appear after a @rule directive.
@madrega(d_oraita) % Biblical
@madrega(d_rabanan) % Rabbinic
@madrega(minhag) % Custom
@madrega(chumra) % Stringency
Strength Ordering: d_oraita > d_rabanan > minhag > chumra
Vocabulary Registration Directives¶
These directives auto-populate base.yaml via the build pipeline. Never edit base.yaml manually.
@sort(name, domain, description)¶
Declares a sort (type) in the ontology vocabulary.
Syntax:
Arguments:
- name — Sort identifier (lowercase)
- domain — Classification domain: physical, normative, classification, temporal, meta
- description — Human-readable description (quoted string)
Optional key-value pairs:
- encoding_note="..." — Rationale for this sort
Examples:
@sort(food, physical, "Physical food items")
@sort(world, normative, "Possible worlds in modal logic")
@sort(food_category, classification, "Types of food (basar, chalav, etc.)")
@sort(integer, classification, "Numeric values")
Build effect: Registered in base.yaml under sorts section. Generates sort-membership atoms in meta.lp.
@subsort(child, parent)¶
Declares a subsort (type hierarchy) relationship.
Arguments:
- child — The child sort (more specific)
- parent — The parent sort (more general)
Examples:
Build effect: Generates subsort(child, parent). fact in compiled output. Enables type hierarchy reasoning in the engine.
@enum(sort_name, [members])¶
Declares an enumeration type with fixed members.
Arguments:
- sort_name — The sort being enumerated (must be declared with @sort first)
- [member1, member2, ...] — List of member constants
Examples:
@enum(food_category, [maakhal, basar, chalav, parve, beheima, chaya, of, dag, mashkeh, tavlin])
@enum(action_type, [achiila, bishul, hanaah, melicha, kavush, netila])
@enum(madrega_type, [d_oraita, d_rabanan, minhag, chumra])
@enum(status_type, [assur, mutar, chiyuv, reshut, mitzvah, sakanah])
@enum(context, [ctx_normal, ctx_hefsed, ctx_shaat_hadchak, ctx_choleh, ctx_pikuach_nefesh])
Build effect: Generates membership facts (food_category(basar)., food_category(chalav)., etc.) in compiled output. Registered in base.yaml under enums section.
@declare(name, [sorts], key=value, ...)¶
Declares a predicate in the ontology vocabulary. This is the primary way to register predicates — they are auto-registered in base.yaml by the build pipeline.
Arguments:
- name — Predicate name (lowercase)
- [sort1, sort2, ...] — List of argument sorts (defines arity)
Required key-value pairs:
- hebrew="..." — Hebrew name
- english="..." — English description
Optional key-value pairs:
- cwa=true|false — Closed World Assumption (default: true)
- derived=true|false — Whether this predicate is derived from rules (default: false)
- mandatory=true|false — Whether instances must be declared (default: false)
- note="..." — Implementation note
Examples:
@declare(is_food, [food], hebrew="מאכל", english="Entity is a food item", cwa=true)
@declare(food_cat, [food, food_category], hebrew="סוג_מאכל", english="Food category", cwa=true)
@declare(status, [food, status_type], hebrew="סטטוס", english="Normative status", cwa=false, derived=true)
@declare(accessible, [world, world], hebrew="נגיש", english="Modal accessibility relation", cwa=false)
Build effect: Registered in base.yaml under predicates section with full metadata. Generates predicate declaration atoms in meta.lp.
When to use: Use @declare for every new predicate you introduce. This ensures it appears in the predicate registry and the dashboard ontology view.
World Definition Directives¶
@world_def(world_name, parent)¶
Defines a Kripke world and its parent(s). This replaces manual world/1 and accessible/2 facts.
Syntax:
Arguments:
- world_name — World identifier
- parent — Single parent world, or [list] for multiple inheritance
Optional key-value pairs:
- era="..." — Historical era (e.g., "rishonim", "acharonim", "contemporary")
- endorsement_mode="active" — World actively endorses inherited positions
- encoding_note="..." — Why this world exists
Examples:
% Single parent
@world_def(mechaber, base)
@world_def(rema, base)
@world_def(sefardi_yo, mechaber)
% Multiple parents (diamond inheritance)
@world_def(ashk_ah, [rema, gra])
% With metadata
@world_def(shach, mechaber, era="acharonim")
Build effect: Generates world(name)., accessible(parent, name)., and world metadata atoms. Must appear at the TOP of world-specific .hll files.
@endorses(world, proposition, key=value, ...)¶
Marks a world's independent affirmation of an inherited position. Use this instead of asserts/2 when a world independently derives the same conclusion as its parent.
Arguments:
- world — The world endorsing
- proposition — The atom being endorsed (can include variables)
Required key-value:
- when="body_condition" — The rule body condition (as a quoted string)
Optional key-value:
- encoding_note="..." — Why this is an endorsement rather than inheritance
Examples:
@endorses(gra, issur(achiila, M, d_oraita),
when="is_beheima_chalav_mixture(M)",
encoding_note="Biur HaGra YD 87:1 cites Chullin 104b-115a directly")
@endorses(gra, issur(bishul, M, d_oraita),
when="is_beheima_chalav_mixture(M)",
encoding_note="Biur HaGra YD 87:1 independent derivation from Torah sources")
Build effect: Generates endorses(world, proposition). fact AND the standard asserts(world, proposition) :- body. rule. The holds_via/3 engine distinguishes endorsed from merely inherited holdings.
When to use vs asserts/2:
- @endorses — World independently reaches the same conclusion as parent (e.g., GRA derives beheima d'oraita from Talmud, same as base world)
- asserts/2 — World has a unique position not in parent
@interprets(commentator, authority)¶
Declares that a commentator interprets a particular authority. Must appear at the top of interpretation files, before any @interpretation directives.
Arguments:
- commentator — The commentator's identifier (e.g., shach, taz)
- authority — The authority being interpreted (e.g., mechaber)
Examples:
Build effect: Generates interprets(commentator, authority). fact. Also registers the commentator in the engine's interpretation resolution system.
@interpretation(commentator, rule_id, action, parameter)¶
Encodes a specific commentator's modification to a rule. Supports nested @makor and @encoding_note sub-directives.
Syntax:
@interpretation(commentator, rule_id, action, parameter)
@makor([source1, source2, ...])
@encoding_note("description")
Arguments:
- commentator — Who is interpreting (e.g., shach, taz)
- rule_id — The rule being interpreted (must be a declared @rule)
- action — Type of modification: adds_condition, removes_condition, expands_scope, narrows_scope
- parameter — The specific modification
Sub-directives (optional, indented):
- @makor([...]) — Source citations for this interpretation
- @encoding_note("...") — Explanation of the interpretation
Examples:
@interprets(shach, mechaber)
@interpretation(shach, r_bb_dag_sakana, adds_condition, cooked_together)
@makor([shach("yd:87:5")])
@encoding_note("Shach clarifies sakana applies specifically when cooked together")
@interprets(taz, mechaber)
@interpretation(taz, r_bb_dag_sakana, removes_condition, sakana)
@makor([taz("yd:87:3")])
@encoding_note("Taz argues this is a scribal error - original text said meat, not dairy")
Build effect: Generates adds_condition(commentator, rule_id, parameter). (or removes_condition, expands_scope, narrows_scope) fact, plus makor(interpretation(...), source). facts and encoding note comments.
Output and Documentation Directives¶
@show(predicate/arity)¶
Controls ASP output visibility. Use @show instead of #show in HLL files.
Examples:
Build effect: Generates #show predicate/arity. in compiled .lp output.
Why use @show instead of #show: The build pipeline processes all @show directives to ensure consistent output filtering across all compiled files.
@encoding_note("description")¶
Attaches a rationale note to the preceding directive or rule. Used for documentation and displayed in the dashboard ontology view.
Usage contexts:
- As a standalone directive after any other directive
- As a nested sub-directive inside @interpretation
- As a key-value pair inside @endorses or @sort (via encoding_note="...")
Examples:
% Standalone
@encoding_note("The three repetitions of lo tevashel teach three prohibitions")
% As kv-pair in @endorses
@endorses(gra, issur(achiila, M, d_oraita),
when="is_beheima_chalav_mixture(M)",
encoding_note="Biur HaGra YD 87:1 cites Chullin 104b-115a directly")
Build effect: Generates encoding_note(entity, "text"). atom in meta.lp. Displayed in the dashboard's ontology sidebar panel.
@constraint(name, category, "description")¶
Declares a named integrity constraint with an identifier, category, and description. The constraint body (:- body.) follows on the next line.
Arguments:
- name — Constraint identifier (lowercase)
- category — Category: integrity, disjointness, completeness
- description — Human-readable description (quoted string)
Examples:
@constraint(no_dual_status, integrity, "Food cannot be both assur and mutar")
:- asserts(W, assur(F)), asserts(W, mutar(F)), world(W), is_food(F).
@constraint(exclusive_food_type, disjointness, "Food cannot be both basar and chalav")
:- food_type(F, basar), food_type(F, chalav).
Build effect: Generates the named constraint with a comment in the compiled output. The constraint body is passed through directly as an ASP integrity constraint.
Facts¶
Facts are unconditional statements about the world.
% Simple facts
is_food(chicken).
vessel(pot1).
mixture(m1).
% Facts with arguments
food_type(chicken, basar).
food_type(milk, chalav).
contains(m1, chicken).
contains(m1, milk).
% Classification facts
madrega(r_basar_bechalav, d_oraita).
Rules¶
Rules define logical implications.
% Basic rule
head :- body.
% Rule with multiple conditions (AND)
forbidden(W, achiila, M, ctx_normal) :-
mixture(M),
mixture_contains_basar(M),
mixture_contains_chalav(M).
% Rule with negation
permitted(W, achiila, F, ctx_normal) :-
food(F),
food_type(F, parve),
not has_issur(F).
Negation¶
Use not prefix for negation-as-failure:
% Permitted if not explicitly forbidden
permitted(W, A, F, C) :-
food(F),
not forbidden(W, A, F, C).
% Classification when not classified
classification_unknown(F) :-
food(F),
not food_type(F, basar),
not food_type(F, chalav),
not food_type(F, parve).
Warning: Negating OWA predicates generates a compiler warning.
Data Types¶
Identifiers (Constants)¶
Lowercase identifiers represent constants:
chicken % Food item
basar % Category
achiila % Action type
ctx_normal % Context
d_oraita % Madrega level
Variables¶
Uppercase identifiers represent variables (bind to any value):
Convention: M = Mixture, F = Food, W = World, A = Action, L = Level (madrega)
Strings¶
Quoted text for source references and descriptions:
Numbers¶
Integer literals:
Compound Terms¶
Nested function-like structures:
Surface Syntax (Shortcuts)¶
HLL provides Hebrew-inspired shortcuts that normalize to canonical predicates.
Food Type Shortcuts¶
| Shortcut | Expands To |
|---|---|
basar(X) |
food_type(X, basar) |
chalav(X) |
food_type(X, chalav) |
parve(X) |
food_type(X, parve) |
beheima(X) |
food_type(X, beheima) |
chaya(X) |
food_type(X, chaya) |
of(X) |
food_type(X, of) |
dag(X) |
food_type(X, dag) |
mashkeh(X) |
food_type(X, mashkeh) |
tavlin(X) |
food_type(X, tavlin) |
Example:
% Surface syntax
basar(chicken).
chalav(milk).
% Normalizes to
food_type(chicken, basar).
food_type(milk, chalav).
Status Shortcuts¶
| Shortcut | Expands To |
|---|---|
issur(A, F) |
forbidden(W, A, F, ctx_normal) |
mutar(A, F) |
permitted(W, A, F, ctx_normal) |
Example:
% Surface syntax
issur(achiila, treif_meat).
% Normalizes to (W from @world or variable)
forbidden(base, achiila, treif_meat, ctx_normal).
Comments¶
Line comments start with %:
% This is a comment
is_food(chicken). % Inline comment
% Multi-line comments use multiple %
% Line 1
% Line 2
Complete Examples¶
Example 1: Classic Directives¶
Using @world/@rule/@makor/@madrega directives (block-scoped — multiple @rule blocks per file supported):
% =============================================================
% Basar B'Chalav (Meat and Milk) - Basic Prohibition
% =============================================================
@world(base)
@rule(r_basar_bechalav_issur)
@makor([sa("Yoreh Deah 87:1"), rambam("Maachalot Asurot 9:1")])
@madrega(d_oraita)
% === Food Declarations ===
is_food(beef).
is_food(chicken).
is_food(milk).
is_food(butter).
is_food(carrot).
% === Food Classifications ===
food_type(beef, beheima). % Domesticated land animal
food_type(chicken, of). % Bird
food_type(milk, chalav). % Dairy
food_type(butter, chalav). % Dairy
food_type(carrot, parve). % Neutral
% === Mixture Declaration ===
mixture(beef_milk_stew).
contains(beef_milk_stew, beef).
contains(beef_milk_stew, milk).
% === Prohibition Rule ===
forbidden(W, achiila, M, ctx_normal) :-
world(W),
mixture(M),
mixture_is_basar_bechalav(M).
% === Permission for Parve ===
permitted(W, achiila, F, ctx_normal) :-
world(W),
food(F),
food_type(F, parve),
not has_issur(F).
Example 2: DSL-First with Vocabulary Registration¶
Using @sort, @declare, @world_def for a multi-rule world file:
% mistaber/dsl/worlds/mechaber.hll
% Mechaber world — DSL-first architecture
@sort(food, physical, "Physical food items")
@sort(food_category, classification, "Types of food")
@enum(food_category, [basar, chalav, parve, beheima, chaya, of, dag])
@declare(is_food, [food], hebrew="מאכל", english="Entity is a food item", cwa=true)
@declare(food_cat, [food, food_category], hebrew="סוג_מאכל", english="Food category", cwa=true)
@world_def(mechaber, base)
% === Rule metadata (explicit ASP predicates) ===
rule(r_bb_beheima_achiila).
makor(r_bb_beheima_achiila, sa("yd:87:1")).
madrega(r_bb_beheima_achiila, d_oraita).
scope(r_bb_beheima_achiila, mechaber).
asserts(mechaber, issur(achiila, M, d_oraita)) :-
is_beheima_chalav_mixture(M).
rule(r_bb_dag_sakana).
makor(r_bb_dag_sakana, sa("yd:87:3")).
scope(r_bb_dag_sakana, mechaber).
asserts(mechaber, sakana(M)) :-
is_dag_chalav_mixture(M).
@show(asserts/2)
@show(holds/2)
Build Pipeline¶
After writing or editing any .hll file in mistaber/dsl/, run:
The build pipeline has 4 phases:
- Collect & Parse — Find
.hllfiles by layer order, parse ASTs, extract directives intoBuildManifest - Registry Merge —
YAMLMergermerges@declare/@sort/@enum/@world_def/@interprets/@interpretationintobase.yaml - Compile — Run each
.hllthrough normalize → type_check → emit, producing.lpfiles inontology/ - Generate Meta — Rebuild
meta.lpfrom updatedbase.yaml
Layer order (each layer may reference sorts/predicates from earlier layers):
schema → base → worlds → engine → interpretations → corpus
Passthrough files: .lp files in dsl/ (e.g., engine/preferences.lp for asprin directives) are copied verbatim to ontology/. A naming conflict (both foo.hll and foo.lp in the same directory) is an error.
Atomic writes: All outputs are written to disk only after all 4 phases succeed. If any phase fails, no files are modified.
Type System¶
Normative Predicates¶
These predicates make halachic determinations:
| Predicate | Arity | Description |
|---|---|---|
forbidden(world, action, food, context) |
4 | Action on food is prohibited |
permitted(world, action, food, context) |
4 | Action on food is allowed |
safek(food) |
1 | Food has doubtful status |
Requirement: All normative predicates require @makor directive.
Classification Predicates¶
| Predicate | Arity | Description |
|---|---|---|
is_food(food) |
1 | Entity is a food item |
food_type(food, category) |
2 | Food belongs to category |
food_cat(food, category) |
2 | Alias for food_type |
mixture(mixture) |
1 | Entity is a mixture |
contains(mixture, food) |
2 | Mixture contains food |
vessel(vessel) |
1 | Entity is a vessel |
Enumerated Values¶
Food Categories: maakhal, basar, chalav, parve, beheima, chaya, of, dag, mashkeh, tavlin
Action Types: achiila (eating), bishul (cooking), hanaah (benefit)
Contexts: ctx_normal, ctx_hefsed, ctx_shaat_hadchak, ctx_choleh, ctx_pikuach_nefesh
Madrega Levels: d_oraita, d_rabanan, minhag, chumra
Type Checker¶
The type checker validates the normalized AST against the predicate registry (mistaber/dsl/vocabulary/base.yaml). Most issues are warnings (not errors) because ASP supports multi-arity predicates and local helpers.
Errors (stop compilation)¶
| Check | Description |
|---|---|
Missing hebrew/english in @declare |
Required fields for predicate registration |
Undefined sort in @declare signature |
Sort must exist in registry or local @sort |
| Arity conflict with existing predicate | Same predicate name, different arity already registered |
Invalid @sort domain |
Must be: physical, normative, classification, temporal, meta |
Invalid @interpretation action |
Must be: adds_condition, removes_condition, restricts_scope, expands_scope |
Self-referential @world_def parent |
World cannot be its own parent |
Invalid @madrega value |
Must match registry's madrega_type enum |
Warnings (compilation continues)¶
| Check | Description |
|---|---|
| Undeclared predicate | Predicate not in registry or local @declare |
| Arity mismatch | Argument count differs from declaration |
| Invalid enum value | Constant not in enumeration |
| OWA predicate with negation | May cause unsafe permissiveness |
Normative rule without @makor |
Source citations expected |
Error Messages¶
Parse Errors¶
Common Causes: - Missing period at end of fact/rule - Unbalanced parentheses - Invalid identifier (starting with uppercase when constant expected)
Normalization Errors¶
Common Causes: - Wrong arity for surface syntax predicates - Using undefined shortcuts
Type Check Errors¶
TypeCheckError: Undeclared predicate: unknwon_pred/2
TypeCheckError: Arity mismatch for food_type: expected 2, got 3
TypeCheckError: Missing @makor directive for normative rule with head: forbidden
TypeCheckError: Undefined sort 'vessel_type' in @declare signature
TypeCheckError: Invalid @sort domain 'spatial' - must be: physical, normative, classification, temporal, meta
Type Check Warnings¶
Meaning: Negating an open-world predicate can lead to unintended conclusions.
Best Practices¶
- Always use @makor: Cite sources for all normative rules
- Use descriptive rule IDs:
r_basar_bechalav_issurnotr1 - Use @declare for new predicates: Register every new predicate via
@declarein.hll - Group related facts: Organize by entity type
- Comment complex rules: Explain halachic reasoning
- Use surface syntax:
basar(X)is clearer thanfood_type(X, basar) - Test incrementally: Compile and test small changes
- Avoid OWA negation: Prefer explicit positive conditions
- Run the build: Always run
python -m mistaber.dsl.buildafter editing.hllfiles