Skip to content

Releases: kidoz/dotprolog

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 08:28

Added

  • SWI-Prolog's initialization(Goal, When) directive in Modern: now runs Goal where the
    directive stands, after_load is initialization/1, and main runs Goal after every other
    initialization goal and ends the program with SWI's status — 0 when Goal succeeds, 1 when it fails,
    and 2 when it raises an error, with SWI's message — so SWI scripts that start with
    :- initialization(main, main). run unchanged. Another When is a
    domain_error(initialization_type, When). StrictIso rejects the directive as an extension.

Fixed

  • dotnet prolog run reports an error a directive raises while its file loads, and exits with status
    70, rather than crashing with an unhandled .NET exception.
  • Consulting a file that has directives or initialization goals from a running goal no longer
    crashes RunPendingGoals, and so dotnet prolog run: the file's goals run as soon as the
    consulting goal ends, ahead of the goals still waiting.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 06:40

Added

  • \uXXXX and \UXXXXXXXX escapes in quoted text in Modern, with exactly four and eight
    hexadecimal digits, as SWI-Prolog reads them. A surrogate escape is a syntax error rather than half
    of a pair, and StrictIso rejects both forms, which ISO does not have.
  • In Modern, a symbol or punctuation character outside ASCII reads as a one-character atom, as
    SWI-Prolog reads it: f(😀) is f('😀'), [€, ∀] is a list of two atoms, and writeq/1 writes
    such an atom bare. Like SWI-Prolog 10.1, it never joins its neighbours, so 😀😀 is a syntax error
    and - 😀 is the prefix operator applied to 😀. Brackets and quotation marks outside ASCII, such
    as «, stay syntax errors. Unquoted names follow Unicode's identifier properties: any letter or
    letter number that is not uppercase starts an atom, as in 中文 and Ⅰ, and combining marks and
    superscript digits continue one, as in x́ and x².

