Skip to content

luciangreen/Starlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Starlog Documentation

Date: 2025-12-24
Author: luciangreenPlease

Overview

Starlog is a library that allows developers to write Starlog syntax directly inside normal .pl files and run it immediately, without converting files. Starlog is a variant of Prolog that uses a more functional notation, particularly for built-in predicates where the output parameter is represented using an "is" operator rather than as the last argument of a predicate.

What is Starlog?

Starlog is a Prolog variant that uses the notation Result is function(Args) instead of Prolog's function(Args, Result). This makes code more readable when working with transformations and operations that produce a result, as the output variable appears first in the expression rather than at the end of a parameter list.

With Starlog, you can write Starlog syntax directly in your Prolog source files, and the library will automatically expand it into equivalent Prolog goals at load-time using goal_expansion and term_expansion hooks.

Quick Start

Basic Usage in a File

:- use_module(starlog).

% Write Starlog syntax directly in your code
test(Result) :- 
    Result is "Hello" : " " : "World".

% It automatically expands to:
% test(Result) :- 
%     string_concat("Hello", " ", _G1),
%     string_concat(_G1, "World", Result).

Interactive Use in REPL

For direct typing in the REPL, use starlog_call/1:

?- use_module(starlog).
?- starlog_call(A is "x":"y").
A = "xy".

?- starlog_call(L is [1] & [2]).
L = [1, 2].

% Method chains for fluent operations
?- starlog_call(R is reverse([1,2,3]) >> length).
R = 3.

Variable-Bound Starlog Goals

The library automatically detects and handles variable-bound Starlog goals, allowing you to bind a goal to a variable and execute it later:

?- use_module(starlog).
?- A = (C is no_eval(eval(1+1))), A.
C = 2.

?- B = (D is "Hello" : "World"), B.
D = "HelloWorld".

?- E = (F is [1,2] & [3,4]), E.
F = [1, 2, 3, 4].

How it works: When the library detects the pattern Var = (StarlogGoal), Var, it automatically transforms it to Var = (StarlogGoal), starlog_call(Var) during compilation. This ensures that the Starlog expression is properly expanded when executed.

Important notes:

  • This pattern detection works in Prolog files and at the interactive REPL
  • It does NOT work with -g command-line goals (use starlog_call/1 instead)
  • For more complex scenarios where the goal is not executed immediately after binding, use starlog_call/1 explicitly:
% When the goal is stored and executed later
process_goal(Goal) :-
    % ... other code ...
    starlog_call(Goal).

?- MyGoal = (X is "a":"b":"c"), process_goal(MyGoal).
X = "abc".

Saving Results to Variables

The library provides predicates to explicitly save call results to variables:

starlog_call/2 - Execute and Save Result

Execute a Starlog goal and explicitly return the result in a variable:

?- starlog_call(X is "hello":"world", Result).
Result = "helloworld".

?- starlog_call(Y is [1,2] & [3,4], Result).
Result = [1, 2, 3, 4].

?- starlog_call(Z is reverse([1,2,3]), Result).
Result = [3, 2, 1].

starlog_eval/2 - Evaluate Expression

Evaluate a Starlog expression and return the result:

?- starlog_eval("x":"y", Result).
Result = "xy".

?- starlog_eval(1+1, Result).
Result = 2.

?- starlog_eval([a] & [b,c], Result).
Result = [a, b, c].

starlog_no_eval/2 - Preserve Expression

Preserve a Starlog expression without evaluation:

?- starlog_no_eval(1+1, Result).
Result = 1+1.

?- starlog_no_eval("hello":"world", Result).
Result = "hello":"world".

?- starlog_no_eval([a] & [b], Result).
Result = [a] & [b].

These predicates are useful for:

  • Explicitly capturing results for later use
  • Building symbolic expressions as data structures
  • Template systems and meta-programming
  • Functional composition patterns

Starlog Syntax

Important: By default, Starlog evaluates all expressions. The eval() function is only needed when you want to force evaluation inside no_eval() contexts.

The library supports the following Starlog patterns:

Special Operators

Special operators are used for common operations:

  • String concatenation: C is (A : B) - expands to string_concat(A, B, C)
  • List append: C is (A & B) - expands to append(A, B, C)
  • Atom concatenation: C is (A • B) - expands to atom_concat(A, B, C)
  • List to term: T is ..=([f,0,1]) - expands to T =.. [f,0,1] (creates term f(0,1))
  • Term to list: L is =..(f(0,1)) - expands to f(0,1) =.. L (creates list [f,0,1])

