mthom / scryer-prolog

A modern Prolog implementation written mostly in Rust.
BSD 3-Clause "New" or "Revised" License
2.06k stars 123 forks source link

simplify generated libraries map by using `include_str!` #2457

Closed Skgland closed 3 months ago

Skgland commented 3 months ago

Instead of "copy-paste-ing" the content of the prolog libraries into the generated libraries.rs file, construct a path relative to the generated file to the prolog library file and use include_str! to include it.

This means instead of generating a file like (notice the horizontal scroll bar)

Old generated file ~882kb ```rust use indexmap::IndexMap; const LIB_ARITHMETIC: &str = "/** Arithmetic predicates\n\nThese predicates are additions to standard the arithmetic functions provided by `is/2`.\n*/\n\n:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2,\n number_to_rational/3, popcount/2,\n rational_numerator_denominator/3]).\n\n:- use_module(library(charsio), [write_term_to_chars/3]).\n:- use_module(library(error)).\n:- use_module(library(lists), [append/3, member/2]).\n\n\n%% expmod(+Base, +Expo, +Mod, -R).\n%\n% Modular exponentiation. Base, Expo and Mod must be integers.\nexpmod(Base, Expo, Mod, R) :-\n ( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)\n ; member(N, [Base, Expo, Mod]), \\+ integer(N) ->\n type_error(integer, N, expmod/4)\n ; Expo < 0 -> domain_error(not_less_than_zero, Expo, expmod/4)\n ; expmod_(Base, Expo, Mod, 1, R)\n ).\n\nexpmod_(_, _, 1, _, 0) :- !.\nexpmod_(_, 0, _, R, R) :- !.\nexpmod_(Base0, Expo0, Mod, C0, R) :-\n Expo0 /\\ 1 =:= 1,\n C is (C0 * Base0) mod Mod,\n !,\n Expo is Expo0 >> 1,\n Base is (Base0 * Base0) mod Mod,\n expmod_(Base, Expo, Mod, C, R).\nexpmod_(Base0, Expo0, Mod, C, R) :-\n Expo is Expo0 >> 1,\n Base is (Base0 * Base0) mod Mod,\n expmod_(Base, Expo, Mod, C, R).\n\n%% lcm(+A, +B, -Lcm) is det.\n%\n% Calculates the Least common multiple for A and B: the smallest positive integer\n% that is divisible by both A and B.\n% \n% A and B need to be integers.\nlcm(A, B, X) :-\n builtins:must_be_number(A, lcm/2),\n builtins:must_be_number(B, lcm/2),\n ( \\+ integer(A) -> type_error(integer, A, lcm/2)\n ; \\+ integer(B) -> type_error(integer, B, lcm/2)\n ; (A = 0, B = 0) -> X = 0\n ; builtins:can_be_number(X, lcm/2),\n\tX is abs(B) // gcd(A,B) * abs(A)\n ).\n\n%% lsb(+X, -N).\n%\n% True iff N is the least significat bit of integer X\nlsb(X, N) :-\n builtins:must_be_number(X, lsb/2),\n ( \\+ integer(X) -> type_error(integer, X, lsb/2)\n ; X < 1 -> domain_error(not_less_than_one, X, lsb/2)\n ; builtins:can_be_number(N, lsb/2),\n X1 is X /\\ (-X),\n msb_(X1, -1, N)\n ).\n\n%% msb(+X, -N).\n%\n% True iff N is the most significant bit of integer X\nmsb(X, N) :-\n builtins:must_be_number(X, msb/2),\n ( \\+ integer(X) -> type_error(integer, X, msb/2)\n ; X < 1 -> domain_error(not_less_than_one, X, msb/2)\n ; builtins:can_be_number(N, msb/2),\n X1 is X >> 1,\n msb_(X1, 0, N)\n ).\n\nmsb_(0, N, N) :- !.\nmsb_(X, M, N) :-\n X1 is X >> 1,\n M1 is M + 1,\n msb_(X1, M1, N).\n\n%% number_to_rational(+Real, -Fraction).\n%\n% True iff given a number Real, Fraction is the same number represented as a fraction.\nnumber_to_rational(Real, Fraction) :-\n ( var(Real) -> instantiation_error(number_to_rational/2)\n ; integer(Real) -> Fraction is Real rdiv 1\n ; (rational(Real) ; float(Real)) ->\n number_to_rational(1.0e-6, Real, Fraction)\n ; type_error(number, Real, number_to_rational/2)\n ).\n\n% If 0 <= Eps0 <= 1e-16 then the search is for \"infinite\" precision.\nnumber_to_rational(Eps0, Real0, Fraction) :-\n ( var(Eps0) -> instantiation_error(number_to_rational/3)\n ; \\+ number(Eps0) -> type_error(number, Eps0, number_to_rational/3)\n ; Eps0 < 0 -> domain_error(not_less_than_zero, Eps0, number_to_rational/3)\n ; Eps_ is Eps0 rdiv 1,\n rational_numerator_denominator(Eps_, EpsN, EpsD),\n Eps = EpsN/EpsD\n ),\n ( var(Real0) -> instantiation_error(number_to_rational/3)\n ; \\+ number(Real0) -> type_error(number, Eps0, number_to_rational/3)\n ; Real_ is Real0 rdiv 1,\n rational_numerator_denominator(Real_, RealN, RealD),\n Real = RealN/RealD\n ),\n E0/E1 = Eps,\n P0/Q0 = Real,\n ( P0 < 0 -> I1 is -1 + P0 // Q0\n ; I1 is P0 // Q0\n ),\n P1 is P0 mod Q0,\n Q1 = Q0,\n ( P1 =:= 0 -> Fraction is I1 + 0 rdiv 1\n ; Qn1n is max(P1 * E1 - Q1 * E0, 0),\n Qn1d is Q1 * E1,\n Qn1 = Qn1n/Qn1d,\n Qp1n is P1 * E1 + Q1 * E0,\n Qp1d = Qn1d,\n Qp1 = Qp1n/Qp1d,\n stern_brocot_(Qn1, Qp1, 0/1, 1/0, P2/Q2),\n Fraction is I1 + P2 rdiv Q2\n ),\n !.\n\nstern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-\n Fn1 is A + C,\n Fd1 is B + D,\n simplify_fraction(Fn1/Fd1, Fn/Fd),\n S1 is sign(Fn * Qnd - Fd * Qnn),\n S2 is sign(Fn * Qpd - Fd * Qpn),\n ( S1 < 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, Fn/Fd, C/D, Fraction)\n ; S2 > 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, Fn/Fd, Fraction)\n ; Fraction = Fn/Fd\n ).\n\nsimplify_fraction(A0/B0, A/B) :-\n G is gcd(A0, B0),\n A is A0 // G,\n B is B0 // G.\n\n%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator).\n%\n% True iff given a fraction Fraction, Numerator is the numerator of that fraction\n% and Denominator the denominator.\nrational_numerator_denominator(R, N, D) :-\n write_term_to_chars(R, [], Cs),\n append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),\n number_chars(N, Ns),\n number_chars(D, Ds).\n\n%% popcount(+Number, -Bits1).\n%\n% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation\n% of that number has.\npopcount(X, N) :-\n must_be(integer, X),\n '$popcount'(X, N).\n"; /* skipping LIB_ASSOC to LIB_WHEN due to size */ const LIB_XPATH: &str = "/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n The original copyright and licence information is shown below.\n\n Adapted in June 2020 by Markus Triska (triska@metalevel.at)\n Part of Scryer Prolog.\n\n In order to improve portability and reliability of this library, I\n have changed various parts of the original code to make it\n conforming to the Prolog ISO standard. I have removed all\n occurrences of proprietary non-standard ad hoc data types.\n\n In Scryer Prolog, we represent strings as lists of characters.\n This makes them amenable to processing with DCGs and other\n built-in mechanisms such as existing predicates on lists.\n Scryer Prolog represents lists of characters very compactly,\n making it an ideal representation for large amounts of text. It\n fully conforms to the ISO standard and is very portable.\n\n This library is intended to be used in tandem with load_html/3\n from library(sgml), which creates DOM trees from markup documents.\n It can be combined with http_open/3 from library(http/http_open)\n to read such documents directly from network connections.\n\n For instance, to inspect all links to Prolog files (*.pl) on\n Scryer Prolog's project page, we can use:\n\n :- use_module(library(http/http_open)).\n :- use_module(library(sgml)).\n :- use_module(library(xpath)).\n :- use_module(library(dcgs)).\n\n link_to_pl_file(File) :-\n http_open(\"https://github.com/mthom/scryer-prolog\", S, []),\n load_html(stream(S), DOM, []),\n xpath(DOM, //a(@href), File),\n phrase((...,\".pl\"), File).\n\n Yielding:\n\n ?- link_to_pl_file(File).\n %@ File = \"/mthom/scryer-prolog/blob/master/src/lib/dcgs.pl\"\n %@ ; File = \"/mthom/scryer-prolog/blob/master/src/lib/pio.pl\"\n %@ ; File = \"/mthom/scryer-prolog/blob/master/src/lib/tabling.pl\"\n %@ ; ... .\n\n Parts of the original functionality may not yet work. Please\n consider such parts opportunities for improvements, and file\n issues in case you need additional features.\n\n The original copyright information follows.\n\n ======================================================================\n\n\n Author: Jan Wielemaker\n E-mail: J.Wielemaker@vu.nl\n WWW: http://www.swi-prolog.org\n Copyright (c) 2009-2019, University of Amsterdam\n VU University Amsterdam\n CWI, Amsterdam\n All rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n\n 1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n 2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n:- module(xpath,\n [ xpath/3, % +DOM, +Spec, -Value\n xpath_chk/3, % +DOM, +Spec, -Value\n\n op(400, fx, //),\n op(400, fx, /),\n op(200, fy, @)\n ]).\n\n:- use_module(library(lists),[member/2,memberchk/2,reverse/2]).\n:- use_module(library(charsio)).\n:- use_module(library(error)).\n:- use_module(library(dcgs)).\n:- use_module(library(si)).\n\n/** Select nodes in an XML DOM\n\nThe library xpath.pl provides predicates to select nodes from an XML DOM\ntree as produced by `library(sgml)` based on descriptions inspired by the\n[XPath language](http://www.w3.org/TR/xpath).\n\nThe predicate `xpath/3` selects a sub-structure of the DOM\nnon-deterministically based on an XPath-like specification. Not all\nselectors of XPath are implemented, but the ability to mix `xpath/3` calls\nwith arbitrary Prolog code provides a powerful tool for extracting\ninformation from XML parse-trees.\n*/\n\nelement_name(element(Name,_,_), Name).\nelement_attributes(element(_,Attributes,_), Attributes).\nelement_content(element(_,_,Content), Content).\n\n%% xpath_chk(+DOM, +Spec, ?Content) is semidet.\n%\n% Semi-deterministic version of `xpath/3`.\n\nxpath_chk(DOM, Spec, Content) :-\n xpath(DOM, Spec, Content),\n !.\n\n%% xpath(+DOM, +Spec, ?Content) is nondet.\n%\n% Match an element in a DOM structure. The syntax is inspired by\n% XPath, using () rather than [] to select inside an element.\n% First we can construct paths using / and //:\n%\n% - *//Term*\n% Select any node in the DOM matching term.\n%\n% - */Term*\n% Match the root against Term.\n%\n% - *Term*\n% Select the immediate children of the root matching Term.\n%\n% The Terms above are of type _callable_. The functor specifies\n% the element name. The element name `*` refers to any element.\n% The name _self_ refers to the top-element itself and is often\n% used for processing matches of an earlier `xpath/3` query. A term\n% NS:Term refers to an XML name in the namespace NS. Optional\n% arguments specify additional constraints and functions. The\n% arguments are processed from left to right. Defined conditional\n% argument values are:\n%\n% - *`index(?Index)`*\n% True if the element is the Index-th child of its parent,\n% where 1 denotes the first child. Index can be one of:\n%\n% - *`Var`*\n% `Var` is unified with the index of the matched element.\n% - *`last`*\n% True for the last element.\n% - *`last - IntExpr`*\n% True for the last-minus-nth element. For example,\n% `last-1` is the element directly preceding the last one.\n% - *`IntExpr`*\n% True for the element whose index equals `IntExpr`.\n% - *`Integer`*\n% The N-th element with the given name, with 1 denoting the\n% first element. Same as `index(Integer)`.\n% - *`last`*\n% The last element with the given name. Same as\n% `index(last)`.\n% - *`last - IntExpr`*\n% The IntExpr-th element before the last.\n% Same as `index(last-IntExpr)`.\n%\n% Defined function argument values are:\n%\n% - *`self`*\n% Evaluate to the entire element\n% - *`content`*\n% Evaluate to the content of the element (a list)\n% - *`text`*\n% Evaluates to all text from the sub-tree, represented\n% as a list of characters.\n% - *`text(atom)`*\n% Evaluates to all text from the sub-tree as an atom.\n% - *`normalize_space`*\n% As `text`, but uses `normalize_space/2` to normalise\n% white-space in the output\n% - *`number`*\n% Extract an integer or float from the value. Ignores\n% leading and trailing white-space\n% - *`@Attribute`*\n% Evaluates to the value of the given attribute. Attribute\n% can be a compound term. In this case the functor name\n% denotes the element and arguments perform transformations\n% on the attribute value. Defined transformations are:\n%\n% - *`number`*\n% Translate the value into a number using\n% `xsd_number_chars/2`.\n% - *`integer`*\n% As `number`, but subsequently transform the value\n% into an integer using the `round/1` function.\n% - *`float`*\n% As `number`, but subsequently transform the value\n% into a float using the `float/1` function.\n% - *`lower`*\n% Translate the value to lower case, preserving\n% the type.\n% - *`upper`*\n% Translate the value to upper case, preserving\n% the type.\n%\n% In addition, the argument-list can be _conditions_:\n%\n% - *`Left = Right`*\n% Succeeds if the left-hand unifies with the right-hand.\n% If the left-hand side is a function, this is evaluated.\n% The right-hand side is _never_ evaluated, and thus the\n% condition `content = content` defines that the content\n% of the element is the atom `content`.\n% The functions `lower_case` and `upper_case` can be applied\n% to Right (see example below).\n% - *`contains(Haystack, Needle)`*\n% Succeeds if Needle is a sub-list of Haystack.\n% - *`XPath`*\n% Succeeds if XPath matches in the currently selected\n% sub-DOM. For example, the following expression finds\n% an `h3` element inside a `div` element, where the `div`\n% element itself contains an `h2` child with a `strong`\n% child.\n%\n% ```\n% //div(h2/strong)/h3\n% ```\n%\n% This is equivalent to the conjunction of XPath goals below.\n%\n% ```\n% ...,\n% xpath(DOM, //(div), Div),\n% xpath(Div, h2/strong, _),\n% xpath(Div, h3, Result)\n% ```\n%\n% #### Examples\n%\n% Match each table-row in DOM:\n%\n% ```\n% xpath(DOM, //tr, TR)\n% ```\n%\n% Match the last cell of each tablerow in DOM. This example\n% illustrates that a result can be the input of subsequent `xpath/3`\n% queries. Using multiple queries on the intermediate TR term\n% guarantee that all results come from the same table-row:\n%\n% ```\n% xpath(DOM, //tr, TR),\n% xpath(TR, /td(last), TD)\n% ```\n%\n% Match each `href` attribute in an `` element\n%\n% ```\n% xpath(DOM, //a(@href), HREF)\n% ```\n%\n% Suppose we have a table containing rows where each first column\n% is the name of a product with a link to details and the second\n% is the price (a number). The following predicate matches the\n% name, URL and price:\n%\n% ```\n% product(DOM, Name, URL, Price) :-\n% xpath(DOM, //tr, TR),\n% xpath(TR, td(1), C1),\n% xpath(C1, /self(normalize_space), Name),\n% xpath(C1, a(@href), URL),\n% xpath(TR, td(2, number), Price).\n% ```\n%\n% Suppose we want to select books with genre=\"thriller\" from a\n% tree containing elements ``\n%\n% ```\n% thriller(DOM, Book) :-\n% xpath(DOM, //book(@genre=thiller), Book).\n% ```\n%\n% Match the elements `` _and_ ``:\n%\n% ```\n% //table(@align(lower) = center)\n% ```\n%\n% Get the `width` and `height` of a `div` element as a number,\n% and the `div` node itself:\n%\n% ```\n% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div)\n% ```\n%\n% Note that `div` is an infix operator, so parentheses must be\n% used in cases like the following:\n%\n% ```\n% xpath(DOM, //(div), Div)\n% ```\n\nxpath(DOM, Spec, Content) :-\n in_dom(Spec, DOM, Content).\n\nin_dom(//Spec, DOM, Value) :-\n !,\n element_spec(Spec, Name, Modifiers),\n sub_dom(I, Len, Name, E, DOM),\n modifiers(Modifiers, I, Len, E, Value).\nin_dom(/Spec, E, Value) :-\n !,\n element_spec(Spec, Name, Modifiers),\n ( Name == self\n -> true\n ; element_name(E, Name)\n ),\n modifiers(Modifiers, 1, 1, E, Value).\nin_dom(A/B, DOM, Value) :-\n !,\n in_dom(A, DOM, Value0),\n in_dom(B, Value0, Value).\nin_dom(A//B, DOM, Value) :-\n !,\n in_dom(A, DOM, Value0),\n in_dom(//B, Value0, Value).\nin_dom(Spec, element(_, _, Content), Value) :-\n element_spec(Spec, Name, Modifiers),\n count_named_elements(Content, Name, CLen),\n CLen > 0,\n nth_element(N, Name, E, Content),\n modifiers(Modifiers, N, CLen, E, Value).\n\nelement_spec(Var, _, _) :-\n var(Var),\n !,\n instantiation_error(Var).\nelement_spec(NS:Term, NS:Name, Modifiers) :-\n !,\n callable_name_arguments(Term, Name0, Modifiers),\n star(Name0, Name).\nelement_spec(Term, Name, Modifiers) :-\n !,\n callable_name_arguments(Term, Name0, Modifiers),\n star(Name0, Name).\n\ncallable_name_arguments(Atom, Name, Arguments) :-\n atom(Atom),\n !,\n Name = Atom, Arguments = [].\ncallable_name_arguments(Compound, Name, Arguments) :-\n Compound =.. [Name|Arguments].\n\n\nstar(*, _) :- !.\nstar(Name, Name).\n\n\n%! sub_dom(-Index, -Count, +Name, -Sub, +DOM) is nondet.\n%\n% Sub is a node in DOM with Name.\n%\n% @param Count is the total number of nodes in the content\n% list Sub appears that have the same name.\n% @param Index is the 1-based index of Sub of nodes with\n% Name.\n\nsub_dom(1, 1, Name, DOM, DOM) :-\n element_name(DOM, Name0),\n \\+ Name \\= Name0.\nsub_dom(N, Len, Name, E, element(_,_,Content)) :-\n !,\n sub_dom_2(N, Len, Name, E, Content).\nsub_dom(N, Len, Name, E, Content) :-\n list_si(Content),\n sub_dom_2(N, Len, Name, E, Content).\n\nsub_dom_2(N, Len, Name, Element, Content) :-\n ( count_named_elements(Content, Name, Len),\n nth_element(N, Name, Element, Content)\n ; member(element(_,_,C2), Content),\n sub_dom_2(N, Len, Name, Element, C2)\n ).\n\n\n%! count_named_elements(+Content, +Name, -Count) is det.\n%\n% Count is the number of nodes with Name in Content.\n\ncount_named_elements(Content, Name, Count) :-\n count_named_elements(Content, Name, 0, Count).\n\ncount_named_elements([], _, Count, Count).\ncount_named_elements([element(Name,_,_)|T], Name0, C0, C) :-\n \\+ Name \\= Name0,\n !,\n C1 is C0+1,\n count_named_elements(T, Name0, C1, C).\ncount_named_elements([_|T], Name, C0, C) :-\n count_named_elements(T, Name, C0, C).\n\n\n%! nth_element(?N, +Name, -Element, +Content:list) is nondet.\n%\n% True if Element is the N-th element with name in Content.\n\nnth_element(N, Name, Element, Content) :-\n nth_element_(1, N, Name, Element, Content).\n\nnth_element_(I, N, Name, E, [H|T]) :-\n element_name(H, Name0),\n \\+ Name \\= Name0,\n !,\n ( N = I,\n E = H\n ; I2 is I + 1,\n ( nonvar(N), I2 > N\n -> !, fail\n ; true\n ),\n nth_element_(I2, N, Name, E, T)\n ).\nnth_element_(I, N, Name, E, [_|T]) :-\n nth_element_(I, N, Name, E, T).\n\n\n%! modifiers(+Modifiers, +I, +Clen, +DOM, -Value)\n%\n%\n\nmodifiers([], _, _, Value, Value).\nmodifiers([H|T], I, L, Value0, Value) :-\n modifier(H, I, L, Value0, Value1),\n modifiers(T, I, L, Value1, Value).\n\nmodifier(M, _, _, _, _) :-\n var(M),\n !,\n instantiation_error(M).\nmodifier(Index, I, L, Value0, Value) :-\n implicit_index_modifier(Index),\n !,\n Value = Value0,\n index_modifier(Index, I, L).\nmodifier(index(Index), I, L, Value, Value) :-\n !,\n index_modifier(Index, I, L).\nmodifier(Function, _, _, In, Out) :-\n xpath_function(Function),\n !,\n xpath_function(Function, In, Out).\nmodifier(Function, _, _, In, Out) :-\n xpath_condition(Function, In),\n Out = In.\n\nimplicit_index_modifier(I) :-\n integer(I),\n !.\nimplicit_index_modifier(last).\nimplicit_index_modifier(last-_Expr).\n\nindex_modifier(Var, I, _L) :-\n var(Var),\n !,\n Var = I.\nindex_modifier(last, I, L) :-\n !,\n I =:= L.\nindex_modifier(last-Expr, I, L) :-\n !,\n I =:= L-Expr.\nindex_modifier(N, I, _) :-\n N =:= I.\n\nxpath_function(self, DOM, DOM). % self\nxpath_function(content, Element, Value) :- % content\n element_content(Element, Value).\nxpath_function(text, DOM, Text) :- % text\n text_of_dom(DOM, chars, Text).\nxpath_function(text(As), DOM, Text) :- % text(atom)\n text_of_dom(DOM, As, Text).\nxpath_function(normalize_space, DOM, Text) :- % normalize_space\n text_of_dom(DOM, chars, Text0),\n normalize_space(Text0, Text).\nxpath_function(number, DOM, Number) :- % number\n text_of_dom(DOM, chars, Text0),\n normalize_space(Text0, Text),\n catch(xsd_number_chars(Number, Text), _, fail).\nxpath_function(@Name, element(_, Attrs, _), Value) :- % @Name\n ( atom(Name)\n -> memberchk(Name=Value, Attrs)\n ; compound(Name)\n -> Name =.. [AName|AOps],\n memberchk(AName=Value0, Attrs),\n translate_attribute(AOps, Value0, Value)\n ; member(Name=Value, Attrs)\n ).\nxpath_function(quote(Value), _, Value). % quote(Value)\n\nxpath_function(self).\nxpath_function(content).\nxpath_function(text).\nxpath_function(text(_)).\nxpath_function(normalize_space).\nxpath_function(number).\nxpath_function(@_).\nxpath_function(quote(_)).\n\ntranslate_attribute([], Value, Value).\ntranslate_attribute([H|T], Value0, Value) :-\n translate_attr(H, Value0, Value1),\n translate_attribute(T, Value1, Value).\n\ntranslate_attr(number, Value0, Value) :-\n xsd_number_chars(Value, Value0).\ntranslate_attr(integer, Value0, Value) :-\n xsd_number_chars(Value1, Value0),\n Value is round(Value1).\ntranslate_attr(float, Value0, Value) :-\n xsd_number_chars(Value1, Value0),\n Value is float(Value1).\n% The implementation of these translations is left for later.\n% translate_attr(lower, Value0, Value) :- ...\n% translate_attr(upper, Value0, Value) :- ...\n\nxpath_condition(Left = Right, Value) :- % =\n !,\n var_or_function(Left, Value, LeftValue),\n process_equality(LeftValue, Right).\nxpath_condition(contains(Haystack, Needle), Value) :- % contains(Haystack, Needle)\n !,\n val_or_function(Haystack, Value, HaystackValue),\n val_or_function(Needle, Value, NeedleValue),\n ( phrase((...,seq(NeedleValue),...), HaystackValue)\n -> true\n ).\nxpath_condition(Spec, Dom) :-\n in_dom(Spec, Dom, _).\n\n\n%! process_equality(+Left, +Right) is semidet.\n%\n% Provides (very) partial support for XSLT functions that can be\n% applied according to the XPath 2 specification.\n%\n% For example the XPath expression in [1], and the equivalent\n% Prolog expression in [2], would both match the HTML element in\n% [3].\n%\n% ==\n% [1] //table[align=lower-case(center)]\n% [2] //table(@align=lower_case(center))\n% [3]
\n% ==\n\nprocess_equality(Left, Right) :-\n var(Right),\n !,\n Left = Right.\nprocess_equality(Left, lower_case(Right)) :-\n !,\n downcase_atom(Left, Right).\nprocess_equality(Left, upper_case(Right)) :-\n !,\n upcase_atom(Left, Right).\nprocess_equality(Left, Right) :-\n Left = Right.\n\n\nvar_or_function(Arg, _, Arg) :-\n var(Arg),\n !.\nvar_or_function(Func, Value0, Value) :-\n xpath_function(Func),\n !,\n xpath_function(Func, Value0, Value).\nvar_or_function(Value, _, Value).\n\nval_or_function(Arg, _, Arg) :-\n var(Arg),\n !,\n instantiation_error(Arg).\nval_or_function(Func, Value0, Value) :- % TBD\n xpath_function(Func, Value0, Value),\n !.\nval_or_function(Value, _, Value).\n\n\n%! text_of_dom(+DOM, +As, -Text:(chars | atom)) is det.\n%\n% Text is the joined textual content of DOM.\n\ntext_of_dom(DOM, As, Text) :-\n phrase(text_of(DOM), Text0),\n ( As == chars ->\n Text = Text0\n ; As == atom ->\n atom_chars(Text, Text0)\n ; domain_error(As, atom_or_chars, xpath)\n ).\n\ntext_of(element(_,_,Content)) -->\n text_of_list(Content).\ntext_of([]) -->\n [].\ntext_of([H|T]) -->\n text_of(H),\n text_of(T).\n\n\ntext_of_list([]) -->\n [].\ntext_of_list([H|T]) -->\n text_of_1(H),\n text_of_list(T).\n\n\ntext_of_1(element(_,_,Content)) -->\n text_of_list(Content).\ntext_of_1([C|Cs]) --> seq([C|Cs]).\n\n% For now, we use number_chars/2 to parse XML numbers.\n% If the need arises, we can extend this to additional\n% float constants and syntax that may occur in XML files.\n\nxsd_number_chars(Number, Chars) :-\n number_chars(Number, Chars).\n\nnormalize_space(Cs0, Cs) :-\n must_be(chars, Cs0),\n no_leading_whitespace(Cs0, Cs1),\n reverse(Cs1, Cs2),\n no_leading_whitespace(Cs2, Cs3),\n reverse(Cs3, Cs4),\n single_intermediate_space(Cs4, Cs).\n\nno_leading_whitespace([], []).\nno_leading_whitespace([C0|Cs0], Cs) :-\n ( char_type(C0, whitespace) ->\n no_leading_whitespace(Cs0, Cs)\n ; Cs = [C0|Cs0]\n ).\n\nsingle_intermediate_space([], []).\nsingle_intermediate_space([C0|Cs0], [C|Cs]) :-\n ( char_type(C0, whitespace) ->\n no_leading_whitespace(Cs0, Cs1),\n C = ' ',\n single_intermediate_space(Cs1, Cs)\n ; C = C0,\n single_intermediate_space(Cs0, Cs)\n ).\n"; std::thread_local!{ static LIBRARIES: IndexMap<&'static str, &'static str> = { let mut m = IndexMap::new(); m.insert("arithmetic",LIB_ARITHMETIC); m.insert("assoc",LIB_ASSOC); m.insert("atts",LIB_ATTS); m.insert("between",LIB_BETWEEN); m.insert("builtins",LIB_BUILTINS); m.insert("charsio",LIB_CHARSIO); m.insert("clpb",LIB_CLPB); m.insert("clpz",LIB_CLPZ); m.insert("cont",LIB_CONT); m.insert("crypto",LIB_CRYPTO); m.insert("csv",LIB_CSV); m.insert("dcgs",LIB_DCGS); m.insert("debug",LIB_DEBUG); m.insert("diag",LIB_DIAG); m.insert("dif",LIB_DIF); m.insert("error",LIB_ERROR); m.insert("ffi",LIB_FFI); m.insert("files",LIB_FILES); m.insert("format",LIB_FORMAT); m.insert("freeze",LIB_FREEZE); m.insert("gensym",LIB_GENSYM); m.insert("http/http_open",LIB_HTTP_HTTP_OPEN); m.insert("http/http_server",LIB_HTTP_HTTP_SERVER); m.insert("iso_ext",LIB_ISO_EXT); m.insert("lambda",LIB_LAMBDA); m.insert("lists",LIB_LISTS); m.insert("ops_and_meta_predicates",LIB_OPS_AND_META_PREDICATES); m.insert("ordsets",LIB_ORDSETS); m.insert("os",LIB_OS); m.insert("pairs",LIB_PAIRS); m.insert("pio",LIB_PIO); m.insert("queues",LIB_QUEUES); m.insert("random",LIB_RANDOM); m.insert("reif",LIB_REIF); m.insert("serialization/abnf",LIB_SERIALIZATION_ABNF); m.insert("serialization/json",LIB_SERIALIZATION_JSON); m.insert("sgml",LIB_SGML); m.insert("si",LIB_SI); m.insert("simplex",LIB_SIMPLEX); m.insert("sockets",LIB_SOCKETS); m.insert("tabling/batched_worklist",LIB_TABLING_BATCHED_WORKLIST); m.insert("tabling/double_linked_list",LIB_TABLING_DOUBLE_LINKED_LIST); m.insert("tabling/global_worklist",LIB_TABLING_GLOBAL_WORKLIST); m.insert("tabling/table_data_structure",LIB_TABLING_TABLE_DATA_STRUCTURE); m.insert("tabling/table_link_manager",LIB_TABLING_TABLE_LINK_MANAGER); m.insert("tabling/trie",LIB_TABLING_TRIE); m.insert("tabling/wrapper",LIB_TABLING_WRAPPER); m.insert("tabling",LIB_TABLING); m.insert("terms",LIB_TERMS); m.insert("time",LIB_TIME); m.insert("tls",LIB_TLS); m.insert("ugraphs",LIB_UGRAPHS); m.insert("uuid",LIB_UUID); m.insert("wasm",LIB_WASM); m.insert("when",LIB_WHEN); m.insert("xpath",LIB_XPATH); m }; } ```