Changed

  • In Modern, a character is a Unicode code point rather than a UTF-16 code unit, so a character
    outside the Basic Multilingual Plane is one character with one code: atom_length('😀', N) gives
    1, atom_codes('😀', L) gives [128512], char_code(C, 128512) succeeds, and "😀é" reads as a
    two-element list. The atom and string predicates count, slice, and enumerate by code point —
    atom_length/2, sub_atom/5, atom_concat/3, atom_chars/2, atom_codes/2, char_code/2,
    number_codes/2, atom_number/2's 0'c, string_length/2, sub_string/5, string_code/3,
    split_string/4 — and char_type/2 and code_type/2 classify supplementary characters. A
    surrogate code is not a character code; a host string's unpaired surrogate reads as U+FFFD. The
    ISO predicates keep ISO's representation_error(character_code). StrictIso is unchanged: its
    characters remain UTF-16 code units.
  • In Modern, the reader and writer work in code points too. 0'😀 is 128512, \x…\ escapes reach
    U+10FFFF, and a supplementary letter reads and writes like any other letter — 𝑎bc is an atom,
    𐐀x a variable, and writeq/1 leaves 𝑎bc unquoted. The layout linter's line-length limit
    counts code points.
  • In Modern, streams and format work in code points as well. get_char/1,2, peek_char/1,2,
    get_code/1,2, and peek_code/1,2 read a supplementary character as one character, a peek
    included, and put_char/1,2 and put_code/1,2 write one. format/2,3 counts columns in code
    points, so ~t padding lines up around a supplementary character, and a supplementary fill
    character works. char_conversion/2 maps any character.
  • In Modern, the standard order compares atoms, strings, and functor names by code point, as
    SWI-Prolog does. UTF-16 order had put a supplementary character before U+E000–U+FFFF, so
    msort(['😀', '\xFFFD\'], L) now gives ['\xFFFD\', '😀']; text within the Basic Multilingual
    Plane sorts as before. StrictIso keeps code-unit order.
  • CharacterConversionTable takes and returns character codes as int rather than char, so it
    can hold a supplementary character: Convert(int), Set(int, int), and All() returning
    (int Input, int Output) pairs. Hosts that call it directly need recompiling.
  • char_type/2 and code_type/2 classify characters outside ASCII from the Unicode data, the way
    SWI-Prolog does since 10.1, rather than by rules that put unassigned code points in graph,
    print, and punct. Now an unassigned code point has no type, a format character such as U+200B
    is cntrl, and alpha, upper, and lower follow Unicode's Alphabetic, Uppercase, and Lowercase
    properties, so ª is lower, Ⅰ is upper, and the Devanagari vowel signs are alpha. A number
    such as ² is alnum rather than punct. ASCII characters keep their types.
  • writeq/1 leaves a Modern atom bare when it starts with any letter that is not uppercase, so
    中文 and x́ are no longer quoted.

Fixed

  • format's ~c no longer truncates a code silently: a negative code raises
    format_argument_type(c, Code), and a code past Unicode or in the surrogate range raises
    representation_error(code_point), as SWI-Prolog does.
  • code_type/2's end_of_line holds for vertical tab and form feed, as in SWI-Prolog, and for
    U+0085, U+2028, and U+2029.
  • A quoted atom or string holding a layout character other than the space, such as U+00A0 or U+2028,
    is written with a \xHEX\ escape, because the reader rejects the raw character between quotes;
    such an atom now reads back.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 24 Sep 17:31

Added

  • Lint diagnostics for double-quoted text read one way and used another, judged under the
    double_quotes value in force where the text appears — the mode's or override's initial value,
    moved by set_prolog_flag(double_quotes, _) directives in the file. DPL3011 reports a literal
    passed to a conversion that needs another list kind, such as number_codes(N, "42") while text
    reads as characters. DPL3012 reports text read as characters that reaches, through phrase/2,3
    or a grammar-rule terminal, a grammar in the same file that compares character codes — the case
    that fails silently rather than raising an error. Both help move code-list programs to the
    chars default.
  • The SWI-aligned string predicates read text the way SWI-Prolog does, predicate by predicate,
    through one shared reader. Character and code lists are text for string_length/2,
    split_string/4, string_code/3, atom_string/2, string_to_atom/2, string_chars/2 and
    string_codes/2, number_string/2, the term converters, the format template, and ~s — so
    split_string("a,b", ",", "", P) and term_string(T, "f(a)") work on double-quoted text under
    the chars default — while string_concat/3, sub_string/5, the case mappings, and
    atomic_list_concat/2,3 reject lists as SWI's do. Rejected text raises SWI's errors:
    type_error(text, X), type_error(list, X), type_error(character, E),
    type_error(character_code, E), format_argument_type(Directive, X), and instantiation_error
    for a partial list. A bound output is compared by text, so atom_string(abc, [a,b,c]) succeeds.
    The ISO atom predicates are unchanged, and none of this reaches StrictIso, which has no string
    library.
  • text_to_string/2 and term_string/3, and the codes/2 and chars/2 output sinks for
    with_output_to/2 and format/3, which also accept a stream or alias.

Changed

  • term_to_atom/2 and term_string/2 write the term with quoted(true), as SWI does, rather than
    in canonical form: term_to_atom(1+2, A) gives '1+2'.
  • with_output_to/2 and format/3 check their sink before running the goal or the format, with
    SWI's errors — instantiation_error, type_error(output, Sink), existence_error(stream, Sink) —
    instead of domain_error(output_sink, Sink), and an unbound sink is no longer bound after the goal.
  • number_string/2 reads only strings and character or code lists as its text, and fails rather
    than trimming when the number is surrounded by layout, as SWI does; string_code/3 raises
    type_error(integer, I) and domain_error(not_less_than_zero, I) for a bad index.
  • Numbers are no longer text where SWI does not take them as text: split_string/4,
    string_code/3, and the format template reject them with type_error(text, N), and ~a
    with format_argument_type(a, N).
  • must_be(text, X) raises instantiation_error for a partial or non-ground list.
  • Under the chars default, "" is [], and where lists are text [] is empty text:
    string_length("", N) gives 0 and format("", []) prints nothing. The term converters read []
    as the atom, so read_term_from_atom('[]', T, []) still gives [].

Fixed

  • atomic_list_concat/2,3, upcase_atom/2, downcase_atom/2, and format's ~a accept strings;
    they rejected them with a self-contradictory type_error(atomic, "…").
  • with_output_to(string(S), write([])) gives "[]"; the captured text no longer passes through an
    atom that could be taken for the empty list.
  • string_concat/3 raises type_error(atomic, Culprit) for a bound argument that is not atomic —
    a character list, say — checking left to right as SWI-Prolog does. It raised
    instantiation_error, as if the argument were unbound.

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 21:45

Changed

  • Modern is the default language mode everywhere: new PrologEngine(), new BytecodeProgram(),
    dotnet prolog run and lint, the DotPrologLanguageMode project default, and generated entry
    points, test hosts, and facades. Double-quoted text therefore reads as a list of one-character
    atoms unless a project says otherwise — "abc" = [L|Ls] gives L = a, Ls = [b,c]. ISO/IEC
    13211-1 leaves the initial value implementation defined, so chars is a conforming default,
    and it is the one Scryer, Trealla, ichiban, Flowlog, and Trilog use. StrictIso still starts
    at codes.
  • The ISO conformance case for the initial double_quotes value now checks that it lies in the
    flag's ISO domain rather than pinning codes, since the standard leaves the value to the
    processor.

Removed

  • The Extended language mode. It differed from Modern only in starting double_quotes at
    codes, which the double_quotes=codes flag override already expresses. --mode extended and
    <DotPrologLanguageMode>extended are now rejected as unknown mode names, and
    PrologLanguageMode.Extended no longer exists; PrologLanguageMode.Modern is now the enum's
    first value. dotnet prolog and dotnet prolog lint list modern|strict-iso.

Compatibility

  • A program that relied on the unstated codes default reads double-quoted text differently.
    Mixing the two conventions can fail silently in grammars — a terminal written as "abc" no
    longer matches code input — or raise type errors, as in number_codes(N, "42"). Restore the
    old reading for a whole project with <DotPrologFlags>double_quotes=codes</DotPrologFlags> or
    dotnet prolog run --flag double_quotes=codes, for one file with
    :- set_prolog_flag(double_quotes, codes)., or for an embedding host with
    new PrologFlagOverrides { DoubleQuotes = DoubleQuotesMode.Codes }. A project or host that
    named extended replaces it with that override.

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 18:56

Added

  • Rational numbers: 1r3 literals in the Extended and Modern modes (strict ISO lexing is
    unchanged), rdiv/2 as a 400 yfx operator, exact arithmetic mixing integers and rationals
    with float contagion, rounding functions over rationals, the numerator/1, denominator/1,
    rational/1, and rationalize/1 evaluables, the rational/1 type test, must_be(rational, X),
    number conversion of the NrM spelling, standard-order ranking beside integers, and canonical
    demotion so a denominator of 1 is always an integer. Rationals reach both execution paths and
    the embedding API (PrologRational, PrologInput.Rational), and are verified against
    SWI-Prolog by the differential corpus.

  • Unbounded integers: arithmetic promotes past the 60-bit tagged range to interned big
    integers and normalizes back when results fit, literals of any length read to their exact value
    in source text and runtime term input, and number conversion, formatting (~d, ~D, ~r,
    ~R), ordering, first-argument indexing, the dynamic database, generated C#, and the embedding
    API (PrologBigInteger, PrologInput.Big, PrologMarshal.ToBigInteger) all carry the new
    representation. The bounded flag is now false; max_integer and min_integer keep
    reporting the fixnum promotion threshold. evaluation_error(int_overflow) and
    representation_error(max_integer|min_integer) no longer occur; a shift count or exponent
    whose result would exceed the representation raises resource_error(memory). Verified against
    SWI-Prolog by the differential corpus; the pinned independent ISO corpus now counts 763
    applicable declarations, with five bounded-arithmetic cases correctly gated out by their
    upstream condition(current_prolog_flag(bounded, true)) option.

  • print_message/2 with SWI's message system sized to this machine: the message term is
    translated to Format-Args lines, a user-defined message_hook/3 may intercept them, and what
    remains is written to user_error behind SWI's kind prefixes (ERROR: , Warning: , % ).
    The error(Formal, Context) translations follow SWI 10's wording — including the culprit
    classification of type errors and the special-cased permission and existence messages — and are
    verified against SWI-Prolog by the differential corpus. silent and debug(_) messages are
    not printed; SWI's source-location and thread decorations are not reproduced.

  • A string term type: "..." reads as a distinct interned string under
    set_prolog_flag(double_quotes, string) — an extension value available in the Extended and
    Modern modes and gated out of StrictIso; no mode defaults to it. string/1 is true of a
    string, atom/1 false, atomic/1 true; unification is identity; write/1 emits the bare text
    and writeq/1 the "..." spelling with ISO escapes; the standard order places strings between
    numbers and atoms, as SWI-Prolog 10 sorts them. Strings survive assertz/1, findall/3,
    copy_term/2, nb_setval/2, build-time compiled code, and the embedding API (PrologString),
    and the DotPrologFlags/--flag override surface accepts double_quotes=string.

  • The string library: string_length/2, string_concat/3 with split enumeration,
    atom_string/2, string_to_atom/2, string_chars/2, string_codes/2, number_string/2,
    term_string/2, sub_string/5, split_string/4, string_code/3, string_lower/2,
    string_upper/2; string(S) sinks for format/3 and with_output_to/2; ~s and format
    text accepting strings; and must_be(string, X) with text admitting strings. Verified
    against SWI-Prolog by the differential corpus. The atom predicates deliberately keep ISO's
    type_error(atom, …) for strings.

  • The SWI-aligned library surface: library(error) (must_be/2, is_of_type/2, and the
    error-raising helpers), library(assoc) as AVL trees, library(ordsets), foldl/6,
    findall/4, numbervars/3, atom_to_term/3, and tab/2 — available in the Extended and
    Modern modes, rejected by StrictIso.

  • A SWI compatibility ledger recording, feature by feature,
    which parts of the SWI-Prolog surface the extended modes implement, which are later roadmap
    phases, and which are out of charter.

  • More SWI-aligned predicates: del_assoc/4, transpose_pairs/2, ord_union/2,
    ord_intersection/2, aggregate/3,4 and aggregate_all/4 for the simple specs, variant/2,
    ?=/2, char_type/2 and code_type/2 for bound characters, between/3 with an inf upper
    bound, and the ~r/~R radix format directives.

  • An opt-in differential suite that runs a shared goal corpus against a locally installed
    SWI-Prolog and asserts the outputs agree (DOTPROLOG_RUN_SWI_DIFFERENTIAL_TESTS=1); the corpus
    also runs unconditionally against DotProlog alone.

  • Engine-scoped global variables: nb_setval/2 and nb_getval/2 store and read a detached copy
    that survives backtracking, while b_setval/2 and b_getval/2 hold the live term and the
    assignment is undone when execution backtracks past it, including through catch/3 and at the
    end of the top-level goal.

  • setup_call_cleanup/3 and call_cleanup/2: the cleanup runs exactly once on deterministic
    exit (including the redo that exhausts the alternatives), failure, or a thrown ball, with SWI's
    ball precedence. A surrounding cut that discards pending alternatives does not fire the
    deferred cleanup; once(Goal) gives commit semantics.

  • setarg/3 and nb_setarg/3: destructive argument assignment, undone on backtracking for the
    first form through a value-undo stack interleaved with trail unwinding. nb_setarg/3 accepts
    atomic replacement values only, which the counter idiom needs and heap truncation allows.

  • The occurs_check flag (false, true, error) in the Extended and Modern modes,
    guarding general unification with SWI's occurs_check(Var, Term) error term. Write-mode head
    unification is a documented unchecked window; StrictIso keeps the ISO flag set unchanged.

  • A natural-language processing sample in Modern mode (samples/NaturalLanguage), and the
    DotProlog logo on the project landing pages.

  • nb_current/2, which fails for an unset name and enumerates the set variables when the name
    is unbound; ord_seteq/2 and ord_symdiff/3, completing library(ordsets); and the witnessed
    max/2 and min/2 aggregation specs across aggregate/3,4 and aggregate_all/3,4, which
    compare arithmetically, keep the first solution on a tie, and answer max(Value, Witness) /
    min(Value, Witness). All verified against SWI-Prolog by the differential corpus.

  • The rest of SWI's library(assoc) surface: is_assoc/1, gen_assoc/3, get_assoc/5,
    map_assoc/2,3, del_min_assoc/4, and del_max_assoc/4.

  • Compound aggregation templates: aggregate_all(r(sum(X), count), Goal, r(Sum, Count)) and the
    same shape in aggregate/3,4 and aggregate_all/4, each template argument a spec, with SWI's
    instantiation, type, and domain errors for invalid templates.

  • The ~@ and ~W format directives: ~@ runs a goal once and inserts its output in place —
    a failing or throwing goal still emits the text before it, as SWI's streaming does — and ~W
    writes a term under a write_term/2 option list. write_term itself gains SWI's
    spacing(standard|next_argument) option.

  • portray_clause/1,2 with SWI's listing layout — one goal per line, bracketed disjunction,
    if-then-else, and soft-cut blocks, A, B, ... variable names, _ singletons — byte-identical
    to SWI-Prolog 10 for the covered constructs, pinned by the differential corpus.

  • char_type/2 and code_type/2 enumerate an unbound character over a bound type in code
    order; characters are UTF-16 code units, so Unicode-wide classes cover the BMP.

  • term_size/2: the cells of a detached copy of a term, counting shared subterms once per
    occurrence and raising representation_error(cyclic_term) for a cyclic one.

  • Project-level initial flag overrides: the DotPrologFlags property in a .dplproj (and the
    repeatable --flag option on dotnet prolog run and lint) layers an initial flag value over
    the language mode, e.g. double_quotes=chars while staying in extended mode. The mode remains
    the curated profile; double_quotes (codes, chars, atom) is the first overridable flag.
    Generated code records the initial double_quotes value and refuses to install into an engine
    that starts elsewhere, the same way it already guards the language mode.

Fixed

  • Meta-called control goals now operate on the caller's live terms: the runtime lowering compiles
    only the control skeleton and passes every leaf-goal argument through a register instead of
    rebuilding bound terms, which destructive assignment made observable.
  • A compiled predicate that lowered a meta-called control goal could crash the dispatch loop with
    an index error when appending the lowered bytecode grew the program's code array; the loop now
    refreshes its cached arrays after compiled execution returns.
  • A build-time-compiled source containing :- set_prolog_flag(double_quotes, ...) no longer leaks
    that value into the host engine when its generated Install replays the directive: the flag is
    restored to its entering value afterwards, matching what consulting the same file leaves behind.

Changed

  • An aggregation template that is unknown (aggregate_all(foo, ...)), a variable, or a compound
    mixing specs with other terms now raises the error SWI raises instead of failing silently.
  • char_type/2 and code_type/2 with an unbound character now enumerate instead of raising an
    instantiation error; the error remains when the type is unbound.
  • Modern mode is chartered as the SWI-aligned dialect: an ISO-conforming core, double_quotes
    seeded at chars, and SWI-Prolog as the documented reference for extension behavior.
    -...
Read more

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 23:09

A conformance release. StrictIso now implements the ISO Prolog family rather than its core alone:
ISO/IEC 13211-1:1995 with Technical Corrigenda 1:2007, 2:2012, and 3:2017, the ISO/IEC 13211-2:2000
module system, and the ISO/IEC TS 13211-3:2025 definite clause grammars. Two traceability ledgers
publish the licensed-text audit, one row per normative area with the executable evidence that
covers it, and the audit is what found the defects fixed below.

Modules moved to the standard interface-and-body representation. Extended and Modern keep the
Quintus-family declarations they already accepted, so existing programs are unaffected; strict mode
takes the standard spelling only.

Added

  • ISO/IEC 13211-2 module interfaces and bodies, including export, import, re-export,
    metapredicates, module-local reader state, visible-database reflection, context-sensitive I/O and
    database operations, static clause inspection, and the fixed colon_sets_calling_context flag.
  • Generated-C# and NativeAOT preservation of Part 2 module metadata and retained static clauses.
  • The Corrigendum 3 variable_names/1 write option. It writes the leftmost applicable name for a
    variable and does not bind the option's terms.
  • Traceability ledgers for Part 1 and
    Parts 2 and 3. Each maps a normative area to the
    focused, corpus, generated-C#, cross-path, and NativeAOT evidence that covers it, and paraphrases
    rather than reproduces the licensed publications. The processor-characteristics reference records
    the choices they point at.
  • A pinned Part 1 inventory of the predefined predicates and evaluable functors, so adding or
    removing one has to be a deliberate change rather than a silent drift.
  • Corrigenda cases in the repository conformance corpus, which now stands at 608 and continues to
    run on every execution path.

Changed

  • StrictIso accepts the Part 2 module/1 interface and body/1 representation and rejects the
    Quintus-family module/2, use_module/1,2, and meta_predicate/1 compatibility declarations.

Fixed

  • Runtime-built DCG bodies are expanded into one goal before execution, so | alternatives and
    grammar or embedded cuts have the same behavior as statically translated grammar rules.
  • phrase/3 is steadfast in its third argument, and strict mode treats a runtime-built soft cut as
    an ordinary nonterminal just as it does in static grammar rules.
  • phrase/2 reports the Part 3 list type for an invalid input sequence.
  • Strict mode no longer inherits the extended predefined operators :=, infix ., and $. The
    strict initial table omits them; Extended and Modern keep them, and a strict program can
    still declare its own permitted operators with op/3.
  • op/3 rejects every prefix or postfix declaration of |, not only those below priority 1001.
  • sort/2, sort/4, msort/2, and keysort/2 validate the result argument before unifying with
    it, and keysort/2 checks the pairs on both sides, so an improper result raises its ISO error
    instead of failing.
  • open/4, close/2, and write_term/2,3 reject an unknown option name with the domain error for
    that option type, before the option's value is inspected.

Compatibility

  • Part 2 module text is interfaces and bodies. Source that uses module/2, use_module/1,2, or
    meta_predicate/1 still loads in Extended and Modern, and must move to the standard
    declarations to load under StrictIso.
  • Removing :=, infix ., and $ from the strict initial operator table changes how strict
    source that used them reads. Such source was outside Part 1 to begin with; Extended is
    unchanged.
  • current_prolog_flag/2 enumerates ten flags: the nine from Part 1 and the Part 2
    colon_sets_calling_context. A program that counted the flag set has to count ten.
  • The complete applicable independent Part 1 corpus passes on every execution path. Parts 2 and 3
    rest on licensed traceability plus focused managed, generated-C#, and NativeAOT evidence, not on
    an independent suite. None of this is a claim of SWI-Prolog compatibility.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 08:05

A linting release. dotnet prolog lint reads source and reports on it without consulting it or
running a directive, so a file can be checked without being trusted. It ships with the semantic
rules on by default and an opt-in covington profile carrying the layout guidelines that are
automatically checkable, with every threshold configurable per project.

Added

  • A non-executing dotnet prolog lint command with stable DPL3xxx diagnostics for singleton
    variables and repeated underscore-prefixed singleton markers. It accepts multiple files and the
    shared language modes; warnings remain advisory unless --warnings-as-errors is selected.
  • A reusable PrologLinter API in DotProlog.Compiler, package-consumer coverage for the installed
    tool, and source-linting documentation.
  • An opt-in covington lint profile for spaces, indentation, line and clause length, comma spacing,
    clause and subgoal layout, and trailing whitespace. Numeric layout limits are configurable while
    the default profile remains semantic-only.

Changed

  • The codebase uses implicit types and the current language forms — pattern matching, switch
    expressions, collection expressions, and range and index operators. .editorconfig now states
    those preferences, so the style is expressed where tooling can apply it rather than by convention
    alone. No observable behaviour changes, and the engine benchmarks are unmoved.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 19:10

A language-mode release. A mode now carries the initial ISO flag values that go with its predefined
surface, not just the surface itself, and the new opt-in Modern mode starts double_quotes at
chars — so a double-quoted token reads as a list of one-character atoms, which is the convention
the newer Prolog systems settled on and what makes DCGs over text readable. Extended remains the
default and keeps the ISO initial value codes, so existing programs are unaffected.

Selecting a mode is now done by name everywhere, which replaces the 0.2.0 strict-ISO booleans.

Added

  • A Modern language mode: the extended predefined surface with double_quotes seeded at chars.
    It is available from the embedding constructor, dotnet prolog, generated code, and .dplproj
    builds, and generated source still refuses to install into an engine created in a different mode.
  • dotnet prolog run --mode extended|strict-iso|modern, and the DotPrologLanguageMode property
    for .dplproj projects. Mode names parse case-insensitively from one shared table, so the command
    line and MSBuild cannot drift apart.
  • BytecodeProgram.InitialDoubleQuotes, recording the value the flag was seeded with before any
    source was read.
  • A TextGrammar sample: a .dplproj application in Modern mode that decomposes a string, sums a
    run of numbers, and splits a sentence into words with DCGs written against characters.
  • NativeAOT acceptance coverage for Modern mode, exercising build-time compiled clauses, the
    reader running inside the published binary, a consulted grammar, load-unit flag scope, and the
    mode-mismatch guard.
  • Benchmarks comparing engine construction and consulting across language modes.

Changed

  • double_quotes is scoped to the load unit. A set_prolog_flag(double_quotes, _) directive still
    governs the rest of the file that issued it, and the value in force when that file began is
    restored when it finishes, including when a directive throws. A library that declares its own
    convention can no longer change how whatever is consulted next is read. Deferred
    initialization/1 goals observe the restored value.
  • The bundled bootstrap and standard libraries are read under codes in every mode; they are
    processor implementation, so a host's choice of dialect does not reinterpret them.
  • A program's language mode is validated against an explicit allowlist, so a mode cannot be accepted
    before it has been given its initial flag values.

Removed

  • dotnet prolog run --strict-iso, replaced by --mode strict-iso.
  • The DotPrologStrictIso MSBuild property, replaced by DotPrologLanguageMode. A project setting
    the old property now fails the build rather than silently ignoring it.

Compatibility

  • The initial double_quotes value stays codes in Extended and StrictIso, as ISO/IEC 13211-1
    requires. Modern is an extension and sits outside the conformance claim.
  • Scoping double_quotes to the load unit changes an observable behavior that no previous release
    pinned. Source that relied on a directive outliving its file must set the flag in each file that
    needs it.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 07:58

The second release of DotProlog, and the first with independent conformance evidence: all 768
applicable declarations of the pinned Logtalk 3.101.0 ISO Prolog corpus pass — as consulted
bytecode, as generated C#, across both compiled↔bytecode boundaries, and from a published
NativeAOT executable. The engine gained first-argument clause indexing, the language gained an
opt-in strict ISO mode, and dotnet test now drives Prolog test projects directly.

A changelog section dated 0.1.1 (2026-07-30) was never shipped — no tag, no GitHub release, and
nothing on NuGet.org — so its content is folded in here and this release is 0.2.0.

Added

  • First-argument clause indexing in the bytecode VM. Static multi-clause predicates dispatch
    through a clause table keyed on the first argument, and dynamic predicates skip clauses whose
    first argument could never unify — under the unchanged logical update view. A call with a bound
    first argument that can only reach one clause creates no choice point. Solutions and their order
    are unchanged; build-time generated C# keeps its existing in-order clause chains.
  • The pinned 768-case independent ISO corpus now runs exhaustively through generated C#,
    compiled-to-bytecode, bytecode-to-compiled, and NativeAOT paths in CI.
  • Part 3 DCG semicontexts and Name//Arity declarations for dynamic/1, multifile/1, and
    discontiguous/1.
  • An opt-in strict ISO language mode for embedding, dotnet prolog, generated code, and
    .dplproj builds. It rejects known predefined extensions during source preparation and at
    runtime meta-call and host-binding boundaries.
  • A prolog-test template whose test_* predicates are discovered and run by dotnet test.
  • A clean local-feed consumer gate that installs the packed templates and .NET tool, then builds,
    runs, NativeAOT-publishes, and tests generated projects from a path containing spaces.
  • A uv-managed MkDocs documentation site on Python 3.14 with getting-started, language, .NET
    integration, architecture, and contributing guides, plus strict link validation in CI.
  • The ISO evaluable functors for trigonometry, logarithms, exponentials, rounding, float
    decomposition, and bitwise complement.
  • ISO unify_with_occurs_check/2 with transactional failure and cycle-safe rational-tree
    traversal.
  • ISO current_predicate/1 enumeration for static, dynamic, declared-empty, and runtime-created
    user procedures.

Changed

  • Module preparation now requires one leading module/2 declaration and rejects malformed,
    unexported, or conflicting selected imports.
  • DCG processing uses the Part 3 | operator priority, rejects reserved and predefined grammar
    heads, and applies the specified phrase/2 and terminal-sequence validation.
  • Extended DCGs lower soft cut consistently in static and runtime-loaded source; strict mode treats
    the additional control as an ordinary nonterminal.
  • The repository test suite now uses Microsoft.Testing.Platform v2 through the .NET 10
    global.json test-runner contract, so xUnit and Prolog test projects run together.
  • Release checksums now cover packages, symbols, the SBOM, and all native binaries; the CycloneDX
    tool is pinned, and a missing NuGet API key fails rather than silently skipping publication.
  • Child build processes close standard input, are killed with their process tree if they exceed the
    integration-test limit, and every CI and release job has an explicit timeout.
  • DotProlog.Sdk now carries a real package title and description.
  • Arithmetic now enforces ISO operand signatures, float division result types, bounded-overflow
    errors, and the distinct exceptional cases for zero division, undefined results, and float
    overflow.
  • The repository's ISO-derived conformance corpus now contains 563 passing cases.
  • Integration tests no longer tolerate the zero-tests-ran exit code, child-process output reads
    share the process deadline instead of waiting on a held pipe forever, and a failing release
    verify platform no longer cancels its siblings.

Fixed

  • The repository gates pass again on Windows and Linux: conformance and strict-ISO runner
    rebuilds now use isolated build outputs instead of overwriting the shared test assembly that a
    later no-build step executes, MSBuild worker nodes no longer outlive their step holding task
    assemblies locked, host paths interpolated into quoted atoms escape their backslashes, the
    Logtalk adapter emits the same line endings on every platform, and the packed-package consumer
    gate finds nuget.config on case-sensitive filesystems.
  • The machine protected new environment frames only against the newest choice point, so a frame
    deallocated by last-call optimisation could be overwritten while an older choice point still
    referenced it. Solutions were silently dropped — forall/2 with a compound action and
    findall/3 over goals with in-clause disjunction or negation were the visible cases — and an
    uncaught exception could vanish while backtracking through catch/3. The protection watermark
    is now monotone up the choice-point stack.
  • retract/1 and clause/2 re-read the database generation on every redo, letting clauses
    asserted after the goal started appear mid-enumeration. Both now keep the generation captured at
    the first solution and resume from a stable clause cursor, so asserta/1 between solutions can
    no longer shift or repeat answers.
  • Copying a cyclic term — through copy_term/2, findall/3, assertz/1, or throw/1 — looped
    forever. It now raises catchable representation_error(cyclic_term), and the term writer prints
    cycles as ... instead of hanging.
  • Every meta-call of a control term compiled a fresh clause into the append-only program. Compiled
    control goals are now cached by shape, so long-running loops no longer grow memory without bound.
  • Integer division by zero always raises evaluation_error(zero_divisor); only float 0.0/0.0
    remains undefined.
  • atom_chars/2, atom_codes/2, number_chars/2, and number_codes/2 with a bound first
    argument now convert it and unify with the list instead of raising instantiation_error.
  • Number conversion no longer lets oversized float literals become IEEE infinities or wraps
    oversized radix literals; out-of-range input raises the reader's syntax_error(float_overflow)
    and representation_error(max_integer|min_integer).
  • format/3 accepts real stream handles and aliases, and format(user_error, ...) writes to the
    error stream instead of the current output.
  • phrase/2,3 treated a run-time if-then-else as a plain disjunction and offered the else branch
    as an extra solution.
  • writeq/1 emits the named ISO escapes and delimited hex escapes for control characters, so its
    output always reads back.
  • The build task deletes generated facades whose contract was removed or renamed, dotnet clean
    removes the generated directory, and facade generation reruns when the project file or the set
    of source files changes — not only when a surviving file's timestamp moves.
  • Contract mistakes that previously escaped into raw C# compiler errors — or crashed the build
    task — are reported as DPL2011–DPL2014 diagnostics, and a nondet export with no outputs
    streams one unit value per solution as ADR 0006 promises.
  • Prolog test projects honour run filters, capture user_error into failure reports, and fail a
    looping test after a configurable per-test timeout instead of hanging dotnet test.

Compatibility

  • Requires .NET 10. Packages are platform-neutral; NativeAOT publishing is exercised on Linux,
    Windows, and macOS in CI.
  • Independent evidence, but still no full ISO claim. The 768 applicable declarations of the
    pinned Logtalk 3.101.0 corpus pass on every execution path, and the repository's own 563 cases
    encoded from ISO/IEC 13211-1 pass as well. Licensed-text traceability and the Part 2 module and
    Part 3 grammar completion gates remain open. See COMPATIBILITY.md.
  • The strict ISO mode is opt-in; the default profile is unchanged, so existing programs that
    use predefined extensions keep working.
  • There is still no string type: an atom is the only text term, and the SWI-Prolog string
    predicates are absent rather than aliased to atoms.

Known limitations

  • plc and the foreign-predicate source generator are designed but not implemented.
  • First-argument indexing applies to the bytecode VM; build-time generated C# still tries a
    predicate's clauses in order.
  • A character is a UTF-16 code unit, so atom_length/2 counts a character outside the Basic
    Multilingual Plane as two.

Full Changelog: v0.1.0...v0.2.0

v0.1.0

Choose a tag to compare

@kidoz kidoz released this 27 Jul 20:49

The first release of DotProlog — a Prolog implementation for .NET 10, built as a first-class
SDK language rather than an interpreter you embed. It compiles Prolog to bytecode and runs it on a
virtual machine written in C#, emits no CLR IL, and therefore works unchanged inside a NativeAOT
binary: a published executable can consult a .pl file it has never seen and run it.

Added

  • The language. Facts, rules, unification, backtracking, and the control constructs ,/2,
    ;/2, ->/2, *->/2, \+/1, !/0, and call/1..8, with ISO cut scoping in clause bodies and
    in meta-called goals alike. Exceptions through throw/1 and catch/3, with every engine error
    raised as a catchable error(Formal, Context) term.
  • All solutions. findall/3, bagof/3, setof/3, forall/2, and aggregate_all/3.
  • The database. assertz/1, asserta/1, retract/1, retractall/1, clause/2, abolish/1,
    and :- dynamic, with logical-update-view semantics.
  • A standard library. Text conversion, the list library, sorting, higher-order predicates,
    copy_term/2, term_variables/2, succ/2, plus/3, and format/1,2,3 with column stops.
  • Operators. op/3 and current_op/3, and a term writer that honours them — whatever
    writeq/1 produces reads back as the same term.
  • Grammars. -->/2 translated at load time, with phrase/2,3, {}/1, pushback lists, and
    terminals written as lists or double-quoted strings.
  • Streams. open/3,4, close/1, the current-stream predicates, read/1,2, read_term/2,3,
    character I/O, with_output_to/2, term_to_atom/2, and read_term_from_atom/3.
  • Modules. :- module/2 hides what its export list omits, with use_module/1,2, Module:Goal,
    and :- meta_predicate/1.
  • .dplproj projects through DotProlog.Sdk, an additive MSBuild SDK. A .dpli contract turns
    a Prolog predicate into an idiomatic .NET method, consumable from C#, F#, and Visual Basic.
  • dotnet new templates: prolog-console and prolog-lib.
  • dotnet prolog run, a .NET tool.
  • Testing. DotProlog.Testing runs a project's test_* predicates under
    Microsoft.Testing.Platform, each in a fresh engine.
  • Embedding. PrologEngine.Query/1 enumerates solutions lazily, marshalling each binding into a
    PrologValue; PrologHost binds a predicate once and calls it as Prove, CallOnce, or
    CallAll.

Compatibility

  • Requires .NET 10. Packages are platform-neutral; NativeAOT publishing is exercised on
    Linux, Windows, and macOS.
  • No independent conformance claim. 244 cases encoded from ISO/IEC 13211-1 pass, but they are
    DotProlog's own reading of the standard rather than a third-party suite. Known differences are
    listed in COMPATIBILITY.md.
  • There is no string type: an atom is the only text term, and the SWI-Prolog string predicates are
    absent rather than aliased to atoms.

Known limitations

  • dotnet test cannot yet drive a .dplproj test project; run the test host directly.
  • No binary streams and no stream repositioning.
  • No first-argument clause indexing, so a predicate with many facts is scanned linearly.
  • plc and the foreign-predicate source generator are designed but not implemented.

Full Changelog: https://github.com/kidoz/dotprolog/commits/v0.1.0