Expression Evaluation and Preservation

By default, Starlog evaluates all expressions. This is the standard behavior:

A is 1+1              % A = 2 (arithmetic evaluated)
B is "x":"y"          % B = "xy" (concatenation evaluated)
C is [1] & [2]        % C = [1,2] (append evaluated)

Explicit Evaluation with eval

The eval/1 function explicitly marks an expression for evaluation. While this is the default behavior, it becomes useful when you need to force evaluation inside no_eval contexts:

% Normal evaluation (eval is implicit/default)
A is 1+1              % A = 2

% Explicit eval (same result)
B is eval(1+1)        % B = 2

% Force evaluation inside no_eval
C is no_eval(eval(1+1))         % C = 2 (inner eval forces evaluation)
D is no_eval("x" : eval("y":"z"))  % D = "x":"yz" (nested eval)

Expression Preservation with no_eval

The no_eval/1 function prevents evaluation of expressions, preserving them as data:

% Preserve arithmetic expressions
A is no_eval(1+1)          % A = 1+1 (not 2)

% Preserve Starlog operators
B is no_eval("x":"y")      % B = "x":"y" (not "xy")

% Preserve complex expressions
C is no_eval((1+2)*(3+4))  % C = (1+2)*(3+4) (not 21)

% Use eval to selectively evaluate parts
D is no_eval(eval(1+1) + 3)  % D = 2 + 3 (only 1+1 is evaluated)

This is useful for:

  • Storing formulas as data structures
  • Manipulating expressions symbolically
  • Lazy evaluation patterns
  • Template systems
  • Selectively evaluating parts of expressions with eval

Value-Returning Builtins

Many Prolog predicates can be written in Starlog syntax:

  • Length is string_length("hello") → string_length("hello", Length)
  • Rev is reverse([1,2,3]) → reverse([1,2,3], Rev)
  • Upper is string_upper("hello") → string_upper("hello", Upper)

Nested Expressions

Nested expressions are automatically decomposed into sequential goals. Functions can now be nested on both the left-hand side (LHS) and right-hand side (RHS) of is:

% Nested functions on RHS (original support)
X is reverse(reverse([1,2,3]))      % X = [1,2,3]
X is length(reverse([1,2,3]))       % X = 3

% Nested functions on LHS (new support)
reverse(reverse([1,2,3])) is X      % X = [1,2,3]
length(reverse([1,2,3])) is X       % X = 3

% Nested functions on both sides
reverse(A) is reverse([1,2,3])      % A = [1,2,3]

% Mixed with concat operators
reverse([1,2]&[3,4]) is X           % X = [4,3,2,1]
(reverse([1,2])&reverse([3,4])) is X   % X = [2,1,4,3]

% Deep nesting
reverse(reverse(reverse([1,2,3]))) is X   % X = [3,2,1]
reverse(reverse([1]&[2])&reverse([3]&[4])) is X   % X = [3,4,1,2]

% Traditional nested string/atom concat
E is (A:(B:(D • F))) • C

% Expands to:
atom_concat(D, F, _G1),
string_concat(B, _G1, _G2),
string_concat(A, _G2, _G3),
atom_concat(_G3, C, E).

All combinations and configurations of nested functions with Starlog operators are supported.

Method Chain Syntax

In addition to nested function calls, Starlog supports a fluent method chain syntax using the >> operator. This allows you to chain operations in a left-to-right, more readable style:

% Traditional nested calls
X is d(a(b(1,c)))

% Method chain syntax - reads left to right
X is b(1,c) >> a >> d

% Both produce the same result

The >> operator creates a pipeline where each operation is applied to the result of the previous operation:

% With built-in functions
X is reverse([1,2,3]) >> length        % X = 3
X is sort([3,1,2]) >> reverse          % X = [3,2,1]

% With Starlog operators  
X is ([1,2]&[3,4]) >> reverse >> length    % X = 4
X is ("hello":"world") >> string_length     % X = 10

% With user-defined predicates
% Given: wrap_a(X, a(X)), wrap_d(X, d(X))
X is b(1,c) >> wrap_a >> wrap_d        % X = d(a(b(1,c)))

% Complex chains
X is 5 >> add_one >> double            % X = 12 (if add_one and double are defined)