we generate a file like

New generated file ~6kb ```rust use indexmap::IndexMap; std::thread_local!{ static LIBRARIES: IndexMap<&'static str, &'static str> = { let mut m = IndexMap::new(); m.insert("arithmetic", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\arithmetic.pl")); m.insert("assoc", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\assoc.pl")); m.insert("atts", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\atts.pl")); m.insert("between", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\between.pl")); m.insert("builtins", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\builtins.pl")); m.insert("charsio", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\charsio.pl")); m.insert("clpb", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\clpb.pl")); m.insert("clpz", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\clpz.pl")); m.insert("cont", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\cont.pl")); m.insert("crypto", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\crypto.pl")); m.insert("csv", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\csv.pl")); m.insert("dcgs", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\dcgs.pl")); m.insert("debug", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\debug.pl")); m.insert("diag", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\diag.pl")); m.insert("dif", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\dif.pl")); m.insert("error", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\error.pl")); m.insert("ffi", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\ffi.pl")); m.insert("files", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\files.pl")); m.insert("format", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\format.pl")); m.insert("freeze", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\freeze.pl")); m.insert("gensym", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\gensym.pl")); m.insert("http/http_open", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\http\\http_open.pl")); m.insert("http/http_server", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\http\\http_server.pl")); m.insert("iso_ext", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\iso_ext.pl")); m.insert("lambda", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\lambda.pl")); m.insert("lists", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\lists.pl")); m.insert("ops_and_meta_predicates", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\ops_and_meta_predicates.pl")); m.insert("ordsets", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\ordsets.pl")); m.insert("os", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\os.pl")); m.insert("pairs", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\pairs.pl")); m.insert("pio", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\pio.pl")); m.insert("queues", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\queues.pl")); m.insert("random", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\random.pl")); m.insert("reif", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\reif.pl")); m.insert("serialization/abnf", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\serialization\\abnf.pl")); m.insert("serialization/json", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\serialization\\json.pl")); m.insert("sgml", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\sgml.pl")); m.insert("si", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\si.pl")); m.insert("simplex", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\simplex.pl")); m.insert("sockets", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\sockets.pl")); m.insert("tabling/batched_worklist", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\batched_worklist.pl")); m.insert("tabling/double_linked_list", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\double_linked_list.pl")); m.insert("tabling/global_worklist", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\global_worklist.pl")); m.insert("tabling/table_data_structure", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\table_data_structure.pl")); m.insert("tabling/table_link_manager", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\table_link_manager.pl")); m.insert("tabling/trie", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\trie.pl")); m.insert("tabling/wrapper", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling\\wrapper.pl")); m.insert("tabling", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tabling.pl")); m.insert("terms", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\terms.pl")); m.insert("time", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\time.pl")); m.insert("tls", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\tls.pl")); m.insert("ugraphs", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\ugraphs.pl")); m.insert("uuid", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\uuid.pl")); m.insert("wasm", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\wasm.pl")); m.insert("when", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\when.pl")); m.insert("xpath", include_str!(".\\..\\..\\..\\..\\..\\src\\lib\\xpath.pl")); m }; } ```