% Mixed with other Starlog features
X is (reverse([1,2])&reverse([3,4])) >> flatten  % X = [2,1,4,3]

Key features:

  • Left-to-right reading: More natural flow compared to nested calls
  • Composable: Works with all value-returning builtins and user predicates
  • Equivalent semantics: b(1,c) >> a >> d is exactly equivalent to d(a(b(1,c)))
  • Mixes with Starlog operators: Can use :, &, • in the base expression

Note: Due to SWI-Prolog's dictionary syntax using ., we use >> for method chaining instead of .

Arithmetic is Preserved

Standard arithmetic expressions are left unchanged:

X is 1+2      % Treated as arithmetic, not Starlog
X is Y * 5    % Also arithmetic

When is/2 is Treated as Starlog vs Arithmetic

The library distinguishes between Starlog and arithmetic based on the right-hand side:

  • Starlog: Out is (A : B), Out is func(Args), Out is (A & B), Out is (A • B), Out is no_eval(Expr), Out is eval(Expr)
  • Arithmetic: Out is 1+2, Out is X*Y, Out is sqrt(N) (when sqrt/1 is arithmetic)
  • Dual Starlog expressions: (Expr1) is (Expr2) where both sides are Starlog expressions - see below

Note: eval is the default behavior for Starlog expressions. You only need to use eval() explicitly when forcing evaluation inside no_eval() contexts.

Dual Starlog Expressions

When both the left-hand side and right-hand side of is/2 are Starlog expressions, the library compiles both expressions and unifies their results. This enables powerful pattern matching and equation solving:

% Find A and B such that [1]&A equals B&[2]
?- ([1] & A) is (B & [2]).
A = [2],
B = [1].

% Nested dual expressions
?- (([1] & [2]) & A) is (B & [3]).
A = [3],
B = [1, 2].

% String concatenation dual expressions
?- ("hello" : "world") is ("hello" : "world").
true.

% Nested concatenation with multiple variables
?- (A : a : C) is (b : a : c).
A = b,
C = c.

% List with nested concatenation
?- [A:a:C] is [x:a:y].
A = x,
C = y.

This feature is particularly useful for:

  • Solving equations with Starlog operators
  • Pattern matching with variable substitution
  • Constraint-based programming with Starlog syntax
  • Verifying equivalence of complex expressions
  • Extracting multiple variables from nested concatenation patterns

Supported Built-in Predicates

The library supports automatic expansion for many built-in predicates. Here are the main categories:

String and Atom Operations

  • string_length/1, atom_length/1
  • string_chars/1, atom_chars/1
  • string_upper/1, string_lower/1
  • atom_codes/1, string_codes/1
  • term_string/1, term_to_atom/1
  • sub_string/4, sub_atom/4
  • And more...

List Operations

  • reverse/1, sort/1, flatten/1
  • length/1, member/1
  • intersection/2, union/2, subtract/2
  • nth0/2, nth1/2, last/1
  • min_list/1, max_list/1, sum_list/1
  • And more...

Math Operations

  • ceiling/1, floor/1, round/1, truncate/1
  • abs/1, sign/1, sqrt/1
  • sin/1, cos/1, tan/1
  • log/1, exp/1
  • And more...

Other Operations

  • findall/2
  • find/2 - Execute a goal with cut and collect first solution
  • term_variables/1
  • split_string/3
  • date/0, get_time/0 (nullary operations)
  • And more...

For a complete list, see starlog_registry.pl.

Helper Predicates

find/2 - Find First Solution

The find/2 predicate executes a goal with a cut to get only the first solution. It follows the Starlog pattern of using is for value-returning operations:

% Starlog syntax (recommended):
Result is find(Template, Goal)

% Equivalent to: findall(Template, (Goal, !), [Result])
% with automatic evaluation of Starlog expressions in Template

?- use_module(starlog).
?- starlog_call(Result is find(A, starlog_call([A:a] is [a:a]))).
Result = a.

?- starlog_call(Result is find(X, member(X, [1,2,3]))).
Result = 1.  % Only first solution due to cut

?- starlog_call(Result is find(R, starlog_call(R is "hello":"world"))).
Result = "helloworld".

?- starlog_call(Result is find(L, starlog_call(L is [1,2]&[3,4]))).
Result = [1, 2, 3, 4].

Note: The old 3-argument form find(Template, Goal, Result) is still supported for backward compatibility.

Solving for Multiple Variables with Nested Concatenation

The find/2 predicate can solve for multiple variables simultaneously in nested concatenation patterns:

% Find two variables in a three-way concatenation
?- starlog_call(Result is find([A,C], starlog_call([A:a:C] is [a:a:c]))).
Result = [a, c].

% Find two variables with a separator
?- starlog_call(Result is find([A,C], starlog_call([A:"_":C] is ["hello":"_":"world"]))).
Result = ["hello", "world"].

% Find three variables in a five-way pattern
?- starlog_call(Result is find([A,C,E], starlog_call([A:"-":C:"-":E] is ["x":"-":"y":"-":"z"]))).
Result = ["x", "y", "z"].

% Works with atom concatenation too
?- starlog_call(Result is find([A,C], starlog_call([A•x•C] is [y•x•z]))).
Result = [y, z].

Template Evaluation

Feature: When the template contains Starlog operators like :, •, or &, find/2 automatically evaluates the template after the goal succeeds:

% Template with string concatenation - automatically evaluated
?- starlog_call(Result is find(A:C, starlog_call([A:d:a:C] is [a:d:a:c]))).
Result = "ac".  % Concatenated result, not [a, c]

% Template with atom concatenation - automatically evaluated
?- starlog_call(Result is find(A•C, starlog_call([A•x•C] is [y•x•z]))).
Result = yz.  % Concatenated atom, not [y, z]

% Multiple variables in template
?- starlog_call(Result is find(A:"-":C, starlog_call([A:"-":C] is ["x":"-":"y"]))).
Result = "x-y".  % Fully evaluated concatenation

% List template - NOT evaluated (backward compatible)
?- starlog_call(Result is find([A,C], starlog_call([A:a:C] is [a:a:c]))).
Result = [a, c].  % List of values, not concatenated

This is useful for:

  • Finding the first solution to a goal
  • Executing Starlog expressions and capturing the result
  • Solving equations with only the first solution
  • Pattern matching with automatic cut behavior
  • Extracting multiple values from concatenation patterns
  • Collecting computed results directly from templates

Extending Starlog

You can register your own value-returning builtins:

% Register foo/2 as a value builtin
% Now: Out is foo(A,B) expands to foo(A,B,Out)
?- starlog_register_value_builtin(foo, 2, foo).

% Register with a different Prolog predicate name
?- starlog_register_value_builtin(bar, 1, my_bar).
% Now: Out is bar(X) expands to my_bar(X,Out)

Outputting Starlog Code

The library provides features to convert Prolog code back to Starlog notation with human-friendly variable names (A, B, C, A1, B1, etc.) and pretty printing with proper indentation for nested calls and logical control structures.

Pretty Printed Output

The output automatically formats code with indentation for:

  • Nested calls (findall, and, or, not)
  • Logical control structures (if-then, if-then-else)
  • Complex nested expressions

Example:

?- starlog_output_code(findall(X, (member(X, [1,2,3]), X > 1), Result)).
A is 
  findall(
    B,
    (
      member(B,[1,2,3]),
      B>1
    )
  )

Output Code for a Goal

Use starlog_output_code/1 to convert a Prolog goal to Starlog notation:

?- starlog_output_code(string_concat("x", "y", C),D).
D = (A is "x":"y").

?- starlog_output_code(append([1,2], [3,4], L),D).
D = (A is [1, 2]&[3, 4]).

?- starlog_output_code(reverse([1,2,3], R),D).
D = (A is reverse([1, 2, 3])).

This is useful for:

  • Converting existing Prolog code to Starlog notation
  • Understanding how Prolog predicates map to Starlog syntax
  • Generating Starlog code programmatically

Maximal Compression

Use starlog_output_code/3 or starlog_output_file/3 with the compress(true) option to maximally compress Starlog code by nesting expressions:

?- starlog_output_code((string_concat("hello", " ", T1), 
                        string_concat(T1, "world", T2)), D, [compress(true)]).
D = (A is "hello":" ":"world").

?- starlog_output_code((append([1],[2],L1), reverse(L1,L2)), D, [compress(true)]).
D = (A is reverse([1]&[2])).

The compression algorithm:

  • Nests expressions where an intermediate variable is used only once
  • Preserves variables that are used multiple times
  • Excludes if-then clauses, logical control structures (or, not)
  • Excludes calls without an output that is another's input

Example comparison:

% Without compression (default)
?- starlog_output_code((string_concat("a","b",T1), 
                        string_concat("c","d",T2), 
                        string_concat(T1,T2,T3)), D).
D is (A is "a":"b",B is "c":"d",C is A:B)

% With compression
?- starlog_output_code((string_concat("a","b",T1), 
                        string_concat("c","d",T2), 
                        string_concat(T1,T2,T3)), D, [compress(true)]).
D is (A is "a":"b":("c":"d"))

Output Code for a File

Use starlog_output_file/1 to convert an entire Prolog file to Starlog notation:

?- starlog_output_file('my_program.pl').
% Starlog code output for file: my_program.pl

greet(A,B,C):-D is "Hello, ":A,E is D:" ",C is E:B.
combine_lists(A,B,C,D):-C is A&B,D is reverse(C).
...

Or with maximal compression using starlog_output_file/3:

?- starlog_output_file('my_program.pl', user_output, [compress(true)]).
% Starlog code output for file: my_program.pl

greet(A,B,C):-C is "Hello, ":A:" ":B.
combine_lists(A,B,C,D):-D is reverse(A&B).
...

Or write to a file using starlog_output_file/2 or starlog_output_file/3:

?- open('output.pl', write, Stream),
   starlog_output_file('input.pl', Stream),
   close(Stream).

% With compression
?- open('output.pl', write, Stream),
   starlog_output_file('input.pl', Stream, [compress(true)]),
   close(Stream).

The output uses human-friendly variable names (A, B, C, ..., Z, A1, B1, ...) making the code more readable.

Controlling eval() and no_eval() Output

By default, when outputting Starlog code, the eval() and no_eval() wrappers are stripped from the output, showing only their contents. This makes the output cleaner and more concise, since evaluation is the default behavior in Starlog anyway.

You can control this behavior using the output_eval and output_no_eval options:

% Default behavior - strips both eval() and no_eval()
?- starlog_output_code(A is no_eval(1+1)).
A is 1+1

?- starlog_output_code(B is eval("x":"y")).
B is "xy"
% Note: eval without outputting eval evaluates the expression.

% Keep no_eval() wrappers
?- starlog_output_code(A is no_eval(1+1), _, [output_no_eval(true)]).
A is no_eval(1+1)

% Keep eval() wrappers
?- starlog_output_code(B is eval("x":"y"), _, [output_eval(true)]).
B is eval("x":"y")
% Note: eval with outputting eval doesn't evaluate the expression.

% Keep both wrappers
?- starlog_output_code(C is no_eval(eval(1+1)), _, [output_eval(true), output_no_eval(true)]).
C is no_eval(eval(1+1))

The same options work with starlog_output_file/3:

% Default - strips both eval() and no_eval()
?- starlog_output_file('input.pl', user_output).

% Keep no_eval() wrappers
?- starlog_output_file('input.pl', user_output, [output_no_eval(true)]).

% Keep both eval() and no_eval() wrappers
?- starlog_output_file('input.pl', user_output, [output_eval(true), output_no_eval(true)]).

Options summary:

  • output_eval(false) - Strip eval() wrappers (default)
  • output_eval(true) - Keep eval() wrappers in output
  • output_no_eval(false) - Strip no_eval() wrappers (default)
  • output_no_eval(true) - Keep no_eval() wrappers in output

These options can be combined with compress(true) for maximum control over the output format.

Converting Starlog to Prolog

The library also provides features to convert Starlog code back to standard Prolog with maximal decompression, using human-friendly variable names (A, B, C, A1, B1, etc.).

Convert Individual Goals

Use starlog_to_prolog_code/1 to convert a Starlog goal to standard Prolog:

?- starlog_to_prolog_code(A is "hello":"world").
string_concat("hello","world",A)

?- starlog_to_prolog_code(A is [1,2]&[3,4]).
append([1,2],[3,4],A)

?- starlog_to_prolog_code(A is reverse([1,2]&[3,4])).
append([1,2],[3,4],A),reverse(A,B)

This is useful for:

  • Converting Starlog code to standard Prolog
  • Understanding how Starlog operators map to Prolog predicates
  • Generating Prolog code from Starlog notation

Maximal Decompression

The conversion automatically decompresses nested expressions into sequential goals:

% Nested Starlog expression
?- starlog_to_prolog_code(A is "hello":" ":"world",D).
D = (string_concat("hello", " ", A), string_concat(A, "world", B)).