If a relative path can be constructed trivially such a path is used, otherwise the build script will fallback to using an absolute path.

Note: wasm CI is expected to fail, see #2446 cherry-picked the relevant commit

UWN commented 3 months ago

Does this then mean that the executable depends on other libraries when being called?

Skgland commented 3 months ago

Does this then mean that the executable depends on other libraries when being called?

include_str! will still result in those files being included in the binary at compile-time, as if the file content was written as a string literal at that position.

Skgland commented 3 months ago

One thing one could also do is move the

use indexmap::IndexMap;

std::thread_local!{
    static LIBRARIES: IndexMap<&'static str, &'static str> = {
        let mut m = IndexMap::new();
        m
    }
}

From the generated file to src/machine/mod.rs and only keep the `m.insert("\<lib name>", insert_str!("\<path/to/lib.pl>")); statements in there. I think that would make it less confusing as the static is the visible in the non-generated code.

Skgland commented 3 months ago
  • made the changes suggested in my previous comment
  • change the thread_local static to a normal static with OnceLock
    • the content doesn't change and is the same independent of thread
    • I would assume accessing a thread local and checking whehter it needs to be initialized to be slower than accessing a static and checking whether the OnceLock needs to be initialized
    • added a feature gate for version 1.80 to use LazyLock instead of OnceLock