% Deeply nested expression
?- starlog_to_prolog_code(Result is reverse([1]&[2]&[3]),D).
D = (append([1], [2], A), append(A, [3], B), reverse(B, C)).

The decompression algorithm:

  • Flattens nested expressions into sequential goals
  • Maintains proper variable dependencies and execution order
  • Uses human-friendly variable names (A, B, C, etc.)
  • Preserves semantics of the original Starlog code

Output Code to a Variable

?- starlog_to_prolog_code(A is "hello":"world", Code, [print(false)]).
Code = string_concat("hello", "world", A).

Convert Entire Files

Use starlog_to_prolog_file/1 to convert an entire Starlog file to standard Prolog:

?- starlog_to_prolog_file('my_starlog_code.pl').
% Prolog code output for file: my_starlog_code.pl

greet(A,B):-string_concat("Hello, ",A,B).
combine_and_reverse(A,B,C):-append(A,B,D),reverse(D,C).
...

Or write to a file using starlog_to_prolog_file/2:

?- open('output_prolog.pl', write, Stream),
   starlog_to_prolog_file('input_starlog.pl', Stream),
   close(Stream).

Bidirectional Conversion

The library supports bidirectional conversion between Prolog and Starlog:

% Prolog → Starlog (with compression)
?- starlog_output_code((string_concat("hello"," ",T1), 
                        string_concat(T1,"world",T2)), D, [compress(true)]).
D is (A is "hello":" ":"world")

?- starlog_output_code(string_concat(A, B, C), Code).
Code = (A is B:C).

% Starlog → Prolog (with decompression)
?- starlog_to_prolog_code(A is "hello":" ":"world",D).
D is (string_concat("hello"," ",A),string_concat(A,"world",B))

% Convert chain to nested
?- starlog_output_code(
    (X is reverse([1,2,3]) >> length),
    Code,
    [output_style(nested_calls), print(true)]
).
% Output: A is length(reverse([1,2,3]))

% Convert nested to chain
?- starlog_output_code(
    (X is length(reverse(sort([3,1,2])))),
    Code,
    [output_style(method_chaining), print(true)]
).
% Output: A is sort([3,1,2])>>reverse>>length

% With operators
?- starlog_output_code(
    (X is ([1,2]&[3,4]) >> reverse >> length),
    Code,
    [output_style(nested_calls), print(true)]
).
% Output: A is length(reverse([1,2]&[3,4]))

Debugging

Enable debug output to see expansions:

?- starlog_set_debug(true).
?- starlog_call(A is "x":"y").
Expanding goal: A is "x":"y"
Expanded to: string_concat("x","y",A)
A = "xy".

Examples

Example 1: String Processing

:- use_module(starlog).

process_name(First, Last, Full) :-
    Full is First : " " : Last.

?- process_name("John", "Doe", Name).
Name = "John Doe".

Example 2: List Manipulation

:- use_module(starlog).

combine_and_reverse(A, B, Result) :-
    Combined is A & B,
    Result is reverse(Combined).

?- combine_and_reverse([1,2], [3,4], R).
R = [4, 3, 2, 1].

Example 3: Nested Expressions

:- use_module(starlog).

complex_concat(A, B, C, Result) :-
    Result is (A:B) : C.

?- complex_concat("Hello", " ", "World", R).
R = "Hello World".

Example 4: Expression Preservation with no_eval

:- use_module(starlog).

% Store a formula without evaluating it
store_formula(Formula) :-
    F is no_eval(Formula),
    format('Stored formula: ~w~n', [F]).

?- store_formula(x*2 + y).
Stored formula: x*2+y

% Preserve arithmetic expressions
?- A is no_eval(1+1).
A = 1+1.  % Not evaluated to 2

% Preserve Starlog operators
?- B is no_eval("hello":"world").
B = "hello":"world".  % Not concatenated

Example 5: Selective Evaluation with eval inside no_eval

:- use_module(starlog).

% Evaluate is the default behavior
?- A is 1+1.
A = 2.

% Explicit eval (same as default)
?- B is eval(1+1).
B = 2.

% Force evaluation inside no_eval context
?- C is no_eval(eval(1+1)).
C = 2.  % Inner expression evaluated despite no_eval

% Nested evaluation in preserved structures
?- D is no_eval("Result: " : eval("x":"y")).
D = "Result:":"xy".  % Only eval(...) part is evaluated

% Multiple eval expressions
?- E is no_eval([eval(1+1), eval(2+2), 5]).
E = [2, 4, 5].  % Only eval parts are evaluated

% Complex nested case with lists
?- F is no_eval(eval([1] & [2])).
F = [1, 2].  % List append is evaluated

Example 6: Variable-Bound Starlog Goals

:- use_module(starlog).

% Basic pattern: bind a goal to a variable and execute it
?- A = (C is no_eval(eval(1+1))), A.
C = 2.

% Bind and execute string concatenation
?- B = (D is "Hello" : " " : "World"), B.
D = "Hello World".

% Bind and execute list operations
?- E = (F is reverse([1,2] & [3,4])), E.
F = [4, 3, 2, 1].

% Multiple variable-bound goals
?- G = (H is "first":"part"), G, I = (J is "second":"part"), I.
H = "firstpart",
J = "secondpart".

% For non-immediate execution, use starlog_call/1
process_goal(Goal) :-
    write('Processing: '),
    starlog_call(Goal),
    write('Done!'), nl.

?- MyGoal = (X is "delayed":"execution"), process_goal(MyGoal).
Processing: Done!
X = "delayedexecution".

This feature enables:

  • Dynamic goal construction and execution
  • Storing Starlog expressions as first-class values
  • Meta-programming patterns with Starlog syntax
  • Deferred execution of Starlog expressions

Example 7: Dual Starlog Expressions for Equation Solving

:- use_module(starlog).

% Solve for variables in list append equations
solve_list_equation :-
    ([1] & A) is (B & [2]),
    format('A = ~w, B = ~w~n', [A, B]).

?- solve_list_equation.
A = [2], B = [1]

% Verify equivalence of expressions
verify_concatenation :-
    ("hello" : " " : "world") is ("hello " : "world"),
    write('Expressions are equivalent!'), nl.

?- verify_concatenation.
Expressions are equivalent!

% Nested dual expressions for complex patterns
nested_pattern :-
    (([1] & [2]) & A) is (B & [3]),
    format('A = ~w, B = ~w~n', [A, B]).

?- nested_pattern.
A = [3], B = [1, 2]

This feature is particularly useful for:

  • Solving equations with Starlog operators
  • Pattern matching with variable substitution
  • Constraint-based programming
  • Verifying expression equivalence

Example 8: Operator and Value Selection with Disjunction

:- use_module(starlog).

% Find operator Op where 3 = 1 Op 2
find_operator :-
    findall(Op, (member(Op, [+, -, *, /]), Expr =.. [Op, 1, 2], 3 is Expr), Ops),
    format('Operators that satisfy 3 = 1 Op 2: ~w~n', [Ops]).

?- find_operator.
Operators that satisfy 3 = 1 Op 2: [+]

% Find value A where 3 = 1 + A
find_value :-
    findall(A, (member(A, [1, 2, 3]), 3 is 1+A), As),
    format('Values that satisfy 3 = 1 + A: ~w~n', [As]).

?- find_value.
Values that satisfy 3 = 1 + A: [2]

% Find multiple solutions
find_multiple_operators :-
    findall(Op, (member(Op, [+, -, *, /]), Expr =.. [Op, 2, 3], 6 is Expr), Ops),
    format('Operators that satisfy 6 = 2 Op 3: ~w~n', [Ops]).

?- find_multiple_operators.
Operators that satisfy 6 = 2 Op 3: [*]

This pattern (from the problem statement "3 is 1(A is +;-;/)2") demonstrates:

  • Operator selection from a set of choices
  • Value selection from a set of candidates
  • Using findall with arithmetic constraints
  • Solving mathematical equations by testing alternatives

Example 9: Term Manipulation with Univ Operators

:- use_module(starlog).

% Convert list to term
create_term(List, Term) :-
    Term is ..=(List).

?- create_term([f,0,1], T).
T = f(0,1).

% Convert term to list
term_to_list(Term, List) :-
    List is =..(Term).

?- term_to_list(foo(a,b,c), L).
L = [foo, a, b, c].

% Roundtrip conversion
?- T is ..=([bar,x,y]), L is =..(T).
T = bar(x, y),
L = [bar, x, y].

Example 10: Algebraic Equation Solver

The library includes an algebra solver that can solve equations by applying operations to both sides to isolate the variable:

:- use_module(algebra_solver).

% Solve the equation from the problem statement
% (Y+5)/2 = 2
?- solve_equation((Y+5)/2 is 2, Y, Solution).
Y = -1,
Solution = -1.

% Simple linear equations
?- solve_equation(3*X+5 is 20, X, Solution).
X = 5,
Solution = 5.

% Division equations
?- solve_equation((X-4)/2 is 3, X, Solution).
X = 10,
Solution = 10.

% Quadratic equations (powers)
?- solve_equation(X**2 is 25, X, Solution).
X = 5.0,
Solution = 5.0.

% Variable on either side
?- solve_equation(10 is 2*X, X, Solution).
X = 5,
Solution = 5.

% Complex nested equations
?- solve_equation(((X+2)*3-1)/2 is 7, X, Solution).
X = 3,
Solution = 3.

The solver supports:

  • Basic arithmetic operations: +, -, *, /
  • Power operations: **
  • Nested expressions
  • Variable on either side of the equation
  • Automatically applies inverse operations to isolate the variable

This is useful for:

  • Solving algebraic equations programmatically
  • Educational tools for teaching algebra
  • Mathematical modeling and constraint solving
  • Automated problem solving

Example 11: Gaussian Elimination for Systems of Linear Equations

The library includes a Gaussian elimination solver for systems of linear equations:

:- use_module(gaussian_elimination).

% Solve a system of equations
% 2x + y + 3z = 10
% 3x + 0y + 1.5z = 4.5
% 0x + 0y + z = 1
?- solve_system([[2, 1, 3, 10], [3, 0, 1.5, 4.5], [0, 0, 1, 1]], Solution).
Solution = [1.0, 5.0, 1].

% With solution type detection
?- solve_system([[2, 1, 5], [1, -1, 1]], Solution, Type).
Solution = [2.0, 1.0],
Type = unique.

% Solve in Starlog syntax
?- Solution is solve_system([[2, 1, 5], [1, -1, 1]]).
Solution = [2.0, 1.0].

The Gaussian elimination solver supports:

  • Systems with unique solutions
  • Underdetermined systems (infinite solutions)
  • Detection of inconsistent systems (no solution)
  • Partial pivoting for numerical stability
  • Integration with Starlog syntax

For more details, see GAUSSIAN_ELIMINATION.md.

This is useful for:

  • Solving systems of linear equations programmatically
  • Educational tools for teaching linear algebra
  • Scientific computing and numerical methods
  • Engineering and physics applications

Installation

  1. Clone this repository
  2. Load the library in your Prolog code:
:- use_module('/path/to/starlog').

Or add it to your SWI-Prolog library path.

Testing

Run the test suite:

cd tests
swipl -s test_basic.pl
swipl -s test_nested.pl  
swipl -s test_arithmetic_is.pl
swipl -s test_mixed_prolog_starlog.pl
swipl -s test_no_eval.pl
swipl -s test_eval.pl
swipl -s test_algebra_solver.pl

Requirements

  • SWI-Prolog 8.x or higher

Repository Structure

starlog/
  README.md                           # This file
  LICENSE                             # BSD-3 license
  Requirements.txt                    # Original specification
  Requirements.md                     # Additional requirements
  starlog.pl               # Main library module
  starlog_expand.pl                  # Expander: compile Starlog -> Prolog goals
  starlog_registry.pl                # Builtin mapping registry + extension hooks
  algebra_solver.pl                  # Algebraic equation solver
  demo_output_feature.pl             # Demo: Prolog to Starlog conversion
  demo_starlog_to_prolog.pl          # Demo: Starlog to Prolog conversion
  demo_algebra_solver.pl             # Demo: Algebra solver
  tests/
    test_basic.pl                    # Basic functionality tests
    test_nested.pl                   # Nested expression tests
    test_arithmetic_is.pl            # Arithmetic preservation tests
    test_mixed_prolog_starlog.pl     # Mixed Prolog/Starlog tests
    test_starlog_to_prolog.pl        # Starlog to Prolog conversion tests
    test_starlog_to_prolog_file.pl   # File conversion tests
    test_algebra_solver.pl           # Algebra solver tests

License

BSD-3-Clause License

Articles and Videos

Articles and Videos

Acknowledgments

Original Starlog-Prolog converter concept by luciangreenPlease. This library implements a new approach using SWI-Prolog's goal and term expansion mechanisms to allow direct entry of Starlog syntax in Prolog files.

About

Allows entering Starlog in Prolog

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •